358 lines
15 KiB
PowerShell
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"
|
|
} |