AUTOMATING LAB BUILDOUTS WITH XENSERVER POWERSHELL – PART 4 ROLES, FEATURES, AND OTHER COMPONENTS

XenServer PowerShell Automation Series Index

Part 1 – Understanding the Requirements Part 2 – Creating a Custom ISO Part 3 – Unlimited VM Creation Part 4 – Roles, Features, and Other Components


Introduction

Creating an automated lab has its benefits, but what about the additional configuration of roles and features after all the servers are built? Building out all of these components can take some time, time that you may not have.


For this reason, AXL has the functionality to add a small subset of additional roles and features to any server that was created. The current roles and features that can be installed and configured with AXL include, Active Directory Domain Services (AD DS), Active Directory Certificate Services (AD CS), and Distributed File System (DFS).


It’s important to note that you can only configure these additional roles and features if the custom ISO you created in part 2 has XenServer Tools in it. If you did not select to put XenServer Tools in the ISO, there will be no way to grab the servers IP address after installation.


Upon completion of server creation, you will be prompted whether or not you want to configure additional roles and features. Upon selecting yes, you will get a prompt as show in Figure 1. If you choose to install any of the additional roles and features, the only requirement is AD DS, as noted by it being automatically checked and grayed out via the component selection form, everything else is optional.


Each of the other roles and features require the server to be part of a domain, which is why AD DS is a requirement. The total additional time of completion will depend on the selected roles and features, each one will take a varying amount of time depending on how large the buildout is.


Figure 1 - Component

AD DS Buildout

Upon selecting to build out additional roles and features, you are required to configure AD DS. The complete configuration includes a mandatory AD DS configuration and an optional User, Group, and OU configuration. I should note that at any time during the configuration of any form you wish to go back and reconfigure something, you can do so by selecting the previous button, if present.


The configuration for AD DS is a lot like the normal configuration you would go through if you were doing it directly on the server, however, there are some other configurations that go along with this form that you would normally do prior to domain creation; notably the IP configuration, as seen in Figure 2. Starting at the top, you will need to configure the local administrator username and password (configured when making the custom ISO), domain name, and safe mode password.


In the next section, you will notice a large list box on the left with all the servers you created in the previous form. Each server will need to be configured with an IP, default gateway, subnet mask, and DNS server(s) and can be done by selecting each server individually from the listbox; the DNS server configuration is important when joining a server to the domain, you will want at least one domain controller IP in the DNS server configuration for proper functionality. As you fill in each of the text boxes for each server, an array will simultaneously be filled in with the information input to allow complete control over the configuration.


Figure 2 - Domain Buildout

Below, you will see a code snippet on how the IP configurations are actually changed.



