A production-ready Terraform module that provisions AWS CodeBuild projects as GitHub Actions self-hosted runners. This module provides a cost-effective, scalable, and secure alternative to traditional self-hosted runners.
- β Repository & Organization Support: Runners for specific repositories or entire GitHub organizations
- β Secure Token Management: Support for AWS Secrets Manager or direct token input
- β Flexible Configuration: Multiple compute types, custom buildspecs, and environment variables
- β Security Hardened: Least privilege IAM policies, network isolation, and input validation
- β Monitoring & Logging: CloudWatch dashboards, alarms, and configurable log retention
- β VPC Integration: Private network deployment with security group management
- β Scalable Architecture: Configurable runner count and concurrent job support
NEVER hardcode GitHub tokens in Terraform files or commit them to version control. Use one of these secure approaches:
module "github_secure_runner" {
source = "your-repo/terraform-codebuild-runner/aws"
github_organization = "your-org-name"
github_token_secret_arn = aws_secretsmanager_secret.github_token.arn
enable_github_oidc = true
runner_name = "my-secure-runner"
runner_count = 3
compute_type = "BUILD_GENERAL1_LARGE"
runner_labels = ["self-hosted", "linux", "x64", "secure"]
vpc_id = "vpc-1234567890abcdef0"
subnet_ids = ["subnet-12345678", "subnet-87654321"]
tags = {
Project = "GitHub Actions Runners"
Environment = "Production"
AuthMethod = "OIDC"
}
}
variable "github_token" {
description = "GitHub personal access token"
type = string
sensitive = true
}
module "github_repo_runner" {
source = "your-repo/terraform-codebuild-runner/aws"
github_repository_url = "https://github.com/your-org/your-repo"
github_token = var.github_token
runner_name = "my-repo-runner"
runner_count = 2
compute_type = "BUILD_GENERAL1_MEDIUM"
runner_labels = ["self-hosted", "linux", "x64"]
vpc_id = "vpc-1234567890abcdef0"
subnet_ids = ["subnet-12345678", "subnet-87654321"]
tags = {
Project = "GitHub Actions Runners"
Environment = "Production"
}
}
Set token securely:
# Method 1: Environment variable
export TF_VAR_github_token="ghp_your_token_here"
# Method 2: terraform.tfvars (NEVER commit to git)
echo 'github_token = "ghp_your_token_here"' > terraform.tfvars
resource "aws_secretsmanager_secret" "github_token" {
name = "github-actions-runner-token"
recovery_window_in_days = 0
}
# Set secret value securely (not in Terraform)
# aws secretsmanager put-secret-value --secret-id ${aws_secretsmanager_secret.github_token.id} --secret-string "ghp_your_token_here"
module "github_org_runner" {
source = "your-repo/terraform-codebuild-runner/aws"
github_organization = "your-org-name"
github_token_secret_arn = aws_secretsmanager_secret.github_token.arn
runner_name = "my-org-runner"
runner_count = 3
compute_type = "BUILD_GENERAL1_LARGE"
runner_labels = ["self-hosted", "linux", "x64"]
vpc_id = "vpc-1234567890abcdef0"
subnet_ids = ["subnet-12345678", "subnet-87654321"]
tags = {
Project = "GitHub Actions Runners"
Environment = "Production"
}
}
- Terraform >= 1.0
- AWS Provider >= 4.0
- AWS account with appropriate permissions
- GitHub personal access token with
repo
scope (repository) oradmin:org
scope (organization)
Once deployed, use the runner in your GitHub Actions workflows:
name: Build
on: [push]
jobs:
build:
runs-on: [self-hosted, linux, x64]
steps:
- uses: actions/checkout@v4
- name: Run on CodeBuild runner
run: echo "Hello from self-hosted runner!"
name: Matrix Build
on: [push]
jobs:
test:
runs-on: [self-hosted, linux, x64]
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm test
- Repository-level Runner - Single repository runner with secure token management
- Organization-level Runner - Organization runner with Secrets Manager
- OIDC Secure Runner - Production-ready OIDC authentication (RECOMMENDED)
Name | Description | Type | Default | Required |
---|---|---|---|---|
github_repository_url |
GitHub repository URL for repository-level runners | string |
null |
NoΒΉ |
github_organization |
GitHub organization name for organization-level runners | string |
null |
NoΒΉ |
github_token |
GitHub personal access token | string |
null |
NoΒ² |
github_token_secret_arn |
AWS Secrets Manager secret ARN containing GitHub token | string |
null |
NoΒ² |
runner_name |
Name prefix for the runner | string |
"github-actions-runner" |
No |
runner_count |
Number of concurrent runners to provision | number |
1 |
No |
compute_type |
CodeBuild compute type | string |
"BUILD_GENERAL1_SMALL" |
No |
image |
Docker image to use for the runner | string |
"aws/codebuild/standard:6.0" |
No |
buildspec |
Custom buildspec content | string |
null |
No |
vpc_id |
VPC ID where the runner will be deployed | string |
- | Yes |
subnet_ids |
Subnet IDs where the runner will be deployed | list(string) |
- | Yes |
environment_variables |
Additional environment variables | map(string) |
{} |
No |
runner_labels |
Additional labels for the runner | list(string) |
[] |
No |
runner_group |
Runner group for organization-level runners | string |
"Default" |
No |
concurrent_jobs |
Number of concurrent jobs per runner | number |
1 |
No |
enable_cloudwatch_logs |
Enable CloudWatch logging | bool |
true |
No |
log_retention_days |
Number of days to retain CloudWatch logs | number |
30 |
No |
enable_github_oidc |
Enable GitHub OIDC integration for tokenless authentication | bool |
false |
No |
github_oidc_provider_url |
GitHub OIDC provider URL | string |
"https://token.actions.githubusercontent.com" |
No |
github_oidc_audience |
GitHub OIDC audience | string |
"sts.amazonaws.com" |
No |
tags |
Tags to apply to all resources | map(string) |
{} |
No |
ΒΉ Either github_repository_url
or github_organization
must be provided
Β² Either github_token
or github_token_secret_arn
must be provided
Type | vCPU | Memory | Use Case |
---|---|---|---|
BUILD_GENERAL1_SMALL |
2 | 3 GB | Simple builds, tests |
BUILD_GENERAL1_MEDIUM |
4 | 8 GB | Medium builds, compilation |
BUILD_GENERAL1_LARGE |
8 | 16 GB | Large builds, complex tasks |
BUILD_GENERAL1_2XLARGE |
16 | 64 GB | Heavy workloads, containers |
Name | Description |
---|---|
codebuild_project_name |
Name of the CodeBuild project |
codebuild_project_arn |
ARN of the CodeBuild project |
iam_role_arn |
ARN of the IAM role for the CodeBuild project |
security_group_id |
ID of the security group for the runner |
github_oidc_role_arn |
ARN of the GitHub OIDC role (if OIDC is enabled) |
github_oidc_provider_arn |
ARN of the GitHub OIDC provider (if OIDC is enabled) |
- Tokenless authentication using GitHub's OIDC provider
- Temporary credentials with automatic expiration
- Repository-specific access control
- Audit trail for all authentication attempts
- No persistent secrets to manage or rotate
- AWS Secrets Manager integration for secure token storage
- Encrypted storage using AWS KMS
- Automatic rotation capability
- Fine-grained access policies
- Access logging and monitoring
- Regional restrictions for EC2 operations
- Resource-specific ARNs rather than wildcards
- Explicit deny statements for sensitive services
- Conditional access based on resources
- Minimal required permissions only
- VPC integration with configurable subnets
- Security groups with proper egress rules
- Private network deployment support
- VPC endpoints for private communication
- Network isolation and segmentation
- CloudWatch logging with configurable retention
- Security alerts and notifications
- Audit trails with CloudTrail integration
- Build failure monitoring
- Resource usage tracking
- NEVER hardcode GitHub tokens in Terraform files
- NEVER commit
.tfvars
files containing secrets to version control - NEVER share GitHub tokens in plain text
- ALWAYS use OIDC authentication for production workloads
- ALWAYS rotate tokens regularly (every 90 days)
- ALWAYS use the principle of least privilege
# GitHub Actions workflow
permissions:
id-token: write
contents: read
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ vars.OIDC_ROLE_ARN }}
aws-region: us-east-1
# Set token in environment
export TF_VAR_github_token="ghp_your_token_here"
# Or use .tfvars file (NEVER commit to git)
echo 'github_token = "ghp_your_token_here"' > terraform.tfvars
# Create secret
aws secretsmanager create-secret \
--name github-actions-runner-token \
--description "GitHub token for runners"
# Set secret value securely
aws secretsmanager put-secret-value \
--secret-id github-actions-runner-token \
--secret-string "ghp_your_token_here"
resource "aws_iam_role" "codebuild_role" {
name = "${var.runner_name}-codebuild-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "codebuild.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_security_group" "runner_sg" {
name = "${var.runner_name}-sg"
description = "Security group for GitHub Actions runner"
vpc_id = var.vpc_id
# Restrictive egress rules
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_cloudwatch_metric_alarm" "build_failure_alarm" {
alarm_name = "${var.runner_name}-build-failure-alarm"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "FailedBuilds"
namespace = "AWS/CodeBuild"
period = 300
statistic = "Sum"
threshold = 1
dimensions = {
ProjectName = var.runner_name
}
}
# Monitor OIDC usage
aws logs filter-log-events \
--log-group-name /aws/cloudtrail \
--filter-pattern "AssumeRoleWithWebIdentity"
# Check for suspicious activity
aws logs filter-log-events \
--log-group-name /aws/codebuild/your-runner-name \
--filter-pattern "ERROR|FAILED|DENIED"
# Create Lambda function for token rotation
aws lambda create-function \
--function-name github-token-rotator \
--runtime python3.9 \
--role your-lambda-role-arn \
--handler lambda_function.lambda_handler \
--zip-file fileb://rotate-token.zip
# Set up CloudWatch Events for monthly rotation
aws events put-rule \
--name github-token-monthly-rotation \
--schedule-expression "cron(0 0 1 * ? *)"
# Add Lambda target
aws events put-targets \
--rule github-token-monthly-rotation \
--targets "Id"="1","Arn"="your-lambda-function-arn"
- Log Groups: Configurable retention (1-365 days)
- Dashboards: Build metrics, duration, success/failure rates
- Alarms: Build failure notifications
- Metrics: Build count, duration, success rates
# View runner logs
aws logs tail /aws/codebuild/your-runner-name --follow
# Check build status
aws codebuild batch-get-projects --names your-runner-name
buildspec = <<-EOT
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
python: 3.11
commands:
- echo "Installing custom tools..."
pre_build:
commands:
- pip install -r requirements.txt
build:
commands:
- npm run build
- npm test
post_build:
commands:
- echo "Build completed successfully"
EOT
environment_variables = {
NODE_VERSION = "18"
PYTHON_VERSION = "3.9"
DOCKER_VERSION = "20.10"
CUSTOM_VAR = "custom-value"
}
vpc_id = "vpc-1234567890abcdef0"
subnet_ids = ["subnet-12345678", "subnet-87654321"]
# Additional security groups
security_group_ids = [
aws_security_group.additional_sg.id,
aws_security_group.runner_sg.id
]
- Use
BUILD_GENERAL1_SMALL
for simple tasks - Monitor build times and adjust compute types
- Scale runner count based on demand
Enable local caching in custom buildspecs:
cache:
type: LOCAL
modes:
- LOCAL_CUSTOM_CACHE
- LOCAL_SOURCE_CACHE
- Set up CloudWatch billing alerts
- Monitor CodeBuild usage patterns
- Track runner utilization rates
# Verify token permissions
curl -H "Authorization: token YOUR_TOKEN" https://api.github.com/user
# Check repository access
curl -H "Authorization: token YOUR_TOKEN" https://api.github.com/repos/owner/repo
# Check security group rules
aws ec2 describe-security-groups --group-ids your-sg-id
# Test connectivity to GitHub
curl -I https://github.com
# Check CloudWatch logs
aws logs tail /aws/codebuild/your-runner-name --follow
# Check CodeBuild project status
aws codebuild batch-get-projects --names your-runner-name
# Check IAM role
aws iam get-role --role-name your-runner-codebuild-role
# Test role permissions
aws iam simulate-principal-policy --policy-source-arn your-role-arn --action-names "logs:CreateLogGroup"
- AWS CLI configured with appropriate credentials
- Terraform installed locally
- GitHub personal access token for testing
# Clone repository
git clone https://github.com/your-org/terraform-codebuild-runner.git
cd terraform-codebuild-runner
# Test repository example
cd examples/github-repo
terraform init
terraform plan
# Test organization example
cd ../github-org
terraform init
terraform plan
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Update documentation
- Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation
- π Issue Tracker
- π¬ Discussions
- π§ Email Support
- Initial stable release
- Repository and organization-level runner support
- Comprehensive security hardening
- CloudWatch monitoring and dashboards
- Complete test coverage and documentation