DHCP

Building a Lab in Hyper-V with PowerShell, Part 5

Deploying a DHCP Server

Now that you have your forest and domain installed, including DNS, the next step to setting up a lab is the DHCP server.  Start by creating a new VM for the DHCP server, trey-dhcp-03. (For details on how to create a VM with PowerShell, see Building a Lab in Hyper-V Part 2 and Part 3. ) There's no particular need to make this a GUI installation, so build it as a Server Core installation. We'll do the configuration all in PowerShell anyway.

 

Next, Install the DHCP role, add the local groups required, and authorize it in Active Directory. (Do note the slightly different server name when you go to do that, please, and you don't need or want to promote this server to a domain controller. )

 

Now, as you'll remember from earlier posts in this series, I configure all my VMs with known MAC addresses by first defining the range and then requiring a MAC address final pair parameter to New-myVM.ps1This allows me to now configure a set of reservations for each VM in the lab, simplifying connections and making it a lot easier for me to keep track what is where.

 

Assuming by now you have installed the DHCP role and authorized it in Active Directory, the next step is to set up your IPv4 and IPv6 ranges. We do that by first adding a scope, then setting exclusion ranges and finally scope options. For IPv4, this is three commands:

Add-DhcpServerv4Scope -Name "Trey-Default" `
                      -ComputerName "trey-dhcp-03" `
                      -Description "Default IPv4 Scope for Lab" `
                      -StartRange "192.168.10.1" `
                      -EndRange   "192.168.10.220" `
                      -SubNetMask "255.255.255.0" `
                      -State Active `
                      -Type DHCP `
                      -PassThru

Add-DhcpServerv4ExclusionRange -ScopeID "192.168.10.0" `
                               -ComputerName "trey-dhcp-03" `
                               -StartRange "192.168.10.1" `
                               -EndRange   "192.168.10.20" `
                               -PassThru

Set-DhcpServerv4OptionValue -ScopeID 192.168.10.0 `
                            -ComputerName "trey-dhcp-03" `
                            -DnsDomain "TreyResearch.net" `
                            -DnsServer "192.168.10.2" `
                            -Router "192.168.10.1" `
                            -PassThru

Now,  the same process for IPv6, though I usually do NOT create IPv6 reservations, but do want to set some default values.

Add-DhcpServerv6Scope -Name "Trey-IPv6-Default" `
                      -ComputerName "trey-dhcp-03" `
                      -Description "Default IPv6 Scope for Lab" `
                      -Prefix 2001:db8:0:10:: `
                      -State Active `
                      -PassThru

Add-DhcpServerv6ExclusionRange –ComputerName trey-dhcp-03 `
                               -Prefix 2001:db8:0:10:: `
                               -StartRange 2001:db8:0:10::1 `
                               -EndRange   2001:db8:0:10::20 `
                               -PassThru

Set-DhcpServerv6OptionValue -Prefix 2001:db8:0:10:: `
                            -ComputerName "trey-dhcp-03" `
                            -DnsServer 2001:db8:0:10::2 `
                            -DomainSearchList "TreyResearch.net" `
                            -PassThru

Now, create a CSV file with Names,MAC addresses(ClientID), and IPv4 Addresses. You can use your favourite plain text editor (mine is gVim), or Excel to create the CSV file. My lab has the following for the 192.168.10.xxx range of IP addresses:

Name,ClientID,IPAddress
trey-edge-01,00-15-5D-32-0A-01,192.168.10.1
trey-dc-02,00-15-5D-32-0A-02,192.168.10.2
trey-dhcp-03,00-15-5D-32-0A-03,192.168.10.3
trey-dc-04,00-15-5D-32-0A-04,192.168.10.4
trey-srv-05,00-15-5D-32-0A-05,192.168.10.5
trey-wds-11,00-15-5D-32-0A-0B,192.168.10.11
Trey-Srv-12,00-15-5D-32-0A-0C,192.168.10.12
Trey-Srv-13,00-15-5D-32-0A-0D,192.168.10.13
Trey-Srv-14,00-15-5D-32-0A-0E,192.168.10.14
Trey-Srv-15,00-15-5D-32-0A-0F,192.168.10.15
Trey-Srv-16,00-15-5D-32-0A-10,192.168.10.16
Trey-client-21,00-15-5D-32-0A-15,192.168.10.21
Trey-client-22,00-15-5D-32-0A-16,192.168.10.22
Trey-client-23,00-15-5D-32-0A-17,192.168.10.23
Trey-client-24,00-15-5D-32-0A-18,192.168.10.24
Trey-client-25,00-15-5D-32-0A-19,192.168.10.25

Save the CSV file as "TreyDHCP.csv". Now, to create the reservations, first read in the CSV file with:

$TreyDHCP = Import-CSV TreyDHCP.csv

Then, create the IPv4 reservations with a simple ForEach loop:

ForEach ($addr in $TreyDHCP ) {
   $ErrorActionPreference = "Continue"
   Add-DhcpServerv4Reservation -ScopeID   192.168.10.0 `
                               -Name      $addr.Name `
                               -ClientID  $addr.ClientID `
                               -IPAddress $addr.IPAddress `
                               -PassThru
}

If you run multiple NICs on your lab environment, you'll want to repeat all of the above for the second range of IP addresses.

So, here's the whole thing in a script that supports running remotely.

<#
.Synopsis
Install and configure DHCP for the TreyResearch.net lab environment
.Description
The New-TreyDHCP script installs and configures the DHCP environment for the TreyResearch.net
lab environment. It assumes a DHCP server "trey-dhcp-03" has already been created, but accepts
a parameter to change the server name. 
The script reads a CSV file with the machine names, MAC addresses (ClientIDs), and IPv4
addresses that the that the network will use and then creates IPv4 DHCP reservations for those
machines.
.Example
New-TreyDHCP.ps1
Reads in a list of DHCP addresses from TreyDHCP.csv and configures trey-dhcp-03 as a DHCP
server with those addresses. 
.Example
New-TreyDHCP.ps1 -ComputerName Trey-core-03 -Path c:\temp\dhcp.csv
Reads in a list of DHCP addresses from c:\temp\dhcp.csv and configures the server
Trey-core-03 as a DHCP server with those address reservations. 
.Parameter ComputerName
The server to install and configure DHCP on. Default value is trey-dhcp-03
.Parameter Path
The path to a CSV file with the machine names, client IDs, and IPv4 addresses to configure
DHCP reservations for. The default value is .\TreyDHCP.csv. 
.Inputs
[string]
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 25 March, 2014 (cpr)
   ModHist: 14 March, 2017 (cpr) Added ComputerName parameter and man page
          : 
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False)]
     [alias("server")]
     [string]
     $ComputerName = 'trey-dhcp-03',
     [Parameter(Mandatory=$False)]
     [Alias("filename")]
     [string]
     $Path = '.\TreyDHCP.csv'
     )

