Using Azure DSC to configure a new Active Directory Domain

In this article, I want to show you how easy it is to create a new Active Directory domain for demo environments. If you want something for production, there are some additional steps to take, but I won’t cover that here.

You can use Azure DSC for many configurations, like setting up a domain controller, as I will show here. It is also possible to install Windows Roles and Feature and create your own resources to install and configure software. If you want to read more about Azure DSC, you can go here: https://docs.microsoft.com/en-us/azure/virtual-machines/extensions/dsc-overview  If you want to know more about DSC in general, you can read about it here: https://docs.microsoft.com/en-us/powershell/scripting/dsc/getting-started/wingettingstarted?view=powershell-7.1

I won’t show how to create an automation account, but you can read about it here if you haven’t gotten one yet. https://docs.microsoft.com/en-us/azure/automation/automation-create-standalone-account

So, let’s get started with the guide to get a domain controller up and running in Azure using Azure DSC.

First, I need to create the code for the DSC configuration I want to use in Azure, I have pasted my code in the code block below, and you can copy-paste that into your environment. Notice that I use credentials and variables stored in Azure Automation. I will show you what that looks like later. The code below is a PowerShell script, so you need to save it as a PS1 file and then upload it to your Azure DSC. 

Configuration DC1
{
	$domainCred = Get-AutomationPSCredential -Name "DomainAdmin"
    $DomainName = Get-AutomationVariable -Name "DomainName"
    $DomainDN = Get-AutomationVariable -Name "DomainDN"
	
	# Import the modules needed to run the DSC script
    Import-DscResource -ModuleName 'PSDesiredStateConfiguration'
	Import-DScResource -ModuleName 'ComputerManagementDsc'
	Import-DscResource -ModuleName 'ActiveDirectoryDsc'	

	Node "Localhost"
	{
	    Computer NewComputerName
        {
            Name = "DC1"
        }       	
		
		WindowsFeature ADDSInstall
		{
			Ensure = "Present"
			Name = "AD-Domain-Services"
			DependsOn = "[Computer]NewComputerName"
		}
		WindowsFeature ADDSTools
		{
			Ensure = "Present"
			Name = "RSAT-ADDS"
		}
		WindowsFeature InstallRSAT-AD-PowerShell
		{
			Ensure = "Present"
			Name = "RSAT-AD-PowerShell"
		}
		
		ADDomain $DomainName
        {
            DomainName                    = $DomainName
            Credential                    = $domainCred
            SafemodeAdministratorPassword = $domainCred
			ForestMode                    = 'WinThreshold'
			DependsOn 					  = "[WindowsFeature]ADDSInstall"
        }	
		WaitForADDomain $DomainName
        {
            DomainName           = $DomainName
			WaitTimeout          = 600
			RestartCount         = 2
            PsDscRunAsCredential = $domainCred
        }
		ADOrganizationalUnit 'Demo'
        {
            Name                            = "Demo"
            Path                            = "$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "TopLevel OU"
            Ensure                          = 'Present'
        }
		
		ADOrganizationalUnit 'WebServers'
        {
            Name                            = "WebServers"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "WebServers OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}
		ADOrganizationalUnit 'Administration'
        {
            Name                            = "Administration"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "Administration OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}
		ADOrganizationalUnit 'AdminUsers'
        {
            Name                            = "AdminUsers"
            Path                            = "OU=Administration,OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "Administration OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Administration"
		}
		ADOrganizationalUnit 'ServiceAccounts'
        {
            Name                            = "ServiceAccounts"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "ServiceAccounts OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}
		ADOrganizationalUnit 'Citrix'
        {
            Name                            = "Citrix"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "Citrix OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}		
		ADOrganizationalUnit 'Users'
        {
            Name                            = "Users"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "Users OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}
		ADOrganizationalUnit 'Servers'
        {
            Name                            = "Servers"
            Path                            = "OU=Demo,$domainDN"
            ProtectedFromAccidentalDeletion = $true
            Description                     = "Servers OU"
			Ensure                          = 'Present'
			DependsOn 						= "[ADOrganizationalUnit]Demo"
		}		
		ADUser	 'svc_sql'
		{
			UserName = 'svc_sql'
			Description = "Service account for SQL"
			Credential = $Cred
			PasswordNotRequired = $true
			DomainName = 'MTH-Consulting.dk'
			Path = "OU=ServiceAccounts,OU=Demo,$domainDN"
			Ensure = 'Present'
			DependsOn = "[ADOrganizationalUnit]ServiceAccounts"
			Enabled = $true
			UserPrincipalName = "svc_sql@MTH-Consulting.dk"
			PasswordNeverExpires = $true
			ChangePasswordAtLogon = $false
		}		
	}
}

If you read through the configuration, you can see that I create the domain first, then I create the OU structure, and then I create service accounts, DO remember that if you add admin users or service accounts, DSC will set the password for these and that might screw up your environment a bit if you change it manually later on.
I now have the configuration saved on my local computer, and I now need to upload it into Azure DSC. The PS1 file must be named the same as the configuration name, and in this case, it needs to be “dc1.ps1”. Under my automation account, I click on “State configuration (DSC).

I click on “Configurations” and then on “Add”, the add button does differently this depending on which “tab” is active.

I choose my file and click on “OK”.

DC1 now appears in the list of configurations as not compiled. I can see this since “0” is the count of “Compiled configuration count”.

To fix this, I click on the configuration “DC1” in the list, and then I can click on “Compile”, as shown below.

The compilation typically takes a few minutes, and then the count is increased to “1”.

Now that I have the configuration done, I can create a virtual machine in Azure. I go to the resource group I want to use and then click “Add”, as shown below.

I click on “Windows Server 2016 Datacenter”.

I name my domain controller DC1, choose “Windows Server 2019 Datacenter” as my image, and choose a machine type. I select a B2MS type that will do just fine.

I will provide credentials for the virtual machine and ensure that public access is not available for my VM.

I will change my disk to a standard SSD to keep the cost a bit down.

I will create a new VNet for this VM since it is just a demo. When I usually deploy these demo environments, I have automation to create the VNets and Subnets before I create my virtual machine.

I keep the management and advance page as default, and under tags, I ensure that I know what these resources are used for.

Validation passed just fine, and I now click on “Create” to deploy my new virtual machine for the domain controller.

While the virtual machine is deploying, I can make sure that I have all the pieces in the automation account settled.
I navigate to my automation account and click on “Credentials”.

I click on “Add a credential”.

I fill in the fields, and it will look like the picture below.

Next, I will add my variables to my automation account, so I click on the “Variables” link on the automation account’s left side.

I create two variables with the values as shown below.

The final part I need to do now is to apply my configuration to the virtual machine. I do this by going to my automation account and click on “State configuration (DSC)”

Then I make sure that I am on the “Nodes” tab and click on “Add”.

I click on “DC1”.

Then I click on “Connect”.

I choose the configuration that I created for the domain controller, and I ensure that “ApplyAndAutoCorrect” and “Reboot if needed” are selected.

When I click “OK”, I am directed back to the status page to follow the process.

After about 10-15 mins the configuration was complete, and I could see the status is now “Compliant”.

And to verify, I can log into the domain controller and view the domain structure.

This blog post became a reasonably long post, but I hope it makes sense and it has been informative. If you have any questions, please let me know via Twitter or post a comment.

Comments