Lab Automation: Unattended Windows Deployments including AD Forests, Domains and SQL server – Part IX: Build a Configure-Host.ps1 and Answer Files ISO for a New Child Domain in an Existing Forest

Now that we have two domain controllers in the root domain, we are going to add a child domain. In this case, we will build Child Domain 1 DC01. The same function can be used (by simply passing different parameters) to build Child Domain 2 DC01.

PowerShell Script for New Child Domain

This is the complete function that:

  • Customizes the autounattend.xml (covered in previous post in series)
  • Creates the Configure-Host.ps1 file
  • Builds the answerfiles-iso
  • Uploads the ISO to a vSphere Datastore

As you read the below, you will see a series of ‘Add-Content’ commands. Effectively, we are using these to construct a bespoke Configure-Host.ps1 file that will be included on the answer files ISO we build for the VM. Things to note:

  • In some cases we are creating lines where we want values from the script to be passed into the file.
  • In other cases we are actually creating variables on the remote machine to be used during execution

As before, this is a very similar function to those used to deploy the forest, and add a domain controller to the forest. Key differences are

  • Once again, the command used to actually deploy the child domain is slightly different as we need to use a source server to replicate from
  • Timing of DNS entries.
  • Needing to reference objects that refer to the parent directory, and the child directory
Function createChildDCConfigISO
{
    param(
    [parameter(mandatory=$true)]
    [array]$machineObject,
    [parameter(mandatory=$true)]
    [array]$sharedDetails,
    [parameter(mandatory=$true)]
    [array]$domainDetails
    )
    #Cleanup Temporary folders if they exist
    $folderExists = Test-Path $buildfolder
    If ($folderExists)
    {
       LogMessage "Cleaning Up up Temporary Folders"
       Remove-Item $buildfolder -recurse -force -confirm:$false
    }
    #create necessary folders
    LogMessage "Creating Temporary Folders"
    New-Item -ItemType Directory -Path $buildfolder | Out-File $logFile -encoding ASCII -append 
    New-Item -ItemType Directory -Path "$buildfolder\SupportingFiles" | Out-File $logFile -encoding ASCII -append 
    New-Item -ItemType File -Path "$buildfolder\SupportingFiles\configure-host.ps1" | Out-File $logFile -encoding ASCII -append 
    LogMessage "Creating and Injecting Windows Unattended Installation File"
    
    #Set the target output file
    $DestinationAnswerFilesIsoPath = "$isosPath\$environment-$($machineObject.machinename)-answerfiles.iso"
    
    #Read in the template AutoUnattend.xml for Child-DC, replace key strings with values from JSON inputs and save to working folder
    $AutoUnattendXml = Get-Content $childDCTemplateXml   
    $AutoUnattendXml | ForEach-Object { $_ `
        -replace '<!--REPLACE WITH PRODUCTKEY-->',$commonJSON.isoGeneration.productKey `
        -replace '<!--REPLACE WITH MACHINENAME-->',$machineObject.machinename `
        -replace '<!--REPLACE WITH IPCIDR-->', $machineObject.ipCidr `
        -replace '<!--REPLACE WITH GATEWAY-->',$machineObject.gateway `
        -replace '<!--REPLACE WITH ADMINISTRATOR PASSWORD-->',$commonJson.environment.commonPassword `
        -replace '<!--REPLACE WITH CHILD DOMAIN-->',$sharedDetails.activeDirectory.childDomain `
        -replace '<!--REPLACE WITH ROOT DOMAIN-->',$commonJSON.activeDirectory.rootDomain `
    } | Set-Content "$buildfolder\SupportingFiles\autounattend.xml"

    #Create configure-host.ps1 file content
    LogMessage "Creating Post Installation Scripts"
    #Start transcript on VM being built, to ease troubleshooting
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Start-Transcript "C:\Windows\Setup\Scripts\transcript.txt"'
    
    #Remove any localhost DNS servers and add first dns server (allowing us to resolve existing RootDC)
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" "netsh interface ip del dns Ethernet0 all"
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" "netsh interface ip add dns Ethernet0 $($machineObject.dnsServer1) index=1"

    #Set desired timezone (in this case hard coded)
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'tzutil /s "GMT Standard Time"'

    #Enable RDP
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-Item -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-Item -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty -path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters" -Name "AllowEncryptionOracle" -Value "2" -type DWORD'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 0 -PropertyType dword -Force'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp\" -Name "UserAuthentication" -Value 0'    
    
    #Enable PS Remoting to allow management via remote machines
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Enable-PSRemoting -Force'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-Item wsman:\localhost\client\trustedhosts * -Force'

    #Install the required Windows Feature to deploy AD
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Install-windowsfeature AD-Domain-Services'
    #Install the AD module required
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Import-Module ADDSDeployment'

    #Build up details required to deploy the new DC into the forest, including required credentials object
    $newline = '$PlainSafeModeAdministratorPassword = "'+$commonJSON.activeDirectory.rootDefaultAdministratorPassword+'"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" '$SecureSafeModeAdministratorPassword = ConvertTo-SecureString -String $PlainSafeModeAdministratorPassword -AsPlainText -Force'
    $domainNameParts = $commonJSON.activeDirectory.rootDomain -split "\."
    $netBIOSName = $domainNameParts[0]
    $rootAdministrator = 'Administrator@'+$commonJSON.activeDirectory.rootDomain
    $newline = '$lab_username = "'+$rootAdministrator+'"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" '$lab_credentials = New-Object System.Management.Automation.PSCredential ($lab_username, $SecureSafeModeAdministratorPassword)'
    
    #Build name of target machine to replicate from
    $replicationSource = $domainDetails.templates.rootDC.machinename+'.'+$commonJSON.activeDirectory.rootDomain
    $childDomainNameParts = $sharedDetails.activeDirectory.childDomain -split "\."
    $childNetBIOSName = $childDomainNameParts[0]
    
    #Build command required to deploy the new child domain
    $newline = 'Install-ADDSDomain -Credential $lab_credentials -NewDomainName "'+$childNetBIOSName+'" -ParentDomainName "'+$commonJSON.activeDirectory.rootDomain+'" -InstallDNS:$true -DomainMode "WinThreshold" -ReplicationSourceDC "'+$replicationSource+ '" -DatabasePath "C:\NTDS" -SYSVOLPath "C:\SYSVOL" -LogPath "C:\Logs" -NoRebootOnCompletion:$true -SafeModeAdministratorPassword $SecureSafeModeAdministratorPassword -force:$true'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline

    #Add Domain Admins as local admin on the box (just in case!)
    $newline = 'net localgroup administrators "'+$netBIOSName+'\domain admins" /add'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline

    #Install AD Management Tools
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Install-WindowsFeature -name "RSAT-ADDS"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Install-WindowsFeature -name "RSAT-ADLDS"'

    #Clear the DNS server settings and set the permanent DNS server entries required
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" "netsh interface ip del dns Ethernet0 all"
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" "netsh interface ip add dns Ethernet0 $($machineObject.dnsServer1) index=1"    
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" "netsh interface ip add dns Ethernet0 $($machineObject.dnsServer2) index=2"

    #Copy windows source files to VM and set policy
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'mkdir C:\sources\sxs'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'xcopy D:\sources\sxs C:\sources\sxs /e /v /c'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'set-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing\" -Name "LocalSourcePath" -value "c:\sources\sxs"'

    #Set powershell parameters
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ExecutionPolicy Unrestricted'

    #Supress UAC
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ItemProperty -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Policies\System" -Name "EnableLUA" -Value "0"'
    
    #Disable IPv6
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\Tcpip6\Parameters" -Name "DisabledComponents" -Value 0xff'
    
    #set the credentials used for Auto Login (if required)
    $newline = '$DefaultDomainName = "'+$netBIOSName+'"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline
    $newline = '$DefaultUsername = "'+$netBIOSName+'\Administrator"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline
    $newline = '$DefaultPassword = '
    $newline += "'"
    $newline += $commonJSON.activeDirectory.rootDefaultAdministratorPassword
    $newline += "'"
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" $newline

    #Configure to autologin as domain admin (as required)
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" '$RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty $RegPath -Name "AutoAdminLogon" -Value "1" -type String '
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty $RegPath -Name "DefaultDomainName" -Value "$DefaultDomainName" -type String'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty $RegPath -Name "DefaultUsername" -Value "$DefaultUsername" -type String'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'New-ItemProperty $RegPath -Name "DefaultPassword" -Value "$DefaultPassword" -type String'

    #Disable IE Enhanced Security
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" '$AdminKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" '$UserKey = "HKLM:\SOFTWARE\Microsoft\Active Setup\Installed Components\{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}"'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ItemProperty -Path $AdminKey -Name "IsInstalled" -Value 0 -Force'
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Set-ItemProperty -Path $UserKey -Name "IsInstalled" -Value 0 -Force'

    #Remove the SetupComplete.cmd file
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Remove-Item C:\Windows\Setup\Scripts\SetupComplete.cmd -force'

    #Stop Transcript
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Stop-Transcript'

    #Final Reboot
    Add-Content "$buildfolder\SupportingFiles\configure-host.ps1" 'Restart-Computer -force' 
    LogMessage "Creating Configuration ISO for $environment-$($machineObject.machinename)-answerfiles.iso for environment $environment"

    #Create Answerfiles ISO
    $data = '2#p0,e,b"{0}"#pEF,e,b"{1}"' -f $etfsboot, $efisys
    start-process $oscdimg -args @("-bootdata:$data",'-u2','-udfver102', "$buildfolder\SupportingFiles", $DestinationAnswerFilesIsoPath) -wait -nonewwindow -RedirectStandardError Null1 -RedirectStandardOutput Null2
    
    #Clean Up Function (seperate PowerShell function not shown here)
    isoEnvironmentCleanUp

    #Upload ISO (separate powershell function not shown here)
    uploadISO External $domainDetails $commonJSON.environment.type $sharedDetails $DestinationAnswerFilesIsoPath
}

That’s it for now. Hope you enjoyed the series so far.

Posts in this Series

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s