Function ChangeIPAddresses {
 
    foreach($XenVMServer in ($Global:AllCreatedServers | sort)) {
 
    #Define necessary parameters for IP configuration
    $ConnectionPassword = ConvertTo-SecureString -AsPlainText -Force -String $LocalPasswordTextBox.Text
    $ConnectionCreds = New-Object -typename System.Management.Automation.PSCredential -ArgumentList "$($Global:OldIPAddresses[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)])\$($LocalUsernameTextBox.Text)",$ConnectionPassword
    $NewIPAddress = $Global:IPAddresses[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)]
    $PrefixLength = Convert-IpAddressToMaskLength $Global:SubnetMasks[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)]
    $DefaultGateway = $Global:DefaultGateways[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)]
    $DNSServers = "$($Global:PrimaryDNSServers[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)]),$($Global:SecondaryDNSServers[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)])"
 
        Invoke-Command -ComputerName $Global:OldIPAddresses[($Global:AllCreatedServers | sort).IndexOf($XenVMServer)] -credential $ConnectionCreds -ScriptBlock {
 
            param ($NewIPAddress, $PrefixLength, $DefaultGateway, $DNSServers)
 
            #Define the original IP address
            $OriginalIPAddress = ((Get-NetIPConfiguration).IPv4Address).IPAddress
 
            #Set the DNS Servers
            Set-DnsClientServerAddress -InterfaceAlias (Get-NetIPConfiguration).InterfaceAlias -ServerAddresses $DNSServers
 
            #Disable IPv6
            Disable-NetAdapterBinding -InterfaceAlias (Get-NetIPConfiguration).InterfaceAlias -ComponentID ms_tcpip6
 
            #Set the new IP address with the IP, Subnet Mask, and Default Gateway
            New-NetIPAddress -IPAddress $NewIPAddress -InterfaceAlias (Get-NetIPConfiguration).InterfaceAlias -PrefixLength $PrefixLength -DefaultGateway $DefaultGateway
                
                #Remove the old IP configuration only if the new and old IPs don't match
                if((((Get-NetIPConfiguration).IPv4Address).IPAddress | where {$_ -match $OriginalIPAddress}) -and ($NewIPAddress -NotMatch $OriginalIPAddress)) {
 
                Remove-NetIPAddress -IPAddress (((Get-NetIPConfiguration).IPv4Address).IPAddress | where {$_ -match $OriginalIPAddress}) -InterfaceAlias (Get-NetIPConfiguration).InterfaceAlias -Confirm:$False
 
                }
 
        } -ArgumentList $NewIPAddress, $PrefixLength, $DefaultGateway, $DNSServers -AsJob
    
    WaitScript 2
 
    }
 
}

After all the aforementioned information is filled in, the next thing to configure would be which servers you want to make Domain Controllers. There must be at least one domain controller, if multiples are selected, you can choose which one will be the primary Domain Controller; the first server selected will automatically become the primary, but this can be changed if desired.


Once everything is configured to your liking, you need to validate the configuration by selecting the validate button. This will verify correct syntax for the domain name, safe mode password, IP schemas, and other minor configurations.


Below is a snippet of code outlining the primary Domain Controller promotion process.



Function PromotePrimaryDomainController {
 
    foreach($DCServer in ($DomainControllersListBox.Items | where {$_ -match [regex]'\*'})) {
 
    #Define Domain specific parameters
    $DomainName = $DomainNameTextBox.Text
    $SafeModePassword = ConvertTo-SecureString $SafeModePasswordTextBox.Text -AsPlainText -force
    $ConnectionPassword = ConvertTo-SecureString -AsPlainText -Force -String $LocalPasswordTextBox.Text
    $ConnectionCreds = New-Object -typename System.Management.Automation.PSCredential -argumentlist "$($Global:IPAddresses[($Global:AllCreatedServers | sort).IndexOf($DCServer.Replace("*",''))])\$($LocalUsernameTextBox.Text)",$ConnectionPassword
 
        if($DFSCheckbox.CheckState -eq "Checked") {
    
            $VMStatusTextBox.AppendText("`r`nInstalling DFSR Components on $($DCServer.Replace("*"," ")) for DFS Buildout")
 
            $DFSComponents = Invoke-Command -ComputerName $Global:IPAddresses[($Global:AllCreatedServers | sort).IndexOf($DCServer.Replace("*",""))] -credential $ConnectionCreds -ScriptBlock {
 
            #Install DFSR components if DFS was selected during component selection, this is necessary for DFS buildout functionality
            Install-WindowsFeature FS-DFS-Replication -IncludeManagementTools
 
            } -AsJob
 
            WaitJob $DFSComponents
    
        }
 
        $DCPromotion = Invoke-Command -ComputerName $Global:IPAddresses[($Global:AllCreatedServers | sort).IndexOf($DCServer.Replace("*",""))] -credential $ConnectionCreds -ScriptBlock {
 
        param ($DomainName,$SafeModePassword)
 
        #Create the AD DS Forest with the paramaeters specified in the AD DS buildout form
        Install-ADDSForest -DomainName $DomainName -SafeModeAdministratorPassword $SafeModePassword -DomainNetBIOSName $DomainName.Remove($DomainName.IndexOf(".")).ToUpper() -SYSVOLPath "C:\Windows\SYSVOL" -LogPath "C:\Windows\NTDS" -DatabasePath "C:\Windows\NTDS" -InstallDNS -Force
 
        } -ArgumentList $DomainName,$SafeModePassword -AsJob
 
        WaitJob $DCPromotion
       
        #If the Domain Controller does not reboot automatically within 15 seconds, reboot the machine
        if(Test-Connection -ComputerName $Global:IPAddresses[($Global:AllCreatedServers | sort).IndexOf($DCServer.Replace("*",""))] -Count 1 -ErrorAction SilentlyContinue) {
 
        Invoke-XenVM -Name $DCServer -XenAction CleanReboot 
 
        }
 
    }
 
} 

