Set up an Active Directory lab (part 1)
These steps are for creating a sandpit Active Directory environment in Azure. The intent was to have a re-usable process, scripted but not fully automated, faster than using the portal. Whether its to test AD features or have an AD platform for testing other types of workloads, the steps to create the lab had to be flexible to adapt to changing requirements.
What features are included in this guide?
- Creating resources with Azure CLI
- Creating VMs with and without public IP addresses
- Creating VMs with unmanaged disks
- Bootstrapping VMs to create AD domains and install and configure IIS
- Bootstrapping VMs with static IP addresses and joining the domain
- Creating NSGs with Network Watcher enabled
What are we building?
The requirements for the build:
- Two AD forests for a two-way trust
- Azure AD Connect servers synchronising AD to two trial M365 tenancies
- App servers hosting IIS web sites configured for Windows authentication for testing kerberos authentication
- Windows 10 client VMs for testing user authentication and application access
._________.
| BASTION |
|_________|
.________. .________. .________.
| A-DC01 | | A-DC02 | <---TWO WAY EXTERNAL TRUST---> | B-DC01 |
|________| |________| |________|
._________. ._________. ._________.
| A-APP01 | | A-APP02 | | B-APP01 |
| (Web) | | (AAD C) | |_________|
|_________| |_________|
.____________. .____________.
| A-CLIENT01 | | B-CLIENT01 |
|____________| |____________|
Hostname | Subnet | IP Address | Role |
---|---|---|---|
VM-A-DC01 | domainA-ad (10.1.10/24) | 10.1.10.10 | Domain Controller 1 - Forest root |
VM-A-DC02 | domainA-ad (10.1.10/24) | 10.1.10.11 | Domain Controller 2 - Domain 2 |
VM-A-APP01 | domainA-app (10.1.20/24) | 10.1.20.20 | Web Server |
VM-A-APP02 | domainA-app (10.1.20/24) | 10.1.20.21 | Azure AD Connect |
VM-A-CLIENT01 | domainA-client (10.1.30/24) | 10.1.30.30 | Windows 10 Client |
VM-B-DC01 | domainB-ad (10.1.110/24) | 10.1.110.10 | Domain Controller |
VM-B-APP01 | domainB-app (10.1.120/24) | 10.1.120.20 | Web Server & AAD Connect |
VM-B-CLIENT01 | domainB-client (10.1.130/24) | 10.1.130.30 | Windows 10 Client |
VM-BAS01 | public (10.1.200/24) | DHCP | Bastion Host / Jump Box |
Build environment
These steps use Azure CLI which can be run from an Azure Cloud Shell session.
Pre-requisites
You'll need an Azure subscription with sufficient quota for the number of VMs / vCPUs being created. A free trial subscription doesn't allow quota increases and won't have enough to host all VMs.
View your quota from Azure CLI:
az vm list-usage --location $region | ConvertFrom-Json | where currentValue -gt 0
VM sizing quick reference
In case you want to change VM sizes, I kept a quick reference of Azure VM costs for typical non-production instances. This helped to avoid the pain of (circum)navigating Azure VM pricing page.
These are the hourly cost for Windows VMs in East US region in $AUD - so change as required.
# Instance vCPU RAM Temp Storage PAYG Price $AUD
# B1MS 1 2 GiB 4 GiB $0.0338/hour
# B2S 2 4 GiB 8 GiB $0.0682/hour
# B2MS 2 8 GiB 16 GiB $0.1253/hour
# A2 v2 2 4 GiB 20 GiB $0.1868/hour
# D2s v3 2 8 GiB 16 GiB $0.2582/hour
# A2m v2 2 16 GiB 20 GiB $0.2472/hour
Declare re-usable variables
These were variables to run as a baseline in any new session. If you need to leave and come back later, or your shell expires, re-run these so they're ready for use later.
$rgName = "jr-trustlab-rg"
$vnetName = "jr-trustlab-vnet-01"
$vnetRange = "10.1.0.0/16"
$subnet1Name = "domainA-ad"
$subnet1Range = "10.1.10.0/24"
$subnet2Name = "domainA-app"
$subnet2Range = "10.1.20.0/24"
$subnet3Name = "domainA-client"
$subnet3Range = "10.1.30.0/24"
$subnet4Name = "domainB-ad"
$subnet4Range = "10.1.110.0/24"
$subnet5Name = "domainB-app"
$subnet5Range = "10.1.120.0/24"
$subnet6Name = "domainB-client"
$subnet6Range = "10.1.130.0/24"
$subnet99Name = "public"
$subnet99Range = "10.1.200.0/24"
$storageName = "jrtrustlabstore"
$region = "EastUS"
$vmPass = '' # Password used in local admin & AD recovery
$userPass = '' # Password used for test user accounts
$vm0Name = "vm-bas01"
$vm1Name = "vm-a-dc01"
$vm1IPAddress = "10.1.10.10"
$vm2Name = "vm-a-dc02"
$vm2IPAddress = "10.1.10.11"
$vm3Name = "vm-a-app01"
$vm3IPAddress = "10.1.20.20"
$vm4Name = "vm-a-app02"
$vm4IPAddress = "10.1.20.21"
$vm5Name = "vm-a-client01"
$vm5IPAddress = "10.1.30.30"
$vmB1Name = "vm-b-dc01"
$vmB1IPAddress = "10.1.110.10"
$vmB2Name = "vm-b-app01"
$vmB2IPAddress = "10.1.120.20"
$vmB3Name = "vm-b-client01"
$vmB3IPAddress = "10.1.130.30"
Create resource group
A single RG will host all resources [az group create].
az group create -l $region -n $rgName
Create virtual network
A single VNet will host all resources, segmented using Network Security Groups [az vnet network].
az network vnet create -g $rgName -n $vnetName --address-prefix $vnetRange --subnet-name $subnet1Name --subnet-prefixes $subnet1Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet2Name --address-prefixes $subnet2Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet3Name --address-prefixes $subnet3Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet4Name --address-prefixes $subnet4Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet5Name --address-prefixes $subnet5Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet6Name --address-prefixes $subnet6Range
az network vnet subnet create -g $rgName --vnet-name $vnetName -n $subnet99Name --address-prefixes $subnet99Range
# Optional - create Log Analaytics Workspace for Network Watcher traffic analysis
$logWsName = "jr-trustlab-logs"
az monitor log-analytics workspace create -g $rgName -n $logWsName --quota 1
az provider register --namespace Microsoft.Insights
Create storage account
The storage account will host the VM disks [az storage account].
$storageSku = "Standard_LRS"
$storageKind = "StorageV2"
az storage account create -n $storageName -g $rgName --sku $storageSku --kind $storageKind
Create VM#0 - Bastion Host
This VM is the jump box that is used to RDP to the other "internal" VMs [az vm].
- VM-BAS01
- Unmanaged OS disk
- Windows Server 2019 OS
- Public IP and DNS name of 'jr-bas.eastus.cloudapp.azure.com'
- NSG with flow logs but without Network Watcher
Create NSG
$nsg0Name = "jr-trustlab-nsg-bastion"
$nsg0TrustedIP = "" # Replace with own source IP(s) where you'll allow RDP
az network nsg create -n $nsg0Name -g $rgName
az network vnet subnet update -g $rgName -n $subnet99Name --vnet-name $vnetName --network-security-group $nsg0Name
az network nsg rule create --nsg-name $nsg0Name -g $rgName -n "AllowRDP" --priority 100 --access "allow" --source-address-prefixes $nsg0TrustedIP --destination-address-prefixes $subnet99Range --destination-port-ranges "3389" --protocol "TCP" --description "Allow RDP from trusted IPs"
# Think twice about flow logging on a public IP - could be noisy ... and you pay for data generated
# Or at least consider Flow Logs without Traffic Analytics.
$flow0LogName = "jr-trustlab-flow-public"
az network watcher flow-log create -n $flow0LogName -g $rgName --enabled true --nsg $nsg0Name --storage-account $storageName --location $region --format JSON --log-version 2 --retention 7 #--traffic-analytics --workspace $logWsName
Create VM
$vm0Image = "Win2019Datacenter"
$vm0User = "lcladmin"
$vm0Pass = $vmPass
$vm0Size = "Standard_DS2_v2"
$vm0PublicName = "jr-trustlab-bas" # jr-bas.eastus.cloudapp.azure.com
$vm0PIPName = "jr-trustlab-pip01"
$vm0DiskGuid = [guid]::NewGuid().ToString().Replace("-","").Substring(0,10)
az vm create -n $vm0Name -g $rgName --image $vm0Image --admin-username $vm0User --admin-password $vm0Pass --computer-name $vm0Name --size $vm0Size --vnet-name $vnetName --subnet $subnet99Name --storage-account $storageName --use-unmanaged-disk --public-ip-address-dns-name $vm0PublicName --public-ip-address $vm0PIPName --os-disk-name "$($vm0Name)-OSdisk-$($vm0DiskGuid)" --nsg '""'
# Install-WindowsFeature rsat-adds -IncludeAllSubFeature
az vm extension set -n BGInfo --publisher Microsoft.Compute --version 2.1 --vm-name $vm0Name -g $rgName
Create VM#1 - DomainA DC01
- VM-A-DC01
- Unmanaged OS & data disk
- Windows Server 2019
- Static IP address and more importantly DNS server IP
- Create new AD forest 'corp.local'
Create NSG
# Create NSG - A-DC
$nsg1Name = "jr-trustlab-nsg-a-ad"
az network nsg create -n $nsg1Name -g $rgName
# Add rules to NSG to allow AD traffic
az network vnet subnet update -g $rgName -n $subnet1Name --vnet-name $vnetName --network-security-group $nsg1Name
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Allow_RDP" --priority 100 --access "allow" --source-address-prefixes $subnet99Range --destination-address-prefixes $subnet1Range --destination-port-ranges "3389" --protocol "TCP" --description "Allow RDP from Bastion"
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Allow_AD_TCP" --priority 110 --access "allow" --source-address-prefixes $subnet1Range $subnet2Range $subnet3Range --destination-address-prefixes $subnet1Range --destination-port-ranges 135 389 636 53 88 445 49152-65535 --protocol "TCP" --description "Allow AD traffic TCP"
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Allow_AD_UDP" --priority 111 --access "allow" --source-address-prefixes $subnet1Range $subnet2Range $subnet3Range --destination-address-prefixes $subnet1Range --destination-port-ranges 53 88 389 --protocol "UDP" --description "Allow AD traffic UDP"
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Allow_B_AD_TCP" --priority 120 --access "allow" --source-address-prefixes $vmB1IPAddress --destination-address-prefixes $vm3IPAddress --destination-port-ranges 135 389 636 53 88 445 49152-65535 --protocol "TCP" --description "Allow AD traffic TCP"
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Allow_B_AD_UDP" --priority 121 --access "allow" --source-address-prefixes $vmB1IPAddress --destination-address-prefixes $vm3IPAddress --destination-port-ranges 53 88 389 --protocol "UDP" --description "Allow AD traffic UDP"
az network nsg rule create --nsg-name $nsg1Name -g $rgName -n "Deny_Inbound" --priority 4000 --access "deny" --source-address-prefixes "*" --destination-address-prefixes $subnet1Range --destination-port-ranges "*" --protocol "*" --description "Deny inbound traffic"
# Enable Network Watcher with Traffic Analytics
$flowLogName = "jr-trustlab-flow-ad"
az network watcher flow-log create -n $flowLogName -g $rgName --enabled true --nsg $nsg1Name --storage-account $storageName --location $region --format JSON --log-version 2 --retention 7 --traffic-analytics --workspace $logWsName
Create VM
$vm1Image = "Win2019Datacenter"
$vm1User = "lcladmin"
$vm1Pass = $vmPass
$vm1Size = "Standard_B2ms"
$vm1DiskGuid = [guid]::NewGuid().ToString().Replace("-","").Substring(0,10)
# Create VM
az vm create -n $vm1Name -g $rgName --image $vm1Image --admin-username $vm1User --admin-password $vm1Pass --computer-name $vm1Name --size $vm1Size --vnet-name $vnetName --subnet $subnet1Name --private-ip-address $vm1IPAddress --storage-account $storageName --use-unmanaged-disk --os-disk-name "$($vm1Name)-OSdisk-$($vm1DiskGuid)" --nsg '""' --public-ip-address '""'
# Attach data disk
$disk1Guid = [guid]::NewGuid().ToString().Replace("-","").Substring(0,10)
$disk1Name = "$($vm1Name)-DataDisk-$($disk1Guid)"
$disk1Size = "64"
$disk1Cache = "None"
az vm unmanaged-disk attach -g $rgName --vm-name $vm1Name --new --name $disk1Name --size-gb $disk1Size --caching $disk1Cache
Install ADDS
- Set static IP address and DNS server
- Install Active Directory Domain Services
- Install forest 'corp.local'
$dcA1Domain="corp.local"
$dcA1DomainNetbios="corp"
$scriptADC01_1 = @"
Set-TimeZone -id 'E. Australia Standard Time'
`$disks = Get-Disk | Where partitionstyle -eq 'raw'
If (`$disks) {
`$disks | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -UseMaximumSize -DriveLetter ""G"" | Format-Volume -FileSystem NTFS -NewFileSystemLabel ""Data"" -Confirm:`$false -Force
}
`$domain = ""$dcA1Domain""
`$domainnetbios = ""$dcA1DomainNetbios""
`$IP = ""$vm1IPAddress""
`$MaskBits = 24
`$Gateway = ""$($vm1IPAddress -split "\d{1,3}$" -join "1")""
`$DNS = ""$vm1IPAddress""
`$IPType = ""IPv4""
`$adapter = Get-NetAdapter | ? {`$_.Status -eq ""up""}
`$interface = `$adapter | Get-NetIPInterface -AddressFamily `$IPType
If (`$interface.Dhcp -eq ""Enabled"") {
Get-NetAdapterBinding -ComponentID ms_tcpip6 | Disable-NetAdapterBinding
If ((`$adapter | Get-NetIPConfiguration).IPv4Address.IPAddress) {
`$adapter | Remove-NetIPAddress -AddressFamily `$IPType -Confirm:`$false
}
If ((`$adapter | Get-NetIPConfiguration).Ipv4DefaultGateway) {
`$adapter | Remove-NetRoute -AddressFamily `$IPType -Confirm:`$false
}
`$adapter | New-NetIPAddress -AddressFamily `$IPType -IPAddress `$IP -PrefixLength `$MaskBits -DefaultGateway `$Gateway
`$adapter | Set-DnsClientServerAddress -ServerAddresses `$DNS
}
Install-WindowsFeature AD-Domain-Services, rsat-adds -IncludeAllSubFeature
Install-ADDSForest -DomainName `$domain -SafeModeAdministratorPassword (convertto-securestring '$vm1Pass' -asplaintext -force) -DomainMode Win2012R2 -DomainNetbiosName `$domainnetbios -ForestMode Win2012R2 -DatabasePath """G:\NTDS""" -SysvolPath """G:\SYSVOL""" -LogPath """G:\Logs""" -Force"
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm1Name -g $rgName --scripts $scriptADC01_1
- Add UPN suffix 'contoso.com' to forest
$dcA2UPNSuffix = "contoso.com"
$scriptADC01_2 = @"
try{
Import-Module ActiveDirectory -ErrorAction Stop
}catch{
throw ""Module ActiveDirectory not installed""
}
if ((Get-ADForest).UPNsuffixes -notcontains ""$dcA2UPNSuffix""){
Get-ADForest | Set-ADForest -UPNSuffixes @{add=""$dcA2UPNSuffix""}
}
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm1Name -g $rgName --scripts $scriptADC01_2
Create VM#2 - DomainA DC02
- VM-A-DC02
- Unmanaged OS & data disks
- Windows Server 2019
- Static IP address and DNS server
- Create new AD domain 'contoso.internal' as part of existing forest
Re-use NSG
Nothing to do. This VM lives in the same subnet as VM-A-DC01 and the NSG has already been created & configured.
Create VM
$vm2Image = "Win2019Datacenter"
$vm2User = "lcladmin"
$vm2Pass = $vmPass
$vm2Size = "Standard_B2ms"
$vm2DiskGuid = [guid]::NewGuid().ToString().Replace("-","").Substring(0,10)
az vm create -n $vm2Name -g $rgName --image $vm2Image --admin-username $vm2User --admin-password $vm2Pass --computer-name $vm2Name --size $vm2Size --vnet-name $vnetName --subnet $subnet1Name --private-ip-address $vm2IPAddress --storage-account $storageName --use-unmanaged-disk --os-disk-name "$($vm2Name)-OSdisk-$($vm2DiskGuid)" --nsg '""' --public-ip-address '""'
# Attach data disk
$disk2Guid = [guid]::NewGuid().ToString().Replace("-","").Substring(0,10)
$disk2Name = "$($vm2Name)-DataDisk-$($vm2DiskGuid)"
$disk2Size = "64"
$disk2Cache = "None"
az vm unmanaged-disk attach -g $rgName --vm-name $vm2Name --new --name $disk2Name --size-gb $disk2Size --caching $disk2Cache
Install ADDS
- Set static IP address
- Install Active Directory Domain Services
- Install domain 'contoso.internal'
- Add public domain 'contoso.com' as UPN suffix
$dcA1Domain="corp.local"
$dcA1DomainNetbios="corp"
$dcA1DomainDN="DC=corp,DC=local"
$dcA2Domain="contoso.internal"
$dcA2DomainNetbios="contoso"
$dcA2UPNSuffix="contoso.com"
$dcA2DomainDN="DC=contoso,DC=internal"
$dcBDomain="wingtip.root"
$scriptADC02_1 = @"
Set-TimeZone -id 'E. Australia Standard Time'
`$disks = Get-Disk | Where partitionstyle -eq 'raw'
If (`$disks) {
`$disks | Initialize-Disk -PartitionStyle MBR -PassThru | New-Partition -UseMaximumSize -DriveLetter ""G"" | Format-Volume -FileSystem NTFS -NewFileSystemLabel ""Data"" -Confirm:`$false -Force
}
`$domain = ""$dcA2Domain""
`$domainnetbios = ""$dcA2DomainNetbios""
`$IP = ""$vm2IPAddress""
`$MaskBits = 24
`$Gateway = ""$($vm2IPAddress -split "\d{1,3}$" -join "1")""
`$DNS = ""$vm1IPAddress""
`$IPType = ""IPv4""
`$adapter = Get-NetAdapter | ? {`$_.Status -eq ""up""}
`$interface = `$adapter | Get-NetIPInterface -AddressFamily `$IPType
If (`$interface.Dhcp -eq ""Enabled"") {
Get-NetAdapterBinding -ComponentID ms_tcpip6 | Disable-NetAdapterBinding
If ((`$adapter | Get-NetIPConfiguration).IPv4Address.IPAddress) {
`$adapter | Remove-NetIPAddress -AddressFamily `$IPType -Confirm:`$false
}
If ((`$adapter | Get-NetIPConfiguration).Ipv4DefaultGateway) {
`$adapter | Remove-NetRoute -AddressFamily `$IPType -Confirm:`$false
}
`$adapter | New-NetIPAddress -AddressFamily `$IPType -IPAddress `$IP -PrefixLength `$MaskBits -DefaultGateway `$Gateway
`$adapter | Set-DnsClientServerAddress -ServerAddresses `$DNS
}
Install-WindowsFeature AD-Domain-Services, rsat-adds -IncludeAllSubFeature
`$password = ConvertTo-SecureString '$vm2Pass' -AsPlainText -Force
`$adminCred = New-Object System.Management.Automation.PSCredential -ArgumentList (""$dcA1Domain\$vm1User"", `$password)
Install-ADDSDomain -Credential `$adminCred -SafeModeAdministratorPassword (convertto-securestring '$vm2Pass' -asplaintext -force) -NewDomainName `$domain -NewDomainNetbiosName `$omainnetbios -ParentDomainName ""$dcA1Domain"" -InstallDNS -DomainMode Win2012R2 -DomainType TreeDomain -ReplicationSourceDC ""$vm1Name.$dcA1Domain"" -DatabasePath ""G:\NTDS"" -SYSVOLPath ""G:\SYSVOL"" -LogPath ""G:\Logs"" -Force
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm2Name -g $rgName --scripts $scriptADC02_1
az vm restart -g $rgName -n $vm2Name
Create test user accounts
- Configure AD & create OUs
$scriptADC02_2 = @"
try{
Import-Module ActiveDirectory -ErrorAction Stop
}catch{
throw ""Module ActiveDirectory not installed""
}
`$password = ConvertTo-SecureString '$vm2Pass' -AsPlainText -Force
`$adminCred = New-Object System.Management.Automation.PSCredential -ArgumentList (""$dcA1Domain\$vm1User"", `$password)
Enable-ADOptionalFeature -Identity ""CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$dcA1DomainDN"" -Scope ForestOrConfigurationSet -Target ""$dcA2Domain"" -Confirm:`$false -Credential `$adminCred
if ((Get-ADForest).UPNsuffixes -notcontains ""$dcA2UPNSuffix""){
`$password = ConvertTo-SecureString '$vm1Pass' -AsPlainText -Force
`$adminCred = New-Object System.Management.Automation.PSCredential -ArgumentList (""$dcA1Domain\$vm1User"", `$password)
Get-ADForest | Set-ADForest -UPNSuffixes @{add=""$dcA2UPNSuffix""} -Credential `$adminCred
}
function New-ADOU {
# http://www.alexandreviot.net/2015/04/27/active-directory-create-ou-using-powershell
param([parameter(Mandatory=`$true)] [array]`$ouList)
ForEach(`$OU in `$ouList){
try{
New-ADOrganizationalUnit -Name ""`$(`$OU.Name)"" -Path ""`$(`$OU.Path)""
}catch{
Write-Host `$error[0].Exception.Message
}
}
}
`$ouCSV = ""Name;Path
S_SERVERS;$dcA2DomainDN
S_USERS;$dcA2DomainDN
ServiceAccounts;ou=S_USERS,$dcA2DomainDN
Staff;ou=S_USERS,$dcA2DomainDN
S_WORKSTATIONS;$dcA2DomainDN
S_GROUPS;$dcA2DomainDN""
`$ouList = `$ouCSV | ConvertFrom-CSV -Delimiter "";""
New-ADOU `$ouList
New-ADGroup -Name ""Grp_AllStaff"" -SamAccountName Grp_AllStaff -GroupCategory Security -GroupScope Global -DisplayName ""Grp_All Staff"" -Path ""OU=S_GROUPS,$dcA2DomainDN""
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm2Name -g $rgName --scripts $scriptADC02_2
- Create test users
$scriptADC02_3 = @"
try{
Import-Module ActiveDirectory -ErrorAction Stop
}catch{
throw ""Module ActiveDirectory not installed""
}
function Create-TestUsers {
param(
[parameter(Mandatory=`$true)] [array]`$UserList,
[parameter(Mandatory=`$true)] [string]`$UserPass,
[parameter(Mandatory=`$true)] [string]`$DomainSuffix,
[parameter(Mandatory=`$true)] [string]`$OUPath
)
# https://365lab.net/2014/01/08/create-test-users-in-a-domain-with-powershell/
`$departments = @(""IT"",""Finance"",""Logistics"",""Sourcing"",""Human Resources"")
ForEach(`$user in `$userList){
`$firstname = (Get-Culture).TextInfo.ToTitleCase(`$user.Firstname)
`$lastname = (Get-Culture).TextInfo.ToTitleCase(`$user.Lastname)
`$i = get-random -Minimum 0 -Maximum `$departments.count
`$department = `$departments[`$i]
`$username = `$firstname.Substring(0,2).tolower() + `$lastname.Substring(0,4).tolower()
`$exit = 0
`$count = 1
do {
try {
`$userexists = Get-AdUser -Identity `$username
`$username = `$firstname.Substring(0,2).tolower() + `$lastname.Substring(0,4).tolower() + `$count++
} catch {
`$exit = 1
}
} while (`$exit -eq 0)
`$displayname = `$firstname + "" "" + `$lastname
`$upn = `$username + ""@"" + `$DomainSuffix
`$email = `$firstname + ""."" + `$lastname + ""@"" + `$DomainSuffix
Write-Host ""Creating user `$username in `$OUPath""
New-ADUser -Name `$displayName -DisplayName `$displayName -SamAccountName `$username -UserPrincipalName `$upn -EmailAddress `$email -GivenName `$firstname -Surname `$lastname -description ""Test User"" -Path `$OUPath -Enabled `$true -ChangePasswordAtLogon `$false -Department `$Department -AccountPassword (ConvertTo-SecureString `$userPass -AsPlainText -force)
}
}
`$userOU = ""ou=Staff,ou=S_USERS,$dcA2DomainDN""
`$userPass = '$userPass'
`$usersCSV = ""Firstname;Lastname
barry;tycholiz
benjamin;rogers
bill;rapp
bill;williams
brad;mckay
cara;semperger
carol;stclair
chris;dorland
chris;germany
chris;stokley
cooper;richey
craig;dean
dana;davis
danny;mccarty
dan;hyvl
daren;farmer
darrell;schoolcraft
darron;cgiron
david;delainey""
`$userList = `$usersCSV | ConvertFrom-CSV -Delimiter "";""
`$users = Get-ADUser -Filter * -SearchBase `$userOU | select -expand samAccountName
If ((`$users) -eq `$null) {
Create-TestUsers `$userList `$userPass ""$dcA2UPNSuffix"" `$userOU
}
`$users = Get-ADUser -Filter * -SearchBase `$userOU | select -expand samAccountName
`$group = ""Grp_AllStaff""
Add-ADGroupMember -Identity `$group -Members `$users
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm2Name -g $rgName --scripts $scriptADC02_3
- Create service account for Azure AD Connect
- Create DNS conditional forwarder for Domain B (placeholder)
$scriptADC02_4 = @"
`$displayName = ""svc-azuresync""
`$username = ""svc-azuresync""
`$upn = ""svc-azuresync@$dcA2UPNSuffix""
`$OUPath = ""OU=ServiceAccounts,OU=S_USERS,$dcA2DomainDN""
`$userPass = '$vmPass'
New-ADUser -Name `$displayName -DisplayName `$displayName -SamAccountName `$username -UserPrincipalName `$upn -description ""AAD Connect"" -Path `$OUPath -Enabled `$true -ChangePasswordAtLogon `$false -Department `$Department -AccountPassword (ConvertTo-SecureString `$userPass -AsPlainText -force)
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm2Name -g $rgName --scripts $scriptADC02_4
$scriptADC02_5 = @"
Add-DnsServerConditionalForwarderZone -Name ""$dcBDomain"" -ReplicationScope ""Forest"" -MasterServers $vmB1IPAddress
"@
az vm run-command invoke --command-id RunPowerShellScript --name $vm2Name -g $rgName --scripts $scriptADC02_5
Wrap up
That's it for part 1. More steps to come.