Files
Backup/_NDGOV_WindowsTeam/ITD.Infra-VMware.Administration/Runbook/VMBuilds/test/rb-VMware-NewVMwareVMWindows-tst.ps1
T
Zack Meier 1d304511b8 update
2026-04-15 15:45:50 -05:00

358 lines
15 KiB
PowerShell

[CmdletBinding()]
param
(
[Parameter(Mandatory = $false)]
[object] $WebhookData
)
If ($WebhookData) {
If ($WebhookData.RequestHeader.ITD -eq '6EE8DaFMbswirY38qPfewwT82G9Bdiv8p3SKEdisX88mfnFfiredy6uti4nJprecedegJkTGBHDV3alpineePkM5grainsE56xp3JmuffWjwL') {
Write-Verbose "Header has required data"
}
Else {
Write-Error "Header missing required data"
exit;
}
$InputParams = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)
$FQDN = 'itdzmbuild01.nd.gov'
# Get Credentials
$SPCred = Get-AutomationPSCredential -Name 'SharePoint IaaS ReadWrite'
$ADCred = Get-AutomationPSCredential -Name 'ITD IaaS Automation'
$PSCred = Get-AutomationPSCredential -Name 'ITD IaaS Automation'
$IBCred = New-Object System.Management.Automation.PSCredential ($ADCred.UserName.split('\')[1], $ADCred.Password)
$VMCred = Get-AutomationPSCredential -Name 'VMware Auto'
Clear-DnsClientCache
$SharePointList = Get-ITDVMwareSharePointVMGuestListTst -Credential $SPCred
$SPItem = $SharePointList | Where-Object Title -eq $FQDN
$HostName = $FQDN.split('.')[0]
# Infoblox
[Net.IpAddress]$NetworkId = $SPItem.CIDR.split('/')[0]
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction SilentlyContinue).IPAddress
$SubnetMaskInt = $SPItem.Cidr.split('/')[1]
$Int64 = ([convert]::ToInt64(('1' * $SubnetMaskInt + '0' * (32 - $SubnetMaskInt)), 2))
[Net.IPAddress]$SubnetMask = '{0}.{1}.{2}.{3}' -f ([math]::Truncate($Int64 / 16777216)).ToString(),
([math]::Truncate(($Int64 % 16777216) / 65536)).ToString(),
([math]::Truncate(($Int64 % 65536) / 256)).ToString(),
([math]::Truncate($Int64 % 256)).ToString()
$IPSplit = $SPItem.Cidr.Split('.')
[Net.IPAddress]$DefaultGateway = ($IPSplit[0] + '.' + $IPSplit[1] + '.' + $IPSplit[2] + '.' + (($SPItem.Cidr.split('/')[0].split('.')[-1] -as [int]) + 1) )
If (($IpAddress.Address -band $SubnetMask.Address) -eq ($NetworkId.Address -band $SubnetMask.Address)) {
Write-Verbose "IP Address and CIDR Block match"
}
Else {
Write-Error "DNS record already exists, and does not match CIDR Block" -ErrorAction Stop
}
If ($IpAddress) {
If (($IpAddress.Address -band $SubnetMask.Address) -eq ($NetworkId.Address -band $SubnetMask.Address)) {
Write-Warning "DNS record already exists, CIDR Block match"
}
else {
Write-Error "DNS record already exists, and does not match CIDR Block"
Break
}
}
Else {
New-ITDIbDNSRecordNextAvailableIP -HostName $SPItem.Title -CIDR $SPItem.CIDR -Credential $IBCred
}
If ((Test-NetConnection -ComputerName $IpAddress.IPAddressToString).PingSucceeded) {
Write-Error "IP Address already in use." -ErrorAction Stop
}
# Active Directory
If ($PSEdition -eq 'Core') { Import-Module ActiveDirectory -UseWindowsPowerShell }
$Filter = ("{Name -eq '" + $SPItem.AppName + "'}")
$OUAppName = Get-ADOrganizationalUnit -SearchBase "OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD,dc=nd,dc=gov" -Filter $Filter
If (!($OUAppName)) {
$OUAppName = Get-ADOrganizationalUnit -SearchBase "OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD,dc=nd,dc=gov" -Filter { Name -eq 'All-General' }
}
$ExistingADComputer = Get-ADComputer -Identity $Hostname -ErrorAction SilentlyContinue
If ($ExistingADComputer) {
If ($ExistingADComputer.DistinguishedName -like ("*" + $SPItem.AppName + "*") -or $ExistingADComputer.DistinguishedName -like "*All-General*") {
Write-Warning "AD object already exists, OU path does match"
}
Else {
Write-Error "AD object already exists, OU path mismatch"
Exit
Exit
}
}
else {
switch ($SPItem.Environment) {
'Test' {
$OUAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -like "*Test*"
}
'Production' {
$OUAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -like "*Prod*"
}
}
If ($OUAppNameEnv) { $OUFinal = $OUAppNameEnv }
Else { $OUFinal = $OUAppName }
New-ADComputer -Name $HostName.ToUpper() -Path $OUFinal -Credential $ADCred -Description ($SPItem | Select-Object AppName, Environment | ConvertTo-Json)
}
# Passwordstate
$LocalCredential = New-ITDPassword -Title $FQDN -Username itdadmin -Description 'Local Administrator' -PasswordList CSRC -Credential $PrvCred
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($LocalCredential.Password | ConvertTo-SecureString -AsPlainText -Force))
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ($LocalCredential.UserName, ($LocalCredential.Password | ConvertTo-SecureString -AsPlainText -Force))
# VMware
If (Get-VM -Name $FQDN -ErrorAction SilentlyContinue) {
Write-Error "Virtual machine with the name $FQDN already exists." -ErrorAction Stop
Stop
Stop
}
switch ($SPItem.Cluster) {
"WINDOWS1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data"
$DiskStorageFormat = 'Thin'
}
"WINDOWS2" {
$ViServer = 'itdvmvc2.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data"
$DiskStorageFormat = 'Thin'
}
"SQL1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data"
$DiskStorageFormat = 'EagerZeroedThick'
}
"SQL2" {
$ViServer = 'itdvmvc2.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data"
$DiskStorageFormat = 'EagerZeroedThick'
}
"WAS1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data"
$DiskStorageFormat = 'Thin'
}
"WAS2" {
$ViServer = 'itdvmvc2.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data"
$DiskStorageFormat = 'Thin'
}
"PS1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-PS1-Data"
$DiskStorageFormat = 'Thin'
}
"PS2" {
$ViServer = 'itdvmvc2.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-PS2-Data"
$DiskStorageFormat = 'Thin'
}
"TEL1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL1-Data"
$DiskStorageFormat = 'Thin'
}
"TEL2" {
$ViServer = 'itdvmvc2.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL2-Data"
$DiskStorageFormat = 'Thin'
}
"DCN1" {
$ViServer = 'itdvmvc1.nd.gov'
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-DCN-vMotion-Data"
$DiskStorageFormat = 'Thin'
}
}
# verify disk will fit
$DiskTotal = $SPItem.Disk1 + $SPItem.Disk2 + $SPItem.Disk3
$DatastoreCluster = Get-DatastoreCluster | Where-Object Name -like ("*" + $SPItem.Cluster + "*")
$ClusterDatastoreWithHighestFreeSpaceGB = ($DatastoreCluster | Get-Datastore | Sort-Object FreeSpaceGB -Descending | Select -First 1)
If ($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB -gt $DiskTotal) {
Write-Verbose ("VM DiskTotal " + $DiskTotal + "GB, will fit on " + $ClusterDatastoreWithHighestFreeSpaceGB.Name + " (" + [math]::round($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB, 0) + "GB free)")
}
else {
Write-Verbose ("VM DiskTotal " + $DiskTotal + "GB, will not fit on " + $ClusterDatastoreWithHighestFreeSpaceGB.Name + " (" + [math]::round($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB, 0) + "GB free)")
Write-Error ("New VM " + $FQDN + " needs " + $DiskTotal + "GB of free space on a single datastore in the " + $DatastoreCluster.Name + " datastore cluster.") -ErrorAction Stop
}
$FolderLocation = Get-Folder -Server $ViServer -Name "_New Builds"
switch ($SPItem.OS) {
"Windows Server 2012R2 Standard (64-Bit)" { $Template = "Windows Server 2012R2 Standard" }
"Windows Server 2016 Standard (64-Bit)" { $Template = "Windows Server 2016 Standard" }
"Windows Server 2019 Standard (64-Bit)" { $Template = "Windows Server 2019 Standard" }
Default {}
}
$PortGroupsAvailable = Get-VDPortGroup -Server $ViServer -VDSwitch $VirtualSwitch
$PortGroup = $PortGroupsAvailable | Where-Object Name -eq ("dvPG_" + $SPItem.Vlan_Id + "_" + $NetworkId.IPAddressToString + "_" + $SubnetMaskInt)
If (!($PortGroup)) {
Write-Error "Virtual port group not found" -ErrorAction Stop
Stop
}
$NewOSSpecName = ("AutoBuild-$Hostname-" + (Get-Date -UFormat "%Y%m%d%H%M%S"))
Get-OSCustomizationSpec -Name "Windows (Auto)" -Server $ViServer | New-OSCustomizationSpec -Name $NewOSSpecName -Type Persistent
Get-OSCustomizationSpec -Name $NewOSSpecName | `
Set-OSCustomizationSpec `
-NamingScheme fixed `
-NamingPrefix $Hostname `
-AdminPassword $GuestCredentialBB.GetNetworkCredential().Password `
Get-OSCustomizationSpec -Name $NewOSSpecName | `
Get-OSCustomizationNicMapping | `
Set-OSCustomizationNicMapping `
-IpMode UseStaticIP `
-IpAddress $IpAddress.IPAddressToString `
-SubnetMask $SubnetMask.IPAddressToString `
-DefaultGateway $DefaultGateway.IPAddressToString `
-Dns "10.2.7.40", "10.2.5.40"
$OSSpec = Get-OSCustomizationSpec -Name $NewOSSpecName
$NewVMParams = @{
Name = $FQDN;
ResourcePool = $SPItem.Cluster;
Datastore = $DatastoreCluster;
DiskStorageFormat = $DiskStorageFormat;
Template = $Template;
Location = $FolderLocation;
OSCustomizationSpec = $OSSpec;
}
New-VM @NewVMParams
#If (!($BuildError)) {
$VM = Get-VM -Name $FQDN
# Ensure CPU/Memory Hot-Add Enabled
$vmView = $VM | Get-View
$vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
$vmOptValCPU = New-Object VMware.Vim.OptionValue
$vmOptValMem = New-Object VMware.Vim.OptionValue
$vmOptValCPU.Key = "vcpu.hotadd"
$vmOptValMem.Key = "mem.hotadd"
$vmOptValCPU.Value = "true"
$vmOptValMem.Value = "true"
$vmConfigSpec.ExtraConfig += $vmOptValCPU
$vmConfigSpec.ExtraConfig += $vmOptValMem
$vmView.ReconfigVM($vmConfigSpec)
# Set CPU, Memory, Network
$VM | Set-VM -NumCpu $SPItem.Processors -MemoryGB $SPItem.RAM -Confirm:$false
$VM | Get-NetworkAdapter | Set-NetworkAdapter -Portgroup $PortGroup -Confirm:$false
# Power On VM
$VM | Start-VM
# Wait for Customization to finish
$VMStarted = $false
$VMCustomizationStarted = $false
$VMCustomizationResult = $false
While ($VMStarted -eq $false -or $VMCustomizationStarted -eq $false -or $VMCustomizationResult -eq $false) {
Write-Warning ("Customization wait loop started " + (Get-Date))
Write-Verbose "Current Status:"
Write-Verbose ("VMStarted: " + $VMStarted)
Write-Verbose ("VMCustomizationStarted: " + $VMCustomizationStarted)
Write-Verbose ("VMCustomizationResult: " + $VMCustomizationResult)
$GetVIEventRuntime = Measure-Command -Expression { $VMEvents = Get-VIEvent -Entity $VM -Server $ViServer -ErrorAction SilentlyContinue }
Write-Verbose ("Get-VIEvent last run time: " + $GetVIEventRuntime.TotalSeconds + " seconds")
If ($VMStarted -eq $false) {
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "VMStartingEvent" })) {
$VMStarted = $true
Write-Warning "[$FQDN]:Virtual machine started"
}
}
If ($VMCustomizationStarted -eq $false) {
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationStartedEvent" })) {
$VMCustomizationStarted = $true
Write-Warning "[$FQDN]:Virtual machine customization started"
}
}
If ($VMCustomizationResult -eq $false) {
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationFailed" })) {
$VMCustomizationResult = $true
Write-Error "[$FQDN]:Virtual machine customization failed"
Exit
Exit
}
If (@($VMEvents | Where-Object { $_.GetType().Name -eq "CustomizationSucceeded" })) {
$VMCustomizationResult = $true
Write-Warning "[$FQDN]:Virtual machine customization completed"
}
}
}
# Delete OS Customization Spec
Get-OSCustomizationSpec -Name $NewOSSpecName | Remove-OSCustomizationSpec -Confirm:$false
# Add/Expand Disks
$VMDisk = $VM | Get-HardDisk
$VMDisk1 = $VMDisk | Where-Object Name -EQ "Hard disk 1"
$VMDisk2 = $VMDisk | Where-Object Name -EQ "Hard disk 2"
$VMDisk3 = $VMDisk | Where-Object Name -EQ "Hard disk 3"
If ($SPItem.Disk1) {
If (!$VMDisk1) {
$VM | New-HardDisk `
-CapacityGB $SPItem.Disk1 `
-StorageFormat Thin `
-DiskType Flat `
-Persistence Persistent
}
$VMDisk1 = $VMDisk | Where-Object Name -EQ "Hard disk 1"
If ($VMDisk1.CapacityGB -lt $SPItem.Disk1) {
Set-HardDisk -HardDisk $VMDisk1 -CapacityGB $SPItem.Disk1 -Confirm:$false
}
}
If ($SPItem.Disk2) {
If (!$VMDisk2) {
$VM | New-HardDisk `
-CapacityGB $SPItem.Disk2 `
-StorageFormat Thin `
-DiskType Flat `
-Persistence Persistent
}
$VMDisk2 = $VMDisk | Where-Object Name -EQ "Hard disk 2"
If ($VMDisk2.CapacityGB -lt $SPItem.Disk2) {
Set-HardDisk -HardDisk $VMDisk2 -CapacityGB $SPItem.Disk2 -Confirm:$false
}
}
If ($SPItem.Disk3) {
If (!$VMDisk3) {
$VM | New-HardDisk `
-CapacityGB $SPItem.Disk3 `
-StorageFormat Thin `
-DiskType Flat `
-Persistence Persistent
}
$VMDisk3 = $VMDisk | Where-Object Name -EQ "Hard disk 3"
If ($VMDisk3.CapacityGB -lt $SPItem.Disk3) {
Set-HardDisk -HardDisk $VMDisk3 -CapacityGB $SPItem.Disk3 -Confirm:$false
}
}
# Run Guest OS code
$InvokeVMScriptFunc = {
}
$VM | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptText $InvokeVMScriptFunc
#}
}
Else {
Write-Error "Runbook must be started from webhook"
}