Skip to content

Instantly share code, notes, and snippets.

@smourier
Created September 12, 2018 17:54
Show Gist options
  • Save smourier/5a509761ed19e5790fee6372e5b68934 to your computer and use it in GitHub Desktop.
Save smourier/5a509761ed19e5790fee6372e5b68934 to your computer and use it in GitHub Desktop.
#requires -Modules AcmeSharp, Azure, AzureRM.Websites
#with support for custom subscription id - smourier - 2018/09/12
<#PSScriptInfo
.VERSION 1.4.4
.TITLE GetSSL - Azure Automation
.AUTHOR Dani Alonso, Lee Holmes
.GUID 21904884-3b46-4b37-b388-6a9958592401
.DESCRIPTION Este script es capaz de generar y renovar automaticamente los certificados SSL en sitios alojados en Microsoft Azure. Basado en el script original de Lee Holmes, realizando una serie de correcciones y mejoras que automatiza el correcto proceso en Azure Automation. (by Dani Alonso).
.MANUAL https://w.itpro.es/ssl-spa
.TAGS LetsEncrypt SSL Azure Linux Windows Automation
#>
param(
[Parameter(Mandatory)]
[String] $Credential,
[Parameter(Mandatory)]
[String] $Domain,
[Parameter(Mandatory)]
[String] $RegistrationEmail,
[Parameter(Mandatory)]
[String] $ResourceGroup,
[Parameter(Mandatory)]
[String] $WebApp,
[String] $SubscriptionId,
[Switch] $UseUnixFileVerification
)
Set-StrictMode -Version Latest
function GetSafeFilename
{
param(
$BasePath = ".",
$Text,
$Extension = ".txt"
)
$invalidChars = [IO.Path]::GetInvalidFileNameChars()
$invalidCharsRegex = "[" + (-join ($invalidChars | % { [Regex]::Escape($_) })) + "]"
$baseFilename = $Text -replace $invalidCharsRegex,'_'
$reservedDeviceNames = -split "CON PRN AUX NUL COM1 COM2 COM3 COM4 COM5 COM6 COM7 COM8 COM9 LPT1 LPT2 LPT3 LPT4 LPT5 LPT6 LPT7 LPT8 LPT9"
if($baseFilename -in $reservedDeviceNames)
{
$baseFilename = "_" + $baseFilename
}
$baseFilename = $baseFilename.Substring(0, [Math]::Min(50, $baseFilename.Length))
$counter = 1
$fileName = $baseFilename + $Extension
while(Test-Path (Join-Path $BasePath $fileName))
{
$filename = $baseFilename + "_${counter}${Extension}"
$counter++
}
$fileName.Trim()
}
function PublishWebsiteFile
{
param(
[Parameter(Mandatory)]
$ResourceGroup,
[Parameter(Mandatory)]
$WebApp,
[Parameter(Mandatory)]
$PublishSettingsFile,
[Parameter(Mandatory)]
$RemotePath,
[Parameter(Mandatory)]
$FileContent
)
$RemotePath = $RemotePath.Trim("/\")
$publishSettings = [xml] (Get-Content $PublishSettingsFile -Raw)
$ftpPublishSettings = $publishSettings.publishData.publishProfile | ? publishMethod -eq MSDeploy
$username = $ftpPublishSettings.userName
$password = $ftpPublishSettings.userPWD
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username,$password)))
$apiBaseUrl = "https://$WebApp.scm.azurewebsites.net/api"
Invoke-RestMethod -Uri "$apiBaseUrl/vfs/site/wwwroot/$RemotePath" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo); 'If-Match' = '*'} -Method PUT -Body $FileContent
}
$outputDirectory = GetSafeFilename -Text $Domain -Extension ""
if(-not (Test-Path $outputDirectory))
{
$null = New-Item -Type Directory $outputDirectory
}
Write-Progress "Creating Let's Encrypt registration"
if(-not (Get-AcmeVault))
{
$null = Initialize-ACMEVault -BaseURI https://acme-v01.api.letsencrypt.org/
}
$identifier = -join (([int][char]'a'..[int][char]'z') | Get-Random -Count 10 | % { [char] $_ })
$null = New-ACMERegistration -Contacts mailto:$RegistrationEmail -AcceptTos
$null = New-ACMEIdentifier -Dns $domain -Alias $identifier
Write-Progress "Receiving challenge"
$completedChallenge = Complete-ACMEChallenge -Ref $identifier -Challenge http-01 -Handler manual -Regenerate
$challengeAnswer = ($completedChallenge.Challenges | Where-Object { $_.HandlerName -eq "manual" }).Challenge
$key = $challengeAnswer.FilePath
$target = "$key/index.html"
if($UseUnixFileVerification) { $target = $key }
Write-Progress "Uploading key and challenge to $domain/$target"
$myCredential = Get-AutomationPSCredential -Name $Credential
Add-AzureRmAccount -Credential $myCredential
if($SubscriptionId)
{
Write-Progress "Changing to subscription id $SubscriptionId"
Select-AzureRmSubscription -SubscriptionId $SubscriptionId
}
$mysubscription=(Get-AzureRmContext).Subscription.Id
$mytenant = (Get-AzureRmContext).Subscription.TenantId
Write-Progress "SubscriptionId is $mysubscription"
Write-Progress "TenantId is $mytenant"
Add-AzureRmAccount -TenantId $mytenant -SubscriptionId $mysubscription -Credential $myCredential
$tempFile = New-TemporaryFile
try
{
$null = Get-AzureRmWebAppPublishingProfile -ResourceGroupName $ResourceGroup -Name $WebApp -OutputFile $tempFile
PublishWebsiteFile -ResourceGroup $ResourceGroup -WebApp $WebApp -PublishSettingsFile $tempFile -RemotePath $target -FileContent $challengeAnswer.FileContent
}
finally
{
Remove-Item $tempFile
}
$counter = 0
Write-Progress "Waiting for challenge verification" -PercentComplete ($counter++)
$challenge = Submit-ACMEChallenge -Ref $identifier -ChallengeType http-01
while ($challenge.Status -eq "pending")
{
Start-Sleep -m 500
Write-Progress "Waiting for challenge verification" -PercentComplete ($counter++)
$challenge = Update-ACMEIdentifier -Ref $identifier
}
if($challenge.Status -eq "valid")
{
$rawPassword = -join (([int][char]'a'..[int][char]'z') | Get-Random -Count 20 | % { [char] $_ })
$certIdentifier = -join (([int][char]'a'..[int][char]'z') | Get-Random -Count 10 | % { [char] $_ })
New-ACMECertificate -Identifier $identifier -Alias $certIdentifier -Generate
$certificateInfo = Submit-ACMECertificate -Ref $certIdentifier
Write-Progress "Waiting for IssuerSerialNumber to be issued"
while(-not ((Test-Path variable:\certificate) -or $certificateInfo.IssuerSerialNumber))
{
Start-Sleep -m 500
$certificateInfo = Update-ACMECertificate -Ref $certIdentifier
}
$outputFile = Join-Path $pwd cert1-all.pfx
$null = Get-ACMECertificate -Ref $certIdentifier -ExportPkcs12 $outputFile -CertificatePassword $rawPassword
Get-Item $outputFile
New-AzureRmWebAppSSLBinding -ResourceGroupName $ResourceGroup -WebAppName $WebApp -CertificateFilePath $outputFile -CertificatePassword $rawPassword -Name $Domain
}
else
{
Write-Error (("Certificate generation failed. Status is '{0}', can't continue as it is not 'valid'. " +
"Let's Encrypt could not retrieve the expected content from '$domain/$target'") -f $challenge.Status)
$challenge
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment