Joining a Windows 7 Client to a Domain through PowerShell

Time to join a Windows 7 client to a domain. Now that the Test Domain is up and running ( Check Previous Posts ), we can start joining clients to the domain.

Since it’s a freshly installed Windows 7 client, we dont have to bother about userprofiles and so on.. we’ll save that for later :)

Joining the domain is done through WMI. Just create a new Object containing the Win32_ComputerSystem WMI object and call the JoinDOmainOrWorkGroup() method.


function JoinDomain ([string]$Domain, [string]$User, [string]$Password ) {

	$DomainUser = $Domain + "\" + $User
	$OU = $null

	$ComputerSystem = gwmi Win32_ComputerSystem

	$ComputerSystem.JoinDomainOrWorkGroup(
		$Domain, 
		$Password, 
		$DomainUser, 
		$OU, 
		3
	)
}

Here’s an example on running the function.


PS > JoinDomain PowerShell.nu migaccount Password1

And voila! the client shuts down and on Startup, It’ll have joined the new domain.

Rating 3.00 out of 5
[?]

Adding HomeFolder Through PowerShell

It’s been some time since my last post now, had alot of things to do at work, but now I’m back OnTrack with my blogging.

Last time we checked out how to add group membership through PowerShell, so now we should have a nice test environment in place, based on Star Trek.

In this post, we are going to script up Users homefolders and add each user to the correct folder. We’ll accomplish this through the following four steps:

  • Add a Share on the Server
  • Add unique folders for all Users
  • Add unique Permissions to the FOlders
  • Edit the User Objects in Active-Direcroty

I’m also going to re-use a script I wrote a couple of months ago, but we’ll get back to that.

Let’s start off by creating a Share. This can be done through the Create() method in the WMI class Win32_Share. The Win32_Share is well described in MSDN.

Since we want to make the script re-usable, we should check if the Share already exists. This is a simple procedure through PowerShell.


PS > $Share = "C:\Share"
PS > $ShareName = "Share"
PS > if ((gwmi Win32_Share | Where { $_.Path -eq $Share}).Path -eq $Share ) {
>>  Write-Host "Share: $ShareName already exists." -ForeGroundColor Red
>>  }

If the Share Already exists “Share: Share already exists” will be prompted, if not we can continue with the script.

Now that we know that the share doesn’t exist, we have to check that the folder exists, and if not, create the folder.


PS > if (!(Test-Path $Share)) {
>>  New-Item -Path $Share -type directory | Out-Null
>>  }

and finally, we can create our Share through WMI. Setting type to 0 creates a Disk Drive Share.


PS > $CreateShare = [wmiclass]"Win32_Share"
PS > $CreateShare.Create($Share,$ShareName,$Type) | Out-Null

add-stshare02

Now that the share is up and running, we can create our HomeFolders. First we set up our HomeDrive and HomeFolder variables, we’ll also set up a User for the example.


PS > $Share = "Share"
PS > $User = "jeapic"
PS > $HomeDrive = "H"
PS > $HomeDirectory = "\\" + $env:COMPUTERNAME + "\" + $Share + "\" + $User

Since the script runs on the server where the Share is created, we can use the environment variable to retrieve the computername.

Next, we want to check if the user already has the homedrive and homedirectory set. We can use the Get-AD.ps1 script for this. I’m also adding the -ToObject switch since i want to use the object later on.


PS > $GetUser = ./Get-AD.ps1 -Domain $Domain -User $User -Filter sAMAccountName -ToObject
PS > if ($GetUser.homeDirectory -match $HomeDirecory -AND $GetUser.homeDrive -match $HomeDrive) {
>> Write-Host "User: $User HomeDrive Already Set" -ForeGroundColor Yellow
>> }

If the User already has the HomeDrive set, we won’t continue, if not, we can go ahead and add it. But before connecting the User to the folder, want to create and give the user FullControl of his HomeFolder. Here we can use the Set-FolderPermission.ps1 script


PS > $Domain = "powershell.nu"
PS > $DomainUser = $Domain + "\" + $User
PS > ./Set-FolderPermission.ps1 -Path $HomeDirectory -Access $DomainUser -Permission FullControl

The Set-FolderPermission.ps1 both created the folder and set up the permissions for us, now all we have to do is set HomeDrive and HomeDirectory to the User Object.


PS > $GetUser.Put("homeDirectory",$HomeDirectory)
PS > $GetUser.Put("homeDrive",$HomeDrive)
PS > $GetUser.SetInfo()

And that’s it.

Running the script Doesn’t require the Star Trek Csv file used in the other examples, it does however require you to loop through each Users that you want to add Homefolders to. In order to get a list of all Users within an OU you can use the Get-AD.ps1 script, as shown below.


PS > ./Get-AD.ps1 -domain "LDAP://OU=Star Trek: The Next Generation,DC=powershell,DC=nu" -User AllUsers -Property sAMAccountName | ForEach { 
>> ./Add-STHomeFolder.ps1 -Domain powershell.nu -User $_.sAMAccountName -Share Share -HomeDrive H
>> }

All I have to do now is change the LDAP path above and repeat the ForEach on each OU that contains Users that I want to add a HomeFolder to.

Here are a couple of screenshots on running the scripts:

add-sthomefolder

add-sthomefolder02

Click Here to Download the Add-STShare.ps1 Script.

Click Here to Download the Add-STHomeFolder.ps1 Script.

The Get-AD.ps1 script is also required.

Here’s the Set-Foldepermission.ps1 Script that’s also required.

Rating 4.33 out of 5
[?]

Adding Group Membership Through PowerShell

Time to add some members to our groups. Following the steps in the previous posts, we should now have a couple of Users, groups and computers in our test environment. Group Names are based on the Character position in the Star Trek Csv file so now, all we have to do is match up the Characters with their Positions.

Starting of, Let’s collect the information we need from the Csv File.

There are 68 Characters in the file so I’m only going to select the first one in the eample below.


PS > $CsvFile = Import-Csv StarTrek.csv
PS > ($CsvFile | Select Character, Position, Series)[0] | fl


Character : Jean-Luc Picard
Position  : Commanding Officer
Series    : Star Trek: The Next Generation

What we want to do now is add each member to the correct group. Let’s take the first User, Captain Picard, as an example and see how this is done.

Since there are 4 different Commanding Officer groups, we want to make sure that we connect to the correct one. We know which Starship (OU) the Captain is in since and we have that information in the Csv file so we can use this in combination with the Get-AD.ps1 script.

First we have get the OU:s distinguishedName.


PS > $Domain = "powershell.nu"
PS > $Series = "Star Trek: The Next Generation"
PS > $OU = ./Get-AD.ps1 -Domain $Domain -OU $Series -property distinguishedName
PS > $OU


distinguishedName
-----------------
OU=Star Trek: The Next Generation,DC=powershell,DC=nu


PS > $DomainConnection = "LDAP://" + [string]$OU.distinguishedName
PS > $DomainConnection

LDAP://OU=Star Trek: The Next Generation,DC=powershell,DC=nu

Now that we have the connectionstring, we can use this to narrow down the search in the Get-AD.ps1 script. By typing -Domain followed by an OU:s distinguishedName forces the script to only search within the OU structure.


PS > $Character = "Jean-Luc Picard"
PS > $User = ./Get-AD.ps1 -Domain $DomainConnection -User $Character -property distinguishedName, Name
PS > $UserConnection = "LDAP://" + [string]$User.distinguishedName
PS > $UserConnection

LDAP://CN=Jean-Luc Picard,OU=Users,OU=Star Trek: The Next Generation,DC=powershell,DC=nu

PS > $UserdistinguishedName = [string]$User.distinguishedName
PS > $UserdistinguishedName

