Most CI/CD guides stop at "Terraform provisions your infrastructure." But there's a gap nobody talks about: what happens to the values Terraform just created?
After terraform apply, you have a fresh LAMBDA_URL and an SNS_TOPIC_ARN. Your GitHub Actions pipeline needs both to run smoke tests and send alerts. The typical solution? Copy-paste them manually into GitHub Secrets. Every. Single. Time.
This is exactly what I eliminated in my v2.1 project — and it took the deployment from "almost automated" to genuinely zero manual steps.
The Problem: The Last Manual Step
Here's the scenario. You run Terraform, it creates:
- A Lambda Function URL (dynamically assigned by AWS)
- An SNS topic ARN (unique per account/region)
Your CI/CD pipeline references both of these as GitHub Secrets. But Terraform just created them — so the secrets are stale or missing until someone updates them manually.
This is a surprisingly common pattern in otherwise well-automated setups. The infrastructure is IaC, the pipeline is automated, but there's always one human in the loop doing:
Settings → Secrets → Update → Paste value → Save
Not great.
The Solution: Terraform Writes Back to GitHub
The GitHub Terraform Provider lets you manage GitHub resources — including repository secrets — as Terraform resources.
Here's the core idea:
provider "github" {
owner = "your-github-username"
app_auth {
id = var.github_app_id
installation_id = var.github_app_installation_id
pem_file = file(var.github_app_pem_file_path)
}
}
resource "github_actions_secret" "lambda_url" {
repository = "your-app-repo"
secret_name = "LAMBDA_URL"
plaintext_value = aws_lambda_function_url.main.function_url
}
resource "github_actions_secret" "sns_topic_arn" {
repository = "your-app-repo"
secret_name = "SNS_TOPIC_ARN"
plaintext_value = aws_sns_topic.alerts.arn
}
After terraform apply, both secrets are updated automatically. No human involved.
Why GitHub App Auth (Not a Personal Token)
You might be tempted to use a personal access token for the GitHub provider. Don't.
A GitHub App gives you:
- Scoped permissions — only what it needs (secrets write on specific repos)
- No personal token rotation — the App uses a short-lived JWT, not a long-lived secret
- Audit trail — actions are attributed to the App, not your personal account
To set it up, you need three values in your terraform.tfvars (never commit this file):
github_app_id = "12345"
github_app_installation_id = "67890"
github_app_pem_file_path = "./github-app-private-key.pem"
These stay local. The GitHub Provider uses them to generate short-lived tokens at apply time.
The Full Zero-Manual-Step Loop
Once this is in place, the complete flow looks like this:
-
terraform applyruns - AWS creates Lambda Function URL + SNS Topic (dynamic values)
- Terraform reads those output values
- GitHub Provider writes them directly to GitHub Secrets
- GitHub Actions pipeline picks them up on the next run — no stale values, no manual update
The pipeline can then safely run a smoke test against the freshly deployed Lambda URL, and the CloudWatch alarm already knows where to send alerts via SNS.
This closes what I call the infrastructure feedback loop: deployed infrastructure automatically feeds its runtime configuration back into the CI/CD system.
What This Looks Like End-to-End
In my project (v2.1 of an agnostic serverless Lambda core), the full CI/CD flow after this change is:
- Push to
main - GitHub Actions checks out the app repo, runs quality gates (flake8, bandit, pytest)
- Docker build → push to ECR
- Lambda function updated with the new container image
- Smart smoke test: polls the Lambda URL for 40 seconds, checks the VERSION env var
- If anything fails, CloudWatch alarm fires → SNS → email
Zero manual steps. Zero stale secrets. The only local requirement is having the GitHub App credentials for the initial terraform apply.
Key Takeaways
- The GitHub Terraform Provider is underused — most teams still manage repo secrets by hand
- GitHub App auth is the right choice over personal tokens for automation
- Closing the infrastructure feedback loop (infra → CI/CD config) is the difference between "almost automated" and actually automated
- This pattern works for any dynamic value Terraform creates: API Gateway URLs, RDS endpoints, CloudFront distributions
Resources
- GitHub Provider docs — github_actions_secret
- v2.1 infrastructure repo
- v2 app repo with Locust performance reports
Ervin Wallin — Cloud & Platform Engineer. Building reproducible, secure, serverless systems.
AWS Certified Developer · HashiCorp Terraform Associate
Top comments (0)