if ( (Get-WindowsFeature -Name DHCP -ComputerName $ComputerName) -ne "Installed" ) {
  Install-WindowsFeature -Name DHCP -ComputerName $ComputerName -IncludeManagementTools 
}

if (Test-Path $Path ) { 
   $TreyDHCP = Import-CSV $Path 
} else {
   Throw "This script requires an input CSV file with the DHCP Reservations in it."
}

# Find out if the DHCP Server is already authorized. If it is, 
# we assume all the rest of this is done. 
If ( (Get-DhcpServerInDC).DnsName -match $ComputerName ) {
   $IsAuth = $True 
} else {
   $IsAuth = $False 
   $DnsName = $ComputerName + ".TreyResearch.net"
}

# If the server isn't authorized, then nothing is set yet, so set up 
# our DHCP server. 
if (! $IsAuth) {
   Add-DhcpServerInDC -DnsName $DnsName -PassThru
   # Create local groups for DHCP
   # The WinNT in the following IS CASE SENSITIVE
   $connection = [ADSI]"WinNT://$ComputerName"
   $lGroup = $connection.Create("Group","DHCP Administrators")
   $lGroup.SetInfo()
   $lGroup = $connection.Create("Group","DHCP Users")
   $lGroup.SetInfo()
   Add-DhcpServerv4Scope -Name "Trey-Default" `
                         -Description "Default IPv4 Scope for TreyResearch Lab" `
                         -StartRange "192.168.10.1" `
                         -EndRange   "192.168.10.220" `
                         -SubNetMask "255.255.255.0" `
                         -State Active `
                         -Type DHCP `
                         -ComputerName $ComputerName `
                         -PassThru
   Add-DhcpServerv4ExclusionRange -ScopeID "192.168.10.0" `
                                  -StartRange "192.168.10.1" `
                                  -EndRange   "192.168.10.20" `
                                  -ComputerName $ComputerName `
                                  -PassThru
   Set-DhcpServerv4OptionValue -ScopeID 192.168.10.0 `
                               -DnsDomain "TreyResearch.net" `
                               -DnsServer "192.168.10.2" `
                               -Router "192.168.10.1" `
                               -ComputerName $ComputerName `
                               -PassThru
   Add-DhcpServerv6Scope -Name "Trey-IPv6-Default" `
                         -Description "Default IPv6 Scope for TreyResearch Lab" `
                         -Prefix 2001:db8:0:10:: `
                         -State Active `
                         -ComputerName $ComputerName `
                         -PassThru
   Add-DhcpServerv6ExclusionRange -Prefix 2001:db8:0:10:: `
                                  -StartRange 2001:db8:0:10::1 `
                                  -EndRange   2001:db8:0:10::20 `
                                  -ComputerName $ComputerName `
                                  -PassThru
   Set-DhcpServerv6OptionValue -Prefix 2001:db8:0:10:: `
                               -DnsServer 2001:db8:0:10::2 `
                               -DomainSearchList "TreyResearch.net" `
                               -ComputerName $ComputerName `
                               -PassThru
}


