Overview
The starter kit supports multiple isolated environments (e.g., staging, production) with independent state management and deployment workflows. Each environment lives in its own directory under environments/.
Note: All environment files and GitHub Actions workflows discussed on this page are automatically generated by the make setup command. You don't need to create these files manually. For installation instructions, see the Install guide.
Environment structure
Each environment directory contains:
environments/
└── staging/
├── backend.tf # Terraform and S3 backend configuration
├── main.tf # Provider and module configuration
├── variables.tf # Input variable definitions
└── outputs.tf # Output values from the OIDC module
backend.tf
Configures Terraform version requirements, required providers, and the S3 backend for state storage:
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "terraform-state-123456789012-eu-west-1"
key = "environments/staging/terraform.tfstate"
region = "eu-west-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
}
}
Key features:
- State encryption:
encrypt = trueenables AES-256 server-side encryption - State locking: DynamoDB table prevents concurrent modifications
- Unique state key: Each environment uses its own path (e.g.,
environments/staging/terraform.tfstate)
main.tf
Configures the AWS provider with default tags and calls the OIDC provider module:
# Configure the AWS Provider
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = "staging"
ManagedBy = "terraform"
Repository = "towardsthecloud/aws-terraform-starter-kit-demo"
}
}
}
# OIDC Provider Module
module "oidc_provider" {
source = "../../modules/oidc-provider"
use_existing_oidc_provider = var.use_existing_oidc_provider
github_repo = var.github_repo
role_name = var.role_name
managed_policy_arns = var.managed_policy_arns
}
Key features:
- Default tags: Automatically applied to all resources created by the provider
- Module-based: Uses the
oidc-providermodule frommodules/directory - Configurable OIDC: Can create or reuse existing OIDC providers
variables.tf
Defines all input variables with defaults:
variable "aws_region" {
description = "AWS region for resources"
type = string
default = "eu-west-1"
}
variable "use_existing_oidc_provider" {
description = "Whether to use an existing OIDC provider or create a new one"
type = bool
default = true
}
variable "github_repo" {
description = "GitHub repository name (format: owner/repo)"
type = string
default = "towardsthecloud/aws-terraform-starter-kit-demo"
}
variable "role_name" {
description = "Name of the IAM role for GitHub Actions"
type = string
default = "GitHubActionsServiceRole-Terraform"
}
variable "managed_policy_arns" {
description = "List of IAM policy ARNs to attach to the role"
type = list(string)
default = [
"arn:aws:iam::aws:policy/AdministratorAccess"
]
}
Variables explained:
- aws_region: AWS region for deploying resources
- use_existing_oidc_provider: Set to
truewhen deploying multiple environments in the same AWS account (OIDC provider can only exist once per account) - github_repo: Your GitHub repository in
owner/repoformat - role_name: IAM role name for GitHub Actions authentication
- managed_policy_arns: AWS managed policies attached to the IAM role (default is AdministratorAccess for full permissions)
outputs.tf
Exports values from the OIDC module:
output "oidc_provider_arn" {
description = "ARN of the GitHub OIDC provider"
value = module.oidc_provider.oidc_provider_arn
}
output "role_arn" {
description = "ARN of the GitHub Actions IAM role"
value = module.oidc_provider.role_arn
}
output "role_name" {
description = "Name of the GitHub Actions IAM role"
value = module.oidc_provider.role_name
}
These outputs are used to configure GitHub Actions workflows with the correct IAM role ARN.
GitHub Actions workflow
Each environment gets its own dedicated GitHub Actions workflow file that is automatically generated during setup. For the staging environment, this is .github/workflows/terraform-deploy-staging.yml.
The workflow handles the complete deployment lifecycle:
Workflow triggers:
- Pushes to
mainbranch affecting staging files - Pull requests targeting
main - Manual workflow dispatch
Pipeline stages:
- TFLint Scan: Lints Terraform code for syntax and best practices
- Checkov Security Scan: Scans for security misconfigurations
- Terraform Check: Validates formatting, initialization, and configuration
- Terraform Plan: (on PRs) Generates and posts plan to PR comments
- Terraform Apply: (on push to main) Deploys changes to AWS
Key features:
- OIDC authentication: Uses the IAM role created by the OIDC module (no long-lived credentials)
- Environment protection: Uses GitHub's
environment: stagingfor approval workflows - State management: Configures S3 backend dynamically from environment variables
- Plan artifacts: Saves and uploads plans for review
Environment variables in the workflow:
env:
AWS_ACCOUNT_ID: ${{ vars.AWS_ACCOUNT_ID || '123456789012' }}
AWS_REGION: ${{ vars.AWS_REGION || 'eu-west-1' }}
GITHUB_ACTIONS_ROLE_NAME: ${{ vars.GITHUB_ACTIONS_ROLE_NAME || 'GitHubActionsServiceRole-Terraform' }}
ENVIRONMENT: staging
TF_WORKING_DIR: environments/staging
TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET || 'terraform-state-123456789012-eu-west-1' }}
TF_STATE_LOCK_TABLE: ${{ vars.TF_STATE_LOCK_TABLE || 'terraform-state-lock' }}
These can be customized via GitHub repository variables or use the provided defaults.
Adding a new environment
To add a new environment (e.g., production), use the setup wizard:
make setup
For detailed setup options and non-interactive usage, see the Makefile reference.
The wizard will:
- Detect if you already have a backend (reuses existing S3 bucket and DynamoDB table)
- Prompt you to select the new environment name
- Generate all necessary files:
environments/production/backend.tfenvironments/production/main.tfenvironments/production/variables.tfenvironments/production/outputs.tf.github/workflows/terraform-deploy-production.yml
- Check for existing OIDC provider in your AWS account
- Deploy the OIDC infrastructure for the new environment
- Display the IAM role ARN to use in GitHub Actions
Multi-account setup: If deploying to a different AWS account, switch your AWS credentials before running make setup:
# Switch to production account
assume production-account
# Run setup wizard for production environment
make setup # Select: production
For detailed setup instructions and options, see the Install guide.
Multi-account vs. single-account deployments
Single-account deployment
When deploying multiple environments to the same AWS account, remember that the OIDC provider can only be created once per account:
- First environment: Set
use_existing_oidc_provider = falseto create the OIDC provider - Additional environments: Set
use_existing_oidc_provider = trueto reuse the existing provider
Multi-account deployment
When deploying to separate AWS accounts, each account needs its own OIDC provider:
- Each environment: Set
use_existing_oidc_provider = false
This provides complete isolation between environments with separate IAM configurations.
Default tags
The default_tags block in main.tf automatically applies tags to all resources created by Terraform:
default_tags {
tags = {
Environment = "staging"
ManagedBy = "terraform"
Repository = "towardsthecloud/aws-terraform-starter-kit-demo"
}
}
This ensures consistent tagging across all AWS resources without repeating tag blocks on each resource.
IAM role permissions
The managed_policy_arns variable controls what permissions the GitHub Actions IAM role has. The default is AdministratorAccess for full permissions:
managed_policy_arns = [
"arn:aws:iam::aws:policy/AdministratorAccess"
]
Next steps
- Learn about the Project Structure
- Explore the OIDC provider module implementation