Build a PDC in Azure with DSC
There are a lot ARM templates out there can do this. But in this post, we will go through the nitty gritty of using DSC to automate the PDC setup. Before we begin, I assume you already know what DSC is and does. Otherwise, check it out here.
First, let’s build a new VM in Azure with these PowerShell commands. In this case, the VM will have direct Internet and can be accessed via Internet directly. Just to state here that this is definitely not the practice you want to adopt in production.
\# Create a new resource group
New-AzureRmResourceGroup \-Name $rsgName \-Location $location
\# Create a subnet configuration
$subnetConfig \= New-AzureRmVirtualNetworkSubnetConfig \-Name "adsubnet" \-AddressPrefix 10.188.0.0/24
\# Create a virtual network
$vnet \= New-AzureRmVirtualNetwork \-ResourceGroupName $rsgName \-Location $location \-Name "tomlabVNET1" \-AddressPrefix 10.188.0.0/16 \-Subnet $subnetConfig
\# Create a public IP address and specify a DNS name
$pip \= New-AzureRmPublicIpAddress \-ResourceGroupName $rsgName \-Location $location \-AllocationMethod Static \-IdleTimeoutInMinutes 4 \-Name "tomlabpubdns$(Get-Random)"
\# Create an inbound network security group rule for port 22
$nsgRuleRDP \= New-AzureRmNetworkSecurityRuleConfig \-Name "tomlabSGRuleRDP" \-Protocol "Tcp" \-Direction "Inbound" \-Priority 1000 \-SourceAddressPrefix \* \-SourcePortRange \* \-DestinationAddressPrefix \* \-DestinationPortRange 3389 \-Access "Allow"
\# Create a network security group
$nsg \= New-AzureRmNetworkSecurityGroup \-ResourceGroupName $rsgName \-Location $location \-Name "tomlabWinSG" \-SecurityRules $nsgRuleRDP
\# Define a credential object
$cred \= Get-Credential \-message "login for the new vm" \-UserName "tom"
\# Create a virtual network card and associate with public IP address and NSG
$nic \= New-AzureRmNetworkInterface \-Name "dc1VMNic" \-ResourceGroupName $rsgName \-Location $location \-SubnetId $vnet.Subnets\[0\].Id \-PublicIpAddressId $pip.Id \-NetworkSecurityGroupId $nsg.Id
\# Create a virtual machine configuration
$vmConfig \= New-AzureRmVMConfig \-VMName $vmName \-VMSize "Standard\_DS1\_v2" | \`
Set-AzureRmVMOperatingSystem \-Windows \-ComputerName $vmName \-Credential $cred | \`
Set-AzureRmVMSourceImage \-PublisherName "MicrosoftWindowsServer" \-Offer "WindowsServer" \-Skus "2016-Datacenter" \-Version "latest" | \`
Add-AzureRmVMNetworkInterface \-Id $nic.Id
\# Create the VM
New-AzureRmVM \-ResourceGroupName $rsgName \-Location $location \-VM $vmConfig
Next we need to create an Automation Account in Azure. I am sure you know how to get that done.
As you are already in the portal, let’s get all the prerequisites ready before upload the DSC configuration.
First, go to Modules in the Automation Account and install following modules, if not already installed. They can be imported directly from Gallery.
- xActiveDirectory
- xStorage
- xPendingReboot
Next, go to Credentials in the Automation Account and create following two credentials. The reason behind this is well explained in this Stackoverflow article.
- DCcred – Domain admin account
- DCRecoveryCred – AD Recovery Password, this one you can put anything in the username, as it will not be used
Now we can start write DSC for the PDC. Below is the code. Save it as a ps1 file. You will notice it requires all those 3 modules we just installed in Azure Automation and the PSCredential it pulls from the Automation Account.
#Requires -module xActiveDirecotry
#Requires -module xStorage
#Requires -module xPendingReboot
configuration DSCNewPDC
{
\# Get Automation Credentials from Azure Automation Account
$safemodeAdministratorCred \= Get-AutomationPSCredential \-Name "DCRecoveryCred"
$domainCred \= Get-AutomationPSCredential \-Name "DCcred"
Import-DscResource \-ModuleName xActiveDirectory
Import-DscResource \-ModuleName xStorage
Import-DscResource \-ModuleName xPendingReboot
Node $AllNodes.Where{$\_.Role \-eq "Primary DC"}.Nodename
{
WindowsFeature ADDSInstall
{
Ensure \= "Present"
Name \= "AD-Domain-Services"
}
xWaitforDisk Disk2
{
DiskId \= 2
RetryIntervalSec \= 10
RetryCount \= 30
}
xDisk DiskF
{
DiskId \= 2
DriveLetter \= 'F'
DependsOn \= '\[xWaitforDisk\]Disk2'
}
xPendingReboot BeforeDC
{
Name \= 'BeforeDC'
SkipCcmClientSDK \= $true
DependsOn \= '\[WindowsFeature\]ADDSInstall','\[xDisk\]DiskF'
}
\# Optional GUI tools
WindowsFeature ADDSTools
{
Ensure \= "Present"
Name \= "RSAT-ADDS"
}
\# No slash at end of folder paths
xADDomain FirstDS
{
DomainName \= 'tomlab.briwave.com.au'
DomainAdministratorCredential \= $domainCred
SafemodeAdministratorPassword \= $safemodeAdministratorCred
DatabasePath \= 'F:\\NTDS'
LogPath \= 'F:\\NTDS'
DependsOn \= "\[WindowsFeature\]ADDSInstall","\[xDisk\]DiskF","\[xPendingReboot\]BeforeDC"
}
}
}
Apart from install the necessary roles and features, another thing worth noting is the configuration to check and use a 2nd disk for the NTDS log files. This is due to the factor that Azure OS disk by default has writing cache feature enabled, which could cause data corruption on AD DS. Here is the MS document supporting this argument.
Now, our original VM created with PowerShell does not have a 2nd disk attached. Let’s use the commands below to achieve that.
\# Add a managed disk to VM
$rsgName \= "tomlab-au"
$location \= "Australia Southeast"
$vmName \= "testdc1"
$diskName \= $vmName + '\_datadisk1'
$diskConfig \= New-AzureRmDiskConfig \-AccountType PremiumLRS \-Location $location \-CreateOption Empty \-DiskSizeGB 128
$dataDisk1 \= New-AzureRmDisk \-DiskName $diskName \-Disk $diskConfig \-ResourceGroupName $rsgName
$vm \= Get-AzureRmVM \-Name $vmName \-ResourceGroupName $rsgName
$vm \= Add-AzureRmVMDataDisk \-VM $vm \-Name $diskName \-CreateOption Attach \-ManagedDiskId $dataDisk1.Id \-Lun 1
Update-AzureRmVM \-VM $vm \-ResourceGroupName $rsgName
Next we upload our DSC script to Azure.
After upload the script, we need to compile it so it can be pushed to the Azure Windows VM. This is done through the PowerShell script below. As you can see, we need to specify the parameters and configData.
Param(
\[Parameter(Mandatory=$True)\]
\[String\]$rsgName,
\[Parameter(Mandatory=$True)\]
\[String\]$autoAccountName,
\[Parameter(Mandatory=$True)\]
\[String\]$configName
)
$ConfigData \= @{
AllNodes \= @(
@{
NodeName \= "\*"
PSDscAllowPlainTextPassword \= $True
},
@{
NodeName \= "testdc1"
Role \= "Primary DC"
}
)
}
\# Script to complie DSC Configuration in Azure Automation Account
Start-AzureRmAutomationDscCompilationJob \-ResourceGroupName $rsgName \-AutomationAccountName $autoAccountName \-ConfigurationName $configName \-ConfigurationData $ConfigData
If all goes well, you should see something like below.
Next, we need to apply the complied configuration to the node (Windows VM). You will need to reboot the server after the initial DSC push. This is to apply those roles and features.
Once the configuration is applied, it will take a while for Azure Automation to get the final status.