A Practical Guide In Using AutomatedLab

For security testing purposes I always use a virtual machine as I don’t want the result of my test to influence my local machine or the network that I’m connected to. As such for a long time I’ve been building VM’s manually, creating domains and setting the configuration as needed. As I do advocate automation as a solution to predictability myself, it was about time I started to practice what I preach. As all IT minded people, I wanted to create an automation script myself, just because it’s cool. During my search for information I stumbled upon a set of PowerShell cmdlets that would actually do precisely what I wanted, so I let the idea of creating one myself go and focused on the inner working of these cmdlets, named AutomatedLab.

As the name implies it’s a way to quickly setup a preconfigured lab for testing. There are a few others out there, like Microsoft’s hydration toolkit, but I found this one in particular more useful and relatively easy to master. With this blog post I want to share my experiences with the toolkit and hopefully you can put it to good use in your environment.

Installing AutomatedLab

The team behind AutomatedLab has done a wonderful job in documenting the tool, especially how to get started, so I won’t be listing all the steps as most of these can be found on their Wiki as well. For completeness just make sure that you have a machine capable of running virtualization software. Although AutomatedLAB can also manage virtual machines in Azure, as well as in Hyper-V and VMWare. This post will focus on the Hyper-V part.

For an overview of the actual requirements, see the getting started page on the products Wiki.

Installation is really simple, you can choose to either use the installer (old school) or use the power of PowerShell to get it from the PowerShell gallery. Make sure to open an elevated PowerShell session and use the following commands:

Install-Module -Name AutomatedLab -AllowClobber
Install-Module -Name powershell-yaml

AllowClobber is there because of overlapping cmdlets already present on the system. “powershell-yaml” is an extra dependency that needs to be installed as well. Accept any questions regarding package management and trusting the PowerShell gallery

After installation you still need to configure AutomatedLAB, meaning that you have to assign it a location on the local disk to store it’s files and configuration. The following cmdlet takes care of that:

New-LabSourcesFolder -DriveLetter ‘<DriveLetter>’

Make sure that you have sufficient and fast (SSD) storage space available.

If you rather watch the action than read all about it, check out the quick guide I’ve created on installing AutomatedLab

Getting started

AutomatedLab needs the installation ISO files of Windows (Client and Server class Operating Systems), Linux or Servers like exchange or SCCM to setup the Lab. In the LabSources directory created with  the “New-LabSourcesFolder” cmdlet there’s a ISOs folder, simply copy the ISO files that you wish to use to this folder. Nothing more to it.

Now, to see the content of the ISO files, use the “Get-LabAvailableOperatingSystem” cmdlet to list the available operating systems.

All Available Operating Systems

The Operating Systems listed in the property “OperatingSystemName” are the ones that we can use in the configuration script.

Updating the Operating Systems

AutomatedLab is a pretty complete package. The team even derived a way to update the Operating Systems during deployment. Updates can be stored in the “OSUpdates” directory and installed during deployment or updated during new ISO creation. That’s cool but I wanted more.

My ultimate wish was to not mess around with downloading individual updates and letting it install during initial deployment. I wanted it to be automated, so it would be predicable and it would also launch an operating system that was fully patched and configured the way I wanted to. To this end I created my own ISOs using these steps:

  • Create a Windows Server with the Microsoft Deployment Toolkit installed
  • Create a deployment for the OS that you wish to use in AutomatedLab
  • Deploy the machine and update / configure it accordingly
  • Sysprep the machine and seal it as you would normally do
  • Mount the VHDX and search for left over unattended.xml files, delete all of them
  • Create a new ISO file from the content of the VHDX, this is the base of the AutomatedLab VHDX files
  • Place the ISO files in the LabSources\ISOs directory

A Basic Setup

Now let the fun begin, we’re going to create the most simplistic lab possible. A single Windows machine using an internal virtual switch for network connectivity. It’s really just a few lines of code and you’re set. Consider the following as an example:

New-LabDefinition -Name MyFirstLab -DefaultVirtualizationEngine HyperV
Add-LabMachineDefinition -Name First -OperatingSystem ‘Windows Server 2016 Standard (Desktop Experience)’

Let me explain it line by line.

New-LabDefinition -Name MyFirstLab -DefaultVirtualizationEngine HyperV

This is where you define a new lab with the name of your choice (MyFirstLab) and define the virtualization engine. Defining the latter tells AutomatedLab which cmdlets to use.

