Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save drAlberT/5b48511ec4b891ccd88b1104e84229fe to your computer and use it in GitHub Desktop.
Save drAlberT/5b48511ec4b891ccd88b1104e84229fe to your computer and use it in GitHub Desktop.
Use private GitHub hosted terraform modules with AFT v1.5.1

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment