Skip to content

Commit

Permalink
feat: Add initial buildspec.yml for tf-run-executor configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
Dave Arnold committed May 11, 2026
1 parent 87c0926 commit 97921ff
Showing 1 changed file with 225 additions and 0 deletions.
225 changes: 225 additions & 0 deletions buildspec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
version: 0.2

# ---------------------------------------------------------------------------
# tf-run-executor buildspec
#
# Required env-var overrides per build (supplied by Lambda or manual CLI):
# ACCOUNT_REPO - account repo name, e.g. 229685449397-csvd-dev-platform-dev-gov
# LAYER - terraform layer: common | infrastructure | vpc
# REGION_DIR - region directory: east | west
# GITHUB_TOKEN - GHE PAT (type PLAINTEXT, value from Secrets Manager)
#
# Optional env-var overrides:
# GIT_BRANCH - branch to commit/PR from (default: repo-init)
# TF_RUN_START_TAG - tf-run.data TAG label to start from (default: empty = from top)
# TEMPLATE_REPO - GHE repo containing Jinja2/.tf template files (default: empty)
# TEMPLATE_VARS - JSON map of Jinja2 variables for template rendering (default: {})
# EXTRA_FILES - JSON map {"relative/path": "content"} written after template rendering
# DRY_RUN - "true" = tf plan only, no apply (default: "false")
# TARGET_ACCOUNT_ID - AWS account ID to assume role in before running tf-run
# (default: empty = run with CodeBuild's own credentials,
# i.e. csvd-dev. Set this when targeting a different account.)
# ---------------------------------------------------------------------------

env:
variables:
GITHUB_ORG: "SCT-Engineering"
# S3 prefixes — filenames are resolved at build time from terraform/support VERSION files.
# The S3 bucket must contain the version pinned in terraform/support (keep in sync).
TF_BINARY_S3_PREFIX: "s3://csvd-packer-pipeline-assets/terraform"
GH_CLI_S3_PREFIX: "s3://csvd-packer-pipeline-assets/tools"
CENSUS_CA_S3: "s3://csvd-packer-pipeline-assets/certs/census-ca.pem"
# Org-canonical version governance: clone this repo to read VERSION files
TERRAFORM_SUPPORT_REPO: "terraform/support"
HTTPS_PROXY: "http://proxy.tco.census.gov:3128"
NO_PROXY: "github.e.it.census.gov,169.254.169.254,169.254.170.2"
# Per-build defaults (overridden via environmentVariablesOverride in Lambda)
GIT_BRANCH: "repo-init"
DRY_RUN: "false"
TF_RUN_START_TAG: ""
TEMPLATE_REPO: ""
TEMPLATE_VARS: "{}"
EXTRA_FILES: "{}"
TARGET_ACCOUNT_ID: ""

phases:
install:
commands:
# --- Version governance: clone terraform/support to read org-canonical versions ---
# This repo (github.e.it.census.gov/terraform/support) is the single source of truth
# for which Terraform and gh CLI versions the org has blessed. We read VERSION files
# from it rather than hardcoding versions here.
- git clone --depth 1 "https://${GITHUB_TOKEN}@github.e.it.census.gov/${TERRAFORM_SUPPORT_REPO}.git" /tmp/tf-support
- export TF_VERSION=$(cat /tmp/tf-support/terraform/VERSION)
- export GH_VERSION=$(cat /tmp/tf-support/github-cli-releases/VERSION)
- echo "Using Terraform ${TF_VERSION}, gh CLI ${GH_VERSION}"

# --- Terraform binary (registry.terraform.io is blocked on Census network; use S3) ---
# S3 bucket must contain the version pinned in terraform/support/terraform/VERSION.
- aws s3 cp "${TF_BINARY_S3_PREFIX}/terraform_${TF_VERSION}_linux_amd64.zip" /tmp/terraform.zip
- unzip -o /tmp/terraform.zip -d /usr/local/bin/ && chmod +x /usr/local/bin/terraform
- ln -sf /usr/local/bin/terraform /usr/local/bin/tf

