This Terraform module provisions the baseline infrastructure and access controls for an AWS Landing Zone environment. It establishes secure pipeline access patterns using OIDC authentication, implements IAM permission boundaries, and configures security monitoring through CIS CloudWatch alarms.
- OIDC-based Pipeline Access: Creates IAM roles with OIDC authentication for secure CI/CD pipeline access to AWS resources
- IAM Permission Boundaries: Enforces security guardrails on pipeline roles in both Management and Audit accounts
- CIS CloudWatch Alarms: Monitors and alerts on security events based on CIS AWS Foundations Benchmark
- Multi-channel Notifications: Supports email, Slack, and Microsoft Teams notifications for security alerts
- Breakglass Access: Optional emergency access users with MFA enforcement
- AWS Support Integration: Deploys AWS Support role across all accounts via CloudFormation StackSets
- Cost Management: Dedicated IAM policies and roles for cost visibility and management
The module supports creating OIDC roles for the following pipeline types:
- Accelerator: Manages landing zone configuration and deployment
- Accounts: Provisions and manages AWS accounts via Organizations
- Bootstrap: Handles initial landing zone setup
- Compliance: Manages security and compliance configurations
- Cost Management: Controls costs and budgets
- Identity: Manages IAM Identity Center (formerly AWS SSO)
- Organizations: Configures AWS Organizations structure
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
version = "0.0.1"
aws_accounts = {
audit_account_id = "123456789012"
}
repositories = {
accelerator = {
url = "https://github.com/your-org/aws-accelerator-config"
}
identity = {
url = "https://github.com/your-org/terraform-aws-identity"
}
}
tags = {
Environment = "production"
ManagedBy = "terraform"
}
providers = {
aws.audit = aws.audit
aws.management = aws.management
}
}module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
version = "0.0.1"
aws_accounts = {
audit_account_id = "123456789012"
}
enable_aws_support = true
enable_breakglass = true
enable_cis_alarms = true
breakglass_users = 2
notifications = {
email = {
addresses = ["security@example.com", "ops@example.com"]
}
slack = {
webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
}
}
repositories = {
accelerator = {
url = "https://github.com/your-org/aws-accelerator-config"
role_name = "lz-aws-accelerator"
shared = ["https://github.com/your-org/shared-repo"]
}
identity = {
url = "https://github.com/your-org/terraform-aws-identity"
role_name = "lz-aws-identity"
}
compliance = {
url = "https://github.com/your-org/aws-compliance"
}
cost_management = {
url = "https://github.com/your-org/aws-cost-management"
}
}
tags = {
Environment = "production"
ManagedBy = "terraform"
}
providers = {
aws.audit = aws.audit
aws.management = aws.management
}
}This module implements CIS AWS Foundations Benchmark security monitoring using CloudWatch alarms. The alarms monitor CloudTrail logs for security-relevant events and send notifications through your configured channels.
CIS alarms are enabled by default (enable_cis_alarms = true). They monitor the AWS Control Tower organizational CloudTrail trail by default.
Configure email notifications to receive alerts:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
notifications = {
email = {
addresses = ["security@example.com", "compliance@example.com"]
}
}
# ... other configuration
}Configure Slack notifications using an Incoming Webhook:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
notifications = {
slack = {
webhook_url = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX"
}
}
# ... other configuration
}Configure Microsoft Teams notifications using an Incoming Webhook:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
notifications = {
teams = {
webhook_url = "https://outlook.office.com/webhook/..."
}
}
# ... other configuration
}You can configure multiple notification channels simultaneously:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
notifications = {
email = {
addresses = ["security@example.com"]
}
slack = {
webhook_url = "https://hooks.slack.com/services/..."
}
teams = {
webhook_url = "https://outlook.office.com/webhook/..."
}
}
# ... other configuration
}By default, the module uses the AWS Control Tower organizational trail log group (aws-controltower/CloudTrailLogs). You can specify a different log group:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
organization_log_group_name = "my-custom-cloudtrail-logs"
# ... other configuration
}Some AWS services may generate false positives for unauthorized API call alerts. You can exclude additional services:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_cis_alarms = true
unauthorized_api_calls_extra_excluded_services = [
"s3.amazonaws.com",
"dynamodb.amazonaws.com"
]
# ... other configuration
}The module can provision emergency "breakglass" IAM users with administrator access to the management account. These users are intended for emergency access when normal authentication methods fail.
- MFA Enforcement: Breakglass users must use MFA for all operations (except MFA device management)
- Administrator Access: Full administrative privileges when authenticated with MFA
- Configurable Count: Create multiple breakglass users (default: 2)
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_breakglass = true
breakglass_users = 2 # Creates lza-breakglass0 and lza-breakglass1
# ... other configuration
}Security Note: After deployment, you must manually:
- Configure MFA devices for each breakglass user
- Securely store credentials in a separate system (e.g., password manager, vault)
- Regularly rotate credentials according to your security policy
The module can deploy the AWS Support Access role across all accounts in your organization using CloudFormation StackSets.
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
enable_aws_support = true
aws_support_role_name = "AWSSupportAccessRole" # Default value
aws_support_stack_name = "lz-aws-support-role" # Default value
# ... other configuration
}This creates:
- A CloudFormation StackSet in the management account
- StackSet instances deployed to all accounts in the organization root
- An IAM role in each account allowing AWS Support to access resources
The module creates IAM permission boundaries to enforce security guardrails on pipeline roles:
Applied to all pipeline roles (except cost management). This boundary:
- Allows full administrative access
- Denies modification of the boundary policy itself
- Protects Terraform remote state S3 buckets from deletion
- Protects Terraform state lock DynamoDB tables from deletion
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
default_permissions_boundary_name = "lz-base-default-boundary" # Default value
# ... other configuration
}A separate boundary for cost management roles with restricted permissions:
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
costs_boundary_name = "lz-costs-boundary" # Default value
# ... other configuration
}Each repository configuration supports the following options:
repositories = {
<pipeline_type> = {
# Required: Git repository URL (must be HTTPS)
url = "https://github.com/your-org/repo-name"
# Optional: IAM role name (defaults vary by pipeline type)
role_name = "custom-role-name"
# Optional: Additional repositories that can assume this role
shared = [
"https://github.com/your-org/shared-repo-1",
"https://github.com/your-org/shared-repo-2"
]
# Optional: Additional read-only IAM policy statements
additional_read_permissions = {
CustomReadPolicy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:GetObject", "s3:ListBucket"]
Resource = "*"
}
]
})
}
# Optional: Additional read-write IAM policy statements
additional_write_permissions = {
CustomWritePolicy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["dynamodb:PutItem", "dynamodb:DeleteItem"]
Resource = "*"
}
]
})
}
}
}This module requires two AWS provider configurations:
provider "aws" {
alias = "management"
region = "us-east-1"
# Configure for your management account
}
provider "aws" {
alias = "audit"
region = "us-east-1"
# Configure for your audit/security account
assume_role {
role_arn = "arn:aws:iam::123456789012:role/OrganizationAccountAccessRole"
}
}
module "landing_zone" {
source = "appvia/cloudaccess-lza/aws"
# ... configuration
providers = {
aws.audit = aws.audit
aws.management = aws.management
}
}The terraform-docs utility is used to generate this README. Follow the below steps to update:
- Make changes to the
.terraform-docs.ymlfile - Fetch the
terraform-docsbinary (https://terraform-docs.io/user-guide/installation/) - Run
terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .
| Name | Version |
|---|---|
| aws | >= 6.0.0 |
| aws.audit | >= 6.0.0 |
| aws.management | >= 6.0.0 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| aws_accounts | Map of AWS account names to their account IDs | object({ |
n/a | yes |
| aws_support_role_name | Name of the AWS Support role | string |
"AWSSupportAccessRole" |
no |
| aws_support_stack_name | Name of the stackset used to deploy the aws support role | string |
"lz-aws-support-role" |
no |
| azuredevops_organization_id | The Azure DevOps organization ID (GUID, found under Organization Settings) used to build the OIDC issuer URL (https://vstoken.dev.azure.com/<organization_id>). Required when common_provider is 'azuredevops'. Repository values in var.repositories must then be formatted as '//', and the read-only role for each repository requires a distinct '-ro' suffixed service connection. | string |
null |
no |
| breakglass_users | The number of breakglass users to create | number |
2 |
no |
| common_provider | The CI/CD identity provider used as the OIDC trust for all roles provisioned by this module | string |
"github" |
no |
| costs_boundary_name | Name of the IAM policy to use as a permissions boundary for cost-related roles | string |
"lz-costs-boundary" |
no |
| default_permissions_boundary_name | Name of the default IAM policy used by roles we provision | string |
"lz-base-default-boundary" |
no |
| enable_aws_support | Indicates if we should enable AWS Support role | bool |
true |
no |
| enable_breakglass | Indicates if we should enable breakglass users and group | bool |
false |
no |
| enable_cis_alarms | Indicates if we should enable CIS alerts | bool |
true |
no |
| notifications | Configuration for the notifications | object({ |
{ |
no |
| organization_log_group_name | The name of the CloudWatch log group for the AWS Organization. If not provided, will use the default log group. | string |
"aws-controltower/CloudTrailLogs" |
no |
| repositories | List of repository locations for the pipelines. Each 'url' field should be a Git repository URL when common_provider is 'github' or 'gitlab', or '//' when common_provider is 'azuredevops'. | object({ |
{} |
no |
| tags | Tags to apply to all resources | map(string) |
{} |
no |
| unauthorized_api_calls_extra_excluded_services | Optional list of additional AWS services to exclude from unauthorized API call metric filter. | list(string) |
[] |
no |
No outputs.
