Archive for the ‘Active Directory’ Category


This month we have a new Avaya phone system being implemented for one of my customers and one of the prerequisites to get full functionality is to have all telephone numbers in the popular E.164 format. This would be a pretty simple task if the data was in a consistent format but unfortunately this was not the case, let’s take a look at the state of the data*:

*For security reasons I have recreated the data in my test lab with random names, area codes and telephone numbers etc.

First I will store a collection of Users in a variable named $users, throughout this post I will use the Quest AD cmdlets. This is mainly because they are backwards compatible with 2003 based domains but if you are lucky enough to be 2008 you should be able to convert the commands to Microsoft’s AD Module with little effort.

$users = Get-QADUser -Description "E164 Sample Accounts"

And now display just the name and telephoneNumber properties:

$users | Select-Object Name, telephoneNumber | Format-Table -AutoSize
Name              telephoneNumber
----              ---------------
Georgina.Stewart  554861
Georgina.Stewart1 01234 12 34 56 (only call on Tues)
Eden.Morris       01234-55(4487)
Sarah.Patel       559642
Marley.Adams      550998
Mya.Lewis         (01234) 127075
Sophia.Baker      (55)2912
Filip.Rogers      (55)8996
Noah.Mason        (Part Time)-(01234) 897740
Isabel.Webb       (01234) 036428
Steven.Matthews   (01234) 957823
Courtney.Walker   (01234) 679860
Rayyan.Mitchell   07123 530656
Mary.Graham       07123  822057
Maisy.Barnes      07123 364461
Arabella.Thompson +441234844048
Macy.Adams
Grace.Robinson    (01234) 538794
Summer.Stevens    Call me before 11AM 550997
Frederick.Taylor  01234588239

As you can see we have quite a mix bag here. One of the first things to notice is that the telephoneNumber property has not been strictly used for just numbers and as such it is difficult to see any format patterns, let’s strip all non-digit characters away and take another look:

$users | Select-Object Name, @{name="telephoneNumberDigitsOnly";expression={$_.telephoneNumber -replace "\D"}} | Format-Table –AutoSize

The above line uses a replace with a single argument so this is effectively a remove. The replace uses a regular expression of Non-Digit contained within a calculated property.

Name              telephoneNumberDigitsOnly
----              -------------------------
Georgina.Stewart  554861
Georgina.Stewart1 01234123456
Eden.Morris       01234554487
Sarah.Patel       559642
Marley.Adams      550998
Mya.Lewis         01234127075
Sophia.Baker      552912
Filip.Rogers      558996
Noah.Mason        01234897740
Isabel.Webb       01234036428
Steven.Matthews   01234957823
Courtney.Walker   01234679860
Rayyan.Mitchell   07123530656
Mary.Graham       07123822057
Maisy.Barnes      07123364461
Arabella.Thompson 441234844048
Macy.Adams
Grace.Robinson    01234538794
Summer.Stevens    11550997
Frederick.Taylor  01234588239

Now that looks better with the exception of Summer.Stevens, it’s expected that some accounts will need manual effort so I will need to filter for those as well. We can see from the above table that there are two common number formats (1,2), blanks (3), numbers already in E.164 format and finally we’ll need a catch all(5) for everything else:

  1. Starting with “55” followed by four numbers
  2. Starting with “01 or 07” followed by nine numbers
  3. Blank/Null values
  4. E.164 formatted numbers (No change required)
  5. Neither of the above formats, for example Summer.Stevens

The next step is to create regular expressions to match these four values as the fifth will be a catch for non-matched values, like so:

  1. “^(01|07)\d{9}”
  2. “^55\d{4}”
  3. “^$”
  4. “^44\d{10}”

Now let’s put all of this together and get a feel for how things will look:

foreach ($user in $users) {
	switch -regex ($user | % { $user.telephoneNumber -replace "\D" } ) {
	"^(01|07)\d{9}"		{ Add-Member -InputObject $user -Name "E164Number" -MemberType NoteProperty -Value ([regex]::matches(($user.telephoneNumber -replace "\D"),"^(01|07)\d{9}")[0].Value -replace '^0','+44') ; continue }
	"^55\d{4}"			{ Add-Member -InputObject $user -Name "E164Number" -MemberType NoteProperty -Value ([regex]::matches(($user.telephoneNumber -replace "\D"),"^55\d{4}")[0].Value -replace '^55','+44123455') ; continue }
	"^$"				{ Write-Warning "Blank" ; continue } #Empty
	"^44\d{10}"			{ if ($user.telephoneNumber -match "^\+\d{12}") { Write-Warning "E.164: $($user.sAMAccountName), $($user.telephoneNumber)" } else { Write-Warning "No Match: $($user.sAMAccountName), `"$($user.telephoneNumber)`""} ; continue}
	default 			{ Write-Warning "No Match: $($user.sAMAccountName), `"$($user.telephoneNumber)`"" }
	}
}

As you can see from the above script block I have used a foreach loop that then uses a switch statement. The switch then checks to see if it can match one of the regular expressions patterns from the previous step and if successful adds a new note property named “E164Number” else it will display a warning. Let’s give it a try:

WARNING: E.164: Arabella.Thompson, +441234844048
WARNING: Blank
WARNING: No Match: Summer.Stevens, "Call me before 11AM 550997"

As predicted we have a few warnings, now let’s have a look at the projected telephone numbers:

$users | Select-Object Name, telephoneNumber, E164Number | Format-Table -AutoSize
Name              telephoneNumber                     E164Number
----              ---------------                     ----------
Georgina.Stewart  554861                              +441234554861
Georgina.Stewart1 01234 12 34 56 (only call on Tues)  +441234123456
Eden.Morris       01234-55(4487)                      +441234554487
Sarah.Patel       559642                              +441234559642
Marley.Adams      550998                              +441234550998
Mya.Lewis         (01234) 127075                      +441234127075
Sophia.Baker      (55)2912                            +441234552912
Filip.Rogers      (55)8996                            +441234558996
Noah.Mason        (Part Time)-(01234) 897740          +441234897740
Isabel.Webb       (01234) 036428                      +441234036428
Steven.Matthews   (01234) 957823                      +441234957823
Courtney.Walker   (01234) 679860                      +441234679860
Rayyan.Mitchell   07123 530656                        +447123530656
Mary.Graham       07123  822057                       +447123822057
Maisy.Barnes      07123 364461                        +447123364461
Arabella.Thompson +441234844048
Macy.Adams
Grace.Robinson    (01234) 538794                      +441234538794
Summer.Stevens    Call me before 11AM 550997
Frederick.Taylor  01234588239                         +441234588239

Very nice. Now to make this permanent we need to push the E164Number property into the telephoneNumber property on each of these users:

Backup first:

$users | Export-Csv C:\Support\MyBackup.csv -NoTypeInformation

Once the blackup task has been completed we can now go ahead and apply the new number. Notice that I used an if statement to check that there actually is a value in the E164Number property first and if there isn’t then it will skip that user.

 $users | ForEach-Object { if ($_.E164Number) {Set-QADUser -Identity $_ -PhoneNumber $_.E164Number}} 

Now to confirm the results:

$users | Get-QADuser | Select-Object Name, telephoneNumber | Format-Table -AutoSize
Name              telephoneNumber
----              ---------------
Georgina.Stewart  +441234554861
Georgina.Stewart1 +441234123456
Eden.Morris       +441234554487
Sarah.Patel       +441234559642
Marley.Adams      +441234550998
Mya.Lewis         +441234127075
Sophia.Baker      +441234552912
Filip.Rogers      +441234558996
Noah.Mason        +441234897740
Isabel.Webb       +441234036428
Steven.Matthews   +441234957823
Courtney.Walker   +441234679860
Rayyan.Mitchell   +447123530656
Mary.Graham       +447123822057
Maisy.Barnes      +447123364461
Arabella.Thompson +441234844048
Macy.Adams
Grace.Robinson    +441234538794
Summer.Stevens    Call me before 11AM 550997
Frederick.Taylor  +441234588239

Excellent, the majority of users now have an E164 formatted phone number.

All that’s left now is to identify users that still do not conform to this format so that they can be contacted, this can be achieved by using a Where-Object with another regular expression like so:

$users | Get-QADUser | Where-Object { $_.telephoneNumber -notmatch "^\+\d{12}" } | Select-Object name, email
Name           Email
----           -----
Macy.Adams     Macy.Adams@jfrmilner.lab
Summer.Stevens Summer.Stevens@jfrmilner.lab

Well that wraps up this post; I think you’ll agree it’s a nice real world example of using PowerShell with regular expressions to solve formatting issues.

Thanks for reading and until next time.

Regards,

jfrmilner


Part 3 of 3

First Post (Part 1 of 3)

Previous Post (Part 2 of 3)

Step 7 – Force a restart of the Servers

I now need to be sure that the servers are online. A quick way to test that a bunch of systems are online after a reboot it to use the Test-Connection cmdlet, for example to single ping the first ten servers that I demoted:

Test-Connection $Servers[0..9] -Count 1

After confirming the servers were back online I restarted them again, this oddly was necessary to freshen up the systems as they seemed a little flaky on the first restart after a demotion. Instead of using the Restart-Computer cmdlet I used the old shutdown.exe command as I found it more reliable dealing with unhappy servers, the command I used was:

$Servers[0..9] | % {  shutdown /r /m \\$($_) }

After the second restart I tested that PowerShell remoting with CredSSP authentication was again working using the same command as before:

icm $Servers { $ENV:ComputerName } -Authentication CredSSP -Credential $Cred

Step 8 – ReDCPROMO to RODC

Completing all the previous steps I was now ready to promote all the member servers to RODCs.

Much like the command used for step 6 here is the command I used to promote the first 10 servers.

icm $Servers[0..9] { dcpromo.exe /unattend:C:\SUPPORT\DCPROMORODCAnswerFile.txt | Tee-Object -filepath C:\SUPPORT\DCPROMORODCResultFile.txt } -Authentication CredSSP -Credential $Cred

Again the output for this command is both seen on the console and saved to the file C:\SUPPORT\DCPROMORODCResultFile.txt local to each server. Due to the IFM cache of AD the whole promotion completed very quickly, with some completing in only a few minutes.

You should now find the RODCs returned to the ‘Domain Controllers’ OU but now the DC Type will show Read-only.

Step 9 – Replicate the Passwords for the User and Computer Objects to the local RODC responsible for authentication

Now that all the DCs had been converted to RODCs I wanted to be sure that I pre-cached all the computer accounts and user account passwords local to that site using the groups created in Step 1. The command line tool RepAdmin can be used for such a task with the /rodcpwdrepl switch, this will first check the computer/user object is allowed to be cached and if confirmed will then add the password hash to the RODCs cache. Interestingly you can only add single user or computer account to the RODC cache using ADUC and not groups or as I needed all accounts contained within an OU.

I achieved this using the help of the Microsoft AD module. As mentioned before the AD Site Name in this company also matches up with the name of the OU holding the objects for that AD Site, so with that in mind I checked what the local site code was and then used that to construct the Distinguished Name of the OU, I then enumerated all users/computers and passed this onto the RepAdmin tool.

Here is the example of populating the RODC cache with User accounts:

icm $Servers[0..9] { import-module ActiveDirectory ; $siteCode = [DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().name  ; Get-ADUser -SearchBase "OU=Staff,OU=Users,OU=$($siteCode),OU=Schools,DC=domain1,DC=sch,DC=uk" -filter * | % { Repadmin /rodcpwdrepl D1-$($siteCode)-001 D1-DC-001 $_.DistinguishedName }} -Authentication CredSSP -Credential $Cred

And finally here is the example of populating the RODC cache with Computer Accounts:

icm $Servers[0..9] { import-module ActiveDirectory ; $siteCode = [DirectoryServices.ActiveDirectory.ActiveDirectorySite]::GetComputerSite().name  ; Get-ADComputer -SearchBase "OU=$($siteCode),OU=Schools,DC=domain1,DC=sch,DC=uk" -filter * | % { Repadmin /rodcpwdrepl D1-$($siteCode)-001 D1-DC-001 $_.DistinguishedName }} -Authentication CredSSP -Credential $Cred

You can easily confirm which accounts are cached on your server by selecting the properties on the RODC, selecting the “Password Replication Policy” tab, clicking the Advanced button and confirming that “Accounts Whose Password are stored on this Read-only Domain Controller” is selected from the drop down box.

I used this process to successfully convert over 150 full DCs to RODCs and I found it an excellent example of how PowerShell remoting can save you significant amounts of time and making a repetitive task like this a breeze!

A couple of final thoughts:

1. Be sure to monitor your replication over the coming days and get familiar with the repadmin command line utility.

2. It would be wise to remove the IFM caches from your local disks as soon as you are ready and be sure not to use them after the tombstone lifetime.

Thanks for reading.

Regards,

jfrmilner

This post is provided “AS IS” with no warranties or guarantees, and confers no rights.

[/sourcecode] 


Part 2 of 3

Previous Post (Part 1 of 3)

Final Post (Part 3 of 3)

Step 4 – Enable CredSSP (multihop-authentication)

Before I could start using DCPROMO I needed to enable multihop-authentication using CredSSP. Now I will not explain this in detail, instead I would like to refer you to the excellent post by Ravikanth Chaganti – http://www.ravichaganti.com/blog/?p=1230

As this will need to be allowed in bulk then I suggest you use the GPO :
Computer Configuration/Administrative Templates/Windows Components/Windows Remote Management (WinRM)/WinRM Service/Allow CredSSP authentication

This policy setting allows you to manage whether the Windows Remote Management (WinRM) service accepts CredSSP authentication from a remote client. Once enabled this policy sets the WinRM service to accept CredSSP authentication from a remote client.

The management client will also need to be configured to allow credentials to be passed onto remote clients. In my case I only wanted to allow this on my management server so I used this command on that system:

Enable-WSManCredSSP -Role Client -DelegateComputer *.domain1.sch.uk

This command once enabled basically allows me to pass my credentials onto any system in the domain that has the WinRM service configured to accept CredSSP authentication.

I recommend that you give this a test run before moving onto the next step.
Create a variable $Cred and store the account you’re going to be using for the DCPROMO commands, for example:

$Cred = Get-Credential DOMAIN1\DomainAdmin

Then use Invoke-Command to echo back the server name(s) of each remote system:

icm $Servers { $ENV:ComputerName } -Authentication CredSSP -Credential $Cred

Step 5 – Create DCPROMO demote and RODC promote answer files using a word replace template method

The idea of this is quite simple, first create a DCPROMO demote and a RODC promotion answer files from a test/lab server and use these as templates. The template will be loaded into RAM and if necessary a word replace will be performed creating the unique answer file needed, the file will then be saved onto the target server. This was the code used to create the two answer files:

$SiteCodes| % {
$DCPROMODemote = gc 'C:\Scripts\DCPROMODemoteTemplate.txt'
$DCPROMODemote | Out-File "\\D1-$($_)-001\C$\Support\DCPROMODemote.txt" -Encoding ascii
Write-Host "File Saved: \\D1-$($_)-001\C$\Support\DCPROMODemote.txt"
$DCPROMORODCTemplate = gc 'C:\Scripts\DCPROMO-RODC-Template.txt'
$DCPROMORODCAnswerFile = $DCPROMORODCTemplate -replace '',$($_)
$DCPROMORODCAnswerFile | Out-File "\\D1-$($_)-001\C$\Support\DCPROMORODCAnswerFile.txt" -Encoding ascii
Write-Host "File Saved: \\D1-$($_)-001\C$\Support\DCPROMORODCAnswerFile.txt"
}

Next the answer file used for the DCPROMO demotion, no word replacement was necessary.

; DCPROMO unattend file - Auth jfrmilner
; Usage:
;   dcpromo.exe /unattend:C:\SUPPORT\DCPROMODemote.txt
;
[DCInstall]
; Demotion
RetainDcMetadata=No
IsLastDCInDomain=No
AdministratorPassword=passwordgoeshere
RebootOnCompletion=Yes

This was the template answer file used for the DCPROMO RODC promotion. All text that matched ‘<SiteCode>’ was replaced with the Site Code passed from the pipeline, for example if the first value in the $SiteCode variable was ‘AFCPS’ then the line ‘PasswordReplicationAllowed=”DOMAIN1\<SiteCode>-Laptops”’ would be changed to ‘PasswordReplicationAllowed=”DOMAIN1\AFCPS-Laptops”’.

; DCPROMO unattend file - Auth jfrmilner
; Usage:
;   dcpromo.exe /unattend:C:\Support\DCPROMORODCAnswerFile.txt
;
[DCInstall]
; Read-Only Replica DC promotion
ReplicaOrNewDomain=ReadOnlyReplica
ReplicaDomainDNSName=domain1.sch.uk
; RODC Password Replication Policy
PasswordReplicationDenied="BUILTIN\Administrators"
PasswordReplicationDenied="BUILTIN\Server Operators"
PasswordReplicationDenied="BUILTIN\Backup Operators"
PasswordReplicationDenied="BUILTIN\Account Operators"
PasswordReplicationDenied="DOMAIN1\Denied RODC Password Replication Group"
PasswordReplicationAllowed="DOMAIN1\Allowed RODC Password Replication Group"
PasswordReplicationAllowed="DOMAIN1\<SiteCode>-Laptops"
PasswordReplicationAllowed="DOMAIN1\<SiteCode>-Pupils"
PasswordReplicationAllowed="DOMAIN1\<SiteCode>-Staff"
PasswordReplicationAllowed="DOMAIN1\<SiteCode>-Workstations"
; D1-RODC-Admins will include the Service Desk Global Group
DelegatedAdmin="DOMAIN1\D1-RODC-Admins"
SiteName=<SiteCode>
InstallDNS=Yes
ConfirmGc=Yes
CreateDNSDelegation=No
UserDomain=domain1.sch.uk
UserName=DOMAIN1\DomainAdmin
Password= passwordgoeshere
ReplicationSourcePath="C:\Support\IFM_RODC"
ReplicationSourceDC=D1-DC-001.domain1.sch.uk
DatabasePath="C:\Windows\NTDS"
LogPath="C:\Windows\NTDS"
SYSVOLPath="C:\Windows\SYSVOL"
SafeModeAdminPassword= passwordgoeshere
; Run-time flags (optional)
; CriticalReplicationOnly=Yes
RebootOnCompletion=Yes

I should point out that the passwords in these files are stored in clear text when they are created. The passwords are removed from each file after they have been used as answer files for the DCPROMO tasks, with this in mind you should consider carefully which account you use and not to create these files until you are ready to use them.

Step 6 – DCPROMO demote the DC to a Member Server

Before I began to demote servers I created a new OU titled ‘DCPROMO Holding’, this OU also had a GPO linked that would enable PowerShell remoting. By default a demoted server would be returned to the Computer OU and because of this would have all GPO’s removed.

With all the prerequisites out of the way I could now start demoting Domain Controllers back to Member Servers.
I did about ten servers at a time and I suggest you do the same. I did this by using the Range operator, for example say $Servers was a 100 item array, writing $Servers[0..9] would select the first 10 and $Servers[10..19] would be the next ten etc..

This is the code I used for the first 10:

icm $Servers[0..9] { dcpromo.exe /unattend:C:\SUPPORT\DCPROMODEMOTE.txt | Tee-Object -filepath C:\SUPPORT\DCPROMODEMOTEResultFile.txt } -Authentication CredSSP -Credential $Cred

Note the Tee-Object cmdlet, this allows the output of the promotion to appear on the console and also a text file. It would not be difficult to parse the text file for detailed information or collect the data and concatenate this information into a single report.

All went to plan, the computer objects for all ten systems were returned to the Computers OU. Checking this OU is actually the quickest way to get an overall feel of the success of the bulk demotion. I then moved the computer objects from the Computers OU to the ‘DCPROMO Holding’ OU.

Thanks for reading. I will post the final part over the course of the week so please check back.

Regards,

jfrmilner

; DCPROMO unattend file – Auth jfrmilner

; Usage:

;   dcpromo.exe /unattend:C:\Support\DCPROMORODCAnswerFile.txt

;

[DCInstall]

; Read-Only Replica DC promotion

ReplicaOrNewDomain=ReadOnlyReplica

ReplicaDomainDNSName=domain1.sch.uk

; RODC Password Replication Policy

PasswordReplicationDenied=”BUILTIN\Administrators”

PasswordReplicationDenied=”BUILTIN\Server Operators”

PasswordReplicationDenied=”BUILTIN\Backup Operators”

PasswordReplicationDenied=”BUILTIN\Account Operators”

PasswordReplicationDenied=”DOMAIN1\Denied RODC Password Replication Group”

PasswordReplicationAllowed=”DOMAIN1\Allowed RODC Password Replication Group”

PasswordReplicationAllowed=”DOMAIN1\<SiteCode>-Laptops”

PasswordReplicationAllowed=”DOMAIN1\<SiteCode>-Pupils”

PasswordReplicationAllowed=”DOMAIN1\<SiteCode>-Staff”

PasswordReplicationAllowed=”DOMAIN1\<SiteCode>-Workstations”

; D1-RODC-Admins will include the Service Desk Global Group

DelegatedAdmin=”DOMAIN1\D1-RODC-Admins”

SiteName=<SiteCode>

InstallDNS=Yes

ConfirmGc=Yes

CreateDNSDelegation=No

UserDomain=domain1.sch.uk

UserName=DOMAIN1\DomainAdmin

Password= passwordgoeshere

ReplicationSourcePath=”C:\Support\IFM_RODC”

ReplicationSourceDC=D1-DC-001.domain1.sch.uk

DatabasePath=”C:\Windows\NTDS”

LogPath=”C:\Windows\NTDS”

SYSVOLPath=”C:\Windows\SYSVOL”

SafeModeAdminPassword= passwordgoeshere

; Run-time flags (optional)

; CriticalReplicationOnly=Yes

RebootOnCompletion=Yes


Part 1 of 3

Next Post (Part 2 of 3)

Final Post (Part 3 of 3)

Scenario: This month presented me with an interesting issue that I would like to share. In effort to provide Role Based Accounts (RBA) to both 1st and 2nd Line Support it became clear that 2nd Line needed to locally administer DCs as they also acted as File Servers at some sites, but being a member of Domain Admins was overkill and as such went against Least Privilege best practices. The solution to this issue was to convert all non-dedicated DCs or FSMO Role holders to Read Only Domain Controllers (RODCs). RODCs run as member servers with a local Security Accounts Manager (SAM) and run AD as an isolated service; this allowed me to configure dedicated local administration. This was achieved by adding the 2nd Line role based account group to the BUILTIN\Administrators group on each local RODC. There are other benefits to RODCs but I’ll not cover those in this post.

Now the issue is that this particular Domain has over 150 DCs that need to be converted to RODCs with the majority being on slow WAN links. I needed to find a way to do this with minimum network traffic, in a consistent manor (to keep the Change Manager happy) and quickly. This was how I did it:

Summary of steps to perform:
1. Sort all Computer and User Objects into Groups that will be used for Password Replication Policies.
2. Set the DNS Server options on each DC NIC to point back to the Core DCs in Head Office.
3. Use the “Install From Media” option of NTDSUTIL to create two local cache copies of the Active Directory. One will be for RODCs and the other will be for Full DCs, the latter could be used for Roll Back scenarios should I wish to restore the DC to its original state.
4. Enable CredSSP used for multihop authentication.
5. Create DCPROMO demote and RODC promote answer files using a word replace template method.
6. DCPROMO demote the DC to a Member Server.
7. Force a restart of the Servers.
8. ReDCPROMO to RODC.
9. Replicate the Passwords for the User and Computer Objects to the local RODC responsible for authentication.

Step 1 – Sort all Computer and User Objects into Groups that will be used for Password Replication Policies

Luckily this step was particularly straight forward for me due to the fact that we use a five letter code for each customer and this code is used to reference the Site, Parent OU and is also used to prefix the Computer Objects. All I needed to do was create an array of all the Site Codes and store it in a variable called $siteCode, then pass that through to a foreach loop that collected all the Computer Objects for that OU and then added them to a Group following a simple pattern match.

This is the code that I used for the first batch of ten (Please note that I used the free Quest AD cmdlets for this and some of the other steps and as such they will need to be installed for this to work):

$siteCodes[0..9] | % {
$siteCode = $($_)
#Sort Computer Membership
$Comps = Get-QADComputer -OU domain1.sch.uk/Schools/$siteCode | ? { $_.memberof.count -ne 1 }
$Comps | ? { $_.Name -match "$($siteCode)W" } | % { Add-QADGroupMember -Identity "$($siteCode)-Workstations" -Member $_.DN }
$Comps | ? { $_.Name -match "$($siteCode)L" } | % { Add-QADGroupMember -Identity "$($siteCode)-Laptops" -Member $_.DN }
}

I also used the below code to list any Computer Objects that were not a member of a single Group, which for me mainly highlighted incorrectly named systems.

$siteCodes[0..9] | % {
$siteCode = $($_)
#Check for Problem Systems
Get-QADComputer -OU domain1.sch.uk/Schools/$siteCode | select name,@{name='MemberShipCount';expression={$_.memberof.count}} | ? { $_.Membershipcount -ne 1 }
}

Step 2 – Set the DNS Server options on each DC NIC to point back to the Full DCs in Head Office.

If you’re going to DCPROMO demote a Server from a DC to a Member Server you will need to be sure that the Server does not reference itself as a DNS Server. I needed to find the teamed NIC that is configured with the DNS registered IP Address of that Server and reconfigure the DNS Search Order. I did this by using PowerShell Remoting to first collect the IP Address from DNS that references the Servers host name and then that information to find the NIC with that configured, and then use a WMI method to set this NICs DNS Search Order to an Array using a couple of DNS Server from Head Office.

#Set DNS
icm $Servers -ScriptBlock {  $DNSArray = '10.10.10.10','10.10.10.11' ; (Get-WmiObject -class win32_networkadapterconfiguration | ? { $_.IPAddress -eq ( ([System.Net.Dns]::GetHostEntry($ENV:ComputerName).AddressList | ? { $_.AddressFamily -eq 'InterNetwork' }).IPAddressToString ) } ).SetDNSServerSearchOrder($DNSArray) }

To Check my configuration Changes:

#Audit DNS
icm $Servers -ScriptBlock { Get-WmiObject -class win32_networkadapterconfiguration | ? { $_.IPAddress -eq ( ([System.Net.Dns]::GetHostEntry($ENV:ComputerName).AddressList | ? { $_.AddressFamily -eq 'InterNetwork' }).IPAddressToString )} } | select __SERVER,IPAddress,DNSServerSearchOrder

Step 3 – Use the “Install From Media” (IFM) option of NTDSUTIL to create two local cache copies of the Active Directory.
One will be for RODCs and the other will be for Full DCs, the latter could be used for Roll Back scenarios should I wish to restore the DC to its original state.

To begin with I needed to increase the amount of memory that a PowerShell remoting session can use from the default 150MB to 1GB due to the memory hungry IFM process. This setting can be restored as soon as you have finished creating your IFM snapshots. Using the Group Policy Management Console (GPMC) I edited the policy that manages Remoting and edited the setting Computer Configuration/Administrative Templates/Windows Components/Windows Remote Shell/ Specify maximum amount of memory in MB per Shell, this option should look like Figure1.

RODC-GPOMaxMemPerShellMB

RODC-GPOMaxMemPerShellMB

The next step is to create the actual IFM snapshots using NTDSUTIL. This again was achieved using PowerShell Remoting; here is the code I used:

icm $Servers -ScriptBlock { ntdsutil "Activate Instance NTDS" ifm "Create SYSVOL Full C:\Support\IFM_Full" q q >> C:\Support\IFM_Full.log }
icm $Servers -ScriptBlock { ntdsutil "Activate Instance NTDS" ifm "Create SYSVOL RODC C:\Support\IFM_RODC" q q >> C:\Support\IFM_RODC.log }

Thanks for reading. I will post the remaining two parts over the course of the week so please check back.

Regards,

jfrmilner


Today I’m going to show an example of a quick script to create Organisational Units (OU). I wrote this script over a year ago when I needed to create an OU structure for a hosted Active Directory (AD) domain and it was successfully used to create thousands of OUs. I have chosen to use the Quest tools because this domain is 2008 and I didn’t have the option of using Microsoft’s new AD cmdlets.

The required structure was the following:

\---<Site Code>
 +---Computers
 +---Groups
 |   +---Resource Groups
 |   |   +---Printers
 |   |   \---Shares
 |   \---User Groups
 +---LFTs
 \---Users
 +---AdminStaff
 +---Pupils
 \---Staff

This particular hosted AD was going to be used by a large number of Schools. As above, the variable “Site Code” of each school will used as the name for the parent OU and each OU will have the “School Name” added as the description. I first created a CSV file with the necessary data, an example:

Site Code School Name
AFCPS Alford Primary School
ALLIN Allington and Sedgebrook CE Primary
AMTOF Amber Hill Toftstead Primary School
ANCAS Ancaster CE Primary School
BASSI Bassingham Primary School
BBCPS Brant Broughton CE and Methodist Primary School
BDJOI Bardney CE and Methodist Primary School
BKNAL Bucknall Primary School
BLYTO Blyton Cum Laughton CE Primary School
BMBER Baumber Primary School

I first need to load the above CSV files into a variable for easy access:

$DomainLookup = Import-Csv C:\Scripts\DomainLookup.csv

As I planned to only create half the OUs in the CSV file and I wanted the flexibility to create OUs ad-hoc I decided it would be best to make a scripted function that accepted data from the pipeline , here’s is the code:

function New-SchoolsOUs{

BEGIN {}

PROCESS 
	{
	$SchoolOU = 'domain1.sch.uk/Schools'
	New-QADObject -Type OrganizationalUnit -ParentContainer $SchoolOU -Name $_."Site Code" -Description $_."School Name" -OutVariable Parent
	New-QADObject -Type OrganizationalUnit -ParentContainer $Parent[0].dn -Name 'Groups' -Description $_."School Name" -OutVariable Groups
	New-QADObject -Type OrganizationalUnit -ParentContainer $Groups[0].dn -Name 'Resource Groups' -Description $_."School Name" -OutVariable ResourceGroups
	New-QADObject -Type OrganizationalUnit -ParentContainer $ResourceGroups[0].dn -Name 'Printers' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $ResourceGroups[0].dn -Name 'Shares' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $Groups[0].dn -Name 'User Groups' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $Parent[0].dn -Name 'LFTs' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $Parent[0].dn -Name 'Users' -Description $_."School Name" -OutVariable UsersOU
	New-QADObject -Type OrganizationalUnit -ParentContainer $UsersOU[0].dn -Name 'Pupils' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $UsersOU[0].dn -Name 'AdminStaff' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $UsersOU[0].dn -Name 'Staff' -Description $_."School Name"
	New-QADObject -Type OrganizationalUnit -ParentContainer $Parent[0].dn -Name 'Computers' -Description $_."School Name"
	}
	
END {}

}

This allows me the flexibility to for example create the first 5 Schools:

$DomainLookup[0..4] | New-SchoolsOUs

Or if needed Schools by name:

$DomainLookup | ? { $_.’Site Code’ -eq ‘BKNAL’ } | New-SchoolsOUs

Now I understand that the above function is quite specific for this task but I’m sure if you are in a similar situation you can edit it as you see fit.


The objective of this post is to provide instruction on enabling PowerShell 2.0 remoting capabilities in non 2008 R2 domains. In this example I will demonstrate in a 2003 domain environment.

Prerequisites

To complete this work instruction you will need the following:

1.     PowerShell version 2.0 will need to be installed on all systems that will be used for remoting. On down level clients PowerShell 2.0 is bundled into the Windows Management Framework.

The Windows Management Framework makes some updated management functionality of Windows 7 and Windows Server 2008 R2 available on Windows XP, Windows Server 2003, Windows Vista, and Windows Server 2008.

Windows Management Framework contains the following three components:

  • Windows Remote Management (WinRM) 2.0
  • Windows PowerShell 2.0
  • Background Intelligent Transfer Service (BITS) 4.0.

I recommend that WSUS is used as the deployment method for the Windows Management Framework on down level clients and it will be assumed from this point that this is already installed as this was released some time ago.

2.     The ‘Windowsremotemanagement.adm’ file will need to be imported into the GPO later in the process. This will need to be downloaded as part of the ‘WS-Management v1.1 Package’ at the following link http://support.microsoft.com/kb/936059 . Once downloaded install onto the system you will be using to create and edit GPO’s.

Create the Group Policy Object (GPO)

Launch the Group Policy Management (GPMC) and create a new GPO titled ‘Windows Remote Management’.

Edit the newly created GPO and add both the ‘Windowsremoteshell.adm’ and ‘Windowsremotemanagement.adm’ Templates, if either of the adm files cannot be found please ensure that the ‘WS-Management Package’ is downloaded and installed as advised in the prerequisites.

Now Expand trough the Computer Configuration policy structure to ‘Administrative Templates > Windows Components > Windows Remote Management (WinRM) > WinRM Service’ and select ‘Allow automatic configuration of listeners’

This policy setting allows you to manage whether the Windows Remote Management (WinRM) service automatically starts and listens on the network for requests on HTTP over port 5985 and if enabled HTTPS on 5986.

Note: The above ports are correct as of WinRM 2.0. The previous version WinRM 1.1 used the default HTTP and HTTPS ports 80 and 443 respectively, the documentation on TechNet and also on the help section for this GPO have not been updated to reflect this at the time of writing.

Enable the GPO and complete the IPv* filters text boxes, an example of a relaxed configuration can be seen in Figure 1

Figure 1

The service listens on the addresses specified by the IPv4 and IPv6 filters. IPv4 filter specifies one or more ranges of IPv4 addresses and IPv6 filter specifies one or more ranges of IPv6addresses. If specified, the service enumerates the available IP addresses on the computer and uses only addresses that fall within one of the filter ranges.

You can use asterisk (*) to indicate that the service listens on all available IP addresses on the computer. When * is used, other ranges in the filter are ignored. If the filter is left blank, the service does not listen on any addresses.

For example, if you want the service to listen only on IPv4 addresses, leave the IPv6 filter empty.

Ranges are specified using the syntax IP1-IP2. Multiple ranges are separated using ‘,’ (comma) as the delimiter.

Example IPv4 filters:

2.0.0.1-2.0.0.20, 24.0.0.1-24.0.0.22

Example IPv6 filters:

3FFE:FFFF:7654:FEDA:1245:BA98:0000:0000-3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562.

Confirm WinRM Listener

Assuming the GPO in now Enabled and Linked to an OU containing the computers targeted for remoting, log onto one of the client machines in the domain, run “gpupdate /force” or wait for the group policy to be deployed to the client machine.

To enumerate the currently applied GPO use the “gpresult” command and confirm that the GPO titled ‘Windows Remote Management’ is listed in the ‘Applied Group Policy Objects’ section.

To enumerate the WinRM listener(s) and confirm the GPO configuration successfully applied use the following command:

winrm e winrm/config/listener

A new GPO source listener should have been created automatically and the output should resemble Figure 2

Figure 2

Open up a PowerShell console on a system connected to the same domain and create an array of computers to which you have just enabled remoting e.g.

$Servers = 'SERVER01','SERVER02','SERVER03','SERVER04',’SERVER05’

Using the Invoke-Command cmdlet I will demonstrate running a simple script block to collect the last boot time:

icm -ComputerName $Servers -ScriptBlock { [System.Management.ManagementDateTimeConverter]::ToDateTime((gwmi Win32_OperatingSystem).LastBootUpTime) } | ft -a pscomputername,datetime

The output of the above command should resemble Figure 3

Figure 3

Over the last year I have been using PowerShell 2.0 remoting capabilities extensively and I will follow up with a few example over the coming weeks.

For those interested I recommend the following for further reading:

Microsoft help documentation on ‘about_Remote_Troubleshooting’

http://technet.microsoft.com/en-us/library/dd347642.aspx

Windows Management Framework Core package (Windows PowerShell 2.0 and WinRM 2.0)

http://support.microsoft.com/kb/968930/en-gb

Windows Remote Management feature for Windows Server 2003 and in Windows XP

http://support.microsoft.com/kb/936059

Thanks for reading,

jfrmilner