Terraform AWS Modules From GitHub: Patterns, Security, and Production Workflows

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
    • Permissions for organization level runners only:
      • Organization
        • Self-hosted runners: Read & write, to register runner
  • 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.pem file

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 //path segment 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 .netrc or GIT_ASKPASS for 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.

Sources

  1. AWS Prescriptive Guidance
  2. DevOps School Terraform Git Source Patterns
  3. GitHub AWS Runners Terraform Module
  4. Terraform AWS Modules Lambda

Related Posts