Category Archives: Operation

Create batch Service Accounts using Power Shell

Once a while, you will have a need to setup a new environment with new service accounts for all your apps/services. We all know, over time, those accounts in the current environments are added gradually one at a time. It would be nice if we can have a script to populate them and have a way to know what they are even.

So here you go, I created these Power Shell scripts to read the settings from a XML file and then create them into the domain AD. So from this point on, you can just maintain these XML config files for you various environments/domains. Easy to maintain, audit, and create a new one.

First, this is the XML file looks like. You can sure add more nodes/attributes to it to fit your needs.

<Env id="UAT">
  <LDAPServer>MyLDAPServerName</LDAPServer>
  <OU>OU=Service Accounts,OU=MyOU,dc=uat,dc=MyDomain</OU>
  <Accounts>
    <Account id="ServiceAccount1">
      <Description>Service Account for Service1</Description>
      <sAMAccountName>ServiceAccount1</sAMAccountName>
      <UserPrincipalName>ServiceAccount1@MyCompany.com</UserPrincipalName>
      <Password>MyPassword1</Password>
    </Account>
    <Account id="ServiceAccount2">
      <Description>Service Account for Service2</Description>
      <sAMAccountName>ServiceAccount2</sAMAccountName>
      <UserPrincipalName>ServiceAccount1@MyCompany.com</UserPrincipalName>
      <Password>MyPassword2</Password>
    </Account>
  <Accounts>
</Env>

Second, let’s create a PS file to do the creation

function SetupAServiceAccount ($ldapServer, $ou, $accountName, $sAMAccountName, $userPrincipalName, $accountPassword, $accountDescription)
{
	### There is a limitation of maximum length (20) of sAMAccountname. So we have to watch the error for it
	$errorDeviceNotFunctioning = 'Exception calling "SetInfo" with "0" argument(s): "A device attached to the system is not functioning.'

	### Error for object (account) already exist. We will ignore it and proceed to update the account
	$errorObjectAlreadyExist = 'Exception calling "SetInfo" with "0" argument(s): "The object already exists.'

	### Initialize variables
$errorString = ""

	### Get the AD object
	$objOU=[adsi]"LDAP://$ldapServer/$ou"

	### Try to Create a new account
	$objUser = $objOU.Create("user", "cn=$accountName")
	$objUser.Put("sAMAccountName", $sAMAccountName)
	try
	{
	   $objUser.SetInfo()
	   $objUser=""
	   write-host "*** Account Creation Success: $accountName ***"
	}
	catch
	{
	   [string] $errorString = $_
	}
	if ($errorString.Contains($errorDeviceNotFunctioning) -eq $True)
	{
		write-host "%%% Creation Unsuccess: $accountName - $errorString %%%"
		write-host "%%% The length of sAMAccountName cannot be more than 20 %%%"
	}
	elseif ($errorString.Contains($errorObjectAlreadyExist) -eq $True -Or $errorString -eq "")
	{
		if ($errorString.Contains($errorObjectAlreadyExist) -eq $True)
		{
			write-host "*** Account Alreday Exist, will update it instead ***"
		}
		if ($errorString -eq "")
		{
			write-host "*** Prepare to update ***"
		}
$objUser=[adsi]"LDAP://$ldapServer/cn=$accountName,$ou"
		$objUser.SetPassword($accountPassword)
		$objUser.Put("pwdLastSet", -1)
		$objUser.Put("userPrincipalName", $userPrincipalName)
		$objUser.Put("description", $accountDescription)
		$objUser.Put("userAccountControl", 66048)
		try
		{
			$objUser.SetInfo()
			write-host "*** Update Success: $accountName ***"
$objUser = ""
			& 'C:\Program Files (x86)\Windows Resource Kits\Tools\ntrights.exe' +r SeServiceLogonRight -u $userPrincipalName
		}
		catch
		{
			write-host "%%% Update Unsuccess: $accountName - $_ %%%"
		}
	}
	else
	{
		write-host "%%% Creation Unsuccess : $accountName - $errorString %%%"
	}
}

Note: Here I used a tool called notrights.exe to grant the LogonAsService permission to those newly created accounts. It is a Microsoft tool and you can get it from here
Finally, we need to loop through the config file to run by the script we just created.

. ".\SetupAServiceAccount.ps1"

### Read the config XML
$configFile = $args[0]
[xml]$thisEnv = Get-Content $configFile

### Create a service account for each element in the xml file (under accounts)
$ldapServer = $thisEnv.Env.LDAPServer
$ou = $thisEnv.Env.OU
$thisEnv.Env.Accounts.Account| foreach {SetupAServiceAccount $ldapServer $ou $_.id $_.sAMAccountName $_.UserPrincipalName $_.Password $_.Description}