AutomatedLabWac.psm1

function Install-LabWindowsAdminCenter
{
    [CmdletBinding()]
    param
    ( )

    $lab = Get-Lab -ErrorAction SilentlyContinue

    if (-not $lab)
    {
        Write-Error -Message 'Please deploy a lab first.'
        return
    }

    $machines = (Get-LabVM -Role WindowsAdminCenter).Where( { -not $_.SkipDeployment })
    
    if ($machines)
    {
        Start-LabVM -ComputerName $machines -Wait
        $wacDownload = Get-LabInternetFile -Uri (Get-LabConfigurationItem -Name WacDownloadUrl) -Path "$labSources\SoftwarePackages" -FileName WAC.msi -PassThru -NoDisplay
        Copy-LabFileItem -Path $wacDownload.FullName -ComputerName $machines

        $jobs = foreach ($labMachine in $machines)
        {
            if ((Invoke-LabCommand -ComputerName $labMachine -ScriptBlock { Get-Service -Name ServerManagementGateway -ErrorAction SilentlyContinue } -PassThru -NoDisplay))
            {
                Write-ScreenInfo -Type Verbose -Message "$labMachine already has Windows Admin Center installed"
                continue
            }

            $role = $labMachine.Roles.Where( { $_.Name -eq 'WindowsAdminCenter' })
            $useSsl = $true
            if ($role.Properties.ContainsKey('UseSsl'))
            {
                $useSsl = [Convert]::ToBoolean($role.Properties['UseSsl'])
            }


            if ($useSsl -and $labMachine.IsDomainJoined -and (Get-LabIssuingCA -DomainName $labMachine.DomainName -ErrorAction SilentlyContinue) )
            {
                $san = @(
                    $labMachine.Name
                    if ($lab.DefaultVirtualizationEngine -eq 'Azure') { $labMachine.AzureConnectionInfo.DnsName }
                )
                $cert = Request-LabCertificate -Subject "CN=$($labMachine.FQDN)" -SAN $san -TemplateName WebServer -ComputerName $labMachine -PassThru -ErrorAction Stop
            }

            $Port = 443
            if (-not $useSsl)
            {
                $Port = 80
            }
            if ($role.Properties.ContainsKey('Port'))
            {
                $Port = $role.Properties['Port']
            }

            $arguments = @(
                '/qn'
                '/L*v C:\wacLoc.txt'
                "SME_PORT=$Port"
            )

            if ($role.Properties.ContainsKey('EnableDevMode'))
            {
                $arguments += 'DEV_MODE=1'
            }

            if ($cert.Thumbprint)
            {
                $arguments += "SME_THUMBPRINT=$($cert.Thumbprint)"
                $arguments += "SSL_CERTIFICATE_OPTION=installed"
            }
            elseif ($useSsl)
            {
                $arguments += "SSL_CERTIFICATE_OPTION=generate"
            }

            if (-not $machine.SkipDeployment -and $lab.DefaultVirtualizationEngine -eq 'Azure')
            {
                if (-not (Get-LabAzureLoadBalancedPort -DestinationPort $Port -ComputerName $labMachine))
                {
                    $lab.AzureSettings.LoadBalancerPortCounter++
                    $remotePort = $lab.AzureSettings.LoadBalancerPortCounter
                    Add-LWAzureLoadBalancedPort -ComputerName $labMachine -DestinationPort $Port -Port $remotePort
                    $Port = $remotePort
                }
            }

            if ([Net.ServicePointManager]::SecurityProtocol -notmatch 'Tls12')
            {
                Write-Verbose -Message 'Adding support for TLS 1.2'
                [Net.ServicePointManager]::SecurityProtocol += [Net.SecurityProtocolType]::Tls12
            }

            Write-ScreenInfo -Type Verbose -Message "Starting installation of Windows Admin Center on $labMachine"
            Install-LabSoftwarePackage -LocalPath C:\WAC.msi -CommandLine $($arguments -join ' ') -ComputerName $labMachine -ExpectedReturnCodes 0, 3010 -AsJob -PassThru -NoDisplay
        }

        if ($jobs)
        {
            Write-ScreenInfo -Message "Waiting for the installation of Windows Admin Center to finish on $machines"
            Wait-LWLabJob -Job $jobs -ProgressIndicator 5 -NoNewLine -NoDisplay

            if ($jobs.State -contains 'Failed')
            {
                $jobs.Where( { $_.State -eq 'Failed' }) | Receive-Job -Keep -ErrorAction SilentlyContinue -ErrorVariable err
                if ($err[0].Exception -is [System.Management.Automation.Remoting.PSRemotingTransportException])
                {
                    Write-ScreenInfo -Type Verbose -Message "WAC setup has restarted WinRM. The setup of WAC should be completed"
                }
                else
                {
                    Write-ScreenInfo -Type Error -Message "Installing Windows Admin Center on $($jobs.Name.Replace('WAC_')) failed. Review the errors with Get-Job -Id $($installation.Id) | Receive-Job -Keep"
                    return
                }
            }

            Restart-LabVM -ComputerName $machines -Wait -NoDisplay
        }
    }

    Add-LabWacManagedNode
}