Add-LabMachineDefinition -Name First -OperatingSystem ‘Windows Server 2016 Standard (Desktop Experience)’

AutomatedLab needs to know what kind of operating system to deploy and which name to give to the VM and host. Configuring the VM during deployment is mostly done using this cmdlet.


This cmdlet starts the deployment. It checks the availability of the parent virtual disks (And creates them if absent), creates the virtual switch and a whole lot more.

Visual Indication During Lab Deployment

That’s really it. After executing it takes approximately 5 minutes to have the first machine up and running.

To get a little more advanced and provide Internet connectivity using the Windows 10 “Default Switch” you only need to add this line above the line with the “Add-LabMachineDefinition” cmdlet. It would look something like this:

Add-LabVirtualNetworkDefinition -Name ‘Default Switch’ -HyperVProperties @{SwitchType = ‘External’; AdapterName = ‘vEthernet (Default Switch)’}

To obtain the information for the Name property use the “Get-Vmswitch” and check out the name defined there. To get the AdapterName use the Name property from the “Get-NetAdapter” cmdlet.

One more thing that also needs to be changed is telling the “Add-LabMachineDefinition” which network to use, because we’re taking control of this part it needs to know what to do. Simply add “-Network ‘Default Switch’” to the line of code and you’re done.

Running these four lines of code results in a deployed Windows Server 2016 virtual machine, that is internet connected through DHCP using the “Default Switch”.

Easy right?

Removing the LAB

After you’re done with the lab and you want it to be gone, another cmdlet can be used that takes care of removing the virtual machine, virtual switch and deletes the appropriate files from the disk. Execute the “Remove-Lab” cmdlet with the appropriate name of the Lab to have it automatically removed. 

Remove-Lab -Name MyFirstLab

All the steps of a basic lab deployment

Advanced Deployment

For my credential theft demo’s I wanted a lab that consisted out of 6 virtual machines. A domain controller, web server, file server, router and two Windows 10 clients. The router needed to sue the Windows 10 default switch to automatically connect the Lab machines to the Internet. I need this because I’m using evaluation media that needs to be activated before I can use it for so many days. I really can’t use the integrated “Default Switch” for this purpose directly on the virtual machines as it rotates the IP subnet at every restart of the service. However I can use it attached to one network card of the virtual router. All these machines needed to be joined to a domain that I would define in the script. One requirement I had was that the domain would also be properly configured, although it’s just a small lab, I want to mimic reality as much as possible. Although one could argue that I would also need more DC’s for that purpose. After a successful deployment I wanted to remove the autologon feature and do some house cleaning. Being a security guy, I wanted to enable the firewall and turn on user account control at the end of the deployment. These scenario’s are covert in the tool itself. The script below takes care of all these steps. I’ve included as much information as possible in the comment section to explain the steps I’ve taken.

$labName = 'BitsOfWater' # The name of the lab
$Network = 'LAB - Virtual Switch' #The Name of the Internal Network
$DomainName = 'bitsofwater.lab' # The Domain name of the LAB
$DoVMDeploymentCleanup = $True # Lab Cleanup after it's deployed

# Get the lab credentials
$LabCredentials = Get-Credential            
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($LabCredentials.Password)            
$LabPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)            
$LabAdmin = $LabCredentials.UserName

# Define the LAB
New-LabDefinition -Name $labName -DefaultVirtualizationEngine HyperV

# Define the domain (only needed if you want to set a custom password)
Add-LabDomainDefinition -Name $DomainName -AdminUser $LabAdmin -AdminPassword $LabPassword

# Define the installation credentials, these must be the same as the domain definition credentails 
# (only needed if you want to set a custom password)
Set-LabInstallationCredential -Username $LabAdmin -Password $LabPassword

# Define the Network (AddressSpace (e.g. -AddressSpace is optional, if not set it will automatically be selected)
# use get-vmswitch for the name of the switch, used in -Name
# use get-netadapter to get the name of the adapter it should get attached to (used in AdapterName)
Add-LabVirtualNetworkDefinition -Name $Network
Add-LabVirtualNetworkDefinition -Name 'Standaard schakeloptie' -HyperVProperties @{SwitchType = 'External'; AdapterName = 'vEthernet (Standaard schak)'}

