Validation/Test-ReadinessChecker.Tests.ps1

param(
    [switch]$TestMFA = $false, [switch]$keepLogs
)
$global:KeepLogs = $KeepLogs

$ErrorActionPreference = 'Stop'
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
Import-Module .\PesterHelper.psm1 -Force -DisableNameChecking
Write-Verbose "Import Data"
$testData = Import-LocalizedData -BaseDirectory $here -FileName PesterHelperData.psd1
$GLOBAL:AllCertificates = $testData.AllCertificates
$Global:AzsReadinessLogFile = Join-Path -Path $ENV:TEMP -ChildPath 'AzsReadinessChecker.log'
$GLOBAL:filesForCleanUp = @()
Install-Module AzureRM.KeyVault -Force
#Connect to Azure
Connect-Azure

#Get JSON
$keyvaultName = $testdata.pesterdata.ptkkvName
$jsonPath = Save-File -VaultName $keyvaultName -secretName $testdata.DeploymentData -outputPath $ENV:Temp\DeploymentData.json
$GLOBAL:filesForCleanUp += $jsonPath

$CustomAzureEnvironmentUri = Get-PlainTextSecret -VaultName $keyvaultName -secretName $testData.CustomAzureEnvironment
$deploymentDataJSON = Get-Content -Path $jsonPath | ConvertFrom-Json
$regionName = $deploymentDataJSON.DeploymentData.RegionName
$externalFQDN = $deploymentDataJSON.DeploymentData.ExternalDomainFQDN
Describe -Name "Certificate Validation" {
    BeforeAll{
        #Download certificates neccessary
        Connect-Azure
        $AllCertificateNames = $testdata.AllCertificates.Keys
        Set-SecurityProtocol -securityProtocol $targetSecurityProtocol
        $certificateSecrets = Get-AzureKeyVaultSecret -VaultName $keyvaultName | Where-Object Name -in $AllCertificateNames
        Set-SecurityProtocol -securityProtocol $restoreSecProtocol
        $i = 1

        $pwd = New-RandomPassword

        foreach ($secret in $certificateSecrets) {
            [int]$percentageComplete = $i / $certificateSecrets.count * 100
            Write-Progress -Activity "Downloading Certificates" -Status "$percentageComplete% Complete:" -PercentComplete $percentageComplete -CurrentOperation $secret.Name
            $destFile = "$env:TEMP\$($secret.Name).PFX"
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName $secret.Name -outputPath $destFile -asSecret:$true
            $AllCertificates[$secret.name].pfxPath = $pfx.pfxPath
            $AllCertificates[$secret.name].pfxPassword = $pfx.pfxPassword
            $i++
            $GLOBAL:filesForCleanUp += $pfx.pfxPath
        }

        Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force -DisableNameChecking
        # Get Deployment Certificate data

        if (Get-ChildItem c:\Windows\System32\CertPKICmdlet.dll | Where-Object {$_.versioninfo.ProductVersion -ge '10.0.17134.1'}){
            $GoodPfxEncryptionPath = "$env:TEMP\pfxEncryptionPass.pfx"
            $BadPfxEncryptionPath = "$env:TEMP\pfxEncryptionFail.pfx"
            $tempCert = Import-PfxCertificate -Exportable -Password $AllCertificates['MissingDNSName'].pfxPassword -CertStoreLocation 'Cert:\LocalMachine\My' -FilePath $AllCertificates['MissingDNSName'].pfxPath
            Export-PfxCertificate -NoProperties -Force -CryptoAlgorithmOption TripleDES_SHA1 -ChainOption BuildChain -Password $AllCertificates['MissingDNSName'].pfxPassword -Cert $tempCert -FilePath $GoodPfxEncryptionPath
            Export-PfxCertificate -NoProperties -Force -CryptoAlgorithmOption AES256_SHA256 -ChainOption BuildChain -Password $AllCertificates['MissingDNSName'].pfxPassword -Cert $tempCert -FilePath $BadPfxEncryptionPath
            $GLOBAL:filesForCleanUp += $GoodPfxEncryptionPath
        }
        else {
            Write-Verbose -verbose "Skipping encryption test"
        }
    }
    Context -Name "Key Usage" {
        It "Bad Key Usage Certificates Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['BadUsage'].ExpectedPrefix
                IncludeTests = 'Key Usage'
                ExcludeTests = 'CNG Key'
            }
            Test-KeyUsage -cert (Get-x509 -certinfo $AllCertificates['BadUsage']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Bad Key Usage Certificates Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['BadUsage'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Key Usage'
            }
            Test-KeyUsage -cert (Get-x509 -certinfo $AllCertificates['BadUsage']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
        It "Bad Key Usage declare should fail with message" {
            $keyUsage = 'CRL Sign'
            $certConfig = @{
                DNSName = $AllCertificates['BadSignature'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key'
                KeyUsage = $keyUsage
            }
            $result = Test-KeyUsage -cert (Get-x509 -certinfo $AllCertificates['BadSignature']) -certConfig $certConfig
            $result | Select-Object -ExpandProperty Result | should be "Fail"
            $failureDetails = $result | Select-Object -ExpandProperty FailureDetail
            $failureDetails -match $keyUsage | should be $true
        }
    }
    Context -Name "Signature" {
        It "Bad Signature Certificates Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['BadSignature'].ExpectedPrefix
                IncludeTests = 'Signature Algorithm'
                ExcludeTests = 'CNG Key'
            }
            Test-SignatureAlgorithm -x509 (Get-x509 -certInfo $AllCertificates['BadSignature']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Bad Signature Certificates Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['BadSignature'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Signature Algorithm'
            }
            Test-SignatureAlgorithm -x509 (Get-x509 -certInfo $AllCertificates['BadSignature']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
        It "Bad Signature declared Should fail with message" {
            $certConfig = @{
                DNSName = $AllCertificates['BadSignature'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key'
                HashAlgorithm = 'SHA1RSA','SHA256RSA'
            }
            $result = Test-SignatureAlgorithm -x509 (Get-x509 -certInfo $AllCertificates['BadSignature']) -certConfig $certConfig
            $result | Select-Object -ExpandProperty Result | should be "Fail"
            $result | Select-Object -ExpandProperty FailureDetail | should BeLike "*Please avoid using SHA1RSA,SHA256RSA signature algorithm(s)*"
        }
    }
    Context -Name "Private Key" {
        It "Bad Private Key Certificates Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['BadPrivateKey'].ExpectedPrefix
                IncludeTests = 'Private Key'
                ExcludeTests = 'CNG Key'
            }
            Test-PrivateKey -x509 (Get-x509 -certInfo $AllCertificates['BadPrivateKey']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Bad Private Key Certificates Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['BadPrivateKey'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Private Key'
            }
            Test-PrivateKey -x509 (Get-x509 -certInfo $AllCertificates['BadPrivateKey']) -certConfig $certConfig  | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
    }
    Context -Name "Chain" {
        It "Incomplete Certificate Chain Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['IncompleteChain'].ExpectedPrefix
                IncludeTests = 'Cert Chain'
                ExcludeTests = 'CNG Key'
            }
            Test-CertificateChain -pfxdata (Get-PFXHash -certInfo $AllCertificates['IncompleteChain']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Incomplete Certificate Chain Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['IncompleteChain'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Cert Chain'
            }
            Test-CertificateChain -pfxdata (Get-PFXHash -certInfo $AllCertificates['IncompleteChain']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
        It "Wrong Certificate Chain Order Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['BadChainOrder'].ExpectedPrefix
                IncludeTests = 'Chain Order'
                ExcludeTests = 'CNG Key'
            }
            Test-CertificateChainOrder -pfxdata (Get-PFXHash -certInfo $AllCertificates['BadChainOrder']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Wrong Certificate Chain Order Should SkippedByCOnfig" {
            $certConfig = @{
                DNSName = $AllCertificates['BadChainOrder'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Chain Order'
            }
            Test-CertificateChainOrder -pfxdata (Get-PFXHash -certInfo $AllCertificates['BadChainOrder']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
        It "Untrusted Chain Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['UntrustedChain'].ExpectedPrefix
                IncludeTests = 'Trusted Chain'
                ExcludeTests = 'CNG Key'
            }
            Test-TrustedChain -cert (Get-x509 -certInfo $AllCertificates['UntrustedChain']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Untrusted Chain Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['UntrustedChain'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Trusted Chain'
            }
            Test-TrustedChain -cert (Get-x509 -certInfo $AllCertificates['UntrustedChain']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
    }
    Context -Name "Other Certificates" {
        It "Other certificates present Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['OtherCertificates'].ExpectedPrefix
                IncludeTests = 'Other Certificates'
                ExcludeTests = 'CNG Key'
            }
            Test-OtherCertificates -pfxdata (Get-PFXHash -certInfo $AllCertificates['OtherCertificates']) -certConfig $certConfig -expectedFQDN $AllCertificates['othercertificates'].ExpectedFQDN | Select-Object -ExpandProperty Result | should be "WARNING"
        }
        It "Other certificates should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['OtherCertificates'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Other Certificates'
            }
            Test-OtherCertificates -pfxdata (Get-PFXHash -certInfo $AllCertificates['OtherCertificates']) -certConfig $certConfig -expectedFQDN $AllCertificates['othercertificates'].ExpectedFQDN | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
    }
    Context -Name "KeySize" {
        It "Key Size less than 2048 Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['BadKeySize'].ExpectedPrefix
                IncludeTests = 'Key Length'
                ExcludeTests = 'CNG Key'
            }
            Test-KeySize -cert (Get-x509 -certInfo $AllCertificates['BadKeySize']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Key Size Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['BadKeySize'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Key Length'
            }
            Test-KeySize -cert (Get-x509 -certInfo $AllCertificates['BadKeySize']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
        It "Key Size declared key size Should Fail with message" {
            $keylengthLimit = 4096
            $certConfig = @{
                DNSName = $AllCertificates['BadKeySize'].ExpectedPrefix
                IncludeTests = 'Key Length'
                ExcludeTests = 'CNG Key'
                KeyLength    = $keylengthLimit
            }
            $result = Test-KeySize -cert (Get-x509 -certInfo $AllCertificates['BadKeySize']) -certConfig $certConfig
            $result | Select-Object -ExpandProperty Result | should be "Fail"
            $result | Select-Object -ExpandProperty FailureDetail | should BeLike "*Key size should be $keylengthLimit or higher*"
        }
    }
    Context -Name "DNSName" {
        It "Missing DNS Name Should Fail" {
            $certConfig = @{
                DNSName = $AllCertificates['MissingDNSName'].ExpectedPrefix
                IncludeTests = 'DNS Names'
                ExcludeTests = 'CNG Key'
            }
            Test-DNSNames -cert (Get-x509 -certInfo $AllCertificates['MissingDNSName']) -certConfig $certConfig -ExpectedDomainFQDN  $AllCertificates['MissingDNSName'].ExpectedFQDN | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Missing DNS Name Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['ExpiredCert'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','DNS Names'
            }
            Test-DNSNames -cert (Get-x509 -certInfo $AllCertificates['MissingDNSName']) -certConfig $certConfig -ExpectedDomainFQDN  $AllCertificates['MissingDNSName'].ExpectedFQDN | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
    }
    Context -Name "Expiry" {
        It "Expired Certificate should fail" {
            $certConfig = @{
                DNSName = $AllCertificates['ExpiredCert'].ExpectedPrefix
                IncludeTests = 'Expiry Date'
                ExcludeTests = 'CNG Key'
            }
            Test-CertificateExpiry -cert (Get-x509 -certInfo $AllCertificates['ExpiredCert']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "Fail"
        }
        It "Expired Certificate Should SkipByConfig" {
            $certConfig = @{
                DNSName = $AllCertificates['ExpiredCert'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'CNG Key','Expiry Date'
            }
            Test-CertificateExpiry -cert (Get-x509 -certInfo $AllCertificates['ExpiredCert']) -certConfig $certConfig | Select-Object -ExpandProperty Result | should be "SkippedByConfig"
        }
    }
    Context -Name "CNG" {
        It "PaaS Certificates should fail CNG Keys" {
            $certConfig = @{
                DNSName = $AllCertificates['CngKey'].ExpectedPrefix
                IncludeTests = 'All'
                ExcludeTests = 'foo'
            }
            $cngResult = Test-PrivateKey -cert (Get-x509 -certinfo $AllCertificates['CngKey']) -IsPaaS
            $cngResult | Select-Object -ExpandProperty Result | should be "Fail"
            $cngResult | Select-Object -ExpandProperty FailureDetail | should match 'CNG Certificate detected, support for this certificate type may not currently be available'
        }
    }
    if ($GoodPfxEncryptionPath -and $badPfxEncryptionPath){
        Context -Name "PFXEncryption"{
            It "PFX Encryption Should fail"{
                Test-PfxEncryption -pfxfile $BadPfxEncryptionPath -pfxPassword $AllCertificates['MissingDNSName'].pfxPassword | Select-Object -ExpandProperty Result | should be "Warning"
            }

            It "PFX Encryption Should Pass"{
                Test-PfxEncryption -pfxfile $GoodPfxEncryptionPath -pfxPassword $AllCertificates['MissingDNSName'].pfxPassword | Select-Object -ExpandProperty Result | should be "OK"
            }
            $GLOBAL:filesForCleanUp += $BadPfxEncryptionPath
        }
    }
    Context -Name "PaaS Certificates" {
        BeforeAll {
            Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        }
        # Get PaaS Certificate data
        $AllPaaSCertificateNames = $testdata.AppServices.Keys
        $AllPaaSCertificates = $testdata.AppServices
        $keyvaultName = $testdata.pesterdata.ptkkvName
        Set-SecurityProtocol -securityProtocol $targetSecurityProtocol
        $certificateSecrets = Get-AzureKeyVaultSecret -VaultName $keyvaultName | Where-Object Name -in $AllPaaSCertificateNames
        Set-SecurityProtocol -securityProtocol $restoreSecProtocol
        $i = 1

        $pwd = New-RandomPassword

        $outputDir = "{0}\AppServicesPester" -f $env:TEMP
        Remove-Item $outputDir -Recurse -Force -ErrorAction SilentlyContinue -Verbose
        New-Item $outputDir -ItemType Directory -Force -Verbose

        foreach ($secret in $certificateSecrets) {
            [int]$percentageComplete = $i / $certificateSecrets.count * 100
            Write-Progress -Activity "Downloading Certificates" -Status "$percentageComplete% Complete:" -PercentComplete $percentageComplete -CurrentOperation $secret.Name
            $destFile = "{0}\AppServicesPester\{1}\{2}.pfx" -f $env:TEMP, $secret.Name.replace('AppServices',''), $secret.Name
            New-Item ("{0}\{1}" -f $outputDir,$secret.Name.replace('AppServices','')) -ItemType Directory -Force -Verbose
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName $secret.Name -outputPath $destFile -asSecret:$true
            $AllPaaSCertificates[$secret.name].pfxPath = $pfx.pfxPath
            $AllPaaSCertificates[$secret.name].pfxPassword = $pfx.pfxPassword
            $i++
            $GLOBAL:filesForCleanUp += $pfx.pfxPath
        }
        It "PaaS Certificates Should Pass" {
            $paasPassParam = @{
                CertificateType         = 'AppServices'
                certificatepath         = "$env:temp\AppServicesPester"
                pfxPassword             = (ConvertTo-SecureString $pwd -AsPlainText -Force)
                RegionName              = $regionName
                FQDN                    = $externalFQDN
                outputPath              = "$env:temp\AppServicesPester"
            }
            Invoke-AzsCertificateValidation @paasPassParam | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
        It "PaaS Certificates with JSON Should Pass" {
            $paasJSONPassParam = @{
                CertificateType          = 'AppServices'
                certificatepath          = "$env:temp\AppServicesPester"
                pfxPassword              = (ConvertTo-SecureString $pwd -AsPlainText -Force)
                DeploymentDataJSONPath   = $jsonPath
                outputPath               = "$env:temp\AppServicesPester"
            }
            Invoke-AzsCertificateValidation @paasJSONPassParam | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }

        $customOutputPath = "{0}\PaasPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
        $null = new-item $customOutputPath -ItemType Directory -Force
        $paasJSONReportParam = @{
            CertificateType          = 'AppServices'
            certificatepath          = "$env:temp\AppServicesPester"
            pfxPassword              = (ConvertTo-SecureString $pwd -AsPlainText -Force)
            DeploymentDataJSONPath   = $jsonPath
            outputPath               = $customOutputPath
            CleanReport              = $true
        }

        Invoke-AzsCertificateValidation @paasJSONReportParam
        [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        $reportJsonPath = Get-ChildItem $customOutputPath -Filter *.json | Select-Object -ExpandProperty Fullname
        $reportJSON = Get-Content $reportJsonPath | ConvertFrom-Json
        $GLOBAL:filesForCleanUp += $customOutputPath

        It "PaaS Certificates results should pass duplicate detection" {
            $reportJSON.CertificateValidation.AppServices.ReuseCount | Should Not BeGreaterThan 1
        }

        It "PaaS Certificates Report should have data" {
            $reportJSON.CertificateValidation.AppServices.Result | Should not match 'fail'
            #Have all tests
            $testNamesEx = 'Signature Algorithm|DNS Names|Key Usage|Key Length|Parse PFX|Private Key|Cert Chain|Chain Order|Other Certificates|Expiry Date'
            $reportJSON.CertificateValidation.AppServices.Test | Should match $testNamesEx
            # All results have data
            $certResults = $reportJSON.CertificateValidation.AppServices
            foreach ($certResult in $certResults) {
                $certResult.Result | Should Not BeNullOrEmpty
                if ($certResult.Test -ne 'Parse PFX') {
                    $certResult.FailureDetail | Should BeNullOrEmpty
                }
                $certResult.Test | Should Not BeNullOrEmpty
                $certResult.Path | Should Not BeNullOrEmpty
                $certResult.Thumbprint | Should Not BeNullOrEmpty
                $certResult.CertificateId | Should Not BeNullOrEmpty
                $certResult.ReuseCount | Should Not BeNullOrEmpty
            }
        }
    }
    Context -Name "Azure Stack Edge Certificates" {
        BeforeAll {
            Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        }
        # Get PaaS Certificate data
        $AllASECertificateNames = $testdata.AzureStackEdgeDevice.Keys
        $AllASECertificates = $testdata.AzureStackEdgeDevice
        $keyvaultName = $testdata.pesterdata.ptkkvName
        Set-SecurityProtocol -securityProtocol $targetSecurityProtocol
        $certificateSecrets = Get-AzureKeyVaultSecret -VaultName $keyvaultName | Where-Object Name -in $AllASECertificateNames
        Set-SecurityProtocol -securityProtocol $restoreSecProtocol
        $i = 1

        $pwd = New-RandomPassword

        $outputDir = "{0}\AzureStackEdgeDevicePester" -f $env:TEMP
        Remove-Item $outputDir -Recurse -Force -ErrorAction SilentlyContinue -Verbose
        New-Item $outputDir -ItemType Directory -Force -Verbose

        foreach ($secret in $certificateSecrets) {
            [int]$percentageComplete = $i / $certificateSecrets.count * 100
            Write-Progress -Activity "Downloading Certificates" -Status "$percentageComplete% Complete:" -PercentComplete $percentageComplete -CurrentOperation $secret.Name
            $destFile = "{0}\AzureStackEdgeDevicePester\{1}\{2}.pfx" -f $env:TEMP, $secret.Name.replace('ASE',''), $secret.Name
            New-Item ("{0}\{1}" -f $outputDir,$secret.Name.replace('ASE','')) -ItemType Directory -Force -Verbose
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName $secret.Name -outputPath $destFile -asSecret:$true
            $AllASECertificates[$secret.name].pfxPath = $pfx.pfxPath
            $AllASECertificates[$secret.name].pfxPassword = $pfx.pfxPassword
            $i++
            $GLOBAL:filesForCleanUp += $pfx.pfxPath
        }
        It "Azure Stack Edge Certificates Should Pass" {
            $ASEPassParam = @{
                CertificateType         = 'AzureStackEdgeDevice'
                certificatepath         = $outputDir
                DeviceName              = 'DBG-KARB2NP5J'
                NodeSerialNumber        = 'WIN-KARB2NP5J3O'
                pfxPassword             = (ConvertTo-SecureString $pwd -AsPlainText -Force)
                FQDN                    = 'edgedomain.com'
                outputPath              = $outputDir
            }
            Invoke-AzsCertificateValidation @ASEPassParam | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }

        $reportJsonPath = Get-ChildItem $outputDir -Filter *.json | Select-Object -ExpandProperty Fullname
        $reportJSON = Get-Content $reportJsonPath | ConvertFrom-Json
        $GLOBAL:filesForCleanUp += $outputDir

        It "Azure Stack Edge Certificates results should pass duplicate detection" {
            $reportJSON.CertificateValidation.AzureStackEdgeDevice.ReuseCount | Should Not BeGreaterThan 1
        }

        It "Azure Stack Edge Certificates Report should have data" {
            $reportJSON.CertificateValidation.AzureStackEdgeDevice.Result | Should not match 'fail'
            #Have all tests
            $testNamesEx = 'Signature Algorithm|DNS Names|Key Usage|Key Length|Parse PFX|Private Key|Cert Chain|Chain Order|Other Certificates|Expiry Date'
            $reportJSON.CertificateValidation.AzureStackEdgeDevice.Test | Should match $testNamesEx
            # All results have data
            $certResults = $reportJSON.CertificateValidation.AzureStackEdgeDevice
            foreach ($certResult in $certResults) {
                $certResult.Result | Should Not BeNullOrEmpty
                if ($certResult.Test -ne 'Parse PFX') {
                    $certResult.FailureDetail | Should BeNullOrEmpty
                }
                $certResult.Test | Should Not BeNullOrEmpty
                $certResult.Path | Should Not BeNullOrEmpty
                $certResult.Thumbprint | Should Not BeNullOrEmpty
                $certResult.CertificateId | Should Not BeNullOrEmpty
                $certResult.ReuseCount | Should Not BeNullOrEmpty
            }
        }
    }
    Context -Name "Deployment Certificates" {
        BeforeAll {
            Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        }
        $customOutputPath = "{0}\DeployCertPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
        $null = new-item $customOutputPath -ItemType Directory -Force
        $destFile = "$customOutputPath\dup.pfx"
        $pwd = New-RandomPassword
        $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName PerfectCert -outputPath $destFile -asSecret:$true
        $dirs = "ACSBlob", "ACSQueue", "ACSTable", "Admin Portal", "ARM Admin", "ARM Public", "KeyVault", "KeyVaultInternal", "Public Portal", "Admin Extension Host","Public Extension Host"
        $dirs | ForEach-Object {$dest = New-item "$customOutputPath\Certs\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }

        $deploymentJSONPassParam = @{
            CertificateType          = 'Deployment'
            certificatepath          = "$customOutputPath\Certs"
            pfxPassword              = $pfx.pfxPassword
            DeploymentDataJSONPath   = $jsonPath
            outputPath               = $customOutputPath
            CleanReport              = $true
        }
        Invoke-AzsCertificateValidation @deploymentJSONPassParam
        [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        $reportJsonPath = Get-ChildItem $customOutputPath -Filter *.json | Select-Object -ExpandProperty Fullname
        $reportJSON = Get-Content $reportJsonPath | ConvertFrom-Json
        $GLOBAL:filesForCleanUp += $customOutputPath

        It "Deployment Certificates results should fail duplicate detection" {
            $reportJSON.CertificateValidation.Deployment.ReuseCount | Should BeGreaterThan 1
        }
        It "Deployment Certificates Report should have data" {
            $reportJSON.CertificateValidation.Deployment.Result | Should not match 'fail'
            #Have all tests
            $testNamesEx = 'Signature Algorithm|DNS Names|Key Usage|Key Length|Parse PFX|Private Key|Cert Chain|Chain Order|Other Certificates|PFX Encryption|Expiry Date'
            $reportJSON.CertificateValidation.Deployment.Test | Should match $testNamesEx
            # All results have data
            $certResults = $reportJSON.CertificateValidation.Deployment
            foreach ($certResult in $certResults) {
                $certResult.Result | Should Not BeNullOrEmpty
                if ($certResult.Test -ne 'Parse PFX') {
                    $certResult.FailureDetail | Should BeNullOrEmpty
                }
                $certResult.Test | Should Not BeNullOrEmpty
                $certResult.Path | Should Not BeNullOrEmpty
                $certResult.Thumbprint | Should Not BeNullOrEmpty
                $certResult.CertificateId | Should Not BeNullOrEmpty
                $certResult.ReuseCount | Should Not BeNullOrEmpty
            }
        }
        It "Byte array of none pfx files should fail" {
            $tempInvalidFile = Join-Path $env:temp "$(New-Guid).txt"
            $randomRubbish = Get-Random -Minimum 1 -Maximum ([int]::MaxValue)
            $randomRubbish | out-file -FilePath $tempInvalidFile
            [byte[]]$invalidByteArray = Get-Content $tempInvalidFile -Encoding Byte
            $invalidPassword = ConvertTo-SecureString $randomRubbish -AsPlainText -Force
            $params = @{
                CertificateBinary       = $invalidByteArray
                CertificatePassword     = $invalidPassword
                ExpectedDomainFQDN      = 'certificate.file'
                WarnOnSelfSigned        = $true
                certConfig              = @{DNSName = 'invalid';pfxPath = $tempInvalidFile}
            }
            try {
                Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force -DisableNameChecking
                $null = Test-AzSCertificate @params
            }
            catch {
                $_.exception.message | should match "Unable to read certificate. Does not appear to be a valid certificate. Ensure the certificate is presented as a byte array of a valid pfx file."
            }
            $GLOBAL:filesForCleanUp += $tempInvalidFile
        }
        It "Custom Single Config Passes" {
            # Custom Config
            $dest = New-item "$customOutputPath\CustomSingle" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName
            $customCertificateConfiguration = @{'CustomSingle' = @{
                'DNSName' = '*.table'
                'EnhancedKeyUsage' = '1.3.6.1.5.5.7.3.2'
                }
            }

            $CustomSingleCertTestParams = @{
            'CertificateType' = 'Custom'
            'CustomCertConfig' = $customCertificateConfiguration
            'CertificatePath' = $dest
            'RegionName' = 'east'
            'ExternalFQDN' = 'azurestack.contoso.com'
            'pfxPassword' = $pfx.pfxpassword
            'cleanreport' = $true
            'outputPath' = "$customOutputPath\CustomGroup"
            }

            Invoke-AzsCertificateValidation @CustomSingleCertTestParams | Should be $null
        }
        It "Custom Group Config Passes" {
            $dirs = "Custom1", "Custom2"
            $dirs | ForEach-Object {$dest = New-item "$customOutputPath\CustomGroup\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }
            $customCertificateConfiguration = @{'Custom1' = @{
                'DNSName' = '*.table'
                'EnhancedKeyUsage' = '1.3.6.1.5.5.7.3.2'
                }
                'Custom2' = @{
                    'DNSName' = 'adminportal'
                    'KeyLength' = 2048
                    }
            }

            $CustomGroupCertTestParams = @{
            'CertificateType' = 'Custom'
            'CustomCertConfig' = $customCertificateConfiguration
            'CertificatePath' = "$customOutputPath\CustomGroup"
            'RegionName' = 'east'
            'ExternalFQDN' = 'azurestack.contoso.com'
            'pfxPassword' = $pfx.pfxpassword
            'cleanreport' = $true
            'outputPath' = "$customOutputPath\CustomGroup"
            }
            Invoke-AzsCertificateValidation @CustomGroupCertTestParams | Should be $null
        }
    }
    Context -Name "Deploy Repo" {
        BeforeAll {

            # Deployment
            $deploycerts = "C:\CloudDeployment\Setup\Certificates\"
            $null = new-item $deploycerts\AAD -ItemType Directory -Force
            $null = new-item $deploycerts\ADFS -ItemType Directory -Force
            $pwd = New-RandomPassword

            $destFile = "$deploycerts\dup.pfx"
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName PerfectCert -outputPath $destFile -asSecret:$true
            # AAD
            $dirs = "ACSBlob", "ACSQueue", "ACSTable", "Admin Portal", "ARM Admin", "ARM Public", "KeyVault", "KeyVaultInternal", "Public Portal", "Admin Extension Host","Public Extension Host"
            $dirs | ForEach-Object {$dest = New-item "$deploycerts\AAD\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }
            # ADFS
            $dirs = "ACSBlob", "ACSQueue", "ACSTable", "ADFS", "Admin Portal", "ARM Admin", "ARM Public", "Graph", "KeyVault", "KeyVaultInternal", "Public Portal", "Admin Extension Host","Public Extension Host"
            $dirs | ForEach-Object {$dest = New-item "$deploycerts\ADFS\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }

            $srcerts = "C:\SecretRotation\"
            $null = new-item $srcerts\AAD -ItemType Directory -Force
            $null = new-item $srcerts\ADFS -ItemType Directory -Force

            $destFile = "$srcerts\dup.pfx"
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName PerfectCert -outputPath $destFile -asSecret:$true
            $GLOBAL:standalone = $false
            # AAD
            $dirs = "ACSBlob", "ACSQueue", "ACSTable", "Admin Portal", "ARM Admin", "ARM Public", "KeyVault", "KeyVaultInternal", "Public Portal", "Admin Extension Host","Public Extension Host"
            $dirs | ForEach-Object {$dest = New-item "$srcerts\AAD\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }
            # ADFS
            $dirs = "ACSBlob", "ACSQueue", "ACSTable", "ADFS", "Admin Portal", "ARM Admin", "ARM Public", "Graph", "KeyVault", "KeyVaultInternal", "Public Portal", "Admin Extension Host","Public Extension Host"
            $dirs | ForEach-Object {$dest = New-item "$srcerts\ADFS\$PSITEM" -ItemType Directory -Force; Copy-Item $pfx.pfxPath $dest.FullName }

            $GLOBAL:filesForCleanUp += $deploycerts
            $GLOBAL:filesForCleanUp += $srcerts
        }
        It "Test-AzureStackCerts should succeed for AAD deployment" {
            Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force
            $azureStackCertsResult = Test-AzureStackCerts -ExpectedDomainFQDN east.azurestack.contoso.com -CertificatePassword $pfx.pfxpassword -UseADFS $false | Select-Object -ExpandProperty Result | Should match 'OK'
        }
        It "Test-AzureStackCerts should succeed for ADFS deployment" {
            Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force
            $azureStackCertsResult = Test-AzureStackCerts -ExpectedDomainFQDN east.azurestack.contoso.com -CertificatePassword $pfx.pfxpassword -UseADFS $true | Select-Object -ExpandProperty Result | Should match 'OK'
        }
        It "Test-AzureStackCerts should succeed for AAD Secret Rotation" {
            Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force
            $testCertsParams = @{
                CertificatePassword = $pfx.pfxpassword
                ExpectedDomainFQDN = 'east.azurestack.contoso.com'
                PfxFilesPath = $srcerts
                UseADFS = $false
            }
            $azureStackCertsResult = Test-AzureStackCerts @testCertsParams | Select-Object -ExpandProperty Result | Should match 'OK'
        }
        It "Test-AzureStackCerts should succeed for ADFS Secret Rotation" {
            Import-Module ..\CertificateValidation\PublicCertHelper.psm1 -Force
            $testCertsParams = @{
                CertificatePassword = $pfx.pfxpassword
                ExpectedDomainFQDN = 'east.azurestack.contoso.com'
                PfxFilesPath = $srcerts
                UseADFS = $true
            }
            $azureStackCertsResult = Test-AzureStackCerts @testCertsParams | Select-Object -ExpandProperty Result | Should match 'OK'
        }
    }
}    

Describe "Certificate Generation" {
    Context "Hardware Certificate Generation "{
        $command = get-command New-SelfSignedHardwareCertificate
        $CertificateTypes = $command.Parameters.CertificateType.Attributes.ValidValues
        foreach ($certificateType in $CertificateTypes) {
            It "$certificateType Should Generate Self Signed Certificate in all formats" {
                $customOutputPath = "{0}\hwcertPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
                $null = New-Item -Path $customOutputPath -ItemType Directory -Force
                $GLOBAL:filesForCleanUp += $customOutputPath
                $pfxPassword = ConvertTo-SecureString -String (New-RandomPassword) -AsPlainText -Force
                $newCertOutput = New-SelfSignedHardwareCertificate -CertificateType $certificateType -DnsRecord "NodeName-01" -pfxPassword $pfxPassword -OutputPath $customOutputPath
                Get-Item $newCertOutput | Should Not BeNullOrEmpty

                #check conversion to key/pem
                $passthrucert = New-SelfSignedHardwareCertificate -CertificateType $certificateType -DnsRecord "PassThru-01" -pfxPassword $pfxPassword -OutputPath $customOutputPath -passthru
                $pemDir = ConvertTo-PEM -certificate $passthrucert -path $customOutputPath
                Get-ChildItem $pemDir | where-object {$_.Name -eq "$($passThruCert.Thumbprint).pem"} | Should Not BeNullOrEmpty
                Get-ChildItem $pemDir | where-object {$_.Name -eq "$($passThruCert.Thumbprint).key"} | Should Not BeNullOrEmpty
            }
        }
    }
    Context "DistinguishedName Parameter Combinations" {
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        $command = get-command New-AzsCertificateSigningRequest
        $requestTypes = $command.Parameters.requestType.Attributes.ValidValues
        $IdentitySystems = $command.Parameters.IdentitySystem.Attributes.ValidValues
        $CertificateTypes = $command.Parameters.CertificateType.Attributes.ValidValues
        foreach ($certificateType in $CertificateTypes) {
            foreach ($IdentitySystem in $IdentitySystems) {
                foreach ($requestType in $requestTypes) {
                    $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')

                    $subject = 'OU=Azure Stack,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
                    $params = @{
                        CertificateType   = $CertificateType
                        RegionName        = 'east'
                        ExternalFQDN      = "$IdentitySystem.$requestType.$certificateType.com"
                        DistinguishedName = $subject
                        RequestType       = $requestType
                        IdentitySystem    = $IdentitySystem
                        OutputRequestPath = $customOutputPath
                        OutputPath        = $customOutputPath
                    }
                    $null = New-Item -Path $customOutputPath -ItemType Directory -Force
                    $GLOBAL:filesForCleanUp += $customOutputPath
                    It ("{0} {1} {2}" -f $CertificateType, $IdentitySystem, $requestType) {
                        try {
                            New-AzsCertificateSigningRequest @params | should be $null
                        }
                        catch {
                            $_ | should be $null
                        }
                    }
                }
            }
            if ($certificateType -eq 'Deployment') {
                It "User should use IdentitySystem with Deployment certificate requests" {
                    $params.Remove('IdentitySystem')
                    try {
                        New-AzsCertificateSigningRequest @params
                    }
                    catch {
                        $_ | should contain 'IdentitySystem not provided'
                    }
                }
            }
        }
    }
    Context "AzureStack Edge Request Generation" {
        It "AzureStack Edge Device MultipleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeDevice'
                DeviceName = 'DBG-KARB2NP5J'
                NodeSerialNumber = 'WIN-KARB2NP5J3O'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge Device SingleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeDevice'
                DeviceName = 'DBG-KARB2NP5J'
                NodeSerialNumber = 'WIN-KARB2NP5J3O'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
                RequestType = 'SingleCSR'
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge VPN MultipleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeVPN'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge VPN SingleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeVPN'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
                RequestType = 'SingleCSR'
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge Wifi Client MultipleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeWifiClient'
                NodeSerialNumber = 'WIN-KARB2NP5J3O'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge Wifi Client SingleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeWifiClient'
                NodeSerialNumber = 'WIN-KARB2NP5J3O'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
                RequestType = 'SingleCSR'
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge Wifi Server MultipleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeWifiServer'
                RadiusServerName = 'WIN-RadiusServer'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "AzureStack Edge Wifi Server SingleCSR Request should complete" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack Edge,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType = 'AzureStackEdgeWifiServer'
                RadiusServerName = 'WIN-RadiusServer'
                externalFQDN = 'AzureStackEdgeDevice.contoso.com'
                OutputRequestPath = "$ENV:USERPROFILE\Documents\AzsCertRequests"
                RequestType = 'SingleCSR'
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
    } 
    Context "Subject Variants" {
        It "Simple Subject - No common name" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Azure Stack,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType   = 'AppServices'
                RegionName        = 'noCN'
                ExternalFQDN      = 'subject.variants.com'
                DistinguishedName = $subject
                RequestType       = 'MultipleCSR'
                IdentitySystem    = 'AAD'
                OutputRequestPath = $customOutputPath
                OutputPath        = $customOutputPath
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "Double OUs" {
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'OU=Double,OU=OUs,O=HumongousInsurance,L=Pester-On-Puddle,ST=Pestershire,C=UK'
            $params = @{
                CertificateType   = 'AppServices'
                RegionName        = 'east'
                ExternalFQDN      = 'azurestack.HumongousInsurance.com'
                DistinguishedName = $subject
                RequestType       = 'MultipleCSR'
                IdentitySystem    = 'AAD'
                OutputRequestPath = $customOutputPath
                OutputPath        = $customOutputPath
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        It "Subject values contain commas, with flag" {
            Import-Module $PSScriptRoot\..\CertificateValidation\Microsoft.AzureStack.PublicCertificateRequest.psm1 -Force
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'CN=Comma Values with flag;OU="Azure,Stack";O=HumongousInsurance;L=Pester-On-Puddle;ST=Pestershire;C=UK'
            $params = @{
                CertificateType       = 'AppServices'
                RegionName            = 'commaflag'
                ExternalFQDN          = 'azurestack.HumongousInsurance.com'
                DistinguishedName     = $subject
                DistinguishedNameflag = [System.Security.Cryptography.X509Certificates.X500DistinguishedNameFlags]::UseSemiColons
                RequestType           = 'MultipleCSR'
                IdentitySystem        = 'AAD'
                OutputRequestPath     = $customOutputPath
                OutputPath            = $customOutputPath
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            try {
                New-AzsCertificateSigningRequest @params
            }
            catch {
                $_ | should be $null
            }
        }
        
        It "Subject values contain commas, with no flag should fail" { 
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = 'CN="Comma Values";OU="Azure Stack";O="Humongous,Insurance";L="Pester-On-Puddle";ST="Pestershire";C="UK"'
            $params = @{
                CertificateType   = 'AppServices'
                RegionName        = 'commanoflag'
                ExternalFQDN      = 'azurestack.HumongousInsurance.com'
                DistinguishedName = $subject
                RequestType       = 'MultipleCSR'
                IdentitySystem    = 'AAD'
                OutputRequestPath = $customOutputPath
                OutputPath        = $customOutputPath
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            New-AzsCertificateSigningRequest @params
            Get-ChildItem -Path $customOutputPath -filter '*.req' | should be $null 
        }        
    }
    Context "Attribute Handling" {
        $KeyLengths = $command.Parameters.KeyLength.Attributes.ValidValues
        Foreach ($KeyLength in $KeyLengths) {
            It "KeyLength $KeyLength should be requested" { 
                $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
                $subject = "CN=KeyLength $KeyLength,OU=Azure Stack,O=Humongous Insurance,L=Pester-On-Puddle,ST=Pestershire,C=UK"
                $params = @{
                    CertificateType   = 'AppServices'
                    RegionName        = 'keylength'
                    ExternalFQDN      = 'azurestack.HumongousInsurance.com'
                    DistinguishedName = $subject
                    RequestType       = 'MultipleCSR'
                    IdentitySystem    = 'AAD'
                    OutputRequestPath = $customOutputPath
                    OutputPath        = $customOutputPath
                    KeyLength         = $KeyLength
                }
                $null = New-Item -Path $customOutputPath -ItemType Directory -Force
                $GLOBAL:filesForCleanUp += $customOutputPath
                New-AzsCertificateSigningRequest @params
            
                $infFiles = Get-ChildItem -Path $customOutputPath -filter '*.inf' -Recurse
                foreach ($infFile in $infFiles.fullname) {
                    get-content "$infFile" | Select-String -Pattern "KeyLength = $KeyLength" | should be $true
                }
            }
        }
        $HashAlgorithms = 'SHA256', 'SHA384', 'SHA512'
        Foreach ($HashAlgorithm in $HashAlgorithms) {
            It "HashAlgorithm $HashAlgorithm should be requested" { 
                $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
                $subject = "CN=HashAlgorithm $HashAlgorithm,OU=Azure Stack,O=Humongous Insurance,L=Pester-On-Puddle,ST=Pestershire,C=UK"
                $params = @{
                    CertificateType   = 'AppServices'
                    RegionName        = 'HashAlgorithm'
                    ExternalFQDN      = 'azurestack.HumongousInsurance.com'
                    DistinguishedName = $subject
                    RequestType       = 'MultipleCSR'
                    IdentitySystem    = 'AAD'
                    OutputRequestPath = $customOutputPath
                    OutputPath        = $customOutputPath
                    HashAlgorithm     = $HashAlgorithm
                }
                $null = New-Item -Path $customOutputPath -ItemType Directory -Force
                $GLOBAL:filesForCleanUp += $customOutputPath
                New-AzsCertificateSigningRequest @params
            
                $infFiles = Get-ChildItem -Path $customOutputPath -filter '*.inf' -Recurse
                foreach ($infFile in $infFiles.fullname) {
                    get-content "$infFile" | Select-String -Pattern "HashAlgorithm = $HashAlgorithm" | should be $true
                }
            }
        }
        It "Enhanced Key Usage should be requested" { 
            $customOutputPath = "{0}\csrPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
            $subject = "CN=Enhanced Key Usage,OU=Azure Stack,O=Humongous Insurance,L=Pester-On-Puddle,ST=Pestershire,C=UK"
            $params = @{
                CertificateType   = 'AppServices'
                RegionName        = 'EnhancedKeyUsage'
                ExternalFQDN      = 'azurestack.HumongousInsurance.com'
                DistinguishedName = $subject
                RequestType       = 'MultipleCSR'
                IdentitySystem    = 'AAD'
                OutputRequestPath = $customOutputPath
                OutputPath        = $customOutputPath
                EnhancedKeyUsage  = @('Server Authentication',@{'Custom Usage' = '1.3.6.1.5.7.8.2.1'})
            }
            $null = New-Item -Path $customOutputPath -ItemType Directory -Force
            $GLOBAL:filesForCleanUp += $customOutputPath
            New-AzsCertificateSigningRequest @params
        
            $infFiles = Get-ChildItem -Path $customOutputPath -filter '*.inf' -Recurse
            foreach ($infFile in $infFiles.fullname) {
                get-content "$infFile" | Select-String -Pattern "%szOID_Custom_Usage%" | should be $true
                get-content "$infFile" | Select-String -Pattern 'szOID_Custom_Usage = "1.3.6.1.5.7.8.2.1"' | should be $true
            }
        }
    }
    AfterAll {
        $GLOBAL:filesForCleanUp += Get-ChildItem -Path Cert:\LocalMachine\REQUEST | Where-Object { $_.Subject -match 'Pester-On-Puddle' } 
    }
}

Describe -Name "Certificate Repair" {
    BeforeAll{
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        $regionName = $deploymentDataJSON.DeploymentData.RegionName
        $externalFQDN = $deploymentDataJSON.DeploymentData.ExternalDomainFQDN
    }
    Context "Certificate ImportExport" {
        It "Certificate Import/Export Should Pass" {
            $certName = 'BadPrivateKey'
            $destfile = "$ENV:TEMP\ImportExportPreCert.pfx"
            $pwd = New-RandomPassword
            $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName $certName -outputPath $destFile -asSecret:$true
            $exportPFXPath = "$ENV:TEMP\ImportExportPesterCertificate.pfx"
            Repair-AzsPfxCertificate -pfxPath $pfx.pfxPath -pfxPassword $pfx.pfxPassword -exportPFXPath $exportPFXPath | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
            $GLOBAL:filesForCleanUp += $exportPFXPath
            $GLOBAL:filesForCleanUp += $destfile
        }
    }
}
Describe -Name "Azure Registration"{
    BeforeAll{
        Connect-Azure
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        $registrationUsername = $testdata.Registration.AccountName
        $registrationPassword = $testdata.Registration.Password
        $registrationSubscriptionName = $testdata.Registration.RegistrationSubscriptionID
        $AzureEnvironment = $testdata.Registration.AzureEnvironment
        $registrationSubscriptionID = Get-PlainTextSecret -VaultName $keyvaultName -secretName $registrationSubscriptionName
        $registrationCredential = Get-CredentialSecret -VaultName $keyvaultName -username $registrationUsername -password $registrationPassword
    }
    Context -Name "Azure Registration" {
        It "Azure Registration with JSON Should Pass" {
            Invoke-AzsRegistrationValidation -RegistrationAccount $registrationCredential -deploymentDataJSONPath $jsonPath -RegistrationSubscriptionID $registrationSubscriptionID -outputPath "$ENV:TEMP\PesterRegChecks" -CleanReport  | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
            $json = Get-Content $ENV:TEMP\PesterRegChecks\AzsReadinessCheckerReport.json | ConvertFrom-Json
            $json.AzureValidation.AzureRegistration.Result | Should Be 'OK'
            $json.AzureValidation.AzureRegistration.Assets | Should Not BeNullOrEmpty
            $json.AzureValidation.AzureRegistration.Test | Should Be 'RegistrationAccount'
            $json.AzureValidation.AzureRegistration.ErrorDetails | Should BeNullOrEmpty
            $json.AzureValidation.AzureRegistration.SubscriptionDetail | Should Not BeNullOrEmpty
            $json.AzureValidation.AzureRegistration.SubscriptionDetail.subscription | Should Not BeNullOrEmpty
            $json.AzureValidation.AzureRegistration.SubscriptionDetail.subscription.subscriptionPolicies | Should Not BeNullOrEmpty
        }

        It "Azure Registration Check Should Pass" {
            Invoke-AzsRegistrationValidation -RegistrationAccount $registrationCredential -AzureEnvironment $AzureEnvironment -RegistrationSubscriptionID $registrationSubscriptionID -CleanReport | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }

        It "Azure Registration Check with custom AzureEnvironment Should Pass" {
            Invoke-AzsRegistrationValidation -RegistrationAccount $registrationCredential -CustomCloudARMEndpoint $CustomAzureEnvironmentUri -RegistrationSubscriptionID $registrationSubscriptionID  -CleanReport | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
    }
    AfterAll{
        $GLOBAL:filesForCleanUp += "$ENV:TEMP\PesterRegChecks"
    }
}
Describe -Name "Azure Identity" {
    BeforeAll {
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        Connect-Azure

        foreach ($key in $testdata.Identity.Keys) {
            Write-Verbose ("$key : Username: {0} password: {1}" -f $testData.Identity[$key].AccountName, $testData.Identity[$key].Password)
            $testData.Identity[$key].Credential = Get-CredentialSecret `
                -VaultName $keyvaultName `
                -username $testData.Identity[$key].AccountName `
                -password $testData.Identity[$key].Password
        }
    }
    Context -Name "Azure Identity" {
        $Account = $testdata.Identity.GlobalAdmin
        It "Azure Identity with JSON Should Pass" {
            Invoke-AzsAzureIdentityValidation -AADServiceAdministrator $Account.Credential -deploymentDataJSONPath $jsonPath -CleanReport -outputPath "$ENV:TEMP\PesterIdentityChecks"  | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
            $json = Get-Content $ENV:TEMP\PesterIdentityChecks\AzsReadinessCheckerReport.json | ConvertFrom-Json
            $json.AzureValidation.AzureInstallResult.Result | Should Be 'OK'
            $json.AzureValidation.AzureInstallResult.Assets | Should Not BeNullOrEmpty
            $json.AzureValidation.AzureInstallResult.Test | Should Be 'ServiceAdministrator'
            $json.AzureValidation.AzureInstallResult.ErrorDetails | Should BeNullOrEmpty
        }
        $GLOBAL:filesForCleanUp += "$ENV:TEMP\PesterIdentityChecks"
        It "Azure Identity Should Pass" {
            Invoke-AzsAzureIdentityValidation -AADServiceAdministrator $Account.Credential -AzureEnvironment AzureCloud -AADDirectoryTenantName $Account.DirectoryName | Should Be $null
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
    }
    Context -Name "Azure Identity Units" {
        Set-SecurityProtocol -securityProtocol $targetSecurityProtocol
        Import-Module ..\AzureValidation\Microsoft.AzureStack.AzureValidation.Internal.psm1 -Force -DisableNameChecking
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.Reporting.psm1 -Force
        It "CustomAzureEnvironment should success" {
            $Account = $testdata.Identity.GlobalAdmin
            # injecting global variable for custom config file, which we usually be supplied by the user
            $global:CustomAzureEnvironmentJSON = $CustomAzureEnvironmentUri
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment CustomCloud -CustomCloudARMEndpoint $CustomAzureEnvironmentUri
            $result.result | should be "OK"
            $result.errorDetails | should be $null
        }
        It "AzureChinaCloud should fail" {
            $Account = $testdata.Identity.GlobalAdmin
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureChinaCloud
            $result.result | should be "Fail"
            $result.errorDetails | should match 'Unknown user type detected'
        }
        It "AzureGermanCloud should fail" {
            $Account = $testdata.Identity.GlobalAdmin
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureGermanCloud
            $result.result | should be "Fail"
            $result.errorDetails | should match 'Unknown user type detected'
        }
        It "AzureUSGovernment should succeed" {
            $Account = $testdata.Identity.GlobalAdmin
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureUSGovernment
            $result.result | should be "OK"
            $result.errorDetails | should be $null
        }
        It "AzureCloud should succeed" {
            $Account = $testdata.Identity.GlobalAdmin
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureCloud
            $result.result | should be "OK"
            $result.errorDetails | should be $null
        }
        It "Live Accounts should succeed" {
            $Account = $testdata.Identity.Live
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.Credential.Username.split('@')[1] -AzureEnvironment AzureCloud
            $result.result | should be "OK"
            $result.errorDetails | should be $null
        }
        It "Disabled Accounts should Fail" {
            $Account = $testdata.Identity.Disabled
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureCloud
            $result.result | should be "Fail"
            $result.errorDetails | should match 'User account is disabled'
        }
        It "Temp Password Accounts should Fail" {
            $Account = $testdata.Identity.TempPassword
            $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureCloud
            $result.result | should be "Fail"
            $result.errorDetails | should match 'expired or is a temporary password that needs to be reset before continuing'
        }
        if ($TestMFA) {
            It "MFA Accounts should succeed" {
                $Account = $testdata.Identity.MFA
                $result = Test-AzsServiceAdministrator -AADServiceAdministrator $Account.Credential -AADDirectoryTenantName $Account.DirectoryName -AzureEnvironment AzureCloud
                $result.result | should be "OK"
                $result.errorDetails | should be $null
            }
        }
        else {
            Write-Warning -Message "MFA testing was skipped."
        }
    }
    AfterAll{
        Set-SecurityProtocol -securityProtocol $RestoreSecProtocol
        Get-Module Microsoft.AzureStack.AzureValidation.Internal.psm1 | Remove-Module
    }
}
Describe -Name "Password Tests" {
    #Connect to Azure
    BeforeAll{
        Connect-Azure
        Import-Module ..\Microsoft.AzureStack.ReadinessChecker.psd1 -Force
        $BadPasswordLength = $testdata.passwords.BadPasswordLength
        $BadPasswordComplexity = $testdata.passwords.BadPasswordComplexity
        $BadPasswordLengthSecret = ConvertTo-SecureString -string (Get-PlainTextSecret -VaultName $keyvaultName -secretName $BadPasswordLength) -AsPlainText -Force
        $BadPasswordComplexitySecret = ConvertTo-SecureString -string (Get-PlainTextSecret -VaultName $keyvaultName -secretName $BadPasswordComplexity) -AsPlainText -Force
        $customOutputPath = "{0}\DeployCertPester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
        $null = new-item $customOutputPath -ItemType Directory -Force
        $destFile = "$customOutputPath\badpassword.pfx"
        $pwd = New-RandomPassword
        $pfx = Save-PFXData -pwd $pwd -VaultName $keyvaultName -CertName PerfectCert -outputPath $destFile -asSecret:$true
    }
    Context -Name "PfxPassword Parameter" {
        It "Parameter PfxPassword should fail bad Password Length" {
            try {
                Repair-AzsPfxCertificate -pfxPath $pfx.pfxPath -pfxPassword $BadPasswordLengthSecret -exportPFXPath $env:TEMP\shouldneverexist.pfx
            }
            catch {
                $_.exception.message | should match "Password length cannot be fewer than"
            }
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
        It "Parameter PfxPassword should fail bad Password Complexity" {
            try {
                Repair-AzsPfxCertificate -pfxPath $pfx.pfxPath -pfxPassword $BadPasswordComplexitySecret -exportPFXPath $env:TEMP\shouldneverexist.pfx
            }
            catch {
                $_.exception.message | should match "Password does not meet complexity requirements"
            }
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
    }
    Context -Name "PaaSCertificates" {
        #$PaaSCertBadPassLgth = @{'PaaSDBCert' = @{'pfxPath' = $pfx.pfxPath; 'pfxPassword' = $BadPasswordLengthSecret}}
        $PaaSCertBadPassLgth = @{
            CertificateType         = 'AppServices'
            certificatepath         = "$env:temp\AppServicesPester"
            pfxPassword             = $BadPasswordLengthSecret
            RegionName              = $regionName
            FQDN                    = $externalFQDN
            outputPath              = "$env:temp\AppServicesPester"
        }

        It "Parameter PaaSCertificates should fail bad Password Length" {
            try {
                Invoke-AzsCertificateValidation @PaaSCertBadPassLgth
            }
            catch {
                $_.exception.message | should match "Password length cannot be fewer than"
            }
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
        #$PaaSCertBadPassCplx = @{'PaaSDBCert' = @{'pfxPath' = $pfx.pfxPath; 'pfxPassword' = $BadPasswordComplexitySecret}}
        $PaaSCertBadPassCplx = @{
            CertificateType         = 'AppServices'
            certificatepath         = "$env:temp\AppServicesPester"
            pfxPassword             = $BadPasswordComplexitySecret
            RegionName              = 'east'
            FQDN                    = 'azurestack.contoso.com'
            outputPath              = "$env:temp\AppServicesPester"
        }
        It "Parameter PaaSCertificates should fail bad Password Complexity" {
            try {
                Invoke-AzsCertificateValidation @PaaSCertBadPassCplx
            }
            catch {
                $_.exception.message | should match "Password does not meet complexity requirements."
            }
            [Net.ServicePointManager]::SecurityProtocol | Should be $RestoreSecProtocol
        }
    }
    AfterAll{
        $GLOBAL:filesForCleanUp += $customOutputPath
        $GLOBAL:filesForCleanUp += "$env:temp\AppServicesPester"
        Get-Module Microsoft.AzureStack.ReadinessChecker | Remove-Module
        Connect-Azure
    }
}
Describe -Name "Certificate Folders" {
    BeforeAll {
        Import-Module $PSScriptRoot\..\Microsoft.AzureStack.ReadinessChecker.psd1 -force
        Import-Module $PSScriptRoot\..\CertificateValidation\Microsoft.AzureStack.CertificateValidation.psm1 -force
    }
    Context "Cetificate Folder Structure"{
        $command = get-command New-AzsCertificateFolder
        $CertificateTypes = $command.Parameters.CertificateType.Attributes.ValidValues
        $certificateConfigDataFile = Import-PowerShellDataFile -Path $PSScriptRoot\..\CertificateValidation\Microsoft.AzureStack.CertificateConfig.psd1
        foreach ($certificateType in $CertificateTypes) {
            $certificateConfig = $certificateConfigDataFile.CertificateTypes[$CertificateType]
            It "$certificateType Should Generate Correct Folder structure" {
                $customOutputPath = "{0}\FolderStructurePester{1}" -f $ENV:TEMP, (Get-Date -f 'yyyyMMddHHmmss')
                $null = New-Item -Path $customOutputPath -ItemType Directory -Force
                $GLOBAL:filesForCleanUp += $customOutputPath
                $CertFolderOutput = New-AzsCertificateFolder -certificateType $certificateType -IdentitySystem ADFS -OutputPath $customOutputPath
                #$CertFolderOutput | Should be $true
                $CertFolderOutput | ForEach-Object {New-Item -Path $PSITEM.FullName -Name 'pester.pfx' -ItemType File}
                try {
                    Test-AzsCertificatePlacement -UseADFS:$false -certificatePath (Join-Path $customOutputPath $certificateType) -certConfig $certificateConfig
                }
                catch {
                    $TestPlacementError = $_
                }
                $TestPlacementError | Should Be $null
            }
        }
    }
}
if (-not $keepLogs) {
    Write-Verbose ("Removing {0}" -f ($filesForCleanUp -join ',')) -Verbose
    Remove-TestFiles -in "$ENV:TEMP\AzsReadinessChecker"
    Remove-TestFiles -in $filesForCleanUp
    Get-ChildItem ..\..\AzsReadinessChecker -Recurse | Where-Object extension -in '.log', '.xml', '.json' | Remove-Item -Force
}
else {
    Write-Verbose "Log clean up skipped for troubleshooting, to remove logs run the following" -Verbose
}
# SIG # Begin signature block
# MIIjigYJKoZIhvcNAQcCoIIjezCCI3cCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDoE6c3yxTMlDcL
# KXopklkzY0Ieu4ouInmOO3oNIfxUeaCCDYUwggYDMIID66ADAgECAhMzAAABUptA
# n1BWmXWIAAAAAAFSMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTkwNTAyMjEzNzQ2WhcNMjAwNTAyMjEzNzQ2WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCxp4nT9qfu9O10iJyewYXHlN+WEh79Noor9nhM6enUNbCbhX9vS+8c/3eIVazS
# YnVBTqLzW7xWN1bCcItDbsEzKEE2BswSun7J9xCaLwcGHKFr+qWUlz7hh9RcmjYS
# kOGNybOfrgj3sm0DStoK8ljwEyUVeRfMHx9E/7Ca/OEq2cXBT3L0fVnlEkfal310
# EFCLDo2BrE35NGRjG+/nnZiqKqEh5lWNk33JV8/I0fIcUKrLEmUGrv0CgC7w2cjm
# bBhBIJ+0KzSnSWingXol/3iUdBBy4QQNH767kYGunJeY08RjHMIgjJCdAoEM+2mX
# v1phaV7j+M3dNzZ/cdsz3oDfAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU3f8Aw1sW72WcJ2bo/QSYGzVrRYcw
# VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh
# dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1NDEzNjAfBgNVHSMEGDAW
# gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v
# d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw
# MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx
# XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB
# AJTwROaHvogXgixWjyjvLfiRgqI2QK8GoG23eqAgNjX7V/WdUWBbs0aIC3k49cd0
# zdq+JJImixcX6UOTpz2LZPFSh23l0/Mo35wG7JXUxgO0U+5drbQht5xoMl1n7/TQ
# 4iKcmAYSAPxTq5lFnoV2+fAeljVA7O43szjs7LR09D0wFHwzZco/iE8Hlakl23ZT
# 7FnB5AfU2hwfv87y3q3a5qFiugSykILpK0/vqnlEVB0KAdQVzYULQ/U4eFEjnis3
# Js9UrAvtIhIs26445Rj3UP6U4GgOjgQonlRA+mDlsh78wFSGbASIvK+fkONUhvj8
# B8ZHNn4TFfnct+a0ZueY4f6aRPxr8beNSUKn7QW/FQmn422bE7KfnqWncsH7vbNh
# G929prVHPsaa7J22i9wyHj7m0oATXJ+YjfyoEAtd5/NyIYaE4Uu0j1EhuYUo5VaJ
# JnMaTER0qX8+/YZRWrFN/heps41XNVjiAawpbAa0fUa3R9RNBjPiBnM0gvNPorM4
# dsV2VJ8GluIQOrJlOvuCrOYDGirGnadOmQ21wPBoGFCWpK56PxzliKsy5NNmAXcE
# x7Qb9vUjY1WlYtrdwOXTpxN4slzIht69BaZlLIjLVWwqIfuNrhHKNDM9K+v7vgrI
# bf7l5/665g0gjQCDCN6Q5sxuttTAEKtJeS/pkpI+DbZ/MIIHejCCBWKgAwIBAgIK
# YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm
# aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw
# OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD
# VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la
# UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc
# 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D
# dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+
# lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk
# kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6
# A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd
# X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL
# 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd
# sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3
# T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS
# 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI
# bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL
# BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD
# uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv
# c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf
# MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF
# BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h
# cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA
# YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn
# 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7
# v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b
# pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/
# KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy
# CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp
# mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi
# hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb
# BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS
# oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL
# gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX
# cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFVswghVXAgEBMIGVMH4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p
# Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAFSm0CfUFaZdYgAAAAA
# AVIwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw
# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJHW
# rG9fi4NDdb3pPuY5SdZdPQE7RH0JF1uw6eJ9GFeAMEIGCisGAQQBgjcCAQwxNDAy
# oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20wDQYJKoZIhvcNAQEBBQAEggEAN+Knp0LAafLWwmJu0va37pw1A2g6JKlF63xe
# cxBSfQ+Pf0ObG/SzviGHBdFa0SUo3+SDdCvpGoyegV/TsVlDM9E+CEq3orbz2nPl
# bYc2D/i9i7HDOGF4AYo86FEu5hQC8RBm0CuPIq2ue8QXgI+hVFI2/m9Izf6SGfE7
# KVaUxgNy2jo7lhc0/uz98s2hOqyuM3pEopRYS2NZLe20xEA9o7raRmtx2rP5v4Ul
# vOVFs15dq6Rl13I3e0MSsGfwV1abUbB+1Ppikpmrmshv7oEeEzCbJbRSOP/JV/Kc
# 7hBvN5NB1D+V3KwGph6UGmuWVoYY0tVh3W/5nchy/Ms3rBi4zaGCEuUwghLhBgor
# BgEEAYI3AwMBMYIS0TCCEs0GCSqGSIb3DQEHAqCCEr4wghK6AgEDMQ8wDQYJYIZI
# AWUDBAIBBQAwggFRBgsqhkiG9w0BCRABBKCCAUAEggE8MIIBOAIBAQYKKwYBBAGE
# WQoDATAxMA0GCWCGSAFlAwQCAQUABCAkUHCNraro2ZA4cZe5TYDeEAMA4JvoKqDF
# U07SktknDgIGXioHxEEDGBMyMDIwMDIxMjIyMTAxMy42OTRaMASAAgH0oIHQpIHN
# MIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL
# ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjpBRTJDLUUzMkItMUFGQzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDjwwggTxMIID2aADAgECAhMzAAABFpMi6r+7LU3mAAAA
# AAEWMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTExMzIxNDAzNFoXDTIxMDIxMTIxNDAzNFowgcoxCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVy
# aWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOkFFMkMtRTMy
# Qi0xQUZDMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIB
# IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Pgb8/296ie/Lj2rWq+MIlMZ
# wkSUwZsIKd472tyeVOyNcKgqSCT4zQvz2kd+VD7lYWN3V0USL5oipdp+xp7wH7CA
# HC7zNU21PjdHWPOi2okIlPyTikrQBowo+MOV9Xgd3WqMnJSKEank7QmSHgJimJ2q
# /ZRR5+0Z5uZRejJHkQcJmTB8Gq/wg2E/gjuRl/iGa4fGJu0cHSUiX78m5FEyaac1
# XnkqafSqYR8qb7sn3ZVt/ltbiGUJr874oi2bZduUtCMR0QiWWfBMExcLV4A6ermC
# 98cbbvi/pQb1p1l7vXT2NReD+xkFqzKn0cA3Vi9cc5LjDhY91L18RuHIgU3qHQID
# AQABo4IBGzCCARcwHQYDVR0OBBYEFOW/Xiu4F+gXzUflH3k0/lfIIVULMB8GA1Ud
# IwQYMBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENB
# XzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUH
# AwgwDQYJKoZIhvcNAQELBQADggEBADaDatfaqaPbAy/pSdK8e8XdzN6v9979NSWL
# UsNHoNBFpyr1FTGcvwf0SKIfe0ygt8s8plkAYxMUftUmOnO+OnGXUgTOreXIw4zt
# sepotreHcL094+bn7OUGLPMa56GQii3WUgiGPP0gfNXhXcqSdd9HmXjMhKfRn0jO
# KREJTPqPHLXSxcA1SVTrg8JDtkD+yWVzuuAkSopTGxtJp5PcrYUrMb7nW1coIe7t
# sQiSPp6xFVzKfXFUJ9VzAChucE+8pqXLpV/xU3p/1vf0DgLZMpI22mwAgbe/E6wg
# yDSKyHXI4UsiIlSYASv+IlKOtcXzrXV0IRQUdRyIC1ZiWWL/YggwggZxMIIEWaAD
# AgECAgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzET
# MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV
# TWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBD
# ZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3
# MDEyMTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWl
# CgCChfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/Fg
# iIRUQwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeR
# X4FUsc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/Xcf
# PfBXday9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogI
# Neh4HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB
# 5jCCAeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvF
# M2hahW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAP
# BgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjE
# MFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv
# Y3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEF
# BQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2kvY2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8E
# gZUwgZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcC
# AjA0HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUA
# bgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Pr
# psz1Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOM
# zPRgEop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCv
# OA8X9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v
# /rbljjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99
# lmqQeKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1kl
# D3ouOVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQ
# Hm+98eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30
# uIUBHoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp
# 25ayp0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HS
# xVXjad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi6
# 2jbb01+P3nSISRKhggLOMIICNwIBATCB+KGB0KSBzTCByjELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp
# Y2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046QUUyQy1FMzJC
# LTFBRkMxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoB
# ATAHBgUrDgMCGgMVAIdNW9zyT6CLG1qCDNc++szs3ZZDoIGDMIGApH4wfDELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQEFBQACBQDh7uQAMCIY
# DzIwMjAwMjEzMDQ1MjE2WhgPMjAyMDAyMTQwNDUyMTZaMHcwPQYKKwYBBAGEWQoE
# ATEvMC0wCgIFAOHu5AACAQAwCgIBAAICFskCAf8wBwIBAAICEsQwCgIFAOHwNYAC
# AQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEK
# MAgCAQACAwGGoDANBgkqhkiG9w0BAQUFAAOBgQBXj5lDfJGsYt9IEhaxj3jtB/C4
# zGk4+/nSmi1s/DktHFLd9FEWO3v2VcCqYXs15VvQkd1wGv8bjQroGa6W6l7ZIlRO
# 5l1m9YAFjL1xoNnThzJKUk0fM4EMro0ebkkmCisBAabyf6RVPIFVcMs5vLGym3Rm
# k+lDHQlc1eSzuy0jkjGCAw0wggMJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwAhMzAAABFpMi6r+7LU3mAAAAAAEWMA0GCWCGSAFlAwQCAQUAoIIB
# SjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIM4K
# e11ONKvs8hp+rtEMjYvhXgFewZ2sLARvO3Mq6aUGMIH6BgsqhkiG9w0BCRACLzGB
# 6jCB5zCB5DCBvQQggyKU9qRgKQiXXCmbITbdtLENhYxqIMhBaM+iXtLBkMowgZgw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAARaTIuq/uy1N
# 5gAAAAABFjAiBCDKk0wBInYz+a+1/B/VLAinR118bkU6wbb+lzGbmzqhTTANBgkq
# hkiG9w0BAQsFAASCAQAbB6Zaxz9uiLAn6v1Yn7a6cxQfyrWZx6MUK9vldKP3uw9U
# JxpyCSgXsoUgff03LQyBVzXPS/io+ut+aY+PQKK+Ubsy4h3jCsrzuxOsHx8N7/+u
# c8y5N1RC2Qd1pcr2RjNd551YvUuHq3fXfMeaNqr2ZEDAjE4hc+tv9HOib5eoZ/1s
# Oo33/QwG8E8XO8mo4r/clJQ5BtRGRcCTYTTlWE6o9yQVhC6m40wJERVFtuIwnmn1
# l4Jsfq+hzuydZXpI9Cfte/0rqLTm0GM04l/ADVgv8GqFuu9ctjVvjT7cAvVfJMHO
# ScN7wC8Cx5z4e/CIX9OqKUaAkylsDHzh6QhguiBN
# SIG # End signature block