, ,

Automated Active Directory Deployment with PowerShell

Powershell

For a small presentation at KTSI I created a PowerShell script will automatically will deploys Active Directory Servers, adds other member servers, creates Organization Units and adds users via Powershell Remoting. As source there is a XML configuration file and CSV files for User Data.

Install AD with Powershell

This script is just for Lab deployments not for production, and it is not perfect, but I think maybe some people will enhance this script with their own code.

I do not support this script. it is just something I need to deploy my test environments and nothing more. More it shows diffrent

You can find more information about it works in this document.

XML Config file:


<?xml version="1.0" encoding="utf-8"?>
<lab>
<config>
<servers>
<server name="ADS01" ip="192.168.100.11" id="1" adminpw="passw0rd"/>
<server name="ADS02" ip="192.168.100.12" id="2" adminpw="passw0rd"/>
</servers>
<ad>
<domain name="ktsi.local" netbiosname="ktsi" forestlevel="4" domainlevel="4" safemodepw="passw0rd" />
</ad>
<ous>
<ou name="UserAccounts" path="DC=KTSI,DC=LOCAL" />
<ou name="BASEL" path="OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="CHICAGO" path="OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="NEWYORK" path="OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="SALES" path="OU=BASEL,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="IT" path="OU=BASEL,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="ADMINISTRATION" path="OU=BASEL,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="PRODUCTION" path="OU=BASEL,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="SALES" path="OU=CHICAGO,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="IT" path="OU=CHICAGO,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="ADMINISTRATION" path="OU=CHICAGO,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="PRODUCTION" path="OU=CHICAGO,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="SALES" path="OU=NEWYORK,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="IT" path="OU=NEWYORK,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="ADMINISTRATION" path="OU=NEWYORK,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
<ou name="PRODUCTION" path="OU=NEWYORK,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
</ous>
<users>
<file name="users.csv" path="OU=ADMINISTRATION,OU=BASEL,OU=USERACCOUNTS,DC=KTSI,DC=LOCAL" />
</users>
<members>
<member name="PC101" ip="192.168.100.21" />
<member name="PC101" ip="192.168.100.22" />
<member name="PC101" ip="192.168.100.23" />
</members>
</config>
</lab>

The PowerShell Script:


 

# ---------------------------------------------------------------------------------------------- #

# Powershell AD Creator $Rev: 748 $

# (c) 2011 Thomas Maurer. All rights reserved.

# created by Thomas Maurer

# last Update by $Author: tmaurer $ on $Date: 2010-11-22 14:07:36 +0100 (Mo, 04 Nov 2011) $

# ---------------------------------------------------------------------------------------------- #

 

 

# Set Debug Mode (true/false)

$DebugOn = $false

 

 

 

#region [INFO BLOCK]

# INFO

Write-Host " " -BackgroundColor Black -ForegroundColor White

Write-Host "PowerShell AD Deplyoment" -BackgroundColor Black -ForegroundColor White

Write-Host " " -BackgroundColor Black -ForegroundColor White

Write-Host "Enter Credentials for Remote System:" -BackgroundColor Black -ForegroundColor White

#endregion

 

 

#region [VERBOSE BLOCK]

# Verbose Loop

 

if ($DebugOn -eq $true){

#Debug

Clear-Host

Write-Host "Debug mode on" -BackgroundColor Red -ForegroundColor White

$VerbosePreference = "Continue"

}

else {

#NoDebug

Clear-Host

$VerbosePreference = "SilentlyContinue"

}

#endregion

 

#region [CONFIG BLOCK]

# Get XML Information and create some funny objects...

$global:xmlData = get-content ".\config.xml"

 

# Get Credentials for Remotesystems

$cred = Get-Credential

 

$global:xmlServer01 = $xmlData.lab.config.servers.server | Where-Object { $_.id -eq "1"}

$global:xmlServer02 = $xmlData.lab.config.servers.server | Where-Object { $_.id -eq "2"}

$global:xmlDomain = $xmlData.lab.config.ad.domain

$global:xmlOUS = $xmlData.lab.config.ous

 

#endregion

 

#region [FUNCTION BLOCK]

 

function ConvertTo-Scriptblock {

<#

Function to Convert a String into a Script Block

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$string

)

Begin {

Write-Verbose "Starting Begin Section"

}

Process {

Write-Verbose "Starting Process Section"

try {

Write-Verbose "Convert String to Scriptblock"

$sb = [scriptblock]::Create($string)

return $sb

}

catch {

Write-Host "Could not convert String to Scriptblock" $Error[0]

 

}

}

End {

Write-Verbose "Starting End Section"

}

}

 

 

function Enable-ICMP {

<#

Function to enable ICMP

 

Enable-ICMP -hostip $xmlServer01.ip

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostip

)

begin {

# Write Command

$step001 = @"

netsh firewall set icmpsetting 8

"@

}

