[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"
}