This commit is contained in:
Zack Meier
2026-04-15 15:45:50 -05:00
commit 1d304511b8
613 changed files with 140998 additions and 0 deletions
@@ -0,0 +1,10 @@
$tenantId = '2dea0464-da51-4a88-bae2-b3db94bc0c54'
$AppId = '60244573-7130-4026-9c6d-47de73f8ca29'
$SecureStringPwd = "sQV8Q~sNLcrDl2RgB32gsSEsDVgdFhNMcBdPoaEX"
#$zm = Get-Credential -username $AppId, ($SecureStringPwd | ConvertTo-SecureString -AsPlainText -Force)
$PSCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $AppId, ($SecureStringPwd | ConvertTo-SecureString -AsPlainText -Force)
Connect-AzAccount -ServicePrincipal -Credential $PSCredential -Tenant $tenantId
# laptop
Connect-AzAccount -ServicePrincipal -Credential $IaasAutoAzApp -Tenant $tenantId
@@ -0,0 +1,843 @@
# https://96bdfe01-af80-4575-8f23-e7057184c8f6.webhook.cus.azure-automation.net/webhooks?token=dVIj9zHSJwNeR2Ow8QTOoiSoKaPPux9EVJsQAMiGUPg%3d
<#
$ComputerName = 'itddotfrsql3.nd.gov'
$CPU = 8
$MemoryGB = 32
$OS = 'Windows Server 2019 Datacenter'
$Subnet = '10.21.12.0/22'
$Environment = "Production"
$Platform = "Azure"
$AppName = 'DOT-FacialRec'
$LicensingRestrictions='Microsoft SQL Enterprise'
$Credential = $PrvCred
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory = $false)]
[object] $WebhookData
)
<# New-ITDVMwareWindowsVMAA
#>
function New-ITDAzureWindowsVMAA {
[CmdLetBinding()]
Param(
[Parameter(Mandatory = $true)]
[string]
$ComputerName,
[Parameter(Mandatory = $true)]
[int]
$CPU,
[Parameter(Mandatory = $true)]
[int]
$MemoryGB,
#[Parameter(Mandatory = $true)]
#[int]
#$DiskOS,
#[Parameter(Mandatory = $true)]
#[int]
#$DiskData,
[Parameter(Mandatory = $true)]
[string]
$Subnet,
[Parameter(Mandatory = $true)]
[string]
$OS,
[Parameter(Mandatory = $true)]
[string]
$Environment,
[Parameter(Mandatory = $true)]
[string]
$AppName,
[Parameter(Mandatory = $true)]
[string]
$LicensingRestrictions
#[Parameter(Mandatory = $true)]
#[PSCredential]
#$Credential
)
$Body = "
ComputerName=$ComputerName
CPU=$CPU
MemoryGB=$MemoryGB
VlanId=$VlanId
OS=$OS
Environment=$Environment
Datacenter=$Datacenter
LicensingRestrictions=$LicensingRestrictions
AppName=$AppName
RunningOn=$env:computername
RunningAs=$env:username
Credential=$Credential.UserName
"
Send-MailMessage -From "zmauto@nd.gov" -To "zmeier@nd.gov" -SmtpServer apprelay1.nd.gov -Body $Body -Subject "AutoBuildAzure-$ComputerName"
$FQDN = $ComputerName
$tenantId = '2dea0464-da51-4a88-bae2-b3db94bc0c54'
If ($Credential) {
$IaasAuto = $Credential
}
#$IaasAutoAzApp = Get-AutomationPSCredential -Name 'Azure IaaS Service Principal'
Write-Warning "[$FQDN]:Start"
$SPCred = $IaasAuto
$ADCred = $IaasAuto
$PSCred = $IaasAuto
$VMCred = $IaasAuto
$CcmCred = $IaasAuto
#$RadiusCred = $RadiusCred
$RadiusCred = New-Object System.Management.Automation.PSCredential($IaasAuto.username.split('\')[1], ($IaaSAuto.Password))
Clear-DnsClientCache
$HostName = $FQDN.split('.')[0]
# Infoblox
#$InfobloxVlanMetadata = Get-ITDIbVlan -CIDR $Subnet -Credential $RadiusCred # not valid for Azure
#$Cidr = ($InfobloxVlanMetadata.AssignedTo | Out-String).TrimEnd()
$Cidr = $Subnet
[Net.IpAddress]$NetworkId = $Cidr.split('/')[0]
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction SilentlyContinue).IPAddress
$SubnetMaskInt = $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 = $Subnet.Split('.')
[Net.IPAddress]$DefaultGateway = ($IPSplit[0] + '.' + $IPSplit[1] + '.' + $IPSplit[2] + '.' + (($CIDR.split('/')[0].split('.')[-1] -as [int]) + 1) )
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 $FQDN -CIDR $CIDR -Credential $RadiusCred
Start-Sleep -Seconds 5
[Net.IPAddress]$IpAddress = (Resolve-DnsName -Name $FQDN -ErrorAction SilentlyContinue).IPAddress
If ((Test-NetConnection -ComputerName $IpAddress.IPAddressToString).PingSucceeded) {
Write-Error "IP Address already in use." -ErrorAction Stop
}
}
# Passwordstate
If ($FQDN -like "itdcnd*") {
$PasswordStateList = "Peoplesoft Share PW"
}
Else {
$PasswordStateList = "CSRC"
}
$LocalCredential = New-ITDPassword -Title $FQDN -UserName itdadmin -Description 'Local Administrator' -PasswordList $PasswordStateList -Credential $PSCred
#$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))
$GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($LocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
$GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($LocalCredential.GetNetworkCredential().Password | ConvertTo-SecureString -AsPlainText -Force))
#$GuestCredential = $GuestCredentialBB
switch ($Environment) {
{ $_ -eq 'Test' -or $_ -eq 'Development' } {
Write-Warning 'Environment is Test or Development'
$EnvShortString = 'tst'
#$VMSizeFilter="*_B*s*"
$VMSizeFilter1 = "*_D[0-9]*s_v3"
$VMSizeFilter2 = "*_E[0-9]*s_v3"
$VMSizeFilter3 = "*_F[0-9]*s_v2"
$Subscription = "npd01"
$VNet = "vnet-npd01-001"
$VNetSubnet = "sn-shared-zerotrust-npd-10.21.8.0_22"
}
'Production' {
Write-Warning 'Environment is Production'
$EnvShortString = 'prd'
#$VMSizeFilter="*_D*s_*"
$VMSizeFilter1 = "*_D[0-9]*s_v3"
$VMSizeFilter2 = "*_E[0-9]*s_v3"
$VMSizeFilter3 = "*_F[0-9]*s_v2"
$Subscription = "prd01"
$VNet = "vnet-prd01-001"
$VnetSubnet = "sn-shared-zerotrust-prd-10.21.12.0_22"
}
'CARES-tst' {
Write-Warning 'Environment is CARES-tst'
$EnvShortString = 'tst'
#$VMSizeFilter="*_D*s_*"
$VMSizeFilter1 = "*_D[0-9]*s_v3"
$VMSizeFilter2 = "*_E[0-9]*s_v3"
$VMSizeFilter3 = "*_F[0-9]*s_v2"
$Subscription = "cares01"
$VNet = "vnet-infra-azurenetworking-cares01-npd"
$VnetSubnet = "sn-shared-zerotrust-npd-10.69.254.0_24"
}
'CARES-prd' {
Write-Warning 'Environment is CARES-prd'
$EnvShortString = 'prd'
#$VMSizeFilter="*_D*s_*"
$VMSizeFilter1 = "*_D[0-9]*s_v3"
$VMSizeFilter2 = "*_E[0-9]*s_v3"
$VMSizeFilter3 = "*_F[0-9]*s_v2"
$Subscription = "cares01"
$VNet = "vnet-infra-azurenetworking-cares01-prd"
$VnetSubnet = "sn-shared-zerotrust-prd-10.69.126.0_24"
}
Default {
Write-Error "Environment failed" -ErrorAction Stop
}
}
switch ($OS) {
"Windows Server 2019 Datacenter" {
$VMOS = "Windows"
$Publisher = "MicrosoftWindowsServer"
$Offer = "WindowsServer"
$sku = "2019-Datacenter"
}
"Windows Server 2022 Datacenter" {
$VMOS = "Windows"
$Publisher = "MicrosoftWindowsServer"
$Offer = "WindowsServer"
$sku = "2022-datacenter"
}
Default { Write-Error "Invalid operating system" -ErrorAction Stop }
}
# finalize VM size
Connect-AzAccount -ServicePrincipal -Credential $IaasAutoAzApp -Tenant $tenantId
$location = "centralus"
$VMSize = Get-AzVMSize -Location $location | `
Where-Object { $_.Name -Like "$VMSizeFilter1" -or $_.Name -Like "$VMSizeFilter2" -or $_.Name -Like "$VMSizeFilter3" } | `
Where-Object { $_.NumberOfCores -ge $CPU -and $_.MemoryInMB -ge ($Memory * 1024) } | `
Where-Object Name -NotMatch "_Promo" | `
Sort-Object NumberOfCores, MemoryInMB | `
Select-Object -First 1
# resource group things
$VMOwner = $AppName.split('-')[0].ToLower()
$VMFunction = (($AppName -replace "$VMOwner-") -replace "-").ToLower() -replace " "
#$IPAddress = (Resolve-DnsName -Name $ComputerName -ErrorAction Stop).IpAddress
$ResourceGroup = "rg-$VMOwner-$VMFunction-$EnvShortString"
If ($ResourceGroupIndex) { $ResourceGroup = $ResourceGroup + "-$ResourceGroupIndex" }
$VMName = "vm-$HostName-$EnvShortString"
$NewITDAzureRMVMParams = @{
VMFQDN = $ComputerName
VMSubscription = $Subscription
VMEnvironment = $EnvShortString
VMOwner = $VMOwner
VMFunction = $VMFunction
VMSize = $VMSize.Name
VMOS = $VMOS
VMPublisher = $Publisher
VMOffer = $Offer
VMSku = $sku
VMVnetName = $VNet
VMSubnet = $VNetSubnet
VMLocation = $location
VMIP = $IPAddress.IPAddressToString
VMCredential = $LocalCredential
AppName = $AppName
}
<#
$VMFQDN = $ComputerName
$VMSubscription = $Subscription
$VMEnvironment = $EnvShortString
$VMOwner = $VMOwner
$VMFunction = $VMFunction
$VMSize = $VMSize.Name
$VMOS = $VMOS
$VMPublisher = $Publisher
$VMOffer = $Offer
$VMSku = $sku
$VMVnetName = $VNet
$VMSubnet = $VNetSubnet
$VMLocation = $location
$VMIP = $IPAddress
$VMCredential = $LocalCredential
$AppName = $AppName
#>
New-ITDAzureRMVM @NewITDAzureRMVMParams
# wait for build
$VMName = ('vm-' + $ComputerName.split('.')[0] + '-' + $EnvShortString)
$VMStatus = $null
$VMCreation = $Null
While ($VMCreation -ne "Complete") {
If (( (Get-AzVM -ResourceGroupName $ResourceGroup -Name $VMName -Status).Statuses | Where-Object Code -Match "PowerState").Code -eq "PowerState/running") {
$VMCreation = "Complete"
}
else {
Start-Sleep -Seconds 300
}
}
Start-Sleep -Seconds 30
# Add extra disk
Add-ITDAzureRMNewDataDisk -VMName $VMName -SizeGB 20
# pre-firewall Guest OS customization
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\1-SetWMITags.ps1'
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\3-Disks.ps1'
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\5-TimeZone.ps1'
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\6-PerformanceCounters.ps1'
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\7-DisableWindowsFirewall.ps1'
# determine Active Directory Forest and OU
$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 (!($OUAppName)) {
$OUAppName = Get-ADOrganizationalUnit -SearchBase ("OU=Windows,OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq 'All-General' }
}
$ExistingADComputer = Get-ADComputer -Filter { Name -eq $Hostname }
If ($ExistingADComputer) {
If ($ExistingADComputer.DistinguishedName -like ("*" + $AppName + "*") -or $ExistingADComputer.DistinguishedName -like "*All-General*") {
Write-Warning "AD object already exists, OU path does match"
$OuFinal = $ExistingADComputer.DistinguishedName -replace '^.+?(?<!\\),', ''
}
Else {
Write-Error "AD object already exists, OU path mismatch"
Exit
Exit
}
}
Else {
switch ($Environment) {
'Test' {
If ($AppName -like "Shared-Peoplesoft*") { $EnvString = "Non-Prod" }
Else { $EnvString = "Test" }
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -EQ "$EnvString"
}
'Production' {
If ($AppName -like "Shared-Peoplesoft*") { $EnvString = "Prod" }
Else { $EnvString = "Prod" }
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -EQ "$EnvString"
}
}
If ($OuAppNameEnv) { $OUFinal = $OUAppNameEnv.DistinguishedName }
Else { $OuFinal = $OUAppName.DistinguishedName }
New-ADComputer -Name $HostName.ToUpper() -Path $OUFinal -Credential $ADCred -Description ($SPItem | Select-Object AppName, Environment | ConvertTo-Json)
}
}
# disconnect, wait for firewall, then reconnect
Disconnect-AzAccount
While ($PingStatus -eq $false) {
$PingTest = Test-NetConnection -ComputerName $FQDN
If ($PingTest.PingSuccedded -eq $true) {
$PingStatus = $true
}
Start-Sleep -Seconds 300
}
Connect-AzAccount -ServicePrincipal -Credential $IaasAutoAzApp -Tenant $tenantId
Set-AzContext -Subscription $Subscription
# post-firewall Guest OS customization
switch ($DomainName) {
'nd.gov' {
Write-Warning "Attempting domain join nd.gov"
Write-Warning "Domain: $DomainName"
Write-Warning "OuPath: $OuFinal"
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\8-Domain-ndgov.ps1' -Parameter @{"DomainName" = 'nd.gov'; "OuPath" = ' CN=ITDSOSCMTSQL22,OU=SOS-BLS,OU=SOS-All,OU=Windows,OU=SERVERS,OU=COMPUTERS,OU=ITD,DC=nd,DC=gov' }
}
}
Write-Verbose -Message "Activating windows against $KMS..."
Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\9-KMS.ps1'
<#
Write-Warning "[$FQDN]:Register SCCM Client"
$CcmRegistered = $false
While ($CcmRegistered -eq $false) {
$CcmResult = Invoke-AzVMRunCommand -ResourceGroupName $ResourceGroup -Name $VMName -CommandId 'RunPowerShellScript' -ScriptPath 'C:\AzWinBuild\10-SCCM-a.ps1'
If ($CcmResult.Value -match "SCCM Client is registered") {
Write-Warning "Client is registered."
$CcmRegistered = $true
}
ElseIf ($CcmRegistered -eq $false) { Start-Sleep -Seconds 60 }
}
Write-Warning -Message "[$FQDN]:Approve SCCM Client"
#Start-Sleep -Seconds 30 # ADD LOOP/SMARTS TO WAIT FOR DISCOVERY AND ANOTHER FOR APPROVAL
Invoke-Command -ComputerName itdsccmp2.nd.gov -Credential $CcmCred -ArgumentList $Hostname -ScriptBlock {
Import-Module 'D:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
$PSDrives = Get-PSDrive
If ($PSDrives | Where-Object Name -EQ "ITD") {
# ITD Drive exists, do nothing
}
else {
New-PSDrive -Name "ITD" -PSProvider AdminUI.PS.Provider\CMSite -Root itdsccmp2.nd.gov
}
Set-Location ITD:\
$Device = Get-CMDevice -Name $args[0]
If ($Device.IsApproved -eq 0) {
Approve-CMDevice -DeviceName $args[0]
}
}
#>
}
function New-ITDAzureRMVM {
[CmdletBinding()]
Param
(
# VM Computer Name (FQDN)
[Parameter(Mandatory = $true, Position = 0)]
[String]
$VMFQDN,
# VM Subscription (tst,prd,dr)
[Parameter(Mandatory = $true)]
[string]
$VMSubscription,
# Environment (tst,prd,dr)
[Parameter(Mandatory = $true)]
[string]
$VMEnvironment,
# Owner (infra,shared,ITD,DHS,etc)
[Parameter(Mandatory = $true)]
[string]
$VMOwner,
# Function (testdc, POC, etc)
[Parameter(Mandatory = $true)]
[string]
$VMFunction,
#[Parameter(Mandatory=$true)]
[string]
$ResourceGroupIndexNumber,
# Azure VM Size
[Parameter(Mandatory = $true)]
[string]
$VMSize,
# Azure OS (Windows or Linux)
[Parameter(Mandatory = $true)]
[string]
$VMOS,
# Availability Set
[switch]
$VMAS,
# Availability Set Tier
[string]
$VMASTier,
# Availability Zone
[ValidateSet(1, 2, 3)]
[int]
$VMAvailZone,
# Azure Publisher
[string]
$VMPublisher,
# Azure Offer
[string]
$VMOffer,
# Azure Sku
[string]
$VMSku,
# Azure vNet
[Parameter(Mandatory = $true)]
[string]
$VMVNetName,
# Azure Subnet
[Parameter(Mandatory = $true)]
[string]
$VMSubnet,
# Azure Location
[Parameter(Mandatory = $true)]
[string]
$VMLocation,
# Azure Private IP
[Parameter(Mandatory = $true)]
[string]
$VMIP,
[Parameter(Mandatory = $true)]
[PSCredential]
$VMCredential,
# App name to be used for the RG
[Parameter(Mandatory = $true)]
[string]
$AppName
)
Begin {
$VMName = $VMFQDN.Split('.')[0]
#$VMIndexNumber = "001"
$VMIPConfigName = "ipconfig-$VMName-$VMEnvironment"
$VMNicName = "nic-$VMName-$VMEnvironment" ######
$VMObjectName = "vm-$VMName-$VMEnvironment"
$ResourceGroup = "rg-$VMOwner-$VMFunction-$VMEnvironment"
#If($ResourceGroupIndexNumber){$ResourceGroup = $ResourceGroup + "-$ResourceGroupIndexNumber"}
#removing diag storage account because replaced by managed storage accounts for boot diagnostics
#$VMDiagStorageAcct = "sa" + $VMOwner + $VMFunction + "diag" + $VMEnvironment
$VMOSDiskName = "vm-$VMName-os-$VMEnvironment"
<#
If($VMASTier)
{
$VMASName = "as-$VMOwner-$VMFunction-$VMASTier-$VMEnvironment"
}
Else
{
$VMASName = "as-$VMOwner-$VMFunction-$VMEnvironment"
}
#>
<#
Write-Verbose "Checking Storage Account Length"
If (($VmDiagStorageAcct.Length) -gt 24){
Write-Verbose "Storage Account Length is too long. Truncating function...."
$TruncateCharacters = $VmDiagStorageAcct.Length - 24
$ShortVMFunction = $VMFunction.Substring(0, ($VMFunction.length) - $TruncateCharacters)
$VMDiagStorageAcct = "sa" + $VMOwner + $ShortVMFunction + "diag" + $VMEnvironment
}
If (($VmDiagStorageAcct.Length) -gt 24){
Write-Verbose "Storage Account Length is too long after truncating. Terminating Build"
Return
}
#>
Write-Verbose "Verifying Applicable Inputs are Lowercase"
$VMIPConfigName = $VMIPConfigName.ToLower()
$VMNicName = $VMNicName.ToLower()
$VMObjectName = $VMObjectName.ToLower()
$ResourceGroup = $ResourceGroup.ToLower()
#$VMDiagStorageAcct = $VMDiagStorageAcct.ToLower()
$VMOSDiskName = $VMOSDiskName.ToLower()
}
Process {
Set-AzContext $VMSubscription
$ResGroupExist = ""
Write-Verbose "Creating Resource Group if it doesn't exist"
$ResGroupExist = Get-AzResourceGroup -Name $ResourceGroup -ErrorAction SilentlyContinue
If (@($ResGroupExist).count -gt 1) {
Write-Error "Multiple Resource Groups matched" -ErrorAction Stop
}
If (@($ResGroupExist).count -lt 1) {
Write-Warning "No matching resource group found, creating it now"
New-AzResourceGroup -Name $ResourceGroup -Location $VMLocation -Tag @{ApplicationName = $AppName }
$ResourceGroupName = $ResourceGroupSearch.ResourceGroupName
#Write-Error "No Resource Groups matched" -ErrorAction Stop
}
If (@($ResGroupExist).count -eq 1) {
Write-Warning "Exactly one matching resource group found"
$ResourceGroupName = $ResourceGroupSearch.ResourceGroupName
}
<#
Write-Verbose "Checking Storage Account Availability in Subscription"
If (!(Get-AzureRmStorageAccount | Where-Object {$_.StorageAccountName -eq $VMDiagStorageAcct}))
{
If ((Get-AzureRmStorageAccountNameAvailability -Name $VMDiagStorageAcct).NameAvailable -eq $True)
{
New-AzureRmStorageAccount -Name $VMDiagStorageAcct -Location $VMLocation -ResourceGroupName $ResourceGroup -SkuName "Standard_LRS" -EnableHttpsTrafficOnly $true
#Start-Sleep -Seconds 5
Get-AzureRmStorageAccount -ResourceGroupName $ResourceGroup -Name $VMDiagStorageAcct | Update-AzureRmStorageAccountNetworkRuleSet -DefaultAction Deny -Bypass None
#Update-AzureRmStorageAccountNetworkRuleSet -ResourceGroupName $ResourceGroup -Name $VMDiagStorageAcct -DefaultAction Deny -Bypass None
Add-AzureRmStorageAccountNetworkRule -ResourceGroupName $ResourceGroup -AccountName $VMDiagStorageAcct -IPAddressOrRange "165.234.248.3"
}
Else
{
Write-Verbose "Storage Account already exists. Re-run with a valid storage account"
Return
}
}
#>
<#
Write-Verbose "Evaluating Availability Set"
If($VMAS)
{
Write-Verbose "Checking for existing Availability Set"
$VMASExist = Get-AzAvailabilitySet -ResourceGroupName $ResourceGroup -Name $VMASName -ErrorAction SilentlyContinue
If($VMASExisting)
{
$VMASID = $VMASExist.id
}
Else
{
New-AzAvailabilitySet -ResourceGroupName $ResourceGroup -Name $VMASName -Location $VMLocation -PlatformFaultDomainCount 3 -PlatformUpdateDomainCount 5 -Sku Aligned
$VMASID = (Get-AzAvailabilitySet -ResourceGroupName $ResourceGroup -Name $VMASName).id
}
}
#>
Write-Verbose "Preparing Network Config"
$VNet = (Get-AzVirtualNetwork | where { $_.Name -eq $VMVNetName })
$VNetID = $VNet.id
$Subnet = ($VNet.Subnets | where { $_.Name -eq $VMSubnet })
$SubnetName = $Subnet.name
$SubnetID = $Subnet.id
Write-Verbose "Creating IPConfig and NIC"
$IPConfig = New-AzNetworkInterfaceIpConfig -Name $VMIPConfigName -PrivateIpAddress $VMIP -PrivateIpAddressVersion IPv4 -Subnet $Subnet
$VMNIC = New-AzNetworkInterface -IpConfigurationName $IPConfig -Location $VMLocation -Name $VMNICName -ResourceGroupName $ResourceGroup -SubnetId $SubnetID
$VMNIC.IpConfigurations[0].PrivateIpAllocationMethod = "Static"
$VMNIC.IpConfigurations[0].PrivateIpAddress = $VMIP
Set-AzNetworkInterface -NetworkInterface $VMNIC
Write-Verbose "Building VM Config"
#If($VMAS)
#{
#$vmConfig = New-AzVMConfig -VMName $VMObjectName -VMSize $VMSize -AvailabilitySetId $VMASID
#}
#Else
#{
#If($VMAvailZone)
#{
# Write-Verbose "AvailZone $VMAvailZone"
# $vmConfigParams = @{
# VMName = $VMObjectName
# VMSize = $VMSize
# Zone = $VMAvailZone
# }
#}
#Else
#{
# Write-Verbose "No AvailZone"
$vmConfigParams = @{
VMName = $VMObjectName
VMSize = $VMSize
}
#}
#$vmConfig = New-AzureRmVMConfig -VMName $VMObjectName -VMSize $VMSize
$vmConfig = New-AzVMConfig @vmConfigParams
#}
$vmConfig | Set-AzVMOSDisk -Name $VMOSDiskName -CreateOption FromImage
If ($VMOS -eq "Windows") {
$vmConfig | Set-AzVMOperatingSystem -Windows -ComputerName $VMName -Credential $VMCredential
$vmConfig | Set-AzVMSourceImage -PublisherName $VMPublisher -Offer $VMOffer -Skus $VMSku -Version latest
}
If ($VMOS -eq "Linux") {
$vmConfig | Set-AzVMOperatingSystem -Linux -ComputerName $VMFQDN -Credential $VMCredential
Switch ($VMSubscription) {
#"npd01" {$vmConfig | Set-AzureRmVMSourceImage -Id "/subscriptions/76297098-764c-43de-8525-c9fda1b237be/resourceGroups/rg-infra-templates-tst-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-001"}
#"infra01" {$vmConfig | Set-AzureRmVMSourceImage -Id "/subscriptions/e53aa0c7-824d-40a2-b420-4ab77b1051d2/resourceGroups/rg-infra-templates-prd-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-001"}
#"prd01" {$vmConfig | Set-AzureRmVMSourceImage -Id "/subscriptions/437b2bfa-850e-4464-b6c2-38a68cda7c69/resourceGroups/rg-infra-templates-prd-002/providers/Microsoft.Compute/images/vm-rhel74template-prd-002"}
"npd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/76297098-764c-43de-8525-c9fda1b237be/resourceGroups/rg-infra-templates-tst-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-103" }
"infra01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/e53aa0c7-824d-40a2-b420-4ab77b1051d2/resourceGroups/rg-infra-templates-prd-001/providers/Microsoft.Compute/images/vm-rhel74template-prd-403" }
"prd01" { $vmConfig | Set-AzVMSourceImage -Id "/subscriptions/437b2bfa-850e-4464-b6c2-38a68cda7c69/resourceGroups/rg-infra-templates-prd-002/providers/Microsoft.Compute/images/vm-rhel74template-prd-003" }
}
}
$vmConfig | Add-AzVMNetworkInterface -Id $VMNIC.ID
#$vmConfig | Set-AzureRmVMBootDiagnostics -Enable -ResourceGroupName $ResourceGroup -StorageAccountName $VMDiagStorageAcct
##set to use managed storage account after module conversion to Az
#$vmConfig | Set-AzVMBootDiagnostic -VM $vmConfig -Enable -ResourceGroupName $ResourceGroup
Set-AzVMBootDiagnostic -VM $vmConfig -Enable -ResourceGroupName $ResourceGroup
Write-Verbose "Creating VM"
New-AzVM -VM $vmConfig -ResourceGroupName $resourceGroup -Location $VMLocation -DisableBginfoExtension -LicenseType "Windows_Server" -AsJob
}
End {
}
}
function Add-ITDAzureRMNewDataDisk {
[CmdletBinding()]
Param
(
[string]
$VMName,
[int[]]
$SizeGB
)
Begin {
$search = @()
$Hostname = $VMName.split('-')[1]
$Environment = $VMName.split('-')[2]
Get-AzSubscription | ForEach-Object {
$Subscription = $_
Set-AzContext $_ | Out-Null
$search += Get-AzVM | Where-Object Name -EQ $VMName | Select-Object *, @{n = 'SubscriptionName'; e = { $Subscription.Name } }
}
}
Process {
If ($search.count -eq 1) {
Set-AzContext $search.SubscriptionName
$ResourceGroup = $search.ResourceGroupName.ToLower()
$Location = (Get-AzResourceGroup -Name $ResourceGroup).Location
$VM = Get-AzVM -Name $VMName -ResourceGroupName $ResourceGroup
$Zone = $VM.Zones
ForEach ($Size in $SizeGB) {
$ExistingDisks = @($VM.StorageProfile.DataDisks | Select-Object *, @{n = 'ItdId'; e = { [int]($_.Name -replace "vm-$hostname-app-$environment-") } })
$NewDiskItdIdInt = ($ExistingDisks | Sort-Object ItdId -Descending | Select-Object -First 1).ItdId + 1
$NewDiskItdIdStr = $NewDiskItdIdInt.ToString("000")
$NewDiskName = "vm-$Hostname-app-$Environment-$NewDiskItdIdStr" #vm-itduc4p1-app-tst-001
$LunID = ($ExistingDisks | Sort-Object Lun -Descending | Select-Object -First 1).Lun + 1
$count = 0
If ($ExistingDisks) {
while ($Size -match $ExistingDisks.DiskSizeGB) {
$count++
Write-Warning "SizeGB: $Size, Count: $count"
If ($count -ge 11) {
Write-Error "Disk size not available" -ErrorAction Stop
}
Else {
$Size = $Size - 1
}
}
}
Write-Warning "SizeGB: $Size, Count: $count"
$AzureRmDiskConfigParams = @{
DiskSizeGB = $Size
Location = $Location
CreateOption = "Empty"
SkuName = "Premium_LRS"
}
If ($Zone) {
Write-Verbose "VM is located in Zone $Zone"
$AzureRmDiskConfigParams += @{Zone = $Zone }
}
#$DiskConfig = New-AzureRmDiskConfig -DiskSizeGB $Size -Location $Location -CreateOption Empty -SkuName Premium_LRS
$DiskConfig = New-AzDiskConfig @AzureRmDiskConfigParams
If (!(Get-AzDisk -ResourceGroupName $ResourceGroup -DiskName $NewDiskName -ErrorAction SilentlyContinue)) {
$NewDisk = New-AzDisk -DiskName $NewDiskName -Disk $DiskConfig -ResourceGroupName $ResourceGroup
$VM = Add-AzVMDataDisk -Name $NewDiskName -CreateOption Attach -ManagedDiskId $NewDisk.Id -VM $VM -Lun $LunID -Caching ReadOnly
Update-AzVM -VM $VM -ResourceGroupName $ResourceGroup -AsJob
}
}
}
Else {
Write-Error "Search count invalid" -ErrorAction SilentlyContinue
}
}
End {
}
}
$IaasAutoCred = Get-AutomationPSCredential -Name 'ITD Iaas Automation'
$IaasAutoCred = $IaaSAutoAz
<#
New-ITDAzureWindowsVMAA `
-ComputerName itdbuilddba.nd.gov `
-CPU 2 `
-MemoryGB 8 `
-Subnet '10.21.8.0/22'`
-OS 'Windows Server 2019 Datacenter' `
-Environment 'Test' `
-AppName 'Infra-SoftDev-TFS' `
-LicensingRestrictions 'No Licensing Restrictions' `
-verbose
#>
<#
$params = @{
ComputerName = "itdpurshir1.nd.gov"
CPU = 2;
MemoryGB = 8;
Subnet = '10.69.254.0/24';
OS = 'Windows Server 2022 Datacenter';
Environment = 'CARES-tst';
AppName = 'ITD-POC-Purview';
LicensingRestrictions = 'No Licensing Restrictions';
Verbose = $true;
}
New-ITDAzureWindowsVMAA @params
#>
$params = @{
ComputerName = "itdsoscmtsql22.nd.gov"
CPU = 4;
MemoryGB = 16;
Subnet = '10.21.8.0/22';
OS = 'Windows Server 2022 Datacenter';
Environment = 'Test';
AppName = 'SOS-BLS';
LicensingRestrictions = 'Microsoft SQL Standard';
Verbose = $true;
}
New-ITDAzureWindowsVMAA @params
#>
@@ -0,0 +1,18 @@
$RestMethodParams = @{
Method = 'Post';
Uri = 'https://96bdfe01-af80-4575-8f23-e7057184c8f6.webhook.cus.azure-automation.net/webhooks?token=dVIj9zHSJwNeR2Ow8QTOoiSoKaPPux9EVJsQAMiGUPg%3d';
Headers = @{ITD = 'mXJU74ABYyDHcVY6iJihPDk8LidJ2ibBA2sA3RAwKaBHS6Gw7Rr2Zz5JZAhPm6wMuvY7X54ZzJxAXaM7ig3PHG4MKvtkBf8X7q3jGNcePgUqg9WCwCSJ3JWG7AA6M39x4vpihKeZV'};
Body = [PSCustomObject]@{
ComputerName = 'itddhsharvestp1.nd.gov';
CPU = 2;
MemoryGB = 24;
VlanId = 3822;
OS = 'Windows Server 2022 Datacenter';
Environment = 'Production';
Datacenter = 'Bismarck';
AppName = 'DHS-Orchard-Harvest';
LicensingRestrictions = 'No Licensing Restrictions';
} | ConvertTo-Json;
}
Invoke-RestMethod @RestMethodParams | ConvertTo-Json
@@ -0,0 +1,358 @@
[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"
}