1101 lines
47 KiB
Plaintext
1101 lines
47 KiB
Plaintext
# https://96bdfe01-af80-4575-8f23-e7057184c8f6.webhook.cus.azure-automation.net/webhooks?token=dVIj9zHSJwNeR2Ow8QTOoiSoKaPPux9EVJsQAMiGUPg%3d
|
||
[CmdletBinding()]
|
||
param
|
||
(
|
||
[Parameter(Mandatory = $false)]
|
||
[object] $WebhookData
|
||
)
|
||
|
||
If ($WebhookData) {
|
||
If ($WebhookData.RequestHeader.ITD -eq 'mXJU74ABYyDHcVY6iJihPDk8LidJ2ibBA2sA3RAwKaBHS6Gw7Rr2Zz5JZAhPm6wMuvY7X54ZzJxAXaM7ig3PHG4MKvtkBf8X7q3jGNcePgUqg9WCwCSJ3JWG7AA6M39x4vpihKeZV') {
|
||
Write-Verbose "Header has required data"
|
||
}
|
||
Else {
|
||
Write-Error "Header missing required data"
|
||
exit;
|
||
}
|
||
|
||
# ignore certs https://communities.vmware.com/t5/VMware-PowerCLI-Discussions/Error-running-Invoke-Vmscript-An-error-occurred-while-sending/m-p/475203
|
||
Add-Type @"
|
||
using System.Net;
|
||
|
||
using System.Security.Cryptography.X509Certificates;
|
||
|
||
public class TrustAllCertsPolicy : ICertificatePolicy {
|
||
|
||
public bool CheckValidationResult(
|
||
|
||
ServicePoint srvPoint, X509Certificate certificate,
|
||
|
||
WebRequest request, int certificateProblem) {
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
"@
|
||
|
||
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
|
||
|
||
$InputParams = (ConvertFrom-Json -InputObject $WebhookData.RequestBody)
|
||
$FQDN = $InputParams.Title
|
||
|
||
$SPCred = $StdCred
|
||
$ADCred = $PrvCred
|
||
$PSCred = $PrvCred
|
||
$VMCred = $PrvCred
|
||
$CcmCred = $PrvCred
|
||
#>
|
||
<# 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 ($PSCred.UserName.split('\')[1], $ADCred.Password)
|
||
$VMCred = Get-AutomationPSCredential -Name 'VMware Auto'
|
||
$CcmCred = Get-AutomationPSCredential -Name 'ITD IaaS Automation'#>
|
||
#>
|
||
Clear-DnsClientCache
|
||
$SharePointList = Get-ITDVMwareSharePointVMGuestList -Credential $SPCred
|
||
Write-Warning ("SPList count: " + $SharePointList.count)
|
||
$SPItem = $SharePointList | Where-Object Title -EQ $FQDN
|
||
If ($null -eq $SPItem.Cluster) {
|
||
Write-Error "VMware Cluster not specified in SharePoint"
|
||
Stop
|
||
}
|
||
Write-Warning $SPItem
|
||
$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 $RadiusCred
|
||
[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 BB before baseline, AB after baseline
|
||
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))
|
||
$GuestCredential = $GuestCredentialBB
|
||
|
||
# VMware
|
||
Connect-ITDvCenter -Credential $VMCred
|
||
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'
|
||
$ComputeCluster = Get-Cluster WINDOWS1
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||
$DiskStorageFormat = 'Thin'
|
||
If ($SPItem.LicensingRestrictions -like "*SQL*") {
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS1_FS92_SQL"
|
||
}
|
||
Else {
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS1_FS92_Gen"
|
||
}
|
||
}
|
||
"WINDOWS2" {
|
||
$ViServer = 'itdvmvc2.nd.gov'
|
||
$ComputeCluster = Get-Cluster WINDOWS2
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||
$DiskStorageFormat = 'Thin'
|
||
If ($SPItem.LicensingRestrictions -like "*SQL*") {
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS2_FS92_SQL"
|
||
}
|
||
Else {
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WINDOWS2_FS92_Gen"
|
||
}
|
||
}
|
||
"SQL1" {
|
||
$ViServer = 'itdvmvc1.nd.gov'
|
||
$ComputeCluster = Get-Cluster SQL1
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||
$DiskStorageFormat = 'EagerZeroedThick'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "SQL1_FS92_Gen"
|
||
}
|
||
"SQL2" {
|
||
$ViServer = 'itdvmvc2.nd.gov'
|
||
$ComputeCluster = Get-Cluster SQL2
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||
$DiskStorageFormat = 'EagerZeroedThick'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "SQL2_FS92_Gen"
|
||
}
|
||
"WAS1" {
|
||
$ViServer = 'itdvmvc1.nd.gov'
|
||
$ComputeCluster = Get-Cluster WAS1
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-Data-Server"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WAS1_FS92_Gen"
|
||
}
|
||
"WAS2" {
|
||
$ViServer = 'itdvmvc2.nd.gov'
|
||
$ComputeCluster = Get-Cluster WAS2
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "WAS2_FS92_Gen"
|
||
}
|
||
"PS1" {
|
||
$ViServer = 'itdvmvc1.nd.gov'
|
||
$ComputeCluster = Get-Cluster PS1
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-PS1-Data"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "PS1_FS92_Gen"
|
||
}
|
||
"PS2" {
|
||
$ViServer = 'itdvmvc2.nd.gov'
|
||
$ComputeCluster = Get-Cluster PS2
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-PS2-Data"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "PS2_FS92_Gen"
|
||
}
|
||
"TEL1" {
|
||
$ViServer = 'itdvmvc1.nd.gov'
|
||
$ComputeCluster = Get-Cluster TEL1
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL1-Data"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "TEL1_FS92_Gen"
|
||
}
|
||
"TEL2" {
|
||
$ViServer = 'itdvmvc2.nd.gov'
|
||
$ComputeCluster = Get-Cluster TEL2
|
||
$VirtualSwitch = Get-VDSwitch -Name "dvSwitch-PDC-TEL2-Data"
|
||
$DiskStorageFormat = 'Thin'
|
||
$DatastoreCluster = Get-DatastoreCluster -Name "TEL2_FS92_Gen"
|
||
}
|
||
Default {
|
||
Write-Error "Cluster not found" -ErrorAction Stop
|
||
}
|
||
<#
|
||
"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
|
||
If ($DatastoreCluster) {
|
||
}
|
||
Else {
|
||
$DatastoreCluster = Get-DatastoreCluster | Where-Object Name -Like ("*" + $SPItem.Cluster + "*")
|
||
}
|
||
$ClusterDatastoreWithHighestFreeSpaceGB = ($DatastoreCluster | Get-Datastore | Sort-Object FreeSpaceGB -Descending | Select-Object -First 1)
|
||
If ($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB -gt $DiskTotal) {
|
||
Write-Warning ("VM DiskTotal " + $DiskTotal + "GB, will fit on " + $ClusterDatastoreWithHighestFreeSpaceGB.Name + " (" + [math]::round($ClusterDatastoreWithHighestFreeSpaceGB.FreeSpaceGB, 0) + "GB free)")
|
||
}
|
||
else {
|
||
Write-Warning ("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 = $ComputeCluster | Get-Datacenter | Get-Folder -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 -Like ("dvPG_" + $SPItem.Vlan_Id + "*")
|
||
If (!($PortGroup)) {
|
||
Write-Error "Virtual port group not found" -ErrorAction Stop
|
||
Stop
|
||
}
|
||
If (@($PortGroup).count -gt 1) {
|
||
Write-Error "Multiple port groups found" -ErrorAction Stop
|
||
Stop
|
||
}
|
||
|
||
$NewOSSpecName = ("AutoBuild-$Hostname-" + (Get-Date -UFormat "%Y%m%d%H%M%S"))
|
||
Write-Warning "NewOSSpecName = $NewOSSpecName"
|
||
Get-OSCustomizationSpec -Name "Windows (Auto)" -Server $ViServer | New-OSCustomizationSpec -Name $NewOSSpecName -Type Persistent -Server $ViServer
|
||
|
||
Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer | `
|
||
Set-OSCustomizationSpec `
|
||
-NamingScheme fixed `
|
||
-NamingPrefix $Hostname `
|
||
-AdminPassword $GuestCredentialBB.GetNetworkCredential().Password
|
||
|
||
Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer | `
|
||
Get-OSCustomizationNicMapping | `
|
||
Set-OSCustomizationNicMapping `
|
||
-IpMode UseStaticIP `
|
||
-IpAddress $IpAddress.IPAddressToString `
|
||
-SubnetMask $SubnetMask.IPAddressToString `
|
||
-DefaultGateway $DefaultGateway.IPAddressToString `
|
||
-Dns "10.2.7.40", "10.10.10.10"
|
||
|
||
$OSSpec = Get-OSCustomizationSpec -Name $NewOSSpecName -Server $ViServer
|
||
<#
|
||
$NewVMParams = @{
|
||
Name = $FQDN;
|
||
ResourcePool = $SPItem.Cluster;
|
||
Datastore = $DatastoreCluster;
|
||
DiskStorageFormat = $DiskStorageFormat;
|
||
Template = $Template;
|
||
Location = $FolderLocation;
|
||
OSCustomizationSpec = $OSSpec;
|
||
}
|
||
$NewVMParams
|
||
|
||
New-VM @NewVMParams
|
||
#>
|
||
|
||
#Set-Location C:\Temp # required to make New-VM work, https://communities.vmware.com/thread/591294
|
||
# seems fixed on 2022/07/01
|
||
try {
|
||
Write-Warning $FQDN
|
||
Write-Warning $ComputeCluster.Name
|
||
Write-Warning $DatastoreCluster
|
||
Write-Warning $DiskStorageFormat
|
||
Write-Warning $Template
|
||
Write-Warning $FolderLocation
|
||
Write-Warning $OSSpec
|
||
New-VM -Name $FQDN -ResourcePool $ComputeCluster.Name -Datastore $DatastoreCluster -DiskStorageFormat $DiskStorageFormat -Template $Template -Location $FolderLocation -OSCustomizationSpec $OSSpec
|
||
}
|
||
catch {
|
||
Stop
|
||
Stop
|
||
}
|
||
|
||
#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 = $VM | Get-HardDisk | 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 = $VM | Get-HardDisk | 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 = $VM | Get-HardDisk | Where-Object Name -EQ "Hard disk 3"
|
||
If ($VMDisk3.CapacityGB -lt $SPItem.Disk3) {
|
||
Set-HardDisk -HardDisk $VMDisk3 -CapacityGB $SPItem.Disk3 -Confirm:$false
|
||
}
|
||
}
|
||
|
||
# skipping tag assignment for now
|
||
# Set-ITDVMwareVMTagFromSharePoint -ComputerName $FQDN -SharePointCredential $SPCred -Verbose
|
||
|
||
# Run Guest OS code
|
||
# Enable RDP with NLA - can be done with GPO
|
||
# Enable WinRM - ITD WinRM enable
|
||
# Configure Power Plan - ITD-PowerPlan-HighPerformance
|
||
# Activate Windows, KMS on-premise, Azure to Azure
|
||
# DVD Drive to Z:
|
||
# Expand C:
|
||
# Partition/Format the rest, GPT/NTFS
|
||
# Configure Page File Disk
|
||
# Enabling server manager performance monitors
|
||
# Configure Time Zone and NTP - no GPO available
|
||
# Disable Windows Firewall
|
||
# Join AD
|
||
|
||
#Initialize-ITDServer -Credential $ADCred
|
||
|
||
|
||
Write-Warning -Message "Assigning WMI Tag 000-Prod, SCCM will change it later if required"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Move DVD Drive Mount
|
||
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
|
||
}
|
||
}
|
||
|
||
Write-Warning -Message "Checking for DVD drive..."
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Move DVD Drive Mount
|
||
try {
|
||
$dvd_letter = 'Z'
|
||
$dvd = Get-WmiObject -Class Win32_Volume -Filter "DriveType=5" | Select-Object -First 1
|
||
if ($dvd.Name -notmatch $dvd_letter) {
|
||
Write-Verbose -Message "Found DVD drive, switching to $dvd_letter`:"
|
||
|
||
Set-WmiInstance -InputObject $dvd -Arguments @{DriveLetter = "$dvd_letter`:" } | Out-Null
|
||
|
||
Write-Verbose -Message "DVD drive moved to drive letter $dvd_letter`:"
|
||
}
|
||
else {
|
||
Write-Verbose -Message "No DVD drive changes required, continuing..."
|
||
}
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
|
||
Write-Warning -Message "Checking for unpartitioned space on C: disk..."
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Expand C: Partition To Maximum Extent
|
||
|
||
|
||
try {
|
||
$cSize = ( Get-Partition -DriveLetter C ).Size
|
||
$cMaxSize = ( Get-PartitionSupportedSize -DriveLetter C ).SizeMax
|
||
|
||
if ($cSize -lt $cMaxSize) {
|
||
Write-Verbose -Message "Expanding C: from $($csize / 1GB)GB to $($cMaxSize / 1GB)GB..."
|
||
|
||
Resize-Partition -DriveLetter C -Size $cMaxSize
|
||
|
||
Write-Verbose -Message "C: expanded to $($cMaxSize / 1GB)GB."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "C: is already at maximum size, continuing..."
|
||
}
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
|
||
Write-Warning "Start Extra Disk(s) config"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Initialize Additional Disks
|
||
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 }
|
||
}
|
||
}
|
||
|
||
Write-Warning "Start Page File Configuration"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Configure Page File
|
||
if (Get-Partition -DriveLetter D -ErrorAction SilentlyContinue) {
|
||
Write-Verbose -Message "Setting up pagefile.sys on D:..."
|
||
|
||
try {
|
||
if (-not [IO.File]::Exists('D:\pagefile.sys')) {
|
||
$autoPage = Get-WmiObject -Class Win32_ComputerSystem -EnableAllPrivileges
|
||
$autoPage.AutomaticManagedPagefile = $false
|
||
[void]$autoPage.Put()
|
||
|
||
Write-Verbose -Message "Disabled automatic pagefile management."
|
||
|
||
$pageFile = Get-WmiObject -Class Win32_PageFileSetting -EnableAllPrivileges
|
||
$pageFile.Delete()
|
||
|
||
Write-Verbose -Message "Deleted C:\pagefile.sys."
|
||
|
||
Set-WmiInstance -Class Win32_PageFileSetting -Arguments @{ Name = "D:\pagefile.sys"; InitialSize = 0; MaximumSize = 0; } -EnableAllPrivileges | Out-Null
|
||
|
||
Write-Verbose -Message "System managed page file created on D:\pagefile.sys."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "Pagefile already configured on D:, continuing..."
|
||
}
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
else {
|
||
Write-Verbose -Message "Page file drive not found, cannot set up page file. Continuing server configuration..."
|
||
Write-Warning "Page file drive not found, cannot set up page file. Continuing server configuration..."
|
||
}
|
||
}
|
||
|
||
Write-Warning -Message "Enabling Remote Management..."
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Configure Remote Management (RDP/PoSH)
|
||
try {
|
||
Write-Verbose -Message "Checking WinRM..."
|
||
|
||
if (Test-WSMan -ErrorAction SilentlyContinue) {
|
||
Write-Verbose -Message "WinRM is already enabled."
|
||
}
|
||
else {
|
||
Enable-PSRemoting -Force
|
||
|
||
Write-Verbose -Message "WinRM is now enabled."
|
||
}
|
||
|
||
Write-Verbose -Message "Checking RDP..."
|
||
|
||
$RDP = Get-WmiObject Win32_TerminalServiceSetting -Namespace root\cimv2\TerminalServices
|
||
$NLA = Get-WmiObject Win32_TSGeneralSetting -Namespace root\cimv2\TerminalServices -Filter "TerminalName='RDP-tcp'"
|
||
if ($RDP.AllowTSConnections -eq 0) {
|
||
Write-Verbose -Message "RDP is disabled, enabling..."
|
||
|
||
$RDP.SetAllowTSConnections(1, 1) | Out-Null
|
||
|
||
Write-Verbose -Message "RDP is enabled."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "RDP is already enabled, checking NLA security..."
|
||
}
|
||
|
||
if ($NLA.UserAuthenticationRequired -eq 0) {
|
||
Write-Verbose -Message "RDP is not NLA secured, enabling..."
|
||
|
||
$NLA.SetUserAuthenticationRequired(1) | Out-Null
|
||
|
||
Write-Verbose -Message "RDP is now NLA secured."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "RDP is already NLA secured."
|
||
}
|
||
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
|
||
Write-Warning -Message "Checking current power plan..."
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# Configure Power Plan
|
||
try {
|
||
$powerPlans = powercfg -l
|
||
|
||
if ($powerPlans -match '\*$' -notmatch 'High performance') {
|
||
$currentPlan = [regex]::Match($powerPlans, '(?<=(\())[^)]+(?=(\)\s\*))').Value
|
||
|
||
Write-Verbose -Message "Power plan is currently set to $currentPlan, changing to High Performance..."
|
||
|
||
$highPerformance = [regex]::Match($powerPlans, '([\d\w-\S]+)(?=\s+\(High performance\))').Value
|
||
[void](powercfg -setactive $highPerformance)
|
||
|
||
Write-Verbose -Message "Power plan set to High Performance."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "Power plan already configured for High Performance."
|
||
}
|
||
|
||
[void](& w32tm.exe /resync /nowait)
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
|
||
|
||
$TimeSyncFunc = {
|
||
# Configure Time/Date Settings
|
||
Write-Verbose -Message "Checking current time/date settings..."
|
||
$Domain = "<DomainName>"
|
||
try {
|
||
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."
|
||
}
|
||
<#
|
||
Write-Verbose -Message "Beginning a time synchronization..."
|
||
|
||
if ((Get-Service W32Time).Status -eq 'Stopped') {
|
||
Start-Service W32Time
|
||
}
|
||
|
||
[void](& w32tm.exe /config /manualpeerlist:$Domain /syncfromflags:all /update)
|
||
|
||
[void](& w32tm.exe /resync /nowait)
|
||
|
||
# Start background job to check time service
|
||
$tmJob = Start-Job -Name 'ManualTimeSync' -ScriptBlock {
|
||
do {
|
||
$tmOut = & w32tm.exe /query /status /verbose
|
||
|
||
Start-Sleep -Seconds 10
|
||
} until($tmOut -match '^Last Sync Error: 0\D+$')
|
||
}
|
||
|
||
# If after three minutes time still has not synched, move on. This usually causes no problems.
|
||
if ((Wait-Job -Job $tmJob -Timeout 180).State -eq 'Completed') {
|
||
Write-Warning -Message "Time synchronization with $($Domain.ToLower()) successful."
|
||
}
|
||
else {
|
||
[void](Stop-Job -Job $tmJob)
|
||
Write-Warning -Message "Time synchronization with $($Domain.ToLower()) failed."
|
||
}
|
||
#>
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
$TimeSyncScriptBlock = $TimeSyncFunc -replace '<DomainName>', $DomainName
|
||
$VM | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText $TimeSyncScriptBlock
|
||
#>
|
||
Write-Warning -Message "Enabling the server manager performance monitors..."
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# 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
|
||
}
|
||
}
|
||
|
||
Write-Warning -Message "Disable Windows Firewall"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
# 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..."
|
||
}
|
||
}
|
||
|
||
# Active Directory
|
||
Write-Warning "Join Active Directory (if required)"
|
||
$DomainName = $FQDN.Substring($FQDN.IndexOf(".") + 1)
|
||
$AppName = $SPItem.AppName
|
||
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=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain) -Filter { Name -eq $AppName }
|
||
If (!($OUAppName)) {
|
||
$OUAppName = Get-ADOrganizationalUnit -SearchBase ("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 ("*" + $SPItem.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 ($SPItem.Environment) {
|
||
'Test' {
|
||
If ($AppName -like "Shared-Peoplesoft*") { $EnvString = "Non-Prod" }
|
||
Else { $EnvString = "Test" }
|
||
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -Like "*$EnvString*"
|
||
}
|
||
'Production' {
|
||
$EnvString = "Prod"
|
||
$OuAppNameEnv = Get-ADOrganizationalUnit -SearchBase $OUAppName.DistinguishedName -Filter * | Where-Object Name -Like "*$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)
|
||
}
|
||
|
||
$FirstScriptBlock = { $DomainJoinCred = New-Object System.Management.Automation.PSCredential('svcitdvmdomainjoin', ('hypes-Vgv8h89' | ConvertTo-SecureString -AsPlainText -Force)) }
|
||
$SecondScriptText = 'Add-Computer -DomainName <DomainName> -OUPath "<OuPath>" -Credential $DomainJoinCred'
|
||
$SecondScriptText = $SecondScriptText -replace '<DomainName>', $DomainName
|
||
$SecondScriptText = $SecondScriptText -replace '<OuPath>', $OuFinal
|
||
$SecondScriptText = $SecondScriptText -replace '<OuPath>', ("OU=SERVERS,ou=COMPUTERS,ou=ITD," + $SearchBaseDomain)
|
||
|
||
Write-Warning -Message "Invoke-VMScript to AD join"
|
||
$InvokeVMScriptFunc = [System.Management.Automation.ScriptBlock]::Create("$FirstScriptBlock ; $SecondScriptText")
|
||
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText $InvokeVMScriptFunc
|
||
|
||
Write-Warning -Message "Restart VMGuest, wait for Tools, then 90 seconds after"
|
||
Get-VM -Name $FQDN | Restart-VMGuest -Confirm:$false
|
||
Wait-Tools -VM (Get-VM -Name $FQDN)
|
||
Start-Sleep -Seconds 90
|
||
}
|
||
|
||
Write-Warning "Copying SCCM client installer to C:\temp..."
|
||
Copy-VMGuestFile -Source D:\SCCM_Client\ -Destination C:\temp\SCCM_Client -VM (Get-VM -Name $FQDN) -LocalToGuest -GuestCredential $GuestCredentialAB -Force
|
||
#E:\AutoBuild\SCCM_Client\
|
||
|
||
# Check if SCCM automatically installed the SCCM client and registered it
|
||
$CcmRegistered = $false
|
||
$CcmRegistration = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
Get-Content C:\windows\ccm\logs\ClientIDManagerStartup.log
|
||
}
|
||
If ($CcmRegistration.ScriptOutput -match "Client is registered") {
|
||
Write-Warning "Client is registered."
|
||
$CcmRegistered = $true
|
||
}
|
||
ElseIf ($CcmRegistration.ScriptOutput -match "Client is already registered") {
|
||
Write-Warning "Client is already registered."
|
||
$CcmRegistered = $true
|
||
}
|
||
If ($CcmRegistered -eq $false) {
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
|
||
If (Get-Process -Name ccmsetup -ErrorAction SilentlyContinue) {
|
||
Write-Warning "CCM client is already installing"
|
||
$CcmRegistered = $true
|
||
}
|
||
ElseIf (Get-Process -Name ccmexec -ErrorAction SilentlyContinue) {
|
||
Write-Warning "CCM client is already installed"
|
||
$CcmRegistered = $true
|
||
}
|
||
Else {
|
||
Write-Warning -Message "Installing SCCM Client..."
|
||
Invoke-Expression -Command "C:\temp\SCCM_Client\ccmsetup.exe SMSSITECODE=ITD SMSMP=itdsccmp2.nd.gov DNSSUFFIX=nd.gov"
|
||
}
|
||
}
|
||
}
|
||
|
||
Write-Warning "Register SCCM Client"
|
||
While ($CcmRegistered -eq $false) {
|
||
$CcmRegistration = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
Get-Content C:\windows\ccm\logs\ClientIDManagerStartup.log
|
||
}
|
||
If ($CcmRegistration.ScriptOutput -match "Client is registered") {
|
||
Write-Warning "Client is registered."
|
||
$CcmRegistered = $true
|
||
}
|
||
ElseIf ($CcmRegistration.ScriptOutput -match "Client is already registered") {
|
||
Write-Warning "Client is already registered."
|
||
$CcmRegistered = $true
|
||
}
|
||
ElseIf ($CcmRegistered -eq $false) { Start-Sleep -Seconds 30 }
|
||
}
|
||
|
||
Write-Warning -Message "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]
|
||
}
|
||
}
|
||
|
||
Write-Warning "Trigger SCCM MachinePolicy First Check-in"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
|
||
}
|
||
|
||
Write-Warning "Trigger SCCM MachinePolicy"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
|
||
}
|
||
#Start-Sleep -Seconds 180
|
||
|
||
Write-Warning -Message 'Waiting for network connectivity / Then KVM Activation...'
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialAB -ScriptType PowerShell -ScriptText {
|
||
# Pause until network connectivity is available
|
||
|
||
$KMS = 'kms.nd.gov'
|
||
|
||
try {
|
||
$nwJob = Start-Job -Name 'NetworkCheck' -ScriptBlock {
|
||
Param ( [String]$KMS )
|
||
do {
|
||
$nwStatus = Test-NetConnection -ComputerName $KMS -Port 1688 -InformationLevel Quiet
|
||
|
||
Start-Sleep -Seconds 10
|
||
} until($nwStatus)
|
||
} -ArgumentList $KMS
|
||
|
||
# If after 30 seconds the network connection is not responding continue on
|
||
if ((Wait-Job -Job $nwJob -Timeout 30).State -eq 'Completed') {
|
||
Write-Verbose -Message 'Network connectivity has been verified.'
|
||
}
|
||
else {
|
||
[void](Stop-Job -Job $nwJob)
|
||
Write-Verbose -Message 'Network connectivity could not be verified.'
|
||
}
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
|
||
# Activate via KMS
|
||
Write-Verbose -Message "Activating windows against $KMS..."
|
||
if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||
Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit
|
||
}
|
||
try {
|
||
cscript C:\Windows\System32\slmgr.vbs /skms $KMS | Out-Null
|
||
cscript C:\Windows\System32\slmgr.vbs /ato | Out-Null
|
||
|
||
Write-Verbose -Message "Checking activation status..."
|
||
|
||
$kmsOut = cscript C:\Windows\System32\slmgr.vbs /dli
|
||
|
||
if (($kmsOut | Select-String -Pattern '^License Status:') -match 'Licensed') {
|
||
Write-Verbose -Message "Windows successfully activated."
|
||
}
|
||
else {
|
||
Write-Verbose -Message "Windows failed to activate, run slmgr commands manually. Ensure server time is correct."
|
||
Write-Warning -Message "Windows failed to activate, run slmgr commands manually. Ensure server time is correct."
|
||
}
|
||
}
|
||
catch {
|
||
Throw $_
|
||
Break
|
||
}
|
||
}
|
||
<#
|
||
Write-Warning "Trigger SCCM DiscoverData until it works"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
$discoveryran = $false
|
||
while ($discoveryran -eq $false) {
|
||
try {
|
||
Write-Warning -Message ("Attempt DiscoveryData run " + (Get-Date))
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000003}");
|
||
$discoveryran = $true
|
||
}
|
||
catch {
|
||
Start-Sleep -Seconds 30
|
||
}
|
||
}
|
||
}
|
||
Start-Sleep -Seconds 180
|
||
|
||
Write-Warning "Trigger SCCM MachinePolicy after Discovery"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
|
||
}
|
||
|
||
Write-Warning "Trigger SCCM UpdateDeployment Policy"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000108}"); #updatedeployment
|
||
}
|
||
Start-Sleep -Seconds 30
|
||
|
||
Write-Warning "Trigger SCCM UpdateScan Policy"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000113}"); #updatescan
|
||
}
|
||
Start-Sleep -Seconds 300
|
||
|
||
Write-Warning "Wait for SCCM Baselines"
|
||
While ( (Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText { Get-WmiObject -Namespace root/ccm/dcm -Class SMS_DesiredConfiguration | where-object { $_.DisplayName -like "ITD-WMITag-DTAP*" } | Select-Object DisplayName, LastEvalTime, Status, Version | Sort-Object DisplayName }) -eq $null) {
|
||
Write-Warning -Message ((Get-Date -uformat "%Y/%m/%d %H:%M:%S") + " $FQDN waiting for baselines")
|
||
}
|
||
Start-Sleep -Seconds 60
|
||
|
||
Write-Warning "Evaluate SCCM Baselines"
|
||
$CIBaselineDTAP = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
Get-WmiObject -Namespace root/ccm/dcm -Class SMS_DesiredConfiguration | where-object { $_.DisplayName -like "ITD-WMITag-DTAP*" } | Select-Object DisplayName, LastEvalTime, Status, Version | Sort-Object DisplayName
|
||
}
|
||
#If ($CIBaselineDTAP.ScriptOutput -match "00000000000000.000000+000") {
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
$Baseline = Get-WmiObject -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration | Where-Object DisplayName -like "ITD-WMITag-DTAP*"
|
||
([wmiclass]"\\localhost\root\ccm\dcm:SMS_DesiredConfiguration").TriggerEvaluation($Baseline.Name, $Baseline.Version)
|
||
}
|
||
Start-Sleep -Seconds 30
|
||
|
||
$CIBaselineNum = Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
Get-WmiObject -Namespace root/ccm/dcm -Class SMS_DesiredConfiguration | where-object { $_.DisplayName -like "ITD-ServerBaseline-*" } | Select-Object DisplayName, LastEvalTime, Status, Version | Sort-Object DisplayName
|
||
}
|
||
#If ($CIBaselineNum.ScriptOutput -match "00000000000000.000000+000") {
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
$Baseline = Get-WmiObject -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration | Where-Object DisplayName -like "ITD-ServerBaseline-*"
|
||
([wmiclass]"\\localhost\root\ccm\dcm:SMS_DesiredConfiguration").TriggerEvaluation($Baseline.Name, $Baseline.Version)
|
||
}
|
||
Start-Sleep -Seconds 30
|
||
|
||
|
||
Write-Warning "Evaluate All Remaining Configuration Items"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
$CIs = Get-WmiObject -Namespace root\ccm\dcm -Class SMS_DesiredConfiguration | where-object LastEvalTime -eq "00000000000000.000000+000"
|
||
ForEach ($CI in $CIs) {
|
||
([wmiclass]"\\localhost\root\ccm\dcm:SMS_DesiredConfiguration").TriggerEvaluation($CI.Name, $CI.Version)
|
||
}
|
||
}
|
||
|
||
Write-Warning "Trigger SCCM AppDeployment Policy "
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText {
|
||
[void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000121}");
|
||
}
|
||
Start-Sleep -Seconds 30
|
||
|
||
Write-Warning "Install Available Updates"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType Powershell -ScriptText {
|
||
([wmiclass]'ROOT\ccm\ClientSDK:CCM_SoftwareUpdatesManager').InstallUpdates([System.Management.ManagementObject[]] (Get-WmiObject -query 'SELECT * FROM CCM_SoftwareUpdate' -namespace 'ROOT\ccm\ClientSDK'))
|
||
}
|
||
Start-Sleep -Seconds 600
|
||
|
||
Write-Warning "Run gpupdate /force"
|
||
Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredential -ScriptType PowerShell -ScriptText { Invoke-Expression -Command "gpupdate /force" }
|
||
|
||
<#
|
||
Write-Warning -Message "Restart VMGuest, wait for Tools, then 90 seconds after"
|
||
$GuestCredential = $GuestCredentialAB
|
||
Get-VM -Name $FQDN | Restart-VMGuest -Confirm:$false
|
||
Wait-Tools -VM (Get-VM -Name $FQDN)
|
||
Start-Sleep -Seconds 90
|
||
#>
|
||
|
||
|
||
############################## KEEP TEMPLATE UPDATED INSTEAD
|
||
|
||
############ force baselines
|
||
|
||
<#####
|
||
install applications here
|
||
#>
|
||
<##### verify updates are available
|
||
get-wmiobject -query "SELECT * FROM CCM_SoftwareUpdate" -namespace "ROOT\ccm\ClientSDK"
|
||
#>
|
||
|
||
|
||
<####### pending reboot
|
||
NameSpace=’ROOT\ccm\ClientSDK’
|
||
Class=’CCM_ClientUtilities’
|
||
Method Name=’DetermineIfRebootPending’
|
||
#>
|
||
}
|
||
|
||
Else {
|
||
Write-Error "Runbook must be started from webhook"
|
||
} |