#defining default parameter values, as these ones are the same for all the machines
$PSDefaultParameterValues = @{
    'Add-LabMachineDefinition:EnableWindowsFirewall'= $true
    'Add-LabMachineDefinition:OperatingSystem'= 'Windows Server 2016 Datacenter Evaluation (Desktop Experience)'

# Add Network Adapters for the router. Using this cmdlet gives more control over the settings inside the VM
$netAdapterRouter = @()
$netAdapterRouter += New-LabNetworkAdapterDefinition -VirtualSwitch $Network
$netAdapterRouter += New-LabNetworkAdapterDefinition -VirtualSwitch 'Standaard schakeloptie' -UseDhcp

# Add Root Domain Controller
$postInstallActivity = Get-LabPostInstallationActivity -ScriptFileName ConfigureRootDomain.ps1 -DependencyFolder $labSources\PostInstallationActivities\PWNET
Add-LabMachineDefinition -Name LAB-DC1 -Roles RootDC -Network $Network -DomainName $DomainName -PostInstallationActivity $postInstallActivity

# Add Servers to the domain
Add-LabMachineDefinition -Name LAB-WEB1 -Roles WebServer -Network $Network -DomainName $DomainName
Add-LabMachineDefinition -Name LAB-SRV1 -Roles FileServer -Network $Network -DomainName $DomainName
Add-LabMachineDefinition -Name LAB-Router -Roles Routing -NetworkAdapter $netAdapterRouter -DomainName $DomainName

#Add Clients to the domain
Add-LabMachineDefinition -Name LAB-CLT1 -OperatingSystem 'Windows 10 Enterprise Evaluation' -Network $Network -DomainName $DomainName
Add-LabMachineDefinition -Name LAB-ADM1 -OperatingSystem 'Windows 10 Enterprise Evaluation' -Network $Network -DomainName $DomainName

# Install the lab

# Remove the AutoLogon feature
Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Disable AutoLogon" -ScriptBlock {
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -ErrorAction SilentlyContinue){
        Set-ItemProperty -literalPath 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -Value 0
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount -ErrorAction SilentlyContinue ){
        Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword  -ErrorAction SilentlyContinue ){
        Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword 
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID -ErrorAction SilentlyContinue ){
        Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName -ErrorAction SilentlyContinue ){
        Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName
    if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName -ErrorAction SilentlyContinue ){
        Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName

} -PassThru

# Set User Account Control
Set-LabVMUacStatus -ComputerName (Get-LabVM) -EnableLUA $true -ConsentPromptBehaviorAdmin 5 -ConsentPromptBehaviorUser 3

# Cleanup Deployment Files From Virtual Machines
if ($DoVMDeploymentCleanup){
    Invoke-LabCommand -ComputerName (Get-LabVM) -ScriptBlock {
        if (Test-Path -Path 'C:\AdditionalDisksOnline.ps1'){
            Remove-Item 'C:\AdditionalDisksOnline.ps1' -Force
            if (Test-Path -Path 'C:\Unattend.xml'){
            Remove-Item 'C:\Unattend.xml' -Force
            if (Test-Path -Path 'C:\WSManRegKey.reg'){
            Remove-Item 'C:\WSManRegKey.reg' -Force
            if (Test-Path -Path 'C:\DeployDebug'){
            Remove-Item 'C:\DeployDebug' -Recurse -Force

# Restart all the Domain Controllers lab virtual machines
$LabDCs =Get-LabVM | where {( $_.Roles -like "*DC*" -and $_.OperatingSystemType -eq "Windows" )}
foreach($LabDC in $LabDCs)
    Restart-LabVM -ComputerName $LabDC.Name -NoNewLine -Wait

# Restart All Servers
$LabServers = Get-LabVM | where {( $_.OperatingSystem -Like "*Windows Server*" -and $_.Roles -notlike "*DC*" -and $_.OperatingSystemType -eq "Windows" )}
foreach($LabServer in $LabServers)
    Restart-LabVM -ComputerName $LabServer.Name -NoNewLine -Wait

#Restart All Clients
$LabClients = Get-LabVM | where {( $_.OperatingSystem -NotLike "*Windows Server*" -and $_.OperatingSystemType -eq "Windows" )}
foreach($LabClient in $LabClients)
    Restart-LabVM -ComputerName $LabClient.Name -NoNewLine -Wait

# Show all the installation details
Show-LabDeploymentSummary -Detailed

I hope that this information can be put to good use for your needs. As always if you have any question, remarks or feedback please don’t hesitate to contact me. All files that I’ve used, scripts and more, are available as a download.

One thought on “A Practical Guide In Using AutomatedLab

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s