I'll try to share my approach to use private GitHub hosted terraform modules with AFT v1.5.1. It relies on GH App to create ephemeral tokens during Global Customization stage which will share with the target account so it can be used during Account Customization stage.
Relates to: aws-ia/terraform-aws-control_tower_account_factory#42
Pre-requirements:
- Create a GH APP:
- Permissions: allow the clone of repositories
- Set to a restricted list of terraform modules repos
- Create parameter store entries for GH_APP pem, id and installation_id under AFT_MGT account
module "aft" {
source = "github.com/aws-ia/terraform-aws-control_tower_account_factory?ref=1.5.1"
....
}
provider "aws" {
alias = "aft_management"
region = var.ct_home_region
assume_role {
role_arn = "arn:aws:iam::${var.aft_management_account_id}:role/AWSControlTowerExecution"
session_name = "AWSAFT-Session"
}
}
resource "aws_ssm_parameter" "app_pem" {
provider = aws.aft_management
name = "/gh/tf-modules-app/pem"
type = "SecureString"
value = "TODO"
depends_on = [
module.aft
]
}
resource "aws_ssm_parameter" "app_id" {
provider = aws.aft_management
name = "/gh/tf-modules-app/id"
type = "SecureString"
value = "TODO"
depends_on = [
module.aft
]
}
resource "aws_ssm_parameter" "app_installation_id" {
provider = aws.aft_management
name = "/gh/tf-modules-app/installation-id"
type = "SecureString"
value = "TODO"
depends_on = [
module.aft
]
lifecycle {
ignore_changes = [
value
]
}
}
Then in pre-api-helpers.sh
of the global customizations
we'll create the ephemeral GH token and share it with the vended account:
#!/bin/bash
set -e
echo "Executing Pre-API Helpers"
export AWS_PROFILE=aft-management
gh_app_pem_file='gh_app.pem'
aws ssm get-parameter --name "/gh/tf-modules-app/pem" --query "Parameter.Value" --with-decryption --output text > $gh_app_pem_file
gh_app_id=$(aws ssm get-parameter --name "/gh/tf-modules-app/id" --query "Parameter.Value" --with-decryption --output text)
gh_app_installation_id=$(aws ssm get-parameter --name "/gh/tf-modules-app/installation-id" --query "Parameter.Value" --with-decryption --output text)
gem install jwt
cat >jwt.rb <<EOF
require 'openssl'
require 'jwt' # https://rubygems.org/gems/jwt
# Private key contents
private_pem = File.read("$gh_app_pem_file")
private_key = OpenSSL::PKey::RSA.new(private_pem)
# Generate the JWT
payload = {
# issued at time, 60 seconds in the past to allow for clock drift
iat: Time.now.to_i - 60,
# JWT expiration time (10 minute maximum)
exp: Time.now.to_i + (10 * 60),
# GitHub App's identifier
iss: "$gh_app_id"
}
jwt = JWT.encode(payload, private_key, "RS256")
puts jwt
EOF
jwt=$(ruby jwt.rb)
github_api_url="https://api.github.com/app/installations/$gh_app_installation_id/access_tokens"
r=$(curl --fail-with-body -s -X POST \
-H "Authorization: Bearer ${jwt}" \
-H "Accept: application/vnd.github.v3+json" \
"${github_api_url}")
echo $(echo "$r" | jq -r '.token') > token.txt
export AWS_PROFILE=aft-target
aws ssm put-parameter \
--name "/gh/tf-modules-app/token" \
--value "$(cat token.txt)" \
--type SecureString \
--overwrite
rm token.txt
rm jwt.rb
Finally, during the Account Customization
stage we'll get the token and configure git auth with GH cli.
# #!/bin/bash
set -e
echo "Executing Pre-API Helpers"
gh_cli_version=2.13.0
wget -q https://github.com/cli/cli/releases/download/v$gh_cli_version/gh_$gh_cli_version\_linux_386.rpm
sudo rpm -i gh_$gh_cli_version\_linux_386.rpm
gh --version
aws ssm get-parameter --name "/gh/tf-modules-app/token" --query "Parameter.Value" --with-decryption --output text > token.txt
gh auth login --with-token < token.txt
gh repo list
gh auth setup-git
export GH_TOKEN=$(cat token.txt)
rm token.txt
And thats it, now you can use private terraform modules.
Building on the above example. Here is the same in python:
In the AFT-Management account create 3 parameters in ssm parameter store named:
/github/apps/aft_terraform_modules/app_id
/github/apps/aft_terraform_modules/app_installation_id
/github/apps/aft_terraform_modules/app_private_key
In
aft-global-customizations/api_helpers/pre-api-helpers.sh
add python deps in
aft-global-customizations/api_helpers/python/requirements.txt
for the script itself
aft-global-customizations/api_helpers/get-github-token.py
then do the same as the above examples in
aft-account-customizations/<my account>/api_helpers/pre-api-helpers.sh