With the help of PowerShell DSC, you can automate the creation of an Active Directory domain. This includes promoting a member server to a domain controller and creating users, groups, and containers. This can be particularly helpful when you want to set up a test domain quickly.

One of the most common components of any test environment is Active Directory (AD). AD is a critical part of most environments and is one we need to replicate as closely as possible to production in a test environment. Before technologies like PowerShell Desired State Configuration (DSC) came along, we had to copy the AD database over manually, bring up entirely new domain controllers from scratch, and a lot of other hacks. Nowadays, we can invoke a DSC configuration script that can bring up an entirely new AD domain in no time!

Prerequisites

To automate creating a test domain with DSC, we'll need to ensure we've got a few prerequisites in place. First, we'll need an existing server (physical or virtual) in a workgroup. Next, we'll need to make sure this server has at least PowerShell v4 installed and preferably v5.

In the demonstration, we'll be creating the DSC configuration on another machine and sending it to the server that will be our domain controller. Although it's not necessary if performing the work locally, this computer will need to be able to communicate with the soon-to-be domain controller via Server Message Block (SMB).

Finally, you'll need to have the xActiveDirectory DSC module installed on the server. You can download this by running the following:

Install-Module -Name xActiveDirectory

Project scope

Creating an AD domain can mean a lot to many different people since it's such a large topic. So let's scope our DSC configuration script down a bit. For this article, we'll be:

  • Promoting a member server to a domain controller
  • Creating multiple groups
  • Creating multiple users
  • Creating multiple organizational units

This isn't, by far, everything that's possible. But with the framework down, you can add additional objects to your DSC configuration script more easily later.

Defining Configuration Data

A best practice to use when creating any DSC configuration script is to separate the actual configuration itself from the configuration data. Configuration data can consist of any "static" values the code needs to reference when running. In our case, it includes the domain name, group names, organizational unit paths, and so on. The first task to tackle is defining all of these values in a PSD1 file that contains a hash table with all the data we need.

To expedite the process of creating this file, I've created an example you can download from the TestDomainCreator GitHub repository.

@{

	AllNodes = @(

		@{

			NodeName = '*'

			PsDscAllowDomainUser = $true

            PsDscAllowPlainTextPassword = $true

		},

		@{

			NodeName = 'LABDC'

            Purpose = 'Domain Controller'

            WindowsFeatures = 'AD-Domain-Services'

        }

    )

    NonNodeData = @{

        DomainName = 'mytestlab.local'

        AdGroups = 'Accounting','Information Systems','Executive Office','Janitorial Services'

        OrganizationalUnits = 'Accounting','Information Systems','Executive Office','Janitorial Services'

        AdUsers = @(

            @{

                FirstName = 'Katie'

                LastName = 'Green'

                Department = 'Accounting'

                Title = 'Manager of Accounting'

            }

            @{

                FirstName = 'Joe'

                LastName = 'Blow'

                Department = 'Information Systems'

                Title = 'System Administrator'

            }

            @{

                FirstName = 'Joe'

                LastName = 'Schmoe'

                Department = 'Information Systems'

                Title = 'Software Developer'

            }

            @{

                FirstName = 'Barack'

                LastName = 'Obama'

                Department = 'Executive Office'

                Title = 'CEO'

            }

            @{

                FirstName = 'Donald'

                LastName = 'Trump'

                Department = 'Janitorial Services'

                Title = 'Custodian'

            }

        )

    }

}

This file holds all the configuration item values we need to create a fully functional AD domain.

Creating the DSC configuration script

Next, we need to create the DSC configuration. We can break down this configuration into the four topics I described above. Since we've already created a separate configuration data file, we'll need to reference this inside the DSC configuration script. We'll use the $ConfigurationData variable automatically available in all DSC configuration scripts (if using the ConfigurationData parameter when invoking Start-DscConfiguration).

You can download an example script from GitHub. You'll see from the example that the DSC configuration script is calling each of the AD objects we're creating (groups, organizational units, and users). It then references the appropriate DSC resource within the xActiveDirectory DSC module we downloaded earlier.

The only section of the DSC configuration script that does not fit this mold is when we're installing the appropriate Windows features and promoting the server to a domain controller.

Domain DSC configurationconfiguration NewTestEnvironment
{        
    Import-DscResource -ModuleName xActiveDirectory
    
    ## Authenticate to Azure
    Login-AzureRmAccount

    ## This will be invoked by Azure Automation so grab the Azure Automation DSC credential asset and use it.
    $credParams = @{
        ResourceGroupName = 'Group'
        AutomationAccountName = 'adamautomation'
    }
    $defaultAdUserCred = Get-AutomationPSCredential -Name 'Default AD User Password'
    $domainSafeModeCred = Get-AutomationPSCredential -Name 'Domain safe mode'
            
    Node $AllNodes.where({ $_.Purpose -eq 'Domain Controller' }).NodeName
    {

        @($ConfigurationData.NonNodeData.ADGroups).foreach( {
                xADGroup $_
                {
                    Ensure = 'Present'
                    GroupName = $_
                    DependsOn = '[xADDomain]ADDomain'
                }
            })

        @($ConfigurationData.NonNodeData.OrganizationalUnits).foreach( {
                xADOrganizationalUnit $_
                {
                    Ensure = 'Present'
                    Name = ($_ -replace '-')
                    Path = ('DC={0},DC={1}' -f ($ConfigurationData.NonNodeData.DomainName -split '\.')[0], ($ConfigurationData.NonNodeData.DomainName -split '\.')[1])
                    DependsOn = '[xADDomain]ADDomain'
                }
            })

        @($ConfigurationData.NonNodeData.ADUsers).foreach( {
                xADUser "$($_.FirstName) $($_.LastName)"
                {
                    Ensure = 'Present'
                    DomainName = $ConfigurationData.NonNodeData.DomainName
                    GivenName = $_.FirstName
                    SurName = $_.LastName
                    UserName = ('{0}{1}' -f $_.FirstName.SubString(0, 1), $_.LastName)
                    Department = $_.Department
                    Path = ("OU={0},DC={1},DC={2}" -f $_.Department, ($ConfigurationData.NonNodeData.DomainName -split '\.')[0], ($ConfigurationData.NonNodeData.DomainName -split '\.')[1])
                    JobTitle = $_.Title
                    Password = $defaultAdUserCred
                    DependsOn = '[xADDomain]ADDomain'
                }
            })

        ($Node.WindowsFeatures).foreach( {
                WindowsFeature $_
                {
                    Ensure = 'Present'
                    Name = $_
                }
            })        
        
        xADDomain ADDomain          
        {             
            DomainName = $ConfigurationData.NonNodeData.DomainName
            DomainAdministratorCredential = $domainSafeModeCred
            SafemodeAdministratorPassword = $domainSafeModeCred
            DependsOn = '[WindowsFeature]AD-Domain-Services'
        }
    }         
}

Once you've downloaded the DSC configuration script and the configuration data, and you've tweaked them to your liking, you can then create the MOF file. After this, we can then invoke the file on the server.

. NewTestEnvironment.ps1
NewTestEnvironment -ConfigurationData C:\ConfigurationData.psd1

WARNING: The configuration 'NewTestEnvironment' is loading one or more built-in resources without explicitly importing associated modules. Add Import-DscResource –ModuleName 'PSDesiredStateConfiguration' to your configuration to avoid this message.

    Directory: C:\NewTestEnvironment


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        5/21/2017   4:53 PM          16258 LABDC.mof

Above I'm creating the MOF file for the server I have called LABDC.

Once I've created the MOF file, I can then invoke the DSC configuration on my remote server by running Start-DscConfiguration.

Start-DscConfiguration -Wait -Force -Path .\NewTestEnvironment -Credential (Get-Credential) -Verbose
Start DscConfiguration

Start DscConfiguration

Notice that I'm using a credential. This is necessary because the server you'll be running the DSC configuration script on will be in a workgroup. Thus, you must provide an administrative username and password to make that initial connection.

Subscribe to 4sysops newsletter!

After finishing this process, you will then have a brand new AD domain running on your server!

5 Comments
  1. Jimmy 6 years ago

    Hi

    thanks for your post

    is it possible to customise this configuration in order to create :

    – a real OU structure ( OU / sub OU / sub Ou …)

    – define in wich OU  groups and users should be created?

    thanks

  2. Jimmy 6 years ago

    Sorry, just saw the « path » function, could you just please explain how the path function works in order to define the path?

    (-f ?)

    Thanks

  3. mike 4 years ago

    Didn't find your test users amusing.

  4. tank 4 years ago

    I did find your test users amusing

  5. This is definitely not the place to be advertising your politics. 

    David F. 

    avatar

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account