CN=Jean-Luc Picard,OU=Users,OU=Star Trek: The Next Generation,DC=powershell,DC=nu

So why bother to create one variable holding the distinguishedName and one holding the LDAP connection string ?? well, we’ll get to that in a while but first we need a connection to the group as well.


PS > $Position = "Commanding Officer"
PS > $Group = ./Get-AD.ps1 -Domain $DomainConnection -Group $Position -ToObject
PS > $Group | Format-List *


objectClass          : {top, group}
cn                   : {Commanding Officer}
description          : {Commanding Officer}
distinguishedName    : {CN=Commanding Officer,OU=Groups,OU...
instanceType         : {4}
whenCreated          : {4/15/2009 5:09:08 PM}
whenChanged          : {4/15/2009 5:09:08 PM}
uSNCreated           : {System.__ComObject}
uSNChanged           : {System.__ComObject}
name                 : {Commanding Officer}
objectGUID           : {74 231 168 162 107 193 157 74 161 
objectSid            : {1 5 0 0 0 0 0 5 21 0 0 0 50 71 101...
sAMAccountName       : {COMMANDIN}
sAMAccountType       : {268435456}
groupType            : {-2147483646}
objectCategory       : {CN=Group,CN=Schema,CN=Configuratio...
nTSecurityDescriptor : {System.__ComObject}

Now that we got the correct group, all we have to do is make a simple check so that the Captain isn’t already member of the group and if not, add him to it. Here’s where the distinguishedName and LDAP string come in handy.


PS > if ($Group.member -Contains $UserdistinguishedName) {
Write-Host “The Captain is already member”
} else {
$Group.Add($UserConnection)
}

Now let's how the complete script would handle this.

add-stmember01

Repeating the script tells us that the Characters are already members of the groups.

add-stmember02

And here's a quick check in the Active-Directory snapin.

add-stmember03

Click Here to Download the Complete Script.

The Get-AD.ps1 script is also required.

Click here to download the Csv File

Rating 3.00 out of 5
[?]

Adding Groups Through PowerShell

With our Users and Computers in place, we can start adding groups. The groups will be based on the Postition ocf the characters in the StarTrek Csv file. I’ve chosen position since many characters can have the same position.

First, let’s look at all the unique Positions in the Csv file. I’m also using the Series as a Descrption and it’s also used to place the group in the correct OU.


PS > $CsvFile | Select Position, Series -Unique


Position                     Series
--------                     ------
Commanding Officer           Star Trek: The Next Generation
First Officer                Star Trek: The Next Generation
Chief Engineer               Star Trek: The Next Generation
Chief of Security            Star Trek: The Next Generation
Chief Medical Officer        Star Trek: The Next Generation
Ship's Counselor             Star Trek: The Next Generation
Chief Operations             Star Trek: The Next Generation
Conn Officer                 Star Trek: The Next Generation
Transporter Officer          Star Trek: The Next Generation
Botanist                     Star Trek: The Next Generation
Nurse                        Star Trek: The Next Generation
Bartender                    Star Trek: The Next Generation
Engineering Officer          Star Trek: The Next Generation
Federation Ambassador        Star Trek: The Next Generation
Security Officer             Star Trek: The Next Generation
Medical Officer              Star Trek: The Next Generation
Operations Officer           Star Trek: The Next Generation
Commanding Officer           Star Trek: Deep Space Nine
First Officer                Star Trek: Deep Space Nine
Chief of Security            Star Trek: Deep Space Nine
Chief Medical Officer        Star Trek: Deep Space Nine
Chief Science Officer        Star Trek: Deep Space Nine
Counselor                    Star Trek: Deep Space Nine
Strategic Operations Officer Star Trek: Deep Space Nine
Chief Operations             Star Trek: Deep Space Nine
Civilian                     Star Trek: Deep Space Nine
Bartender                    Star Trek: Deep Space Nine
Commanding Officer           Star Trek: Voyager
First Officer                Star Trek: Voyager
Second Officer               Star Trek: Voyager
Chief Engineer               Star Trek: Voyager
Medical Officer              Star Trek: Voyager
Chief Operations Officer     Star Trek: Voyager
Chief Medical Officer        Star Trek: Voyager
Chef                         Star Trek: Voyager
Aeroponics caretaker         Star Trek: Voyager
Miscellaneous                Star Trek: Voyager
Conn Officer                 Star Trek: Voyager
Science                      Star Trek: Voyager
Engineering Officer          Star Trek: Voyager
Civilian                     Star Trek: Voyager
Commanding Officer           Star Trek: Enterprise
First Officer                Star Trek: Enterprise
Armory Officer               Star Trek: Enterprise
Engineering Officer          Star Trek: Enterprise
Medical Officer              Star Trek: Enterprise
Communications Officer       Star Trek: Enterprise
Chief Engineer               Star Trek: Enterprise

I’m going to use the first entry “Commanding Officer” to explain how the script works. At first, we have to check if the group already exists. we’ll reuse the checker function that I’ve mentioned in previous blog posts.


function Check-distinguishedName ([string]$Domain, [string]$Group) {

   trap {  $Script:distinguishedNameDoesntExist = $True ; continue }
   .Get-AD.ps1 -Domain $Domain -Group $Group -filter distinguishedName | Out-Null
}

Next we have to build up the groups distinguishedName in order to check it.


PS > $Domain = "powershell.nu"
PS > $Position = "Commanding Officer"
PS > $Series = "Star Trek: The Next Generation"
PS > $distinguishedName = "CN=" + $Position + ",OU=Groups,OU=" + $Series +
>> $Series + ($Domain.Replace(".",",DC=")).Insert(0,",DC=")
PS > $distinguishedName

CN=Commanding Officer,OU=Groups,OU=Star Trek: The Next Generation,DC=powershell,DC=nu

Now that we’ve built up a distinguishedName we can check it through the Check-distinguishedName function. If the group doens’t exist, the variable $distinguishedNameDoesntExist is set to $True, which means that we can go ahead and create the group.


PS > Check-distinguishedName $Domain $distinguishedName
PS > $distinguishedNameDoesntExist

True

Now that we’re sure that the group does’t exist we can create a connection to Active-Directory and create the group. First we’ll set up the connectionstring to the correct OU and create a [adsi] connection.


PS > $Connection = "LDAP://OU=Groups" + $Series.Insert(0,",OU=") + 
>> ($Domain.Replace(".",",DC=")).Insert(0,",DC=")
PS > $AD = [adsi] $Connection

Adding the group is quite simple using the Create() method.


PS > $Group = $AD.Create("Group", "CN=$Position")
PS > $Group.SetInfo()

The part that’s not so simple is adding a unique sAMAccountName. Since there’s One Commanding officer for each ship, we will have a total of four different groups with the same name, the distinguishedName doesnt have to be unique as long as its not in the same OU but the sAMAccountName has to be.

First we have to create a sAMAccountName for the group. Since the Group names tend to be quite long, we’ll shorten the groups sAMAccountName through the subString() method if it’s longer than 10 characters. We’ll also remove spaces in the string.


PS > $sAMAccountName = ($Position.replace(" ","")).ToUpper()

PS > if ($sAMAccountName.Length -gt 10) {
  $sAMAccountName = $sAMAccountName.SubString(0,9)
}
PS > $sAMAccountName

COMMANDIN

Now that we have a sAMAccountName, we have to check so that it doesn’t exist. again through a checker function.


function Check-sAMAccountName ([string]$Domain, [string]$Group) {

  trap {  $Script:sAMAccountNameDoesntExist = $True ; continue } 
  .Get-AD.ps1 -Domain $Domain -Group $Group -filter sAMAccountName | Out-Null
}

If the sAMAccountName exists, we will handle this through a while loop and append an integer at the end.


While ($Script:sAMAccountNameDoesntExist -eq $False) {

   $LastChar = $sAMAccountName.SubString($sAMAccountName.Length -1)

   if(1..9 -Contains $LastChar) {

    $sAMAccountName = ($sAMAccountName.TrimEnd([string]$LastChar)) + 
    ([int]$LastChar + 1)
   } else {

    $sAMAccountName = $sAMAccountName + 1
   }

   Check-sAMAccountName -Domain $Domain -Group $sAMAccountName
}

Finally, we can add the additional group information, in this case: sAMAccountName and Description.


$Group.put("Description", $Position)
$Group.put("sAMAccountName", $sAMAccountName)
$Group.setinfo()

Here’s how it looks if we run it in our test environment.

add-stgroup01

Repeating the script shows that the groups already exist.

add-stgroup02

and here’s a quick look in dsa.msc

add-stgroup03

Additionally, let’s look at our Commanding Officer groups. We can do this through the Get-AD.ps1 script


PS > .Get-AD.ps1 -Domain powershell.nu -Group AllGroups -Property sAMAccountName, cn | 
>> Where { $_.cn -match "Commanding Officer" }


sAMAccountName cn
-------------- --
COMMANDIN      Commanding Officer
COMMANDIN1     Commanding Officer
COMMANDIN2     Commanding Officer
COMMANDIN3     Commanding Officer

Isn’t it cool how you can take the result of a script and pipe it to the Where-Object CmdLet.

Click Here to Download the Complete Script.

The Get-AD.ps1 script is also required.

Click here to download the Csv File

Rating 3.00 out of 5
[?]

Adding Computers through PowerShell

Now let’s add a couple of computers to our test environment. The Computer Names are based on the Starships from the Csv file. Since there are alot of characters but not that many different ships we need to get a unique list of ships. We also wnat the Series, Location and Registry values.


PS > $CsvFile = Import-Csv StarTrek.csv
PS > $CsvFile | Select Series, Starship, Location, Registry -unique | fl


Series   : Star Trek: The Next Generation
Starship : USS Enterprise (NCC-1701-D)
Location : Alpha Quadrant
Registry : NCC-1701-D

Series   : Star Trek: Deep Space Nine
Starship : Deep Space Nine
Location : Alpha Quadrant
Registry : DS9

Series   : Star Trek: Voyager
Starship : USS Voyager (NCC-74656)
Location : Delta Quadrant
Registry : NCC-74656

Series   : Star Trek: Enterprise
Starship : Enterprise (NX-01)
Location : Alpha Quadrant
Registry : NX-01

Let’s walk through how the script handles the first Starship. At first we have to check if the Computer exists, We’ll use a checker function that checks if the Computer doesn’t exist.


function Check-distinguishedName ([string]$Domain, [string]$Computer) {

   trap {  $Script:distinguishedNameDoesntExist = $True ; continue } 
   .Get-AD.ps1 -Domain $Domain -Computer $Computer -filter distinguishedName | Out-Null
}

Next we have to check create a variable holding a distinguishedName and check if it exists or not.


PS > $Starship = "USS Enterprise (NCC-1701-D)"
PS > $Domain = "powershell.nu"
PS > $distinguishedName = "CN=" + $Starship + ",OU=Computers,OU=" + $Series + ($Domain.Replace(".",",DC=")).Insert(0,",DC=")

Now we can run the function.


PS > Check-distinguishedName -Domain $Domain -Computer $distinguishedName

Now that we’ve checked the distinguisehdName we can connect to AD and start adding our Computer.


PS > $Connection = "LDAP://OU=Computers" + $Series.Insert(0,",OU=") + ($Domain.Replace(".",",DC=")).Insert(0,",DC=")

PS > $AD = [adsi] $Connection

PS > $Computer = $AD.Create("Computer", "CN=$Starship")
PS > $Computer.SetInfo()

Now for the additional information. We might want to change the sAMAccountName from the default name generated by AD. We also want to check that the name we choose doesn’t exist so we’ll use yet another check function.


function Check-sAMAccountName ([string]$Domain, [string]$Computer) {

   trap {  $Script:sAMAccountNameDoesntExist = $True ; continue } 
   .Get-AD.ps1 -Domain $Domain -Computer $Computer -filter sAMAccountName | Out-Null
}

We’ll base the sAMAccountName on the Ships Registry information. In this case, the registry is NCC-1701-D


PS > $sAMAccountName = ($Registry).ToUpper()

PS > Check-sAMAccountName -Domain $Domain -Computer $sAMAccountName

If the sAMAccountName doesn’t exist the script will use it on the Computer object. If it does exist, theres a while loop that loops through and appends a digit in order to get a unique sAMAccountName. Here’s an example on the where loop.


While ($Script:sAMAccountNameDoesntExist -eq $False) {

   # Create New sAMAccountName

   $LastChar = $sAMAccountName.SubString($sAMAccountName.Length -1)

    if(1..9 -Contains $LastChar) {

    $sAMAccountName = $sAMAccountName.SubString(0,$sAMAccountName.Length -1) + ([int]$LastChar + 1)

   } else {

    $sAMAccountName = $sAMAccountName.SubString(0,$sAMAccountName.Length -1) + 1
   }

   Check-sAMAccountName -Domain $Domain -Computer $sAMAccountName
}

The final Steps of the script add the additional information and enables the computer.


PS > $Computer.put("sAMAccountName", $sAMAccountName)
PS > $Computer.put("Location", $Location)
PS > $Computer.put("Description", $Starship)
PS > $Computer.setinfo()

PS > $Computer.PsBase.InvokeSet("AccountDisabled", $False)
PS > $Computer.SetInfo()

Now let’s run the script in our test environment.

add-stcomputer011

If we repeat the script it’ll tell us that the computers already exist.

add-stcomputer02

If we check in dsa.msc snap-in we can see that the computer objects have been created.

add-stcomputer03

and finally, if we want to retrieve the information through PowerShell, we can use the Get-AD.ps1 script.


PS > .Get-AD.ps1 -Domain powershell.nu -Computer "Uss Enterprise (NCC-1701-D)"


objectClass          : top person organizationalPerson user computer
cn                   : USS Enterprise (NCC-1701-D)
description          : USS Enterprise (NCC-1701-D)
distinguishedName    : CN=USS Enterprise (NCC-1701-D),OU=Computers,OU=Star Trek
                       u
instanceType         : 4
whenCreated          : 4/13/2009 7:19:11 PM
whenChanged          : 4/13/2009 7:19:11 PM
uSNCreated           : System.__ComObject
uSNChanged           : System.__ComObject
name                 : USS Enterprise (NCC-1701-D)
objectGUID           : 31 54 123 251 207 232 62 72 153 101 238 77 71 88 19 27
userAccountControl   : 544
badPwdCount          : 0
codePage             : 0
countryCode          : 0
badPasswordTime      : System.__ComObject
lastLogoff           : System.__ComObject
lastLogon            : System.__ComObject
pwdLastSet           : System.__ComObject
primaryGroupID       : 513
objectSid            : 1 5 0 0 0 0 0 5 21 0 0 0 50 71 101 4 93 25 58 165 36 24
accountExpires       : System.__ComObject
logonCount           : 0
sAMAccountName       : NCC-1701-D
location             : Alpha Quadrant
sAMAccountType       : 805306368
objectCategory       : CN=Computer,CN=Schema,CN=Configuration,DC=powershell,DC=
nTSecurityDescriptor : System.__ComObject

Click Here to Download the Complete Script.

The Get-AD.ps1 script is also required.

Click here to download the Csv File

Rating 3.00 out of 5
[?]