A Clever Method to Call .NET from PowerShell

I came across a very interesting PowerShell technique today courtesy of Guy Bachar’s TechNet Gallery Script Exchange Online Audit Log Report (HTML Format).

This PowerShell script contains .NET code which calls the local Windows Credential Manager.  Pheww!  Could come in handy.

….

.

######################################################################
# API to load credential from generic credential store
######################################################################
$CredManager = @”
using System;
using System.Net;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace SyncSiteMailbox
{
     /// <summary>
     /// </summary>
     public class CredManager
     {
         [DllImport(“advapi32.dll”, SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = “CredReadW”)]
         public static extern bool CredRead([MarshalAs(UnmanagedType.LPWStr)] string target, [MarshalAs(UnmanagedType.I4)] CRED_TYPE type, UInt32 flags, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CredentialMarshaler))] out Credential cred);
        
         [DllImport(“advapi32.dll”, SetLastError = true, CharSet = CharSet.Auto, EntryPoint = “CredFree”)]
         public static extern void CredFree(IntPtr buffer);

        /// <summary>
         /// </summary>
         public enum CRED_TYPE : uint
         {
             /// <summary>
             /// </summary>
             CRED_TYPE_GENERIC = 1,

            /// <summary>
             /// </summary>
             CRED_TYPE_DOMAIN_PASSWORD = 2,

            /// <summary>
             /// </summary>
             CRED_TYPE_DOMAIN_CERTIFICATE = 3,

            /// <summary>
             /// </summary>
             CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 4,

            /// <summary>
             /// </summary>
             CRED_TYPE_MAXIMUM = 5, // Maximum supported cred type
         }
        
         /// <summary>
         /// </summary>
         public enum CRED_PERSIST : uint
         {
             /// <summary>
             /// </summary>
             CRED_PERSIST_SESSION = 1,

            /// <summary>
             /// </summary>
             CRED_PERSIST_LOCAL_MACHINE = 2,

            /// <summary>
             /// </summary>
             CRED_PERSIST_ENTERPRISE = 3
         }
        
         /// <summary>
         /// </summary>
         [StructLayout(LayoutKind.Sequential)]
         internal struct CREDENTIAL
         {
             internal UInt32 flags;
             internal CRED_TYPE type;
             [MarshalAs(UnmanagedType.LPWStr)]
             internal string targetName;
             [MarshalAs(UnmanagedType.LPWStr)]
             internal string comment;
             internal System.Runtime.InteropServices.ComTypes.FILETIME lastWritten;
             internal UInt32 credentialBlobSize;
             internal IntPtr credentialBlob;
             internal CRED_PERSIST persist;
             internal UInt32 attributeCount;
             internal IntPtr credAttribute;
             [MarshalAs(UnmanagedType.LPWStr)]
             internal string targetAlias;
             [MarshalAs(UnmanagedType.LPWStr)]
             internal string userName;
         }
        
         /// <summary>
         /// Credential
         /// </summary>
         public class Credential
         {
             private SecureString secureString = null;

            /// <summary>
             /// </summary>
             internal Credential(CREDENTIAL cred)
             {
                 this.credential = cred;
                 unsafe
                 {
                     this.secureString = new SecureString((char*)this.credential.credentialBlob.ToPointer(), (int)this.credential.credentialBlobSize / sizeof(char));
                 }               
             }

            /// <summary>
             /// </summary>
             public string UserName
             {
                 get { return this.credential.userName; }
             }

            /// <summary>
             /// </summary>
             public SecureString Password
             {
                 get
                 {
                     return this.secureString;
                 }
             }

            /// <summary>
             /// </summary>
             internal CREDENTIAL Struct
             {
                 get { return this.credential; }
             }

            private CREDENTIAL credential;
         }

        internal class CredentialMarshaler : ICustomMarshaler
         {
             public void CleanUpManagedData(object ManagedObj)
             {
                 // Nothing to do since all data can be garbage collected.
             }

            public void CleanUpNativeData(IntPtr pNativeData)
             {
                 if (pNativeData == IntPtr.Zero)
                 {
                     return;
                 }
                 CredFree(pNativeData);
             }

            public int GetNativeDataSize()
             {
                 return Marshal.SizeOf(typeof(CREDENTIAL));
             }

            public IntPtr MarshalManagedToNative(object obj)
             {
                 Credential cred = (Credential)obj;
                 if (cred == null)
                 {
                     return IntPtr.Zero;
                 }

                IntPtr nativeData = Marshal.AllocCoTaskMem(this.GetNativeDataSize());
                 Marshal.StructureToPtr(cred.Struct, nativeData, false);

                return nativeData;
             }

            public object MarshalNativeToManaged(IntPtr pNativeData)
             {
                 if (pNativeData == IntPtr.Zero)
                 {
                     return null;
                 }
                 CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(pNativeData, typeof(CREDENTIAL));
                 return new Credential(cred);
             }

            public static ICustomMarshaler GetInstance(string cookie)
             {
                 return new CredentialMarshaler();
             }
         }   
       

        /// <summary>
         /// ReadCredential
         /// </summary>
         /// <param name=”credentialKey”></param>
         /// <returns></returns>
         public static NetworkCredential ReadCredential(string credentialKey)
         {
             Credential credential;
             CredRead(credentialKey, CRED_TYPE.CRED_TYPE_GENERIC, 0, out credential);
             return credential != null ? new NetworkCredential(credential.UserName, credential.Password) : null;
         }
     }
}
“@

######################################################################
# Load credential APIs
######################################################################
$CredManagerType = $null
try
{
     $CredManagerType = [SyncSiteMailbox.CredManager]
}
catch [Exception]
{
}

if($null -eq $CredManagerType)
{
     $compilerParameters = New-Object -TypeName System.CodeDom.Compiler.CompilerParameters
     $compilerParameters.CompilerOptions = “/unsafe”
     [void]$compilerParameters.ReferencedAssemblies.Add(“System.dll”)
     Add-Type $CredManager -CompilerParameters $compilerParameters
     $CredManagerType = [SyncSiteMailbox.CredManager]
}

######################################################################
# Load tenant credential from generic credential store
######################################################################
$TenantCredential = $null #Primary

#Write-Host “Load tenant credential is from generic credential store.”
try
{
     $credential = $CredManagerType::ReadCredential($TenantCredentialKey)
     if ($null -ne $credential)
     {
         $TenantCredential = New-Object System.Management.Automation.PSCredential ($credential.UserName, $credential.SecurePassword);
     }
}
catch [Exception]
{
     $TenantCredential = $null
     $errorMessage = $_.Exception.Message
     Write-Host “Tenant credential cannot be loaded correctly: $errorMessage.”
}

if ($null -eq $TenantCredential)
{
     Write-Host “Tenant credential cannot be loaded please ensure you have configured in credential manager correctly.”
}

Leave a Reply