No matter what was chosen on the initial component selection screen, after selecting next on the domain buildout form, you will always get the User, Group, OU buildout form if you want to configure any users, groups, or OUs for your environment.


This form is 100% optional and does not require any of the fields to be filled out. If you do not want to configure any users, groups, or OUs, simply just move onto the next form, if any.


However, if you do choose to fill it out, you will notice three different section, each labeled with their intended purpose. Figure 3 depicts what a filled-out form might look like.


Figure 3 - User Group OU Buildout

Each OU added to the structure can be placed under any OU already created and can be as many levels deep as you wish, though I would not recommend any more than 10 levels for any Active Directory structure. For the Users and Groups, you can input the required information and select add, which will add it to the respective list box.


You will notice there is no validate button for this form, that is because the validation is done before any item is added to a list box. This configuration provides the flexibility to allow you to configure any combination of users, groups, OU’s, or none at all.


AD Certificate Services Buildout


Figure 4 - AD CS Buildout

The next form, if this role was chosen from the form in Figure 1, is AD CS. With this form, seen in Figure 4, you have the ability to completely configure a normal AD CS buildout, as well as AD CS Web Enrollment and OCSP Responder.

Each server added to the list box will need to be configured independently, which can be done by selecting each server from the list box and configuring the required fields.

Each field is entirely separate for each server, meaning you can do different configurations for each one, depending on the CA type chosen. Each Server in the list box can either be a root CA or subordinate CA. If you choose to create a subordinate CA, you will have a more limited selection of fields available compared to a root CA configuration. This is because the subordinate CA gets all of its configuration from the root CA. Below is a snippet of code that is used to promote the specified CAs.



Function InstallAllServices {
 
$NonSubordinates = @()
$Subordinates = @()
$AllCAServers = @()
    
    #Fill arrays with Specified Certificate Authorities
    foreach($CAServer in $CertificateAuthoritiesListBox.Items){
 
        if($Global:CATypes[$CertificateAuthoritiesListBox.Items.IndexOf($CAServer)] -notmatch "Subordinate") {
 
        $NonSubordinates += $CAServer
 
        }
 
        else {
        
        $Subordinates += $CAServer
        
        }
 
    }
 
    #Fill primary array starting with all non-subordinate CAs
    foreach($NonSubordinate in $NonSubordinates) {
    
    $AllCAServers += $NonSubordinate
    
    }
 
    #Next, fill primary array with all subordinate CAs
    foreach($Subordinate in $Subordinates) {
    
    $AllCAServers += $Subordinate
    
    }
 
    foreach($CAServer in $AllCAServers){
 
    #Define necessary connection parameters
    $DomainName = $DomainNameTextBox.Text
    $ConnectionPassword = convertto-securestring -AsPlainText -Force -String $LocalPasswordTextBox.Text
    $DomainAdminCreds = new-object -typename System.Management.Automation.PSCredential -argumentlist "$($DomainName.Remove($DomainName.IndexOf(".")).ToUpper())\Administrator",$ConnectionPassword
 
        #If the server is not a subordinate CA, define all parameters
        if($Global:CATypes[$CertificateAuthoritiesListBox