process {

try {

# Run Scriptblock

Write-Host "Enable ICMP on " $hostip "..." -BackgroundColor Black -ForegroundColor White

 

Invoke-Command -ComputerName $hostip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "ICMP successfully enabled..." -BackgroundColor Green -ForegroundColor Black

 

 

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Could not enable ICMP"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

 

 

function Create-ADServer {

<#

Function to Create AD on the first Server

#>

begin {

# Prepair for First AD Server

Write-Host "Preparing Script Block for " + $xmlServer01.name + "..." -BackgroundColor Black -ForegroundColor White

$domainName = $xmldomain.name

$domainNetBiosName = $xmldomain.netbiosname

$domainForestLevel = $xmldomain.forestlevel

$domainDomainLevel = $xmldomain.domainlevel

$domainSafemodePW = $xmldomain.safemodepw

$adTempFolder = "temp4"

$adTempPath = "C:\" + $adTempFolder

$adFilePath = $adTempPath + "\adinstall.txt"

}

process {

try {

# Script Block for First AD Server

$step001 = @"

New-Item -ItemType directory $adTempPath

New-Item -ItemType file $adFilePath

Add-Content $adFilePath "[DCINSTALL]"

Add-Content $adFilePath "InstallDNS=yes"

Add-Content $adFilePath "NewDomain=forest"

Add-Content $adFilePath "NewDomainDNSName=$domainName"

Add-Content $adFilePath "DomainNetBiosName=$domainNetBiosName"

Add-Content $adFilePath "ReplicaOrNewDomain=domain"

Add-Content $adFilePath "ForestLevel=$domainForestLevel"

Add-Content $adFilePath "DomainLevel=$domainDomainLevel"

Add-Content $adFilePath "RebootOnCompletion=yes"

Add-Content $adFilePath "SafeModeAdminPassword=$domainSafemodePW"

Write-Host "Unattend File created successfully..." - -BackgroundColor Green

dcpromo /unattend:$adFilePath

"@

# Run Scriptblock

Write-Host "Configure " $xmlServer01 "..." -BackgroundColor Black -ForegroundColor White

Invoke-Command -ComputerName $xmlServer01.ip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "Configuration for " $xmlServer01.name " successfully..." -BackgroundColor Green -ForegroundColor Black

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Could not Create AD"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

function Join-AD {

<#

Function to add second AD Server

 

Join-AD -hostname $xmlServer02.name -hostip $xmlServer02.ip -dnsip $xmlServer01.ip -domainName $xmldomain.name -adminpw $xmlServer01.adminpw

 

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostname,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostip,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$dnsip,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$domainName,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$adminpw

)

begin {

# Prepair for Second AD Server

}

process {

try {

# Script Block join AD

$step001 = @"

netsh interface ipv4 add dnsserver name="Local Area Connection" address=$dnsip index=1

netdom join $hostname /domain:"$domainName" /userd:administrator /passwordd:$adminpw /REBoot

"@

 

# Run Scriptblock

Write-Host "Adding " $hostname " to domain..." -BackgroundColor Black -ForegroundColor White

 

Invoke-Command -ComputerName $hostip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "Domainjoin for " $hostname " successfully..." -BackgroundColor Green -ForegroundColor Black

 

 

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Could not add System to AD"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

function Add-ADServer {

<#

Function to add a second AD to the Domain

#>

begin {

# Prepair for First AD Server

Write-Host "Preparing Script Block for DCPROMO " $xmlServer02.name "..." -BackgroundColor Black -ForegroundColor White

$server01ip = $xmlServer01.ip

$domainName = $xmldomain.name

$server01pw = $xmlServer01.adminpw

$domainSafemodePW = $xmldomain.safemodepw

}

process {

try {

# Script Block for First AD Server

$step001 = @"

Dcpromo /unattend /replicaOrnewDomain:replica /replicaDomainDNSName:$domainName /ConfirmGC:yes /username:administrator /Password:$server01pw /safeModeAdminPassword:$domainSafemodePW

"@

# Run Scriptblock

Write-Host "Running DCPROMO " $xmlServer02.name "..." -BackgroundColor Black -ForegroundColor White

Invoke-Command -ComputerName $xmlServer02.ip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "DCPROMO " $xmlServer02.name " successfully..." -BackgroundColor Green -ForegroundColor Black

}

catch {

throw "ERROR: Could not add Second Server to AD"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

function Add-ADOU {

<#

Function to add OU to AD

 

Add-ADOU -hostip $xmlServer01.ip -name $namedings -path $pathdings

 

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostip,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$name,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$path

)

begin {

# Prepair for Second AD Server

}

process {

try {

# Script Block join AD

$step001 = @"

Import-Module ActiveDirectory

New-ADOrganizationalUnit -name "$name" -Path "$path"

"@

 

# Run Scriptblock

Write-Host "Adding OU " $name " to " $path "..." -BackgroundColor Black -ForegroundColor White

 

Invoke-Command -ComputerName $hostip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "OU added " $name " successfully..." -BackgroundColor Green -ForegroundColor Black

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Could not add OU to AD"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

function Add-UsersfromFile {

<#

Function to add OU to AD

 

Add-UsersfromFile -hostip $xmlServer01.ip -filename $xmlData.lab.config.users.file.name -path $xmlData.lab.config.users.file.path

 

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostip,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$SamAccountName,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$UserPrincipalName,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$name,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$DisplayName,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$GivenName,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$SurName,

[Parameter(

Mandatory = $false,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$Manager,

[Parameter(

Mandatory = $false,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$Department,

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$path

)

begin {

 

}

process {

try {

# Script Block join AD

$step001 = @"

Import-Module ActiveDirectory

New-ADUser -SamAccountName $SamAccountName -UserPrincipalName $userprinicpalname -DisplayName $displayname -GivenName $GivenName -SurName $SurName -Path "$path" -AccountPassword (ConvertTo-SecureString "test41;" -AsPlainText -force)

"@

 

# Run Scriptblock

Write-Host "Adding Users " $userprinicpalname " to " $path "..." -BackgroundColor Black -ForegroundColor White

 

Invoke-Command -ComputerName $hostip -ScriptBlock (ConvertTo-Scriptblock -String $step001) -Credential $cred

Write-Host "Users " $userprinicpalname " added successfully..." -BackgroundColor Green -ForegroundColor Black

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Could not add Users to AD"

}

}

end {

# Cleanup

$step001 = $null

}

}

 

 

function WaitFor-Host {

<#

Function to wait for Host after Reboot

 

WaitFor-Host -hostip $xmlServer01.ip -service servicename

 

#>

Param(

[Parameter(

Mandatory = $true,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$hostip,

[Parameter(

Mandatory = $false,

ParameterSetName = '',

ValueFromPipeline = $true)]

[string]$service

)

begin {

#

}

process {

try {

# Wait until Server is offline

Write-Host "Waiting for Reboot " $hostip "..." -BackgroundColor Black -ForegroundColor White

Start-Sleep -Seconds 10

 

# Wait for Server Reboot

Write-Host "Waiting for Host " $hostip "..." -BackgroundColor Black -ForegroundColor White

while (!(Test-Connection $hostip -quiet)) {

Write-Host 'Unreachable!'

}

Write-Host "Server up..." -BackgroundColor Green -ForegroundColor Black

Start-Sleep -Seconds 30

}

catch {

# Sending status to CP and EventLog

throw "ERROR: Waiting for reboot failed"

}

}

end {

}

}

 

 

 

 

#endregion

 

 

 

 

#region [MAIN BLOCK]

 

# Enable ICMP

Enable-ICMP -hostip $xmlServer01.ip

Enable-ICMP -hostip $xmlServer02.ip

 

# Create first AD Server

Create-ADServer

Start-Sleep -Seconds 30

WaitFor-Host -hostip $xmlServer01.ip

 

# Add Second AD Server to Domain

Join-AD -hostname $xmlServer02.name -hostip $xmlServer02.ip -dnsip $xmlServer01.ip -domainName $xmldomain.name -adminpw $xmlServer01.adminpw

Start-Sleep -Seconds 30

WaitFor-Host -hostip $xmlServer02.ip

 

# Run DCPROMO on Second AD Server

Add-ADServer

 

# Add OUs to Domain

foreach ($ou in $xmlOUS.ou){

Add-ADOU -hostip $xmlServer01.ip -name $ou.name -path $ou.path

}

 

 

# Add Users to Domain

foreach ($file in $xmlData.lab.config.users.file){

$tempfile = ".\" + $file.name

Import-Csv $tempfile | Foreach-Object {

$userprinicpalname = $_.SamAccountName + "@" + $xmlDomain.name

Add-UsersfromFile -hostip $xmlServer01.ip -SamAccountName $_.SamAccountName -UserPrincipalName $userprinicpalname -Name $_.name -DisplayName $_.name -GivenName $_.GivenName -SurName $_.SurName -Manager $_.Manager -Department $_.Department -Path $file.path

}

}

 

 

 

# Add Members to Domain

foreach ($member in $xmlData.lab.config.members.member){

Join-AD -hostname $member.name -hostip $member.ip -dnsip $xmlServer01.ip -domainName $xmldomain.name -adminpw $xmlServer01.adminpw

}

 

#endregion

 

5 responses

  1. Jeffery Hicks Avatar

    Can you make these files available as text file downloads? Copying and pasting from web pages doesn’t always work. Great example of using an XML configuration file.

    1. Thomas Maurer Avatar

      You can download the files here:
      https://skydrive.live.com/redir.aspx?cid=7298a00d5b74ec3c&resid=7298A00D5B74EC3C!764&parid=7298A00D5B74EC3C!731

      But don’t be surprise I did not have enough time to fix some bugs and some improper code… ;)

  2. Andy Avatar

    Very slick layout. How long did it take you to write?

  3. Thomas Maurer Avatar

    Difficult to say… I think if I add all together, I needed maybe 2-3 hours… most of the time I needed was time for testing…

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.