Active Directory to Windows PowerShell
Windows PowerShell with folder objects and Windows Management Instrumentation (WMI) objects, but so far we haven't talked about how to use PowerShell to create or manipulate data in Active Directory.
One reason we haven't is that working with Active Directory and PowerShell v1 is different from working with the existing providers that both WMI and file system management have. It's not impossible -- just different. This month, we'll introduce how Windows PowerShell works with Active Directory.
Adjusting for a more complex environment
Imagine the following scenario. You've just started working for a new company and are in charge of user accounts. The company had only a single domain but will soon have two. Your predecessor created roaming profile folders without the domain suffix and your boss tells you that they're about to hire a lot of new people, split between the different domains. The current account-creation scripts are all hard coded. Clearly, you need to do some tweaking.
Going forward, you amend the Group Policy for creating roaming profile folders so that the new roaming profile folders will be named …\joe.brown.domainname, where domainname is the name of that user's domain. But that still leaves you with a long list of user accounts to create and a bunch of existing roaming profile folders to rename to keep the storage models consistent.
Step 1: Renaming roaming profile folders
Now that the Group Policies are updated to include the domain name in the profile folder, you must rename the existing roaming profile folders to include the domain suffix. We've talked about how to rename folders in a previous column, so let's review this quickly.
First, get all the roaming profile folders from the parent folder by finding all the containers:
$profiles=get-childitem c:\profiles where {$_.psIsContainer -eq $true}
Next, concatenate the domain name on those existing profile folders as below, where domainname is the real name of the domain. $_ represents the existing name of the folder.
$profiles foreach-object -process {rename-item -path $_.fullname -newname "$_.domainname"}
You've now cleaned up the existing profile folder so it can accommodate multiple domains.
Step 2: Creating new user accounts
Windows PowerShell is written on top of the .NET Framework, which organizes all its data into types. One type is strings; another type is Active Directory. PowerShell knows about strings, so you don't need to tell it that you're creating a string when you type $string = "mystring". It has to be reminded about Active Directory, however, so you remind it by including [ADSI] in square brackets when you want to address objects in Active Directory. To illustrate:
$name = "LDAP://CN=Christa Anderson, OU=ASH_Users, DC=alpineskihouse, DC=com"
This gives $name a value of "LDAP://cn=Christa,ou=ASH-Users,DC=alpineskihouse,dc=com". I have not connected to my user account.
$name = [ADSI] "LDAP://CN=Christa Anderson, OU=ASH_Users, DC=alpineskihouse, DC=com"
This gives $name a value of my domain account so that I can view my account properties or otherwise manipulate it. (As you know if you've worked with Active Directory before, it's case-sensitive. Be very careful about capitalization or the command won't work. It won't always show an error, you just won't connect to the account.)
I can also create a connection to my domain account using the flat WinNT format, as shown below:
$name= [ADSI] "WinNT=//alpineskihouse/christa.anderson"
Once you've assigned the user account to the variable, you can view the parameters exposed through .NET with get-member, as shown below. This command will show you all the properties .NET knows about for that account:
$name get-member
To view any account property, use the write-host cmdlet for the fullname property, plugging in the property as appropriate. This is handy when you're troubleshooting LDAP paths that aren't resolving as you'd expect.
Write-host $name.fullname
With all that said, there's our script to create user accounts from the data in a comma-delimited file. Here's what we're doing:
First, securely collect the new account password. Since the default password will be the same for all accounts created at the same time, it's not ideal, but it's better than hard coding the password into the script. As you can see, the new accounts must all change their password at first logon.
#Type in the password so it's not in the CSV file
$DefaultPassword = Read-Host "Enter
Default Password" -asSecureString
Import-csv c:\CreateUsers.csv foreach {
Next, reference a .CSV file to create the new accounts. This file contains a fair amount of detail about the new account so you can see how it populates. To add more, use get-member to get the names of more properties. I've identified the variables from my file using an f prefix so it's easy to distinguish between a variable name and property name.
Once you've got the information in the CSV, everything else is easy. You're creating a connection to the path as defined by each user's OU and domain information (notice that this format does not hard code any of that data) using SetInfo() to create the object in Active Directory, then making a second pass to edit all the properties of that user account object.
## The following fields in this script come from the text file
## fFirstName = User First Name
## fInitials = USer Middle Initial
## fLastName = User Last Name
## fOU = User OU
## fLDAP1 = First Part of LDAP Path
## fLDAP2 = Second Part of LDAP Path
## fDomain = User Domain
## fAccountStatus = Enable user account = 66048, disable user account = 66050
## fEmailAddress = User EMail Address
## ftelephoneNUmber = User Telephone Number
## fTitle = User Title
## fCompany = user Company
## fDepartment = User Department
## Create Connection to AD
$strLDAP = "LDAP://OU=" + $_.fOU + "," + $_.fLDAP1 + "," + $_.fLDAP2
$objPath = [ADSI] $strLDAP
## Create User Object
$strUser = $_.fFirstName + " " + $_.fLastName
$objUser = $objPath.create("user", "cn=" + $strUser)
$objUser.SetInfo()
## Set User Account Parameters
$varDLName = $_.fFirstName[0] + $_.fInitials + $_.fLastName
$objUser.samaccountname = $varDLName.ToString()
$objUser.givenName = $_.fFirstName
$objUser.initials = $_.fInitials
$objUser.sn = $_.fLastName
$objUser.displayName = $_.fFirstName + " " + $_.fInitials + " " + $_.fLastName
$objUser.userPrincipalName = $_.fFirstName + "." + $_.fInitials + "." + $_.fLastName + "@" + $_.fDomain
$objUser.company = $_.fCompany
$objUser.department = $_.fDepartment
$objUser.title = $_.fTitle
$objUser.mail = $_.fEmailAddress
$objUser.telephoneNumber = $_.fTelephoneNumber
##Set Default Password and Force Password Change at First Logon
$objUser.SetPassword($DefaultPassword.ToString())
$objUser.pwdLastSet = 0
##Set User Account Status (Enabled or Disabled)
$objUser.userAccountControl = $_.fAccountStatus
##Write data to AD
$objUser.SetInfo()
}
You have now populated Active Directory with as many accounts as you included in your CSV file, specified by OU and domain as required.
To summarize, using Active Directory with Windows PowerShell isn't difficult if you remember a few things:
LDAP is case-sensitive, so watch capitalization in your paths.
To access an object in Active Directory, make sure you specify the [ADSI] type.
You can see properties for objects in Active Directory by plugging them into get-member.
If a path you're creating from variables isn't resolving properly, you can always view it as a string using write-host and check your grammar.
One reason we haven't is that working with Active Directory and PowerShell v1 is different from working with the existing providers that both WMI and file system management have. It's not impossible -- just different. This month, we'll introduce how Windows PowerShell works with Active Directory.
Adjusting for a more complex environment
Imagine the following scenario. You've just started working for a new company and are in charge of user accounts. The company had only a single domain but will soon have two. Your predecessor created roaming profile folders without the domain suffix and your boss tells you that they're about to hire a lot of new people, split between the different domains. The current account-creation scripts are all hard coded. Clearly, you need to do some tweaking.
Going forward, you amend the Group Policy for creating roaming profile folders so that the new roaming profile folders will be named …\joe.brown.domainname, where domainname is the name of that user's domain. But that still leaves you with a long list of user accounts to create and a bunch of existing roaming profile folders to rename to keep the storage models consistent.
Step 1: Renaming roaming profile folders
Now that the Group Policies are updated to include the domain name in the profile folder, you must rename the existing roaming profile folders to include the domain suffix. We've talked about how to rename folders in a previous column, so let's review this quickly.
First, get all the roaming profile folders from the parent folder by finding all the containers:
$profiles=get-childitem c:\profiles where {$_.psIsContainer -eq $true}
Next, concatenate the domain name on those existing profile folders as below, where domainname is the real name of the domain. $_ represents the existing name of the folder.
$profiles foreach-object -process {rename-item -path $_.fullname -newname "$_.domainname"}
You've now cleaned up the existing profile folder so it can accommodate multiple domains.
Step 2: Creating new user accounts
Windows PowerShell is written on top of the .NET Framework, which organizes all its data into types. One type is strings; another type is Active Directory. PowerShell knows about strings, so you don't need to tell it that you're creating a string when you type $string = "mystring". It has to be reminded about Active Directory, however, so you remind it by including [ADSI] in square brackets when you want to address objects in Active Directory. To illustrate:
$name = "LDAP://CN=Christa Anderson, OU=ASH_Users, DC=alpineskihouse, DC=com"
This gives $name a value of "LDAP://cn=Christa,ou=ASH-Users,DC=alpineskihouse,dc=com". I have not connected to my user account.
$name = [ADSI] "LDAP://CN=Christa Anderson, OU=ASH_Users, DC=alpineskihouse, DC=com"
This gives $name a value of my domain account so that I can view my account properties or otherwise manipulate it. (As you know if you've worked with Active Directory before, it's case-sensitive. Be very careful about capitalization or the command won't work. It won't always show an error, you just won't connect to the account.)
I can also create a connection to my domain account using the flat WinNT format, as shown below:
$name= [ADSI] "WinNT=//alpineskihouse/christa.anderson"
Once you've assigned the user account to the variable, you can view the parameters exposed through .NET with get-member, as shown below. This command will show you all the properties .NET knows about for that account:
$name get-member
To view any account property, use the write-host cmdlet for the fullname property, plugging in the property as appropriate. This is handy when you're troubleshooting LDAP paths that aren't resolving as you'd expect.
Write-host $name.fullname
With all that said, there's our script to create user accounts from the data in a comma-delimited file. Here's what we're doing:
First, securely collect the new account password. Since the default password will be the same for all accounts created at the same time, it's not ideal, but it's better than hard coding the password into the script. As you can see, the new accounts must all change their password at first logon.
#Type in the password so it's not in the CSV file
$DefaultPassword = Read-Host "Enter
Default Password" -asSecureString
Import-csv c:\CreateUsers.csv foreach {
Next, reference a .CSV file to create the new accounts. This file contains a fair amount of detail about the new account so you can see how it populates. To add more, use get-member to get the names of more properties. I've identified the variables from my file using an f prefix so it's easy to distinguish between a variable name and property name.
Once you've got the information in the CSV, everything else is easy. You're creating a connection to the path as defined by each user's OU and domain information (notice that this format does not hard code any of that data) using SetInfo() to create the object in Active Directory, then making a second pass to edit all the properties of that user account object.
## The following fields in this script come from the text file
## fFirstName = User First Name
## fInitials = USer Middle Initial
## fLastName = User Last Name
## fOU = User OU
## fLDAP1 = First Part of LDAP Path
## fLDAP2 = Second Part of LDAP Path
## fDomain = User Domain
## fAccountStatus = Enable user account = 66048, disable user account = 66050
## fEmailAddress = User EMail Address
## ftelephoneNUmber = User Telephone Number
## fTitle = User Title
## fCompany = user Company
## fDepartment = User Department
## Create Connection to AD
$strLDAP = "LDAP://OU=" + $_.fOU + "," + $_.fLDAP1 + "," + $_.fLDAP2
$objPath = [ADSI] $strLDAP
## Create User Object
$strUser = $_.fFirstName + " " + $_.fLastName
$objUser = $objPath.create("user", "cn=" + $strUser)
$objUser.SetInfo()
## Set User Account Parameters
$varDLName = $_.fFirstName[0] + $_.fInitials + $_.fLastName
$objUser.samaccountname = $varDLName.ToString()
$objUser.givenName = $_.fFirstName
$objUser.initials = $_.fInitials
$objUser.sn = $_.fLastName
$objUser.displayName = $_.fFirstName + " " + $_.fInitials + " " + $_.fLastName
$objUser.userPrincipalName = $_.fFirstName + "." + $_.fInitials + "." + $_.fLastName + "@" + $_.fDomain
$objUser.company = $_.fCompany
$objUser.department = $_.fDepartment
$objUser.title = $_.fTitle
$objUser.mail = $_.fEmailAddress
$objUser.telephoneNumber = $_.fTelephoneNumber
##Set Default Password and Force Password Change at First Logon
$objUser.SetPassword($DefaultPassword.ToString())
$objUser.pwdLastSet = 0
##Set User Account Status (Enabled or Disabled)
$objUser.userAccountControl = $_.fAccountStatus
##Write data to AD
$objUser.SetInfo()
}
You have now populated Active Directory with as many accounts as you included in your CSV file, specified by OU and domain as required.
To summarize, using Active Directory with Windows PowerShell isn't difficult if you remember a few things:
LDAP is case-sensitive, so watch capitalization in your paths.
To access an object in Active Directory, make sure you specify the [ADSI] type.
You can see properties for objects in Active Directory by plugging them into get-member.
If a path you're creating from variables isn't resolving properly, you can always view it as a string using write-host and check your grammar.
Comments