Terraform AWS modules from GitHub have become the default way teams codify reusable infrastructure for Lambda, VPCs, GitHub runners, and other AWS services. Using modules sourced directly from Git repositories gives version pinning, subdirectory reuse, and private access control, but the way you reference, vet, and monitor those modules determines stability and supply chain risk.
Why Module Source Matters for AWS Terraform
Modules from GitHub let you pull tested infrastructure patterns into your own code without copy-paste. For AWS workloads the most common consumption models are public registry modules with GitHub backing, direct Git URLs for internal catalogs, and short syntax shortcuts for verified public repos.
Favor certified modules from the Terraform Registry that are published by verified creators such as AWS or HashiCorp partners. For custom modules, review publisher history, support levels, and usage reputation, even if the module is from your own organization. By not allowing modules from unknown or unvetted sources, you can reduce the risk of injecting vulnerabilities or maintenance issues into your code.
Consuming modules only from trusted sources and monitoring changes provide stability and security. Vetted modules enhance productivity while minimizing supply chain risk.
Trusted Sources and Supply Chain Hygiene
Using modules only from trusted sources is the first control.
- Favor certified modules from the Terraform Registry that are published by verified creators such as AWS or HashiCorp partners.
- For custom modules, review publisher history, support levels, and usage reputation, even if the module is from your own organization.
- By not allowing modules from unknown or unvetted sources, you can reduce the risk of injecting vulnerabilities or maintenance issues into your code.
Monitoring is the second control.
Subscribe to notifications for new module releases from trusted publishers:
- Watch GitHub module repositories to get alerts on new versions of the module.
- Monitor publisher blogs and changelogs for updates.
- Get proactive notifications for new versions from verified, highly rated sources instead of implicitly pulling in updates.
Contributing to community modules also improves the ecosystem you depend on.
Submit fixes and enhancements for community modules that are hosted in GitHub:
- Open pull requests on modules to address defects or limitations that you encounter in your usage.
- Request new best practice configurations to be added to existing OSS modules by creating issues.
Contributing to community modules enhances reusable, codified patterns for all Terraform practitioners.
Git Source URL Patterns for Terraform Modules
Terraform supports loading modules from Git repositories using either HTTPS, SSH, or GitHub short syntax. The structure of a Terraform Git source URL follows:
git::<protocol>://<host>/<repo>.git//<subdir>?ref=<branch|tag|commit>
| Part | Example |
|---|---|
| git:: prefix | tells Terraform to treat it as a Git source |
| https:// or ssh:// | Git protocol |
| //modules/x | points to subdirectory inside the repo |
| ?ref= | tag, branch, or commit hash |
GitHub HTTPS
Use this when you don’t want SSH keys or are using public repos.
javascript
module "vpc" {
source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git"
}
With Subdirectory
javascript
module "s3" {
source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket"
}
With Tag / Branch
javascript
source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket?ref=v1.2.0"
source = "git::https://github.com/your-org/terraform-modules.git//modules/s3_bucket?ref=main"
GitHub SSH
Use SSH if you’re pulling private modules using a deploy key or SSH agent.
javascript
module "eks" {
source = "git::ssh://[email protected]/your-org/terraform-modules.git//modules/eks_cluster?ref=main"
}
Use git::ssh:// for SSH – don’t use raw [email protected].
GitHub Short Syntax
Short syntax works only for the root of the repo, no subdirectories, no tags.
javascript
module "vpc" {
source = "github.com/terraform-aws-modules/terraform-aws-vpc"
}
This works only for the root of the repo and does not support subdirectory selection.
GitLab HTTPS
javascript
module "firewall" {
source = "git::https://gitlab.com/your-org/infra-modules.git//firewall?ref=main"
}
Bitbucket HTTPS
javascript
module "app" {
source = "git::https://bitbucket.org/your-team/terraform-modules.git//modules/app?ref=release-1.0"
}
Private Git Repos with SSH
javascript
module "db" {
source = "git::ssh://[email protected]/your-org/terraform-db-modules.git//rds?ref=main"
}
Make sure your runner, Terraform Cloud or CI, has access via SSH key. You can also use .netrc or GIT_ASKPASS for HTTPS auth in private repos.
Pro Tips
| Tip | Reason |
|---|---|
| Always pin a tag using ?ref= | Prevents accidental upgrades from moving branches |
Practical AWS Module Examples
GitHub Runner Module
The Terraform module requires configuration from the GitHub App and the GitHub App requires output from Terraform. Therefore you first create the GitHub App and configure the basics, then run Terraform, and afterwards finalize the configuration of the GitHub App.
Setup GitHub App part 1
Go to GitHub and create a new app. Be aware you can create apps for your organization or for a user. For now, we only support organization level apps.
- Create an app in GitHub
- Choose a name
- Choose a website, mandatory, not required for the module
- Disable the webhook for now, we will configure this later or create an alternative webhook
- Permissions for all runners:
- Repository:
- Actions: Read-only, check for queued jobs
- Checks: Read-only, receive events for new builds
- Metadata: Read-only, default/required
- Permissions for repo level runners only:
- Repository:
- Administration: Read & write, to register runner
- Repository:
- Permissions for organization level runners only:
- Organization
- Self-hosted runners: Read & write, to register runner
- Organization
- Repository:
- Save the new app
- On the General page, make a note of the "App ID" and "Client ID" parameters
- Generate a new private key and save the
app.private-key.pemfile
Setup terraform module
Download lambdas
To apply the terraform module, the compiled lambdas .zip files need to be available either locally or in an S3 bucket.
They can either be downloaded from the GitHub release page or built locally. To read the files from S3, set the lambda_s3_bucket variable and the specific object key for each lambda.
The lambdas can be downloaded manually from the release page or using the download-lambda terraform module, requires curl to be installed on your machine. In the download-lambda directory, run terraform init && terraform apply. The lambdas will be saved to the same directory.
For local development you can build all the lambdas at once using .ci/build.sh or individually using yarn dist.
Service-linked role
To create spot instances the AWSServiceRoleForEC2Spot role needs to be added to your account. You can do that manually by following the AWS docs. To use terraform for creating the role, either add the following resource or let the module manage the service linked role by setting create_service_linked_role_spot to true. Be aware this is an account global role, so maybe you don't want to manage it via a specific deployment.
javascript
resource "aws_iam_service_linked_role" "spot" {
aws_service_name = "spot.amazonaws.com"
}
Terraform module
Next create a second terraform workspace and initiate the module, or adapt one of the examples.
Note that github_app.key_base64 needs to be a base64-encoded string of the .pem file i.e. the output of base64 app.private-key.pem. The decoded string can either be a multiline value or a single line value with new lines represented with literal characters.
javascript
module "github-runner" {
source = "github-aws-runners/github-runner/aws"
version = "REPLACE_WITH_VERSION"
aws_region = "eu-west-1"
vpc_id = "vpc-123"
subnet_ids = ["subnet-123", "subnet-456"]
prefix = "gh-ci"
github_app = {
key_base64 = "base64string"
id = "1"
webhook_secret = "webhook_secret"
}
webhook_lambda_zip = "lambdas-download/webhook.zip"
runner_binaries_syncer_lambda_zip = "lambdas-download/runner-binaries-syncer.zip"
runners_lambda_zip = "lambdas-download/runners.zip"
enable_organization_runners = true
}
Run terraform by using the following commands
terraform init
terraform apply
The terraform output displays the API gateway url, endpoint, and secret, which you need in the next step.
The lambda for syncing the GitHub distribution to S3 is triggered via CloudWatch, by default once per hour. After deployment the function is triggered via S3 to ensure the distribution is cached.
Set up the webhook / GitHub App part 2
At this point you have two options. Either create a separate webhook, enterprise, org, or repo, or create a webhook in the App.
Option 1: Webhook
- Create a new webhook at the repo level for repo level runners, or org for org level runners
- Provide the webhook url, which should be part of the output of terraform
- Provide the webhook secret,
terraform output -raw <NAME_OUTPUT_VAR> - Ensure the content type is
application/json
AWS Lambda Module
Terraform module, which creates almost all supported AWS Lambda resources as well as taking care of building and packaging of required Lambda dependencies for functions and layers.
This Terraform module is the part of serverless.tf framework, which aims to simplify all operations when working with the serverless in Terraform:
- Build and install dependencies - read more
The module provides reusable patterns for Lambda packaging, IAM, permissions, and layer management from a GitHub sourced module.
Best Practices for GitHub-Sourced AWS Modules
- Pin references. Always use
?ref=with an immutable tag or commit hash. Branch references move and cause non-deterministic plans. - Use subdirectories to keep a single monorepo for multiple AWS modules. The
//pathsegment lets you expose only the intended module. - Separate authentication from code. For private repos use SSH deploy keys or OIDC, and ensure Terraform Cloud or CI runners have access via SSH key. You can also use
.netrcorGIT_ASKPASSfor HTTPS auth in private repos. - Monitor changes. Watch GitHub module repositories to get alerts on new versions, monitor publisher blogs and changelogs for updates, and get proactive notifications for new versions from verified, highly rated sources instead of implicitly pulling in updates.
- Vet publishers. Favor certified modules from the Terraform Registry that are published by verified creators such as AWS or HashiCorp partners. For custom modules, review publisher history, support levels, and usage reputation, even if the module is from your own organization.
Conclusion
Sourcing Terraform AWS modules from GitHub gives teams speed and reuse, but only when the source is vetted, the reference is pinned, and changes are monitored. Using Git URLs with explicit protocol, host, subpath, and ref provides precise control over what code enters your AWS environment. Combining trusted publisher selection, notification subscriptions, and active contribution back to community modules creates a secure, stable supply chain for infrastructure as code.