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

# Create a virtual network
$vnet = New-AzureRmVirtualNetwork -ResourceGroupName $rsgName -Location $location -Name "tomlabVNET1" -AddressPrefix -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 = ''             
            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.

$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.



  1. I have bookmarked your site since this site contains important data in it. I am truly content with articles quality and introduction about azure. You rock for keeping extraordinary stuff. oracle fusion scm online training


Post a comment

Popular posts from this blog

Install AWS CLI on WSL Ubuntu

On Premise Mailbox user missing in Exchange Online GAL

Migrate Azure AD Connect Between AD Forests