ForEach ($addr in $TreyDHCP ) {
   $ErrorActionPreference = "Continue"
   Add-DhcpServerv4Reservation -ScopeID 192.168.10.0 `
                               -Name $addr.Name `
                               -ClientID $addr.ClientID `
                               -IPAddress $addr.IPAddress `
                               -ComputerName $ComputerName `
                               -PassThru
}

I hope you find this script useful, and I'd love to hear comments, suggestions for improvements, or bug reports as appropriate. As always, if you use this script as the basis for your own work, please respect my copyright and provide appropriate attribution.

Building a Lab in Hyper-V with PowerShell, Part 4

Creating a new forest

In the previous sections of this series, I've covered how to build VMs using PowerShell, but labs aren't much good if they don't actually have any structure. So, let's create a new forest and domain to manage our labs. I'm going to assume for this post that you've gotten started already and created a new Windows Server 2012R2 or Windows Server 2016 virtual machine. For this, it can be a graphical install or a Server Core installation and either Server Standard or Datacenter. Since we're going to be using only PowerShell to create the forest, there's no need for a GUI.

The things we'll need to have identified before we start are:

  • Server IP address
  • Server name
  • DNS namespace for the root domain of the forest
  • Domain name for the root domain of the forest
  • DNS Server type (AD-integrated or standalone)

Set Server IP Address

We need set our server to a fixed IP address. While not absolutely required, I think it's a really bad idea to not do this. And, since our lab doesn't yet have DHCP in it, you need to anyway. (We'll add a DHCP server in the next installment. )

To configure the network adapter for a static IP address, I need to know either the interface alias (name) or the interface index. To get those, use Get-NetAdapter from a PowerShell window. (Note: if you're doing this on a new Windows Server Core installation, you can open a PowerShell window with Start PowerShell.exe at the command prompt. To start a PowerShell window automatically for this user, at logon, see my May post. )

Get-NetAdapter | Format-Table -AutoSize Name,Status,IFIndex,MacAddress

Name       Status ifIndex MacAddress
----       ------ ------- ----------
Ethernet 2 Up           3 00-15-5D-32-0A-02
Ethernet   Up           5 00-15-5D-32-CE-02

Which tells us that the DC has two network adapters, and the one that is on the Local-10 switch (from New-myVM.ps1) is at an ifIndex of 3, while the one on the "199 Network" switch has an ifIndex of 5. Now, we'll set the static IP addresses for these two adapters. First, the NIC on Local-10:

# Set IPv4
$NIC2 = Get-NetAdapter -ifIndex 3
$NIC2 | Set-NetIPInterface -DHCP Disabled
$NIC2 | New-NetIPAddress -AddressFamily  IPv4 `
                         -IPAddress      192.168.10.2 `
                         -PrefixLength   24 `
                         -Type Unicast `
                         -DefaultGateway 192.168.10.1
# Set IPv6
$NIC2 | New-NetIPAddress -AddressFamily  IPv6 `
                         -IPAddress      2001:db8:0:10::2 `
                         -PrefixLength   64 `
                         -Type Unicast `
                         -DefaultGateway 2001:db8:0:10::1

# Set DNS Server Addresses to self
Set-DnsClientServerAddress -InterfaceIndex  $NIC2.ifIndex `
                           -ServerAddresses 192.168.10.2,2001:db8:0:10::2

#Now, for the 199 Network, which I use for internal communications between lab hosts, I want to set a pure IPv4 address with no IPv6, so instead of setting an IPv6 address for the NIC, I'll disable it with Disable-NetAdapterBinding.

$NIC = Get-NetAdapter -ifIndex 5

# Disable IPv6
Disable-NetAdapterBinding -Name $NIC.Name -ComponentID ms_tcpip6

# Set IPv4 to 192.168.199.2
$NIC | Set-NetIPInterface -Dhcp Disabled
$NIC | New-NetIPAddress -AddressFamily IPv4 `
                        -IPAddress     192.168.199.2 `
                        -PrefixLength  24 `
                        -Type Unicast
# Set DNS to self
Set-DnsClientServerAddress -InterfaceIndex  $NIC.ifIndex `
                           -ServerAddresses 192.168.199.2

(Note: Set-NetAdapterBinding is not available on Windows 7/Server 2008 R2)

 

Set Server Name

Next, let's set the name of the server to match our naming conventions for this lab. We do this now, knowing it will force a reboot before we go any further.

Rename-Computer -NewName trey-dc-02 -Restart -Force

This will give the computer a new name and restart it.

 

Create Forest and Install AD-integrated DNS

Now that we have static IP addresses for our network adapters, and we've set the name of the server, we can go ahead and create our AD forest. First, we install Active Directory and update the PowerShell Help files with:

Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
Update-Help -SourcePath \\labhost\PSHelp

This installs the ActiveDirectory and ADDSDeployment modules that we'll need to create the forest. Now, we promote the server to be the first domain controller in the new forest. Before we do the actual install, we test to make sure we don't have any issues with Test-ADDSForestInstallation:

Test-ADDSForestInstallation `
         -DomainName 'TreyResearch.net' `
         -DomainNetBiosName 'TREYRESEARCH' `
         -DomainMode 6 `
         -ForestMode 6 `
         -NoDnsOnNetwork `
         -SafeModeAdministratorPassword (ConvertTo-SecureString `
                                                  -String 'P@ssw0rd' `
                                                  -AsPlainText `
                                                  -Force) `
         -NoRebootOnCompletion

Even though this is a brand new forest in an isolated lab setting, it's still a good practice to test before you actually deploy. And it doesn't cost all that much time or annoyance. I've included the SafeModeAdministratorPassword parameter to avoid the prompts for it. This is a lab, not real life. :) Also note that we're setting the forest and domain modes to Server2012R2. If you need earlier versions of domain controllers in your lab, you can set the mode accordingly.

The results of the test are as expected:

WARNING: Windows Server 2016 domain controllers have a default for the security setting named "Allow cryptography
algorithms compatible with Windows NT 4.0" that prevents weaker cryptography algorithms when establishing security
channel sessions.

For more information about this setting, see Knowledge Base article 942564
(http://go.microsoft.com/fwlink/?LinkId=104751).

WARNING: A delegation for this DNS server cannot be created because the authoritative parent zone cannot be found or it
 does not run Windows DNS server. If you are integrating with an existing DNS infrastructure, you should manually
create a delegation to this DNS server in the parent zone to ensure reliable name resolution from outside the domain
"TreyResearch.net". Otherwise, no action is required.


Message                          Context                                  RebootRequired  Status
-------                          -------                                  --------------  ------
Operation completed successfully Test.VerifyDcPromoCore.DCPromo.General.3          False Success

With that confirmation, we can go ahead and finish creating the forest and configuring DNS with the command:

Install-ADDSForest `
    -DomainName 'TreyResearch.net' `
    -DomainNetBiosName 'TREYRESEARCH' `
    -DomainMode 6 `
    -ForestMode 6 `
    -NoDnsOnNetwork `
    -SkipPreChecks `
    -SafeModeAdministratorPassword (ConvertTo-SecureString `
                                                  -String 'P@ssw0rd' `
                                                  -AsPlainText `
                                                  -Force) `
    -Force

You'll notice that the options here match our test pass, except I chose to bypass a second test. If you want to keep your SafeMode Administrator password private you can eliminate that parameter and you'll be prompted at the command line. When this finishes and the server has rebooted, you can log in with the TREYRESEARCH\Administrator account and the local Administrator password you had before you promoted the VM to be a domain controller.  This may or may not be the same as the SafeModeAdministratorPassword you set during the installation.

Configuring Windows Server 2016 core as a DHCP Server with PowerShell

As I mentioned last time, I'm setting up a new domain controller and DHCP server for my internal domain on Windows Server 2016 Core, and I'm exclusively using PowerShell to do it. For both the DHCP Server and AD DS roles, we need to configure a fixed IP address on the server, so let's do that first. From my Deploying and Managing Active Directory with Windows PowerShell book from Microsoft Press, here's my little very quick and dirty script to set a fixed IP address:

# Quick and dirty IP address setter

[CmdletBinding()]
Param ([Parameter(Mandatory=$True)][string]$IP4,
       [Parameter(Mandatory=$True)][string]$IP6 
      )
$Network = "192.168.10."
$Network6 = "2001:db8:0:10::"
$IPv4 = $Network + "$IP4"
$IPv6 = $Network6 + "$IP6"
$Gateway4 = $Network + "1"
$Gateway6 = $Network6 + "1"

Write-Verbose "$network,$network6,$IP4,$IP6,$IPv4,$IPv6,$gateway4, $gateway6"

$Nic = Get-NetAdapter -name Ethernet
$Nic | Set-NetIPInterface -DHCP Disabled
$Nic | New-NetIPAddress -AddressFamily IPv4 `
                        -IPAddress $IPv4 `
                        -PrefixLength 24 `
                        -type Unicast `
                        -DefaultGateway $Gateway4
Set-DnsClientServerAddress -InterfaceAlias $Nic.Name `
                           -ServerAddresses 192.168.10.2,2001:db8:0:10::2
$Nic |  New-NetIPAddress -AddressFamily IPv6 `
                         -IPAddress $IPv6 `
                         -PrefixLength 64 `
                         -type Unicast `
                          -DefaultGateway $Gateway6

ipconfig /all

I warned you it was a quick and dirty script. But let's quickly look at what it does. First, we get the network adapter into a variable, $Nic. Then we turn off DHCP with Set-NetIPInterface, and configure the IPv4 and IPv6 addresses with New-NetIPAddress. Finally, we use Set-DnsClientServerAddress to configure the DNS Servers for this server.

 

Next, let's join the server to the TreyResearch.net domain with another little script. OK, I admit, you could do this all as a simple one-liner, but I do it so often that I scripted it.

<#
.Synopsis
Joins a computer to the domain
.Description
Joins a new computer to the domain. If the computer hasn't been renamed yet, 
it renames it as well.
.Parameter NewName
The new name of the computer
.Parameter Domain
The domain to join the computer to. Default value is TreyResearch.net
.Example
Join-myDomain -NewName trey-wds-11
.Example
Join-myDomain dc-contoso-04 -Domain Contoso.com
.Notes
     Name: Join-myDomain
   Author: Charlie Russel
Copyright: 2017 by Charlie Russel
         : Permission to use is granted but attribution is appreciated
  ModHist:  9 Apr, 2014 -- Initial
         : 25 Feb, 2015 -- Updated to allow name already matches
         :
#>
[CmdletBinding()]
Param ( [Parameter(Mandatory=$true,Position=0)]
        [String]$NewName,
        [Parameter(Mandatory=$false,Position=1)]
        [String]$Domain = "TreyResearch.net"
       )

$myCred = Get-Credential -UserName "$Domain\Charlie" `
                         -Message "Enter the Domain password for Charlie."

if ($ENV:COMPUTERNAME -ne $NewName ) {
   Add-Computer -DomainName $Domain -Credential $myCred -NewName $NewName -restart
} else {
   Add-Computer -DomainName $Domain -Credential $myCred -Restart
}

After the server restarts, log in with your domain credentials, not as "Administrator".  The account you logon with should be at least Domain Admin or equivalent, since you're going to be adding DHCP to the server and promoting it to be a domain controller.

 

To add the necessary roles to the server, use:

Install-WindowsFeature -Name DHCP,AD-Domain-Services `
                       -IncludeAllSubFeature `
                       -IncludeManagementTools

Next, download updated Get-Help files with Update-Help. Once you've got those, go ahead and restart the server, and when it comes back up, we'll do the base configuration for DHCP to enable it in the domain, and create the necessary accounts. Creating scopes, etc., is the topic of another day. Probably as part of my Lab series.

 

First, enable the DHCP server in AD (this assumes the $NewName from earlier was 'trey-core-03'. )

Add-DhcpServerInDC -DnsName 'trey-core-03' -PassThru

And, finally, create the necessary local groups:

# Create local groups for DHCP
# The WinNT in the following IS CASE SENSITIVE
$connection = [ADSI]"WinNT://trey-core-03"
$lGroup = $connection.Create("Group","DHCP Administrators")
$lGroup.SetInfo()
$lGroup = $connection.Create("Group","DHCP Users")
$lGroup.SetInfo()

This uses ADSI to create a local group, since there's no good way built into base PowerShell to do it except through ADSI.

 

Finally, we'll use my Promote-myDC.ps1 script to promote the server to domain controller. Again, I could easily do this by hand, but I'm building and rebuilding labs often enough that I scripted it. I'm lazy! Do it once, use the PowerShell interactive command line. Do it twice? Write a script!

<#
.Synopsis
Tests a candidate domain controller, and then promotes it to DC.
.Description
Promote-myDC first tests if a domain controller can be successfully promoted,
and, if the user confirms that the test was successful, completes the
promotion and restarts the new domain controller.
.Example
Promote-myDC -Domain TreyResearch.net

Tests if the local server can be promoted to domain controller for the
domain TreyResearch.net. The user is prompted after the test completes
and must press the Y key to continue the promotion.
.Parameter Domain
The domain to which the server will be promoted to domain controller.
.Inputs
[string]
.Notes
    Author: Charlie Russel
 Copyright: 2017 by Charlie Russel
          : Permission to use is granted but attribution is appreciated
   Initial: 05/14/2016 (cpr)
   ModHist: 02/14/2017 (cpr) Default the domain name for standard lab builds
          :
#>
[CmdletBinding()]
Param(
     [Parameter(Mandatory=$False,Position=0)]
     [string]$Domain = 'TreyResearch.net'
     )

Write-Verbose "Testing if ADDSDeployment module is available"
If ( (Get-WindowsFeature -Name AD-Domain-Services).InstallState -ne "Installed" ) {
   Write-Verbose "Installing the ActiveDirectory Windows Feature, since you seem to have forgotten that."
   Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools
   Write-Host ""
}

If ( (Get-WindowsFeature -Name AD-Domain-Services).InstallState -ne "Installed" ) {
   throw "Failed to install the ActiveDirectory Windows Feature."
}

Write-Verbose "Testing if server $env:computername can be promoted to DC in the $Domain domain"
Write-Host ""
Test-ADDSDomainControllerInstallation `
         -NoGlobalCatalog:$false `
         -CreateDnsDelegation:$false `
         -CriticalReplicationOnly:$false `
         -DatabasePath "C:\Windows\NTDS" `
         -DomainName $Domain `
         -LogPath "C:\Windows\NTDS" `
         -NoRebootOnCompletion:$false `
         -SiteName "Default-First-Site-Name" `
         -SysvolPath "C:\Windows\SYSVOL" `
         -InstallDns:$true `
         -Force
Write-Host ""
Write-Host ""
Write-Host ""

Write-Host -NoNewLine "If the above looks correct, press Y to continue...  "
$Key = [console]::ReadKey($true)
$sKey = $key.key

Write-Verbose "The $sKey key was pressed."
Write-Host ""
Write-Host ""
If ( $sKey -eq "Y" ) {
   Write-Host "The $sKey key was pressed, so proceeding with promotion of $env:computername to domain controller."
   Write-Host ""
   sleep 5
   Install-ADDSDomainController `
              -SkipPreChecks `
              -NoGlobalCatalog:$false `
              -CreateDnsDelegation:$false `
              -CriticalReplicationOnly:$false `
              -DatabasePath "C:\Windows\NTDS" `
              -DomainName $Domain `
              -InstallDns:$true `
              -LogPath "C:\Windows\NTDS" `
              -NoRebootOnCompletion:$false `
              -SiteName "Default-First-Site-Name" `
              -SysvolPath "C:\Windows\SYSVOL" `
              -Force:$true
} else {
   Write-Host "The $sKey key was pressed, exiting to allow you to fix the problem."
   Write-Host ""
   Write-Host ""
}

This uses a little trick I haven't talked about before -

$Key = [console]::ReadKey($true)
$sKey = $key.key

This reads in a single keystroke and gets the value of the key. Because of the way this works, "Y" and "y" are equivalent. Useful to give yourself a last chance out if something doesn't look right, though obviously you'll want to remove those bits if you're creating a script that needs to run without interactive input.