# --- Census CA certificate (required for TLS to github.e.it.census.gov) ---
- aws s3 cp "$CENSUS_CA_S3" /etc/pki/ca-trust/source/anchors/census-ca.pem
- update-ca-trust extract

# --- tf-run toolchain (sourced from terraform/support, already cloned above) ---
# Canonical versions live in terraform/support local-app/ — no copies kept in this repo.
- cp /tmp/tf-support/local-app/tf-run/tf-run.sh /usr/local/bin/tf-run
- cp /tmp/tf-support/local-app/tf-control/tf-control.sh /usr/local/bin/tf-control.sh
- cp /tmp/tf-support/local-app/tf-directory-setup/tf-directory-setup.py /usr/local/bin/tf-directory-setup.py
- chmod +x /usr/local/bin/tf-run /usr/local/bin/tf-control.sh /usr/local/bin/tf-directory-setup.py
# Create tf-{action} symlinks expected by tf-run and account repo steps
- >
for action in init plan apply destroy refresh output validate import state fmt taint console; do
ln -sf /usr/local/bin/tf-control.sh /usr/local/bin/tf-${action};
done
# --- Python deps for tf-directory-setup.py and template rendering ---
- pip3 install --quiet jinja2 python-dateutil pyyaml

# --- gh CLI (S3 bucket must contain the version pinned in terraform/support) ---
- aws s3 cp "${GH_CLI_S3_PREFIX}/gh_${GH_VERSION}_linux_amd64.tar.gz" /tmp/gh.tar.gz
- mkdir -p /tmp/gh-cli
- tar -xzf /tmp/gh.tar.gz -C /tmp/gh-cli --strip-components=1
- cp /tmp/gh-cli/bin/gh /usr/local/bin/gh && chmod +x /usr/local/bin/gh

build:
commands:
# --- Configure git to rewrite SSH URLs to HTTPS ---
# Account repos reference Terraform modules via ssh://git@github.e.it.census.gov/...
# This rewrite makes those module fetches work transparently via HTTPS + PAT,
# avoiding the need for a per-repo deploy key.
- git config --global url."https://${GITHUB_TOKEN}@github.e.it.census.gov/".insteadOf "ssh://git@github.e.it.census.gov/"
- git config --global url."https://${GITHUB_TOKEN}@github.e.it.census.gov/".insteadOf "git@github.e.it.census.gov:"

# --- Clone account repo ---
- git clone "https://${GITHUB_TOKEN}@github.e.it.census.gov/${GITHUB_ORG}/${ACCOUNT_REPO}.git" repo
- cd repo
- git checkout -B "${GIT_BRANCH}"

# --- Render template repo (if specified) into account repo ---
# Clone TEMPLATE_REPO, render .j2 files with TEMPLATE_VARS via Jinja2,
# copy non-template files as-is. Results land in the account repo tree
# at the same relative paths. EXTRA_FILES applied afterwards can override.
- |
if [ -n "${TEMPLATE_REPO}" ]; then
git clone "https://${GITHUB_TOKEN}@github.e.it.census.gov/${GITHUB_ORG}/${TEMPLATE_REPO}.git" /tmp/template-repo
python3 - <<'PYEOF'
import json, os, pathlib, shutil
from jinja2 import Environment, FileSystemLoader, StrictUndefined
template_vars = json.loads(os.environ.get('TEMPLATE_VARS', '{}'))
src_root = pathlib.Path('/tmp/template-repo')
dst_root = pathlib.Path('.') # already inside cloned account repo
rendered = 0
copied = 0
for src in src_root.rglob('*'):
if src.is_dir() or any(part.startswith('.git') for part in src.parts):
continue
rel = src.relative_to(src_root)
if src.suffix == '.j2':
# Render Jinja2 template; strip .j2 extension in destination
dst = dst_root / rel.with_suffix('')
dst.parent.mkdir(parents=True, exist_ok=True)
env = Environment(
loader=FileSystemLoader(str(src.parent)),
undefined=StrictUndefined,
keep_trailing_newline=True,
)
content = env.get_template(src.name).render(**template_vars)
dst.write_text(content)
rendered += 1
else:
dst = dst_root / rel
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
copied += 1
print(f'Template repo: rendered {rendered} .j2 file(s), copied {copied} file(s)')
PYEOF
else
echo 'No TEMPLATE_REPO specified — skipping template rendering'
fi
# --- Write extra config files passed in from Lambda (JSON map path -> content) ---
# Applied after template rendering; keys here override template output.
- |
python3 -c "
import json, os, pathlib
files = json.loads(os.environ.get('EXTRA_FILES', '{}'))
for path, content in files.items():
p = pathlib.Path(path)
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(content)
print(f'Wrote {len(files)} extra file(s)')
"
# --- Commit and push (--allow-empty handles no-change case) ---
- git add -A
- |
git -c user.email="sc-automation@census.gov" \
-c user.name="SC Automation" \
commit -m "SC automation: ${LAYER}/${REGION_DIR} [${ACCOUNT_REPO}]" \
--allow-empty
- git push origin "${GIT_BRANCH}"

