NEW & IMPROVED FORMULA: Build an AD DS Forest in Azure

Hello... from the snow!

(BTW - that's NOT me right at the very back!)

 

Cycling seems to be the best way to get around at the moment. This week, apart from riding my bike, what else have I done whilst 'snowed in'?

Well, one task has been to update a script I wrote last year that builds an AD DS forest in Azure:

Build an Active Directory Forest in Microsoft Azure

 

The script now merges any existing Azure virtual network configuration with the virtual network configuration required for the AD DS forest:

...you'd think automating the merge would be simple enough - it wasn't

...you may also think a Cmdlet would be available for this task - there isn't one

Today, I'm going to talk a little about that merge process.

 

Maintaining the Azure vNetConfig

The script now builds a test Windows Server 2012 Active Directory forest, complete with members servers, in an established Azure subscription. Why is this important? Well, you don't have to have a 'vanilla' subscription to make use of the script, so you can easily spin up an AD forest in your existing Azure subscription for testing / DR purposes.

The Azure virtual network configuration is steeped in mystical XML:

Get-AzureVNetConfig

 

To preserve the existing configuration and add entries to it we need to manipulate the XML. Here's a summary of what the script does:

  1. Populates an XML template with the required DNS and virtual network settings
  2. Checks for existing vNetconfig:
    1. if nothing's returned, writes the template to a netcfg file and commits it to Azure
    2. if settings are returned, makes a copy of the existing configuration
  3. Checks for and adds a DNS entry to the in-memory copy of the vNetConfig
  4. Checks for and adds a virtual network entry to the in-memory copy of the vNetConfig
  5. Writes the in-memory copy back to a netcfg file
  6. Submits the netcfg file to Azure

 

Here's some of the choice bits from the script...

The XML template is created with a Here-String and tailored to the new Cloud Service by the insertion of variable values:

$NetCfg = @"

<?xml version="1.0" encoding="utf-8"?>

<NetworkConfiguration xmlns:xsd="https://www.w3.org/2001/XMLSchema" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://schemas.microsoft.com/ServiceHosting/2011/07/NetworkConfiguration">

<VirtualNetworkConfiguration>

<Dns>

<DnsServers>

<DnsServer name="$($ServicePrefix)DC01" IPAddress="10.0.$($ClassCSubnetNumber).4" />

</DnsServers>

</Dns>

<VirtualNetworkSites>

<VirtualNetworkSite name="$($ServicePrefix)vNet" Location="$($Location)">

<AddressSpace>

<AddressPrefix>10.0.$($ClassCSubnetNumber).0/24</AddressPrefix>

</AddressSpace>

<Subnets>

<Subnet name="$($ServicePrefix)Subnet1">

<AddressPrefix>10.0.$($ClassCSubnetNumber).0/28</AddressPrefix>

</Subnet>

</Subnets>

<DnsServersRef>

<DnsServerRef name="$($ServicePrefix)DC01" />

</DnsServersRef>

</VirtualNetworkSite></VirtualNetworkSites>

</VirtualNetworkConfiguration>

</NetworkConfiguration>

"@

 

It can then be saved to a file:

Set-Content -Value $NetCfg -Path $NetCfgFile

 

The existing virtual network configuration is loaded into memory like this:

$vNetConfig = Get-AzureVNetConfig

$vNetConfig = [XML]$vNetConfig.XMLConfiguration 

  

The XMLConfiguration property of the object returned by Get-AZureVNetConfig contains our existing configuration. It then has to be strongly cast to an XML object - [XML]$vNetConfig - so we can work with it in PowerShell.

The updated XML template can be loaded from the file like this (again notice that it is strongly type with [XML] ):

[XML]$NetCfg = Get-Content -Path $NetCfgFile

 

Now that we have the updated template ( $NetCfg) and a copy of the existing configuration in memory ( $vNetConfig), we can append nodes from the template onto the existing configuration. Here's how we add a DNS server:

#Create a template for an entry to the DNSservers node

$DnsServerEntry = $vNetConfig.ImportNode($NetCfg.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers.DnsServer, $True)

#Add the template to out copy of the vNetConfig in memory

$vNetConfig.NetworkConfiguration.VirtualNetworkConfiguration.Dns.DnsServers.AppendChild($DnsServerEntry) | Out-Null

 

We create a template for the DNS server entry using the ImportNode() method of the $vNetConfig object. This references the populated node of the $NetCfg object:

<DnsServer name="PoShDC01" IPAddress="10.0.50.4" />

 

We then use the AppendChild() method of the $vNetConfig object to insert the new DNS object .This process is repeated for the VirtualNetworkSite node information.

NB - depending on the particulars of the existing virtual network configuration we might make use of the ReplaceChild() method - see the Update-AzureVNetConfig function in the script for more details.

Once all of the necessary updates have been made to the in memory version of the virtual network configuration we need to save it back to disk and then to Azure:

#Copy the in-memory config back to a file

Set-Content -Value $vNetConfig.InnerXml -Path $NetCfgFile

#Create a new virtual network from the config file

Set-AzureVNetConfig -ConfigurationPath $NetCfgFile

 

Let it snow, let it snow, let it snow...