function New-ITDWindowsVMAzureStep2 { [CmdletBinding()] param ( [string] $FQDN, [string] $AppName, [string] $VMEnvironment, [PSCredential] $Credential ) begin { $search = @() #$Hostname = $VMName.split('-')[1] $HostName = $FQDN.split('.')[0] ###$VMEnvironment = $VMName.split('-')[2] #### Write-Verbose -Message ("Searching for Az VM name like " + $HostName) Get-AzSubscription | ForEach-Object { $Subscription = $_ Set-AzContext $_ | Out-Null $search += Get-AzVM | Where-Object Name -Like "*$HostName*" | Select-Object *, @{n = 'SubscriptionName'; e = { $Subscription.Name } } } } process { switch (@($Search).count) { { $_ -gt 1 } { Write-Error -Message "More than one virtual machine match found." } { $_ -lt 1 } { Write-Error -Message "Less than one virtual machine match found." } { $_ -eq 1 } { Write-Verbose -Message "Prep VM variables" $VMName = $search.Name $ResourceGroupName = $search.ResourceGroupName #$VMEnvironment = $VMName.split('-')[2] $SubscriptionName = $search.SubscriptionName Set-AzContext -Subscription $SubscriptionName $VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $VMName Write-Verbose -Message "Set firewall tag" $Tags = @{"Network"="StandardWindows"} New-AzTag -ResourceId $VM.Id -Tag $Tags Write-Verbose -Message "Wait two minutes before Pre-Firewall Guest OS customization" #Start-Sleep -Seconds 120 Write-Verbose -Message "Begin Pre-Firewall Guest OS customization" $InvokeAzVMRunCommandParams = @{ ResourceGroupName = $ResourceGroupName; Name = $VMName; CommandId = 'RunPowerShellScript' } Write-Verbose -Message "1-Set WMI Tags" $ScriptBlock = { try { Write-Verbose "Create new Class" $Class = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null); $Class["__CLASS"] = "ITD"; $Class.Qualifiers.Add("Static", $true) $Class.Properties.Add("MyKey", [System.Management.CimType]::String, $false) $Class.Properties["MyKey"].Qualifiers.Add("Key", $true) $Class.Properties.Add("LastModified", [System.Management.CimType]::String, $false) $Class.Properties.Add("DTAP", [System.Management.CimType]::String, $false) $Class.Properties.Add("Baseline", [System.Management.CimType]::String, $false) $Class.Put() Write-Verbose "Create single ITD Object" Set-WmiInstance -Class ITD -Arguments @{LastModified = (Get-Date); DTAP = "Prod"; Baseline = "000" } } catch { Throw $_ Break } } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock Write-Verbose -Message "3-Disk Configuration" $ScriptBlock = { try { # Non-initialized and MBR-initialized disks will have 0 partitions by default, but GPT-initialized disks will have 1 system reserved partition by default $disks = Get-Disk | Where-Object { $_.NumberOfPartitions -eq 0 -or ( $_.PartitionStyle -eq 'GPT' -and $_.NumberOfPartitions -eq 1 ) } | Sort-Object -Property Number if ($disks) { Write-Verbose -Message "Found $(@($disks).Count) unpartitioned disks." # Prevent the "You must format this partition before using it." popup if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Stop-Service ShellHWDetection -ErrorAction SilentlyContinue } foreach ($disk in $disks) { if ($disk.IsOffline) { Set-Disk $disk.Number -IsOffline $false Write-Verbose -Message "Brought disk $($disk.Number)($("{0:n0}GB" -f ($disk.Size / 1GB))) online..." } if ($disk.IsReadOnly) { Set-Disk $disk.Number -IsReadOnly $false } if ($disk.PartitionStyle -eq 'RAW') { Initialize-Disk $disk.Number -PartitionStyle GPT -ErrorAction SilentlyContinue } $diskParam = @{ FileSystem = 'NTFS' Confirm = $false } $driveLetter = [Int][Char]'D' while (Get-Volume -DriveLetter $([Char]$driveLetter) -ErrorAction SilentlyContinue) { $driveLetter++ } $diskParam.DriveLetter = [Char]$driveLetter if (@($disks).IndexOf($disk) -eq 0 -and (-not (Get-Volume -DriveLetter D -ErrorAction SilentlyContinue))) { $diskParam.NewFileSystemLabel = 'Temporary Storage' } elseif (@($disks).IndexOf($disk) -eq 1 -and (-not (Get-Volume -DriveLetter E -ErrorAction SilentlyContinue))) { $diskParam.NewFileSystemLabel = 'Data' } [void](New-Partition -DiskNumber $disk.Number -DriveLetter $diskParam.DriveLetter -UseMaximumSize) [void](Format-Volume @diskParam) } } else { Write-Verbose -Message "No unpartitioned disks found, continuing..." } } catch { Throw $_ Break } finally { if (Get-Service ShellHWDetection -ErrorAction SilentlyContinue) { Start-Service ShellHWDetection -ErrorAction SilentlyContinue } } } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock Write-Verbose -Message "5-Time Zone" $ScriptBlock = { if ((Get-TimeZone).Id -ne 'Central Standard Time') { Write-Verbose -Message "Current time zone set to $((Get-TimeZone).Id), setting to Central Standard Time." Set-TimeZone -Id 'Central Standard Time' Write-Verbose -Message "Time zone set to Central Standard Time." } else { Write-Verbose -Message "Time zone is already set to Central Standard Time." } } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock Write-Verbose -Message "6-Enable Performance Counters" $ScriptBlock = { # Enable Performance Counters try { if (Get-ScheduledTask -TaskName "Server Manager Performance Monitor" | Where-Object State -NE "Running" -ErrorAction SilentlyContinue) { Enable-ScheduledTask -TaskPath "\Microsoft\Windows\PLA\" -TaskName "Server Manager Performance Monitor" | Start-ScheduledTask Write-Verbose -Message "Performance monitors enabled." } else { Write-Verbose -Message "Performance monitors already enabled, continuing..." } } catch { Throw $_ Break } } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock Write-Verbose -Message "7-Disable Windows Firewall" $ScriptBlock = { # Disable Windows Firewall Write-Verbose -Message "Checking for active Windows Firewall..." if ((Get-NetFirewallProfile).Enabled -contains 'True') { Write-Verbose -Message "Windows Firewall is still enabled, disabling it..." Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled False Write-Verbose -Message "Windows Firewall disabled." } else { Write-Verbose -Message "Windows Firewall already disabled, continuing..." } } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock # determine Active Directory Forest and OU Write-Verbose -Message "8a-Determine domain join" $DomainName = $FQDN.Substring($FQDN.IndexOf(".") + 1) switch ($DomainName) { 'nd.gov' { $SearchBaseDomain = "dc=nd,dc=gov" } 'ndcloud.gov' { $SearchBaseDomain = "dc=ndcloud,dc=gov" } } If ($DomainName -eq "nd.gov") { $OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq $AppName } If ($null -eq $OUAppName) { Write-Verbose -Message "Dedicated AppName OU for $AppName not found, placing in All-General OU" $OUAppName = Get-ADOrganizationalUnit -Server $DomainName -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq 'All-General' } } Write-Verbose -Message ("OUAppName: " + $OUAppName.distinguishedName) -Verbose # Determine if the Environment sub-ou is special for the selected AppName switch ($OUAppName.Name) { 'Shared-PeopleSoft-HigherEd' { switch ($VMEnvironment) { 'Test' { $EnvString = "Non-Prod" } 'Production' { $EnvString = "Prod" } } } 'Shared-PeopleSoft-State' { switch ($VMEnvironment) { 'Test' { $EnvString = "Non-Prod" } 'Production' { $EnvString = "Prod" } } } Default { switch ($VMEnvironment) { 'Test' { $EnvString = "Test" } 'Production' { $EnvString = "Prod" } } } } # Gather all sub OUs for the AppName OU Write-Verbose -Message "EnvString is $EnvString" -Verbose $SubOUs = Get-ADOrganizationalUnit -Server $DomainName -SearchBase $OUAppName -Filter * Write-Verbose -Message ("SubOUs:") -Verbose $SubOUs | ForEach-Object { Write-Verbose -Message $_.DistinguishedName -Verbose } $LikeFilter = ("OU=$EnvString,OU=" + $OUAppName.Name + "*") Write-Verbose -Message "LikeFilter: $LikeFilter" $OUToUse = $SubOUs | Where-Object { $_.DistinguishedName -like $LikeFilter } Write-Verbose -Message ("OUToUse: " + $OUToUse.distinguishedName) -Verbose <#$MatchFilter = ("OU=$EnvString,OU=" + $OUAppName.Name) Write-Verbose -Message "MatchFiler: $MatchFilter" $OutToUseMatch = ($SubOUs | Where-Object {$_.DistinguishedName -match "^$MatchFilter"}) Write-Verbose -Message ("OUToUseMatch: " + $OUToUseMatch.distinguishedName) -Verbose #> # Determine if AD Computer object already exists $ExistingADComputer = Get-ADComputer -Filter { Name -eq $HostName } If ($ExistingADComputer) { Write-Verbose -Message "AD Object already exists in AD" # Determine if AD Computer object is in the correct OU, or a sub-OU of the correct OU If ($ExistingADComputer.DistinguishedName -like ("*" + $OUToUse.DistinguishedName)) { Write-Verbose -Message "AD Object is in the correct OU, will attempt domain join" $AttemptDomainJoin = $true } Else { Write-Verbose -Message "AD Object is NOT in the correct OU, domain join will not occur, needs human review" $AttemptDomainJoin = $false } } Else { $AttemptDomainJoin = $true } If ($AttemptDomainJoin) { $OuFinal = $OUToUse.DistinguishedName Write-Verbose -Message "OuFinal: $OuFinal" -Verbose $FirstScriptBlock = { $DomainJoinCred = New-Object System.Management.Automation.PSCredential('svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force)) } $SecondScriptText = 'Add-Computer -DomainName -OUPath "" -Credential $DomainJoinCred -Server itddc42.nd.gov -Verbose; Restart-Computer -Force -Verbose;' $SecondScriptText = $SecondScriptText -replace '', $DomainName $SecondScriptText = $SecondScriptText -replace '', $OuFinal #Write-Verbose -Message "[$FQDN]:Invoke-VMScript to AD join" $InvokeVMScriptFunc = [System.Management.Automation.ScriptBlock]::Create("$FirstScriptBlock ; $SecondScriptText") } Write-Verbose -Message $InvokeVMScriptFunc.tostring() -Verbose ### <# wait for firewall and reconnect Write-Verbose -Message "8b-Verify if firewall is open before domain join attempt" $connectivity = $false while ($connectivity -eq $false) { If ( (Test-NetConnection -ComputerName $FQDN).PingSuccedded) { Write-Verbose -Message "Ping successful" -Verbose $connectivity = $true } Else { Write-Verbose -Message "Ping failed, may need to wait for PA IP sync" -Verbose Start-Sleep -Seconds 60 } } #> Write-Verbose -Message "8c-Attempt domain join" switch ($DomainName) { 'nd.gov' { Write-Verbose -Message "Attempting domain join nd.gov" Write-Verbose -Message "Domain: $DomainName" Write-Verbose -Message "OuPath: $OuFinal" <#Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptPath 'C:\AzWinBuild\8-Domain-ndgov.ps1' -Parameter @{ "DomainName" = 'nd.gov'; "OuPath" = "$OuFinal" }#> $ScriptBlock = { Param( [string] $DomainName, [string] $OuPath ) Write-Host $OuPath Test-NetConnection -ComputerName nd.gov $DomainJoinCred = New-Object System.Management.Automation.PSCredential('ndgov\svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force)) Add-Computer -DomainName $DomainName -OUPath $OuPath -Credential $DomainJoinCred Restart-Computer -Force } Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $ScriptBlock -Parameter @{ "DomainName" = 'nd.gov'; "OuPath" = "$OuFinal" } #Invoke-AzVMRunCommand @InvokeAzVMRunCommandParams -ScriptString $InvokeVMScriptFunc } } } } } } end { } }