# --- Assume cross-account role (if TARGET_ACCOUNT_ID is set) ---
# CodeBuild runs in csvd-dev by default. To run tf-run apply against resources
# in a different AWS account, set TARGET_ACCOUNT_ID. The role
# sc-automation-codebuild-role must exist in that account and trust the
# CodeBuild IAM role from csvd-dev.
- |
if [ -n "${TARGET_ACCOUNT_ID}" ]; then
PARTITION=$(aws sts get-caller-identity --query Arn --output text | cut -d: -f2)
ROLE_ARN="arn:${PARTITION}:iam::${TARGET_ACCOUNT_ID}:role/sc-automation-codebuild-role"
echo "Assuming cross-account role: ${ROLE_ARN}"
CREDS=$(aws sts assume-role \
--role-arn "${ROLE_ARN}" \
--role-session-name "sc-automation-${ACCOUNT_REPO}" \
--query Credentials \
--output json)
export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | python3 -c "import json,sys; print(json.load(sys.stdin)['AccessKeyId'])")
export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | python3 -c "import json,sys; print(json.load(sys.stdin)['SecretAccessKey'])")
export AWS_SESSION_TOKEN=$(echo "$CREDS" | python3 -c "import json,sys; print(json.load(sys.stdin)['SessionToken'])")
echo "Successfully assumed role in account ${TARGET_ACCOUNT_ID}"
else
echo "No TARGET_ACCOUNT_ID set — running with CodeBuild role (csvd-dev)"
fi
# --- Run Terraform in target layer/region directory ---
# tf-run auto-proceeds on non-TTY stdin (read -t timeout defaults to "y")
- cd "${LAYER}/${REGION_DIR}"
- |
if [ "${DRY_RUN}" = "true" ]; then
tf-plan -no-color
elif [ -n "${TF_RUN_START_TAG}" ]; then
TFARGS="-auto-approve" tf-run apply "tag:${TF_RUN_START_TAG}"
else
TFARGS="-auto-approve" tf-run apply
fi
# --- Open PR (idempotent: skip if PR already exists) ---
- |
GH_HOST=github.e.it.census.gov \
GH_TOKEN="${GITHUB_TOKEN}" \
gh pr create \
--title "SC automation: ${LAYER}/${REGION_DIR} [${ACCOUNT_REPO}]" \
--body "Triggered by Service Catalog provisioning of **${ACCOUNT_REPO}**." \
--base main \
--head "${GIT_BRANCH}" \
|| echo "PR already exists or create failed, continuing"
post_build:
commands:
- echo "BUILD_RESULT=${CODEBUILD_BUILD_SUCCEEDING}"
# Emit PR_URL so Lambda can parse it from the build output
- |
PR_URL=$(GH_HOST=github.e.it.census.gov \
GH_TOKEN="${GITHUB_TOKEN}" \
gh pr view \
--repo "${GITHUB_ORG}/${ACCOUNT_REPO}" \
"${GIT_BRANCH}" \
--json url -q .url 2>/dev/null || echo "")
echo "PR_URL=${PR_URL}"

0 comments on commit 97921ff

Please sign in to comment.