function Add-LabWacManagedNode
{
    [CmdletBinding()]
    param
    ( )

    $lab = Get-Lab -ErrorAction SilentlyContinue

    if (-not $lab)
    {
        Write-Error -Message 'Please deploy a lab first.'
        return
    }

    $machines = Get-LabVM -Role WindowsAdminCenter

    # Add hosts through REST API
    foreach ($machine in $machines)
    {
        $role = $machine.Roles.Where( { $_.Name -eq 'WindowsAdminCenter' })
        # In case machine deployment is skipped, we are using the installation user credential of the machine to make the connection
        $wacCredential = if ($machine.SkipDeployment)
        {
            $machine.GetLocalCredential()
        }
        else
        {
            $machine.GetCredential($lab)
        }

        $useSsl = $true
        if ($role.Properties.ContainsKey('UseSsl'))
        {
            $useSsl = [Convert]::ToBoolean($role.Properties['UseSsl'])
        }

        $Port = 443
        if (-not $useSsl)
        {
            $Port = 80
        }
        if ($role.Properties.ContainsKey('Port'))
        {
            $Port = $role.Properties['Port']
        }

        if (-not $machine.SkipDeployment -and $lab.DefaultVirtualizationEngine -eq 'Azure')
        {
            $azPort = Get-LabAzureLoadBalancedPort -DestinationPort $Port -ComputerName $machine
            $Port = $azPort.Port
        }

        $filteredHosts = if ($role.Properties.ContainsKey('ConnectedNode'))
        {
            Get-LabVM | Where-Object -Property Name -in ($role.Properties['ConnectedNode'] | ConvertFrom-Json)
        }
        else
        {
            Get-LabVM | Where-Object -FilterScript { $_.Name -ne $machine.Name -and -not $_.SkipDeployment }
        }

        if ($filteredHosts.Count -eq 0) { return }

        $wachostname = if (-not $machine.SkipDeployment -and $lab.DefaultVirtualizationEngine -eq 'Azure') 
        {
            $machine.AzureConnectionInfo.DnsName
        }
        elseif ($machine.SkipDeployment)
        {
            $machine.Name
        }
        else
        {
            $machine.FQDN
        }
        Write-ScreenInfo -Message "Adding $($filteredHosts.Count) hosts to the admin center for user $($wacCredential.UserName)"
        $apiEndpoint = "http$(if($useSsl){'s'})://$($wachostname):$Port/api/connections"

        $bodyHash = foreach ($vm in $filteredHosts)
        {
            @{
                id   = "msft.sme.connection-type.server!$($vm.FQDN)"
                name = $vm.FQDN
                type = "msft.sme.connection-type.server"
            }
        }

        try
        {
            [ServerCertificateValidationCallback]::Ignore()

            $paramIwr = @{
                Method      = 'PUT'
                Uri         = $apiEndpoint
                Credential  = $wacCredential
                Body        = $($bodyHash | ConvertTo-Json)
                ContentType = 'application/json'
                ErrorAction = 'Stop'
            }

            if ($PSEdition -eq 'Core' -and (Get-Command Invoke-RestMethod).Parameters.COntainsKey('SkipCertificateCheck'))
            {
                $paramIwr.SkipCertificateCheck = $true
            }

            $response = Invoke-RestMethod @paramIwr
            if ($response.changes.Count -ne $filteredHosts.Count)
            {
                Write-ScreenInfo -Type Error -Message "Result set too small, there has likely been an issue adding the managed nodes. Server response:`r`n`r`n$($response.changes)"
            }

            Write-ScreenInfo -Message "Successfully added $($filteredHosts.Count) machines as connections for $($wacCredential.UserName)"
        }
        catch
        {
            Write-ScreenInfo -Type Error -Message "Could not add server connections. Invoke-RestMethod says: $($_.Exception.Message)"
        }
    }
}