AzureAppGWMigration.ps1

<#PSScriptInfo
 
.VERSION 1.0.1
 
.GUID be3b84b4-e9c5-46fb-a050-699c68e16119
 
.AUTHOR Microsoft Corporation
 
.COMPANYNAME Microsoft Corporation
 
.COPYRIGHT Microsoft Corporation. All rights reserved.
 
.TAGS Azure, Az, ApplicationGateway, AzNetworking
 
#>


<#
 
.SYNOPSIS
AppGateway v1 -> v2 migration
 
.DESCRIPTION
This script will help you create a Standard_v2 sku app gateway with the same configuration as your Standard sku gateway.
 
.PARAMETER ResourceId
Application Gateway ResourceId, like "/subscriptions/<your-sub-id>/resourceGroups/<your-rg>/providers/Microsoft.Network/applicationGateways/<v1AppGatewayName>"
.PARAMETER SubnetAddressRange
The subnet address in CIDR notation, where you want to deploy v2 application gateway (Make sure the subnet is empty or contains only application gateway standard_v2/waf_v2 sku resources).
.PARAMETER AppGwName
Name of v2 app gateway, default will be <v1-app-gw-name>_v2
.PARAMETER SslCertificates
Comma seperated list of Ssl certificate to be attached to app gateway listeners (set using New-AzApplicationGatewaySSLCertificate command).
Note: Passing reference to all ssl certs used in v1 gateway is required to get same configuration in v2 app gateway
.PARAMETER TrustedRootCertificates
Comma seperated list of trusted root certificates (set using New-AzApplicationGatewayTrustedRootCertificate command). For more details refer https://aka.ms/appgwmigrationdoc
.PARAMETER PrivateIpAddress
Private Ip address to be assigned to v2 app gateway.
.PARAMETER ValidateMigration
Post migration validation by comparing ApplicationGatewayBackendHealth response.
.PARAMETER PublicIpResourceId
Public Ip Address resourceId (if already exists) can be attached to application gateway. If no input is given script will create a public ip resource for you in the same resource group
.PARAMETER EnableAutoscale
Enable autoscale configuration for app gateway v2 instances
 
.EXAMPLE
$password = ConvertTo-SecureString <your-password> -AsPlainText -Force
$mySslCert1 = New-AzApplicationGatewaySslCertificate -Name "Cert01" -CertificateFile <Cert-File-Path> -Password $password
$mySslCert2 = New-AzApplicationGatewaySslCertificate -Name "Cert02" -CertificateFile <Cert-File-Path> -Password $password
.\migration.ps1 -ResourceId "/subscriptions/<your-sub-id>/resourceGroups/<your-rg>/providers/Microsoft.Network/applicationGateways/<v1AppGatewayName>" -SubnetAddressRange <CIDR like 10.0.3.0/24> -sslCert $mySslCert1,$mySslCert2
 
.INPUTS
String
Microsoft.Azure.Commands.Network.Models.PSApplicationGatewaySslCertificate[]
Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayTrustedRootCertificate[]
 
.OUTPUTS
PSApplicationGateway
 
.LINK
https://aka.ms/appgwmigrationdoc
https://docs.microsoft.com/en-us/azure/application-gateway/
https://docs.microsoft.com/en-us/azure/application-gateway/ssl-overview#end-to-end-ssl-with-the-v2-sku
 
.NOTES
Note - Passing reference to all ssl certs used in v1 gateway is required to get same configuration in v2 app gateway
#>


#Requires -Module Az.Network
#Requires -Module Az.Resources
Param([Parameter(Mandatory = $True)][string] $ResourceId,
[Parameter(Mandatory = $True)][string] $SubnetAddressRange,
[string] $AppGwName,
[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewaySslCertificate[]] $SslCertificates,
[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayTrustedRootCertificate[]] $TrustedRootCertificates,
[string] $PublicIpResourceId,
[string] $PrivateIpAddress,
[switch] $ValidateMigration,
[switch] $EnableAutoscale
)

if (!(Get-Module -ListAvailable -Name Az.Network)) 
{
    Write-Error ("You need 'Az' module to proceed. Az is a new cross-platform PowerShell module that will replace AzureRM. You can install this module by running 'Install-Module Az' in an elevated PowerShell prompt.")
    Write-Warning ("If you see error 'AzureRM.Profile already loaded. Az and AzureRM modules cannot be imported in the same session', You would need to close the current session and start new one.")
    exit
}

Import-Module -Name Az.Network
$sw = [Diagnostics.Stopwatch]::StartNew()
$resource = Get-AzResource -ResourceId $resourceId
$resourcegroup = $resource.ResourceGroupName
$location = $resource.Location
$V1AppGwName = $resource.Name
$appendString = "_v2"
$dict = @{}
$migrationCompleted = $false
$isNewSubnetCreated = $false
$isNewIPCreated = $false
if ( !$AppGwName )
{ 
    $AppGwName = $V1AppGwName + $appendString;
}

# Connect-AzAccount
$matchResponse = $resourceId -match "/subscriptions/(.*?)/resourceGroups/"
$subscription = $matches[1]
$context = Set-AzContext -Subscription $subscription
$AppGw = Get-AzApplicationGateway -Name $V1AppGwName -ResourceGroupName $resourcegroup
Write-Host "Creating Name:$AppGwName app gateway . . ."

# cleanup resources
Function Private:Cleanup()
{
    if ($newAppGw)
    {
        Remove-AzApplicationGateway -Name $newAppGw.Name -ResourceGroupName $resourcegroup -Force
    }
    if ($isNewIPCreated)
    {
        Write-Host ("Removing IP $PublicIpResourceName")
        Remove-AzPublicIpAddress -Name $PublicIpResourceName -ResourceGroupName $resourcegroup -Force -ErrorAction SilentlyContinue
    }
    if ($isNewSubnetCreated)
    {
        Write-Host ("Removing subnet $subnetname")
        $vnet = Remove-AzVirtualNetworkSubnetConfig -Name $subnetname -VirtualNetwork $vnet | Set-AzVirtualNetwork
    }

    Write-Host ("Resource Cleanup Finished")
    exit
}

Function Private:GetPrivateFrontendIp()
{
    if (!$PrivateIpAddress)
    {
        $SubnetStartAddress = [ipaddress]$SubnetAddressRange.Split("/")[0]
        # select an ip address beyond reserved Ip address range
        $SubnetSize = [int][math]::pow( 2, (32 - [int]$SubnetAddressRange.Split("/")[1]))
        $AddressOffset = (Get-Random -Minimum 4 -Maximum ($SubnetSize - 2))
        $IpAddressRangeToAdd = [ipaddress]"$AddressOffset"
        return New-Object System.Net.IPAddress($SubnetStartAddress.Address + $IpAddressRangeToAdd.Address)
    }
    else 
    {
        return $fp.PrivateIPAddress
    }
}

Function Private:ValidateInput()
{
    if (!$appgw -or !($appgw.sku.Tier -in "Standard","WAF"))
    {
        Write-Warning("Could not detect any V1 ('Standard' or 'WAF') resource as per your input parameters. Please double check input parameters.")
        exit
    }

    $Listeners = Get-AzApplicationGatewayHttpListener -ApplicationGateway $Appgw
    # ssl cert is necessary if you have 'https' enabled listeners in app gateway
    if (($Listeners |  Where-Object { $_.Protocol -match "https" }).count -GT 0 -and (($null -EQ $SslCertificates) -or ($SslCertificates.count -EQ 0)) )
    {
        Write-Warning ("Providing '-SslCertificates <cert>' is mandatory if you have 'https' listeners in your V1 ('Standard' or 'WAF') resource.")
        exit
    }

    if ($SslCertificates)
    {
        $SslCertificates | ForEach-Object { 
            if (!$_ -or ($_.GetType() -NE (New-Object -TypeName Microsoft.Azure.Commands.Network.Models.PSApplicationGatewaySslCertificate).GetType()))
            {
                Write-Error ("Invalid input - 'SslCertificates'. Expected object of type : 'Microsoft.Azure.Commands.Network.Models.PSApplicationGatewaySslCertificate' ")
                exit
            }
            else 
            {
                $_.Id = $_.Id -replace "/resourceGroups/.*/sslCertificates/","/resourceGroups/ResourceGroupNotSet/providers/Microsoft.Network/applicationGateways/ApplicationGatewayNameNotSet/sslCertificates/"
            }
         }
    }
    
    if (!$TrustedRootCertificates -or ($TrustedRootCertificates.count -EQ 0))
    {
        $TrustedRootCertificates = (New-Object System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayTrustedRootCertificate])
    }
    else
    {
        $TrustedRootCertificates | ForEach-Object { 
            if (!$_ -or ($_.GetType() -NE (New-Object -TypeName Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayTrustedRootCertificate).GetType()))
            {
                Write-Error ("Invalid input - 'TrustedRootCertificates'. Expected object of type : 'Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayTrustedRootCertificate' ")
                exit
            }
            else 
            {
                $_.Id = $_.Id -replace "/resourceGroups/.*/trustedRootCertificates/","/resourceGroups/ResourceGroupNotSet/providers/Microsoft.Network/applicationGateways/ApplicationGatewayNameNotSet/trustedRootCertificates/"
            }
        }
    }
}

Function Private:GetApplicationGatewaySku($gwSkuTier)
{
    if ($gwSkuTier -EQ "Standard")
    { 
        return New-AzApplicationGatewaySku -Name Standard_v2 -Tier Standard_v2
    }
    else
    {
        return New-AzApplicationGatewaySku -Name WAF_v2 -Tier WAF_v2
    }
}

Function Private:GetCapacityUnits($AppgwSku)
{
    # Min/Max Max Capacity for Autoscale
    $MinMaxCapacity = 2
    $MaxMaxCapacity = 125

    switch($AppgwSku.Name)
    {
        {$_ -in "WAF_Small","Standard_Small"} { $MinCapacity = [math]::floor($AppgwSku.Capacity/2); $MaxCapacity = $AppgwSku.Capacity; }
        {$_ -in "WAF_Medium","Standard_Medium"} { $MinCapacity = $AppgwSku.Capacity; $MaxCapacity = [math]::ceiling(1.5*$AppgwSku.Capacity); }
        {$_ -in "WAF_Large","Standard_Large"} { $MinCapacity = $AppgwSku.Capacity; $MaxCapacity = [math]::ceiling(2.5*$AppgwSku.Capacity); }
    }

    if ($MaxCapacity -GT $MaxMaxCapacity)
    {
        Write-Warning ("Your current V1 ('Standard' or 'WAF') has a large number of instances that exceed the limit for provisioning equivalently scaled V2 instances using our V1->V2 SKU conversion factors. Please consider reducing the number of instances for your V1 Application Gateway/WAF resource, or contact Azure Support to increase your subscription limits.")
        $MaxCapacity = $MaxMaxCapacity
    }
    elseif ($MaxCapacity -LT $MinMaxCapacity)
    {
        $MaxCapacity = $MinMaxCapacity
    }
    return $MinCapacity, $MaxCapacity
}

Function Private:IsSslCertificateMatch($newSslCert, $existingSslCert)
{
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
    $cert.Import([System.Convert]::FromBase64String($newSslCert.Data),$newSslCert.Password,4)

    $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection
    $certCollection.Import([System.Convert]::FromBase64String($existingSslCert.PublicCertData))
    if (($certCollection | Where-Object { $_.Equals($cert) }).Count -GT 0)
    {
        return $true
    }
    else 
    {
        return $false
    }
}

ValidateInput
Write-Host ("Input parameters validated")
try{
    #define sku & autoscale
    $sku = GetApplicationGatewaySku($AppGw.Sku.Tier)

    $capacity = GetCapacityUnits($AppGw.Sku)
    if ($enableAutoscale)
    {   
        $autoscaleConfig = New-AzApplicationGatewayAutoscaleConfiguration -MinCapacity $capacity[0] -MaxCapacity $capacity[1]
    }
    else
    {
        $sku.Capacity = $capacity[1]
    }
    
    # create subnet with appropiate nsg
    $GatewayConfig = Get-AzApplicationGatewayIPConfiguration -ApplicationGateway $AppGw
    $matchResponse = $GatewayConfig.subnet.id -match "/resourceGroups/(.*?)/.*/virtualNetworks/(.*?)/subnets/(.*)"
    $vnetname = $matches[2]
    $vnet = Get-AzvirtualNetwork -Name $vnetname -ResourceGroupName $matches[1]
    $V1Subnet = Get-AzVirtualNetworkSubnetConfig -Name $matches[3] -VirtualNetwork $vnet
    $agv2Subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet | Where-Object { $_.AddressPrefix -Match $SubnetAddressRange }
    if( $null -eq $agv2Subnet )
    {
        $subnetname = $AppGwName + "Subnet"
        $vnet = Add-AzVirtualNetworkSubnetConfig -Name $subnetname -AddressPrefix $SubnetAddressRange -VirtualNetwork $vnet -NetworkSecurityGroupId $V1Subnet.NetworkSecurityGroup.Id
        $vnet = Set-AzVirtualNetwork -VirtualNetwork $vnet
        if (!$vnet)
        {
            Write-Warning ("Please check if you have provided the correct SubnetAddressRange")
            return
        }
        $agv2Subnet = Get-AzVirtualNetworkSubnetConfig -Name $subnetname -VirtualNetwork $vnet
        $isNewSubnetCreated = $true
        Write-Host ("Created Subnet $($agv2Subnet.Name) for V2 Application Gateway / WAF. Address Prefix : $SubnetAddressRange")
    }

    if (!$agv2Subnet)
    {
        Write-Warning ("Failed to create Subnet. This might happen if VNet resource is in failed state. Please correct that and retry execution")
        return
    }
    else
    {
        Write-Host ("Using Subnet: $($agv2Subnet.Name)")
    }

    # Create FrontendIpConfig
    if ($PublicIpResourceId)
    {
        $PublicIpResource = Get-AzResource -ResourceId $PublicIpResourceId -ErrorAction SilentlyContinue
        if($PublicIpResource)
        {
            $PublicIpResourceName = $PublicIpResource.Name
            $matchResponse = $PublicIpResourceId -match "/resourceGroups/(.*?)/providers"
            $pip = Get-AzPublicIpAddress -Name $PublicIpResourceName -ResourceGroupName $matches[1] -ErrorAction SilentlyContinue
        }
    }

    if ( $null -eq  $pip )
    {
        $PublicIpResourceName = $AppGwName + "-IP"
        $isNewIPCreated = $true
        $pip = New-AzPublicIpAddress -ResourceGroupName $resourcegroup -name $PublicIpResourceName -location $location -AllocationMethod "Static" -Sku Standard
    }

    $fip = New-Object System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayFrontendIPConfiguration]
    $fp = (Get-AzApplicationGatewayFrontendIPConfig -ApplicationGateway $AppGw | Where-Object { $_.PublicIPAddress -NE $null })
    if ($fp)
    {
        $fipName = $fp.Name
    }
    else 
    {
        $fipName = $AppGwName + "PublicFrontendIPConfig"
    }
    # Compulsary create public frontend ip config in case of v2
    $fip.Add((New-AzApplicationGatewayFrontendIPConfig -Name $fipName -PublicIPAddress $pip))
    $dict[$fp.Id] = $fip[0]
    # Create private frontend ip config only if it is present in v1 also
    $fp = (Get-AzApplicationGatewayFrontendIPConfig -ApplicationGateway $AppGw | Where-Object { $_.PublicIPAddress -EQ $null })
    if ($fp)
    {
        $fip.Add((New-AzApplicationGatewayFrontendIPConfig -Name $fp.Name -PrivateIPAddress $(GetPrivateFrontendIp).IPAddressToString -Subnet $agv2Subnet))
        $dict[$fp.Id] = $fip[1]
    }

    if (!$fip)
    {
        Write-Warning ("Failed to create FrontendIpConfig. This should not have happened ideally. Please retry execution after sometime.")
        return
    }
    else
    {
        Write-Host ("Created FrontendIpConfiguration")
    }

    # Create Frontend ports
    $FrontEndPorts = Get-AzApplicationGatewayFrontendPort  -ApplicationGateway $AppGw 
    $FrontEndPorts | ForEach-Object {$dict[$_.Id] = $_;$_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/"); }

    # Create gatewayIpConfig
    $GatewayConfig = Get-AzApplicationGatewayIPConfiguration -ApplicationGateway $AppGw
    $gwIPconfig = New-AzApplicationGatewayIPConfiguration -Name $GatewayConfig.Name -Subnet $agv2Subnet
    if (!$gwIPconfig)
    {
        Write-Warning ("Failed to create GatewayIpConfig. This should not have happened ideally. Please retry execution after sometime.")
        return
    }
    else
    {
        Write-Host ("Created GatewayIpConfiguration")
    }

    # Create probes
    $probes = Get-AzApplicationGatewayProbeConfig -ApplicationGateway $appgw
    $probes | ForEach-Object { $dict[$_.Id] = $_; $_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/"); }
    Write-Host ("Created Health Probes")

    # Create BackendPools
    $BackendPools = Get-AzApplicationGatewayBackendAddressPool -ApplicationGateway $AppGw
    $BackendPools | ForEach-Object {$dict[$_.Id] = $_; $_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/"); }
    Write-Host ("Created Backend Pool")

    # Backend http settings
    $SettingsList  = Get-AzApplicationGatewayBackendHttpSettings -ApplicationGateway $AppGw
    $SettingsList | ForEach-Object { 
        $_.AuthenticationCertificates = $null
        if ($_.Protocol -EQ "https")
        { $_.TrustedRootCertificates = $TrustedRootCertificates }
        if($_.Probe -and $dict.ContainsKey($_.Probe.Id)) { $_.Probe = $dict[$_.Probe.Id]; }
        $dict[$_.Id] = $_;
        $_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/"); 
    }
    Write-Host ("Created Backend HttpSettings")

    # Ssl Certs
    $existingSslCertificates = Get-AzApplicationGatewaySslCertificate -ApplicationGateway $appgw
    foreach ($certA in $existingSslCertificates) { 
        $flag = $false;
        $dict[$certA.Id] = $SslCertificates[0].Id
        foreach($certB in $SslCertificates) {
            if(IsSslCertificateMatch $certB $certA)
            {
                $dict[$certA.Id] = $certB.id
                $flag = $true
                break
            }
        }
        if($flag -eq $false)
        {
            Write-Warning ("No ssl certificate provided for '$($certA.Name)'. Please ensure all SSL certificates used in V1 ('Standard' or 'WAF') resource are included.")
        }
    }

    # Create Listeners
    $v2listener = New-Object System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayHttpListener]
    $Listeners = Get-AzApplicationGatewayHttpListener -ApplicationGateway $Appgw
    $Listeners | ForEach-Object {
        $command = "New-AzApplicationGatewayHttpListener -Name $($_.Name) -Protocol $($_.Protocol) -FrontendPortId $($dict[$_.FrontendPort.Id].id) -FrontendIpConfigurationId $($dict[$_.FrontendIpConfiguration.Id].id) -RequireServerNameIndication $($_.RequireServerNameIndication) ";`
        if ($_.HostName)
        {
            $command += " -Hostname $($_.HostName)"
        }
        if ($_.Protocol -EQ "https")
        {
            if ($dict.ContainsKey($_.SslCertificate.Id))
            {
                $command = $command + " -SslCertificateId $($dict[$_.SslCertificate.Id])"
            }
            else 
            {
                $command = $command + " -SslCertificateId $($SslCertificates[0].Id)"
            }
        }
        $z = Invoke-Expression $command;
        if ($z)
        {
            $customError = Get-AzApplicationGatewayHttpListenerCustomError -HttpListener $_
            if ($customError)
            {
                $z.CustomErrorConfigurations = $customError
            }

            $v2listener.Add($z);
            $dict[$_.id] = $z;
        }
    }
    if ($v2listener.count -NE $listeners.count )
    {
        Write-Warning ("Failed to create Listeners. Please check you have given correct inputs and retry.")
        return
    }
    else
    {
        Write-Host ("Created Listeners")
    }

    # RedirectionConfig
    $RedirectConfig = Get-AzApplicationGatewayRedirectConfiguration -ApplicationGateway $AppGw;
    $RedirectConfig | ForEach-Object { 
        if ($_.TargetListener)
        {
            $_.TargetListener.Id = $dict[$_.TargetListener.Id].id
        }
        $dict[$_.id] = $_;
        $_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/");
    }

    # Request Routing Rule
    $urlpath = Get-AzApplicationGatewayUrlPathMapConfig -ApplicationGateway $appgw
    $urlpath | ForEach-Object { 
        $_.PathRules | ForEach-Object {
            if ($_.BackendAddressPool)
            {
                $_.BackendAddressPool.id = $dict[$_.BackendAddressPool.id].id;
            }
            if ($_.RedirectConfiguration)
            {
                $_.RedirectConfiguration.id = $dict[$_.RedirectConfiguration.id].id;
            }
            if ($_.BackendHttpSettings)
            {
                $_.BackendHttpSettings.id = $dict[$_.BackendHttpSettings.id].id;
            }
        }
        
        if ($_.DefaultBackendAddressPool)
        {
            $_.DefaultBackendAddressPool.Id = $dict[$_.DefaultBackendAddressPool.Id].id
        }
        if ($_.DefaultBackendHttpSettings)
        {
            $_.DefaultBackendHttpSettings.Id = $dict[$_.DefaultBackendHttpSettings.Id].id
        }
        if($_.DefaultRedirectConfiguration)
        {
            $_.DefaultRedirectConfiguration.Id = $dict[$_.DefaultRedirectConfiguration.Id].id
        }
        $dict[$_.Id] = $_;
        $_.Id = $_.Id.Replace("/applicationGateways/$V1AppGwName/","/applicationGateways/ApplicationGatewayNameNotSet/");
    }

    #request routing rules
    $Rules = Get-AzApplicationGatewayRequestRoutingRule -ApplicationGateway $AppGW
    $v2Rules = New-Object System.Collections.Generic.List[Microsoft.Azure.Commands.Network.Models.PSApplicationGatewayRequestRoutingRule]
    $Rules | ForEach-Object {
        if($dict.ContainsKey($_.HttpListener.Id))
        {
            $command = "New-AzApplicationGatewayRequestRoutingRule -Name $($_.Name) -RuleType $($_.RuleType) -HttpListenerId $($dict[$_.HttpListener.Id].id)";
            if ($_.BackendHttpSettings -and $dict.ContainsKey($_.BackendHttpSettings.Id))
            {
                $command += " -BackendHttpSettingsId $($dict[$_.BackendHttpSettings.Id].id) -backendAddressPoolId $($dict[$_.BackendAddressPool.Id].id)";
            }
            elseif ($_.RedirectConfiguration.Id -and $dict.ContainsKey($_.RedirectConfiguration.Id))
            {
                $command += " -RedirectConfigurationId $($dict[$_.RedirectConfiguration.Id].id)"
            }  
            elseif ($_.UrlPathMap.Id -and $dict.ContainsKey($_.UrlPathMap.Id))
            {
                $command += " -UrlPathMapId $($dict[$_.UrlPathMap.Id].id)"
            }
            else {Write-Error "No rule can be created for", $_.Name;}
            $z = Invoke-Expression ($command);
            if ($z)
            {
                $v2rules.Add($z);
            }
        }
    }

    if ($v2Rules.count -NE $rules.count )
    {
        Write-Warning ("Failed to create Request routing rules. Please check you have given correct input and retry. Please report if the problem continues.")
        return
    }
    else
    {
        Write-Host ("Created Request Routing Rules")
    }

    # AppGateway Custom Error Config
    $customError = Get-AzApplicationGatewayCustomError -ApplicationGateway $appgw

    $sslpolicy = Get-AzApplicationGatewaySslPolicy -ApplicationGateway $AppGw
    $wafConfig = Get-AzApplicationGatewayWebApplicationFirewallConfiguration -ApplicationGateway $AppGw

    # create app gateway
    $command = 'New-AzApplicationGateway -Name $appgwname -ResourceGroupName $resourcegroup -Location $location -Sku $(Select-Object -InputObject $sku) -GatewayIPConfigurations $(Select-Object -InputObject $gwipconfig) -FrontendIpConfigurations $(Select-Object -InputObject $fip) -Tag @{migrated = "ByScript"}';
    $command += ' -FrontendPorts $(Select-Object -InputObject $FrontEndPorts) -BackendAddressPools $(Select-Object -InputObject $BackendPools) -BackendHttpSettingsCollection $(Select-Object -InputObject $SettingsList) -HttpListeners $(Select-Object -InputObject $v2listener) -RequestRoutingRules $(Select-Object -InputObject $v2rules) '
    if ($enableAutoscale)
    { $command += '-AutoScaleConfiguration $(Select-Object -InputObject $autoscaleConfig)' }
    if ($appgw.EnableHttp2)
    { $command += ' -EnableHttp2 ' }
    if ($TrustedRootCertificates)
    { $command += ' -TrustedRootCertificate $TrustedRootCertificates'}
    if($urlpath.Count -gt 0)
    { $command += ' -UrlPathMaps $($urlpath)' }
    if($probes.Count -gt 0)
    { $command += ' -Probes $(Select-Object -InputObject $probes)' }
    if($RedirectConfig.Count -gt 0)
    { $command += ' -RedirectConfigurations $(Select-Object -InputObject $RedirectConfig)' }
    if ($SslCertificates.Count -gt 0 )
    { $command += ' -SslCertificates $(Select-Object -InputObject $SslCertificates)' }
    if ($sslpolicy)
    {   $command += ' -SslPolicy $(Select-Object -InputObject $sslpolicy) ' }
    if($wafConfig)
    {   $command += ' -WebApplicationFirewallConfiguration $wafConfig' }
    if ($customError)
    {   $command += ' -CustomErrorConfiguration $customError' }

    Write-Warning("Creating new V2 Application Gateway / WAF may take up to ~7mins. Please wait for the command to complete.")
    $newAppGw = Invoke-Expression ($command)
    if ( $newAppGw )
    {
        Write-Host ("Successfully created V2 Application Gateway / WAF, Name : $($newAppGw.Name),`
         PublicIPAddress : $($pip.IpAddress),`
         Subnet Name (Prefix) : $($agv2Subnet.Name) ( $($agv2Subnet.AddressPrefix) )"
) 
    }
    else
    {
        Write-Error ("Creation of V2 Application Gateway / WAF failed. Please retry after sometime. Please contact Azure Support if error persists after several retries.")
        return
    }
    $sw.Stop()
    $migrationCompleted = $true
    if( $validateMigration)
    {
        # compare backend health for v1 and v2 app gateway
        $x = Get-AzApplicationGatewayBackendHealth -Name $V1AppGwName -ResourceGroupName $resourcegroup
        $y = Get-AzApplicationGatewayBackendHealth -Name $AppGwName -ResourceGroupName $resourcegroup
        for ($i = 0; $i -lt $x.BackendAddressPools.Count; $i++) {
            $x1 = $x.BackendAddressPools[$i].BackendHttpSettingsCollection
            $y1 = $y.BackendAddressPools[$i].BackendHttpSettingsCollection
            $dict = @{}
            for ($j = 0; $j -lt $x1.Count; $j++) {
                $x1[$j].Servers | ForEach-Object { $dict[$_.Address] = $_.Health }
                $y1[$j].Servers | ForEach-Object { 
                    if ($_.Health -EQ $dict[$_.Address]) {
                        Write-Host ("Backend Health reported equal for - $($_.Address) ")
                    }
                    else {
                        Write-Warning ("Backend Health reported difference for - $($_.Address), v1 - $($dict[$_.Address]), v2 - $($_.health)")
                    }
                }
            }
        }
    }
    
    return $newAppGw
}
catch [Exception]
    {
        Write-Output $_.Exception | format-list -force
    }
finally
{
    if ($migrationCompleted -EQ $false)
    {
        cleanup
    }
    else 
    {
        Write-Host ("Migration Complete. TimeTaken : $($sw.Elapsed.TotalSeconds) seconds")
    }
}
# SIG # Begin signature block
# MIIjuAYJKoZIhvcNAQcCoIIjqTCCI6UCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBbGLV0aV8Tmy5l
# zheNdADCbvrSRSoN5I2EFnuR6wBSv6CCDYEwggX/MIID56ADAgECAhMzAAABA14l
# HJkfox64AAAAAAEDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMTgwNzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDRlHY25oarNv5p+UZ8i4hQy5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU5
# 3Rt+M2wR/Dsm85iqXVNrqsPsE7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94S
# nYW/fHaGfXKxdpth5opkTEbOttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjR
# hIjiATwi/K+WCMxdmcDt66VamJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8A
# DwH+S15UD8rEZT8K46NG2/YsAzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0
# NIZ5rCrVXzCRO+QUauuxygQjAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8w
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJ
# Uw0/DSbsokTYDdGfY5YGSz8eXMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHn
# wya0rPUn00iThoj+EjWRZCLRay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjY
# DBu2KWAndjQkm925l3XLATutghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs
# 1JPyZ6vic8g9o838Mh5gHOmwGzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCy
# RhxdXnYqWJ/U7vL0+khMtWGLsIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuy
# bR/5WcF9ttE5yXnggxxgCto9sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpD
# MYLa2ISuubAfdfX2HX1RETcn6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy
# 2TUxuNty+rR1yIibar+YRcdmstf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v4
# 6Mp79mwV+QMZDxGFqk+av6pX3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOla
# P470I3gwsvKmOMs1jaqYWSRAuDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6Iwh
# SkHC3uuOW+4Dwx1ubuZUNWZncnwa6lL2IsRyP64wggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVjTCCFYkCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAQNeJRyZH6MeuAAAAAABAzAN
# BglghkgBZQMEAgEFAKCB4DAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg0fSpgSMA
# Evo4HcU7Rk5UhV7+zyTx7xJWFwBdmAIOIdcwdAYKKwYBBAGCNwIBDDFmMGSgSIBG
# AEEAegB1AHIAZQAgAEEAcABwAGwAaQBjAGEAdABpAG8AbgAgAEcAYQB0AGUAdwBh
# AHkAIABNAGkAZwByAGEAdABpAG8AbqEYgBZodHRwczovL21pY3Jvc29mdC5jb20g
# MA0GCSqGSIb3DQEBAQUABIIBACRcIFW0OG/YzqcM4HRNAPsvGYrOKCiRNuYlcK4Y
# M7wLLhM53Re7+1vP+WpsmCJLXOD/JhIcgvisV8g80VxKMGtehMRv2M2nE4+ICR/b
# 2SKjkQOjitdm/h1sf0prcpyHdUbG+vikLgkAzpP/f6cfFPyuwfNR9m3wXOWOyPJg
# dUJomAopLKwFbdB0FZC1tsoU0mK9adnzVcj+YWMx7juQJhfLWbVb2EPqPK2wJoE1
# o44sENEkrADmw6KfZuHWP3h94we9KseViJJTP4NEWCUxORQAoLw4Dio/8u6oI7LT
# lM4I220R7v8JKli5bAzDugvD46oHZ0Wvk/vLNLysb1MQQk+hghLlMIIS4QYKKwYB
# BAGCNwMDATGCEtEwghLNBgkqhkiG9w0BBwKgghK+MIISugIBAzEPMA0GCWCGSAFl
# AwQCAQUAMIIBUQYLKoZIhvcNAQkQAQSgggFABIIBPDCCATgCAQEGCisGAQQBhFkK
# AwEwMTANBglghkgBZQMEAgEFAAQgqOvjkRpxsnY8TDK5KVmAOq5i60Ojr3PcvEoB
# /p58XH8CBlzLH+qQNxgTMjAxOTA1MjAwOTE1NTEuMzE3WjAEgAIB9KCB0KSBzTCB
# yjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAldBMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29m
# dCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEmMCQGA1UECxMdVGhhbGVzIFRT
# UyBFU046MkFENC00QjkyLUZBMDExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0
# YW1wIHNlcnZpY2Wggg48MIIE8TCCA9mgAwIBAgITMwAAANevWm7Aqj3OtgAAAAAA
# 1zANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe
# Fw0xODA4MjMyMDI2NTBaFw0xOTExMjMyMDI2NTBaMIHKMQswCQYDVQQGEwJVUzEL
# MAkGA1UECBMCV0ExEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0
# aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjoyQUQ0LTRCOTIt
# RkEwMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgc2VydmljZTCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2IiFriCQzhsN8dtyFkYo4i4BJI
# YhK+/ArS0gCQvuEhu0bWvRf5hvO3SUjcdzLoL+jPFkgc8u0NR0tfb4Tpe0aPzEfD
# pDXElZBvG2wy90uizNznWuTA8ggF8bMUxAHuJiEf1TVA8YZVFYSIwPdkZ2mQOQfu
# ryc8yCrNWNN2QdbKdVqp00yOy32WEpQFUeym2ACM6g11zBf9fP0JHF62XOLqHMu4
# W5njuucUiOow9/saeRvN3C9Re+iIx0/s9UBO1bUzSXs3LVXFylOdQGsiWpoiX07e
# mCCCOg8st9mP57G6VmLwr8TllUcndghnssxS9CBM08uzp43jIm/BbnK5YL0CAwEA
# AaOCARswggEXMB0GA1UdDgQWBBTfuKdVJ//3abwzVmM2a00poOpVODAfBgNVHSME
# GDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVodHRw
# Oi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1TdGFQ
# Q0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5o
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBDQV8y
# MDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MA0GCSqGSIb3DQEBCwUAA4IBAQBMC2hSxPkAtbJMcbCc2p56g7gIKNEHuLQ4s95B
# yO/slZMD1B/Q7M42l6mpXy/DESXI4xg0xGLwxARVyQOLUOnCrMkCHxZIiaNoECD2
# HQX1jA65G0YNYXevoETvZA6Inwq1ZFQUXa8+zMIpArZmif2C7c4aK8CaYHL7kucL
# ySrvlzu4ozj9mvUutsKKc8XsZkEIaRHBDNGUtCnzU314KY4EqkN0+N6yocY9vwwC
# TnQcDWWZ7/NECfpECQXj4NY4v+s27l8uHnU+PGBA+feBRPhD9qHV1H3Kg83SVw2c
# 8/H28Anm/aybDSfVqODDgU68KpsMuPyqzXaXq3jGpPrXuW0wMIIGcTCCBFmgAwIB
# AgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUwNzAx
# MjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw
# JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJKoZI
# hvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDVpQoA
# goX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/xYIiE
# VEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFnkV+B
# VLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13Hz3w
# V3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaICDXo
# eByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOCAeYw
# ggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYbxTNo
# WoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW
# BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH
# AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/BIGV
# MIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUHAgIw
# NB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBlAG4A
# dAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z66bM
# 9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lTjMz0
# YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIArzgP
# F/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWvL/62
# 5Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/fZZq
# kHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZJQ96
# LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqwUB5v
# vfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d9LiF
# AR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLixqduW
# sqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh0sVV
# 42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4Iuto2
# 29Nfj950iEkSoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJBgNVBAYTAlVTMQsw
# CQYDVQQIEwJXQTEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRp
# b25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjJBRDQtNEI5Mi1G
# QTAxMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBzZXJ2aWNloiMKAQEw
# BwYFKw4DAhoDFQDNNnbiNNGnJTal65xNVuX8XQcRraCBgzCBgKR+MHwxCzAJBgNV
# BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m
# dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA4IywbTAiGA8y
# MDE5MDUyMDEyNDk0OVoYDzIwMTkwNTIxMTI0OTQ5WjB3MD0GCisGAQQBhFkKBAEx
# LzAtMAoCBQDgjLBtAgEAMAoCAQACAhDqAgH/MAcCAQACAhF8MAoCBQDgjgHtAgEA
# MDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAI
# AgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAkxQVntiY0HKahTkyLXNX483a0Bmm
# 8Jo1q0wOdLakmV6VczJbrP6EHr8ZD8mvCiyOlh/us1Gwue8Owt7xqGuWsSnqkAT8
# 0YOxbGD5J7lL2CNf8mtJA1k4wxk6vbRg+1LnU0bLhv01Q+bO1usOW1dtvCr6iJbl
# yu3eX/oHR+BupwcxggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMAITMwAAANevWm7Aqj3OtgAAAAAA1zANBglghkgBZQMEAgEFAKCCAUow
# GgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCBoEA5x
# BOac9Ooz1UkYLkf8gwjeTK+QzBIu6dfxn3plLzCB+gYLKoZIhvcNAQkQAi8xgeow
# gecwgeQwgb0EIKWOYAwnD0Ee/d5KKHT7KEFyzshq4pOD9WLEEUbOJOM4MIGYMIGA
# pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADXr1puwKo9zrYA
# AAAAANcwIgQgskQaDUGWHnFJp3XV+sKUJliL9cCFCsVWckswwkGMqbMwDQYJKoZI
# hvcNAQELBQAEggEAwFg0cNQoJC2jDJ05fOtkmZ5AUtzbMc1+m38ivufGnp71IbZu
# S0+x26+8RrJ+tbknaDXtyvfYl43jltFtwPvRT2FBpV9D8DJRnz9KEi98x+TlYeh1
# HdauhMHrq6k/42tbgEJ3eR5Cxp9ul16UNCZHroEJ1/F7HwRmnPX1mEf7HG52NXYm
# SL103mSiDH7MFTLb0UkWvevZHgtb35Uc9mEZ14U5MpnGjGGufEb0IsWaFK2gZwqY
# u0BkspJFFP2dSMDWi+crh+qJg7W6YCAJjQfs3poM5pkiLiDYWyUlzI8mRvlG9GgD
# EJ6ymz2aJtbtO4Vhxg8Lo5cWCtltCYkxt6cLSw==
# SIG # End signature block