Skip to content

Instantly share code, notes, and snippets.

@jborean93
Created February 2, 2022 00:44
Show Gist options
  • Save jborean93/85641e43877a969fc297ebcfab4b3680 to your computer and use it in GitHub Desktop.
Save jborean93/85641e43877a969fc297ebcfab4b3680 to your computer and use it in GitHub Desktop.
APIS that wrap the LMAccess Net*ServiceAccount APIS for Managed Service Accounts
Add-Type -Namespace LmAccess -Name Native -MemberDefinition @'
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "NetAddServiceAccount")]
private static extern int NativeNetAddServiceAccount(
IntPtr ServerName,
string AccountName,
IntPtr Password,
AddServiceFlags Flags);
/// <summary>Add a sMSA or gMSA to the current host.</summary>
/// <param name="accountName">The name of the MSA to install.</param>
/// <param name="flags">Optional flags for the install - currently these do not apply to gMSA accounts.</param>
public static void NetAddServiceAccount(string accountName, AddServiceFlags flags)
{
int errCode = NativeNetAddServiceAccount(IntPtr.Zero, accountName, IntPtr.Zero, flags);
if (errCode != 0)
throw new System.ComponentModel.Win32Exception(RtlNtStatusToDosError(errCode));
}
[DllImport("Netapi32.dll")]
private static extern int NetApiBufferFree(
IntPtr Buffer);
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "NetEnumerateServiceAccounts")]
private static extern int NativeNetEnumerateServiceAccounts(
IntPtr ServerName,
int Flags,
out int AccountsCount,
out IntPtr Accounts);
/// <summary>Enumerates all the managed service accounts installed on the host.</summary>
/// <returns>The names of all the managed service accounts that are installed.</returns>
public static string[] NetEnumerateServiceAccounts()
{
int count = 0;
IntPtr buffer = IntPtr.Zero;
int errCode = NativeNetEnumerateServiceAccounts(IntPtr.Zero, 0, out count, out buffer);
if (errCode != 0)
throw new System.ComponentModel.Win32Exception(RtlNtStatusToDosError(errCode));
try
{
if (count == 0)
{
return Array.Empty<string>();
}
IntPtr[] strptrs = new IntPtr[count];
Marshal.Copy(buffer, strptrs, 0, count);
string[] accounts = new string[count];
for (int i = 0; i < strptrs.Length; i++)
{
accounts[i] = Marshal.PtrToStringUni(strptrs[i]);
}
return accounts;
}
finally
{
NetApiBufferFree(buffer);
}
}
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "NetIsServiceAccount")]
private static extern int NativeNetIsServiceAccount(
IntPtr ServerName,
string AccountName,
out bool IsService);
/// <summary>Tests whether a managed service account is installed on the current server.</summary>
/// <param name="accountName">The name of the MSA to test.</param>
/// <returns>True if it is installed otherwise False</returns>
public static bool NetIsServiceAccount(string accountName)
{
bool isService = false;
int errCode = NativeNetIsServiceAccount(IntPtr.Zero, accountName, out isService);
if (errCode != 0)
throw new System.ComponentModel.Win32Exception(RtlNtStatusToDosError(errCode));
return isService;
}
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "NetQueryServiceAccount")]
private static extern int NativeNetQueryServiceAccount(
IntPtr ServerName,
string AccountName,
int InfoLevel,
out IntPtr Buffer);
/// <summary>Gets information about the specified managed service account.</summary>
/// <param name="accountName">The service account to query.</param>
/// <returns>The state of the service account specified.</returns>
public static MsaInfoState NetQueryServiceAccount(string accountName)
{
IntPtr buffer = IntPtr.Zero;
int errCode = NativeNetQueryServiceAccount(IntPtr.Zero, accountName, 0, out buffer);
if (errCode != 0)
throw new System.ComponentModel.Win32Exception(RtlNtStatusToDosError(errCode));
try
{
return (MsaInfoState)Marshal.ReadInt32(buffer);
}
finally
{
NetApiBufferFree(buffer);
}
}
[DllImport("Netapi32.dll", CharSet = CharSet.Unicode, EntryPoint = "NetRemoveServiceAccount")]
private static extern int NativeNetRemoveServiceAccount(
IntPtr ServerName,
string AccountName,
RemoveServiceFlags Flags);
/// <summary>Removes a sMSA or gMSA from the current host.</summary>
/// <param name="accountName">The name of the MSA account to remove.</param>
/// <param name="flags">Optional flags for the removal - currently these do not apply to gMSA accounts.</param>
public static void NetRemoveServiceAccount(string accountName, RemoveServiceFlags flags)
{
int errCode = NativeNetRemoveServiceAccount(IntPtr.Zero, accountName, flags);
if (errCode != 0)
throw new System.ComponentModel.Win32Exception(RtlNtStatusToDosError(errCode));
}
[DllImport("Ntdll.dll")]
private static extern int RtlNtStatusToDosError(
int Status);
[Flags]
public enum AddServiceFlags : uint
{
None = 0x00000000,
/// <summary>Do not create the sMSA in AD, only link to the host if it already exists.</summary>
LinkToHostOnly = 0x00000001,
}
[Flags]
public enum RemoveServiceFlags : uint
{
None = 0x00000000,
/// <summary>Only unlink the sMSA from the host, do not delete in AD.</summary>
UnlinkFromHostOnly = 0x00000001,
}
public enum MsaInfoState
{
/// <summary>The account does not exist or the gMSA is not installed.</summary>
NotExist = 1,
/// <summary>The account exists but is not an MSA.</summary>
NotService,
/// <summary>The credential cannot be fetched from AD.</summary>
CannotInstall,
/// <summary>The sMSA can be installed.</summary>
CanInstall,
/// <summary>The MSA account is installed.</summary>
Installed,
}
'@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment