Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jdalley
Last active January 1, 2023 18:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdalley/076b4e78545efc570eb30d2f6f250bda to your computer and use it in GitHub Desktop.
Save jdalley/076b4e78545efc570eb30d2f6f250bda to your computer and use it in GitHub Desktop.
AWS CLI - blue/green deployment via swapping DNS aliases and SSL certificates between two Elastic Beanstalk environments.
# This script gets the DNSNames of two defined Route53 domains (one is a subdomain of the other),
# and swaps the aliases to perform a blue/green deployment. Or for whatever other reason you can imagine.
# Note: If you were doing this for two different non-subdomain domains; you'd have two Hosted Zone Ids here:
$hostedZoneId = '<some-hosted-zone-id>'
$stagingDNSAlias = aws route53 list-resource-record-sets `
--hosted-zone-id $hostedZoneId `
--query "ResourceRecordSets[?Name == 'staging.yoursite.com.'].AliasTarget.DNSName" `
--output text
$prodDNSAlias = aws route53 list-resource-record-sets `
--hosted-zone-id $hostedZoneId `
--query "ResourceRecordSets[?Name == 'www.yoursite.com.'].AliasTarget.DNSName" `
--output text
if ([string]::IsNullOrEmpty($stagingDNSAlias) -or [string]::IsNullOrEmpty($prodDNSAlias)) {
Write-Host "Didn't get expected result from AWS, exiting..."
Exit
}
Write-Host "Current EC2 server aliases: `n Staging: $stagingDNSAlias `n Prod: $prodDNSAlias"
# Change batch:
# Notes:
# - HostedZoneId for this change should be the public ElasticBeanstalk zone id for your AWS environment:
# http://docs.aws.amazon.com/general/latest/gr/rande.html#elasticbeanstalk_region
# - Z38NKT9BP95V3O is the us-west-2 ElasticBeanstalk Hosted Zone Id.
$changeBatch = @"
{
\"Comment\": \"Swap Web domain aliases for staging.yoursite.com and www.yoursite.com\",
\"Changes\": [
{
\"Action\": \"UPSERT\",
\"ResourceRecordSet\": {
\"Name\": \"staging.yoursite.com.\",
\"Type\": \"A\",
\"AliasTarget\": {
\"HostedZoneId\": \"Z38NKT9BP95V3O\",
\"EvaluateTargetHealth\": false,
\"DNSName\": \"$($prodDNSAlias)\"
}
}
},
{
\"Action\": \"UPSERT\",
\"ResourceRecordSet\": {
\"Name\": \"www.yoursite.com.\",
\"Type\": \"A\",
\"AliasTarget\": {
\"HostedZoneId\": \"Z38NKT9BP95V3O\",
\"EvaluateTargetHealth\": false,
\"DNSName\": \"$($stagingDNSAlias)\"
}
}
}
]
}
"@
Write-Host "Change batch we're sending to aws route53 change-resource-record-sets: `n$changeBatch`n"
# Swap DNS Aliases:
Write-Host "AWS Route53 change-resource-record-sets response: `n"
$changeResponse = aws route53 change-resource-record-sets `
--hosted-zone-id $hostedZoneId `
--change-batch $changeBatch | ConvertFrom-Json
Write-Host "Status: $($changeResponse.ChangeInfo.Status)"
Write-Host "Comment: $($changeResponse.ChangeInfo.Comment)"
Write-Host "SubmittedAt: $($changeResponse.ChangeInfo.SubmittedAt)"
Write-Host "Id: $($changeResponse.ChangeInfo.Id)`n"
# Check for Status to move from PENDING to INSYNC before continuing:
$changeCheckDelay = 2 # Seconds
$changeCheckTimeout = 5 # Minutes
$startTime = Get-Date
$done = $null
Write-Host "Checking change status every $changeCheckDelay seconds until INSYNC..."
do {
$statusResponse = aws route53 get-change --id $changeResponse.ChangeInfo.Id | ConvertFrom-Json
Write-Host "Change status: $($statusResponse.ChangeInfo.Status)"
if ($statusResponse.ChangeInfo.Status -eq "INSYNC") {
$done = $true
Write-Host "Change INSYNC. DNS Aliases swapped. Continuing..."
}
else {
Start-Sleep -Seconds $changeCheckDelay
}
} while ($done -eq $null -and $startTime.AddMinutes($changeCheckTimeout) -gt (Get-Date))
if ($done -eq $null) {
Write-Host "Didn't get an INSYNC status response within $changeCheckTimeout minutes, exiting without swapping SSL Certs."
Exit
}
Write-Host "`nPreparing to swap SSL Certificates between my-eb-environment-a and my-eb-environment-b..."
# This gets the current SSLCertificateId from my-eb-environment-a and my-eb-environment-b
# ElasticBeanstalk environment configurations, and swaps them.
# Get Environment Ids for my-eb-environment-a and my-eb-environment-b, used in update command below:
$envIdA = aws elasticbeanstalk describe-environments `
--environment-names "my-eb-environment-a" `
--output text `
--query "Environments[0].EnvironmentId"
$envIdB = aws elasticbeanstalk describe-environments `
--environment-names "my-eb-environment-b" `
--output text `
--query "Environments[0].EnvironmentId"
Write-Host "EnvironmentId for my-eb-environment-a: $envIdA"
Write-Host "EnvironmentId for my-eb-environment-b: $envIdB"
# Get the current SSL Certificate ARN for both environments:
$sslArnA = aws elasticbeanstalk describe-configuration-settings `
--application-name "my-application-name" `
--environment-name "my-eb-environment-a" `
--query "ConfigurationSettings[0].OptionSettings[?OptionName=='SSLCertificateId' && Namespace=='aws:elb:loadbalancer'].Value" `
--output text
$sslArnB = aws elasticbeanstalk describe-configuration-settings `
--application-name "my-application-name" `
--environment-name "my-eb-environment-b" `
--query "ConfigurationSettings[0].OptionSettings[?OptionName=='SSLCertificateId' && Namespace=='aws:elb:loadbalancer'].Value" `
--output text
Write-Host "Current SSL Cert ARN for my-eb-environment-a: $sslArnA"
Write-Host "Current SSL Cert ARN for my-eb-environment-b: $sslArnB"
# Swap the SSL Certificates between the ElasticBeanstalk environments:
Write-Host "Updating environment for my-eb-environment-a..."
aws elasticbeanstalk update-environment `
--environment-id $envIdA `
--option-settings "Namespace=aws:elb:loadbalancer,OptionName=SSLCertificateId,Value=$sslArnB"
Write-Host "Environment updated."
Write-Host "Updating environment for my-eb-environment-b..."
aws elasticbeanstalk update-environment `
--environment-id $envIdB `
--option-settings "Namespace=aws:elb:loadbalancer,OptionName=SSLCertificateId,Value=$sslArnA"
Write-Host "Environment updated."
Write-Host "`nSSL Certificate swap complete."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment