How to automate free SSL renewal on Azure Load Balancer/Appication Gateway?

How to apply SSL on azure load balancer or application gateway and automate to renew it with Azure Automation account and Runbook?

To generate and automate free SSL certificate on application gateway of load balancer, you need to go through below steps:

- Create a storage account
- Create a public container and two folders inside ".well-known / acme-challenge" to upload tokens
- Create Automation account and then create "Run As Account"
- Create runbook with the large script we have already
- Install "ACME-PS" and "ACMESharp" modules from Gallery of Automation Account
- Inside Application Gateway, Rules and create a path based rule for port 443

Please watch video to understand it properly:

Script for runbook:

#######################################################################################
# Script that renews a Let's Encrypt certificate for an Azure Application Gateway
# Pre-requirements:
#      - Have a storage account in which the folder path has been created:
#        '/.well-known/acme-challenge/', to put here the Let's Encrypt DNS check files

#      - Add "Path-based" rule in the Application Gateway with this configuration:
#           - Path: '/.well-known/acme-challenge/*'
#           - Check the configure redirection option
#           - Choose redirection type: permanent
#           - Choose redirection target: External site
#           - Target URL:
#                - Example: 'https://storageaccount.blob.core.windows.net/public'
#      - For execution on Azure Automation: Install "ACME-PS" and "ACMESharp" modules from Gallery of Automation Account
#
#######################################################################################

Param(
    [string]$domain
)
$AGName = 'Website-LB';
$EmailAddress = 'test@test.com';
$STResourceGroupName = 'Test';
$AGResourceGroupName = 'Test';
$storageName = 'storageaccount';
$AGOldCertName = 'certificate';


# Ensures that no login info is saved after the runbook is done
Disable-AzContextAutosave
Import-Module ACME-PS
# Log in as the service principal from the Runbook
$connection = Get-AutomationConnection -Name AzureRunAsConnection
Login-AzAccount -ServicePrincipal -Tenant $connection.TenantID -ApplicationId $connection.ApplicationID -CertificateThumbprint $connection.CertificateThumbprint

# Create a state object and save it to the harddrive
$state = New-ACMEState -Path $env:TEMP
$serviceName = 'LetsEncrypt'

# Fetch the service directory and save it in the state
Get-ACMEServiceDirectory $state -ServiceName $serviceName -PassThru;

# Get the first anti-replay nonce
New-ACMENonce $state;

# Create an account key. The state will make sure it's stored.
New-ACMEAccountKey $state -PassThru;

# Register the account key with the acme service. The account key will automatically be read from the state
New-ACMEAccount $state -EmailAddresses $EmailAddress -AcceptTOS;

# Load an state object to have service directory and account keys available
$state = Get-ACMEState -Path $env:TEMP;

# It might be neccessary to acquire a new nonce, so we'll just do it for the sake of the example.
New-ACMENonce $state -PassThru;

$dnsList = @($domain,"www.$domain");

# Create the identifier for the DNS name
$identifiers = @();
foreach ($dns in $dnsList) { $identifiers += New-ACMEIdentifier $dns }

# Create the order object at the ACME service.
$order = New-ACMEOrder $state -Identifiers $identifiers;

# Fetch the authorizations for that order
$authZ = Get-ACMEAuthorization -State $state -Order $order;

foreach ($dns in $dnsList) {
    for ($i=0; $i -lt $authZ.Length; $i++) {
        if ( $authZ[$i].Identifier.value -eq $dns ) {
            # Select a challenge to fullfill
            $challenge = Get-ACMEChallenge $state $authZ[$i] "http-01";

            # Inspect the challenge data
            $challenge.Data;

            # Create the file requested by the challenge
            $fileName = $env:TMP + '\' + $challenge.Token;
            Set-Content -Path $fileName -Value $challenge.Data.Content -NoNewline;

            $blobName = ".well-known/acme-challenge/" + $challenge.Token
            $storageAccount = Get-AzStorageAccount -ResourceGroupName $STResourceGroupName -Name $storageName
            $ctx = $storageAccount.Context
            Set-AzStorageBlobContent -File $fileName -Container "public" -Context $ctx -Blob $blobName -Force

            # Signal the ACME server that the challenge is ready
            $challenge | Complete-ACMEChallenge $state;
        }
    }
}
Write-Output "wait begins";
# Wait a little bit and update the order, until we see the states
while($order.Status -notin ("ready","invalid")) {
    Start-Sleep -Seconds 10;
    $order | Update-ACMEOrder $state -PassThru;
}
Write-Output "wait ends";
if ($order.Status -eq "ready" )
{
    Write-Output "order is now ready";
    # We should have a valid order now and should be able to complete it
    # Therefore we need a certificate key
    $certKey = New-ACMECertificateKey -Path "$env:TEMP\$domain.key.xml";

    # Complete the order - this will issue a certificate singing request
    Complete-ACMEOrder $state -Order $order -CertificateKey $certKey;
    Write-Output "wait for certificate ";
    # Now we wait until the ACME service provides the certificate url
    while(-not $order.CertificateUrl) {
        Start-Sleep -Seconds 15
        $order | Update-Order $state -PassThru
    }
    Write-Output "wait for certificate ends";
    # As soon as the url shows up we can create the PFX
    $password = ConvertTo-SecureString -String "password" -Force -AsPlainText
    Export-ACMECertificate $state -Order $order -CertificateKey $certKey -Path "$env:TEMP\$domain.pfx" -Password $password;

    # Delete blob to check DNS
    Remove-AzStorageBlob -Container "public" -Context $ctx -Blob $blobName

    ### RENEW APPLICATION GATEWAY CERTIFICATE ###
    $appgw = Get-AzApplicationGateway -ResourceGroupName $AGResourceGroupName -Name $AGName
    Set-AzApplicationGatewaySSLCertificate -Name $AGOldCertName -ApplicationGateway $appgw -CertificateFile "$env:TEMP\$domain.pfx" -Password $password
    Set-AzApplicationGateway -ApplicationGateway $appgw
} else {
    Write-Output $order;
    throw "The Order was invalid";
}