$IaasAuto = Get-Credential -UserName ndgov\svcitdiaasauto
$StdCred = $IaasAuto
$PrvCred = $IaasAuto

$FQDNs = @"
itdzmtest111.ndtestdev.nd.dev
"@

$FQDNs = ConvertTo-Array -MultiLineString $FQDNs

ForEach ($FQDN in $FQDNs) {
    Write-Warning "[$FQDN]:Start"
    $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))
    $GuestCredentialAB = New-Object System.Management.Automation.PSCredential ('itdadmin', ($LocalCredential.Password | ConvertTo-SecureString -AsPlainText -Force))
    $GuestCredentialBB = New-Object System.Management.Automation.PSCredential ('Administrator', ($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-Data-Server"
            $DiskStorageFormat = 'Thin'
            $DatastoreCluster = Get-DatastoreCluster -Name "PS1_FS92_Gen"
        }
        "PS2" {
            $ViServer = 'itdvmvc2.nd.gov'
            $ComputeCluster = Get-Cluster PS2
            $VirtualSwitch = Get-VDSwitch -Name "dvSwitch-SDC-Data-Server"
            $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" }
        "Windows Server 2019 Datacenter (64-Bit)" { $Template = "Windows Server 2019 Standard" }
        "Windows Server 2022 Datacenter (64-Bit)" { $Template = "Windows Server 2022 Standard" }
        Default { Write-Error "Invalid template" -ErrorAction Stop }
    }

    $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
    Write-Warning -Message "[$FQDN]:Modify disks 1,2,3"
    $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 "[$FQDN]:Assigning WMI Tag 000-Prod, SCCM will change it later if required"
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Checking for DVD drive..."
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Checking for unpartitioned space on C: disk..."
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Start Extra Disk(s) config"
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Start Page File Configuration"
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Enabling Remote Management..."
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Checking current power plan..."
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 $GuestCredentialBB -ScriptType PowerShell -ScriptText  $TimeSyncScriptBlock
    #>
    Write-Warning -Message "[$FQDN]:Enabling the server manager performance monitors..."
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]:Disable Windows Firewall"
    Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -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 "[$FQDN]: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 "[$FQDN]:Invoke-VMScript to AD join"
        $InvokeVMScriptFunc = [System.Management.Automation.ScriptBlock]::Create("$FirstScriptBlock ; $SecondScriptText")

        Get-VM -Name $FQDN | Invoke-VMScript -GuestCredential $GuestCredentialBB -ScriptType PowerShell -ScriptText $InvokeVMScriptFunc
        
        Write-Warning -Message "[$FQDN]: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 ("[$FQDN]:Copying SCCM client installer to C:\temp... " + (Get-Date))
    Copy-VMGuestFile -Source C:\SCCM_Client\ -Destination C:\temp\SCCM_Client -VM (Get-VM -Name $FQDN) -LocalToGuest -GuestCredential $GuestCredentialAB -Force
    Write-Warning ("[$FQDN]:SCCM client copy complete " + (Get-Date))
    #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 "[$FQDN]: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 "[$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]
        }
    }

    Write-Warning "[$FQDN]: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 "[$FQDN]: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 '[$FQDN]: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 "[$FQDN]:End"
}



Invoke-Command -ComputerName $FQDNs -Credential $PrvCred -ScriptBlock { Get-Process -Name ccmexec*, cohesity*, cyserver*, nessus*, vmtoolsd* }

Write-Warning "[$FQDN]:Trigger SCCM MachinePolicy First Check-in"
Invoke-Command -ComputerName $FQDNs -Credential $PrvCred -ScriptBlock {
    [void] ([wmiclass] "\\localhost\root\ccm:SMS_Client").TriggerSchedule("{00000000-0000-0000-0000-000000000021}");
}