Skip to content

appvia/terraform-aws-landing-zones

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Github Actions

Terraform AWS Landing Zone

Description

Note, this module is not intended to be used outside of the organization, as the template provides a consistent blueprint for the provisioning of accounts with the Appvia AWS estate.

Usage

Please refer to one of the application, platform or sandbox pipelines for an example of how to use this module.

Notification Features

Tenants are able to provision notifications within the designated region. This first step to ensure notifications is enabled.

notifications = {
  email = {
    addresses = ["MY_EMAIL_ADDRESS"]
  }
  slack = {
    webhook = "MY_SLACK_WEBHOOK"
  }
}

Security Features

The notifications can used to send notifications to users via email or slack, for events related to costs, security and budgets.

Service Control Policies

Additional service control policies can be applied to the account. This is useful for ensuring that the account is compliant with the organization's security policies, specific to the accounts requirements.

You can configure additional service control policies using the var.service_control_policies variable, such as the below example

data "aws_iam_policy_document" "deny_s3" {
  statement {
    effect    = "Deny"
    actions = ["s3:*"]
    resources = ["*"]
  }
}

module "account" {
  service_control_policies = {
    "MY_POLICY_NAME" = {
      name   = "deny-s3"
      policy = data.aws_iam_policy_document.deny_s3.json
    }
  }
}

AWS Config Compliance Packs

AWS Config Conformance Packs are collections of AWS Config rules and remediation actions that are packaged together for common compliance and security best practices. You can configure compliance packs using the var.aws_config variable to ensure your account meets specific compliance requirements.

Compliance packs can be created using either a template body (YAML or JSON) or a template URL. You can also override default parameters in the compliance pack template to customize the rules for your specific requirements.

Basic Compliance Pack Configuration

module "account" {
  aws_config = {
    enable = true
    compliance_packs = {
      "security-best-practices" = {
        template_body = file("${path.module}/templates/security-best-practices.yml")
      }
    }
  }
}

Compliance Pack with Template URL

data "http" "security_hub_enabled" {
  url = "https://s3.amazonaws.com/aws-service-catalog-reference-architectures/AWS_Config_Rules/Security/SecurityHub/SecurityHub-Enabled.json"
}

module "account" {
  aws_config = {
    enable = true
    compliance_packs = {
      "security-hub-enabled" = {
        template_body = data.http.security_hub_enabled.body
      }
    }
  }
}

Compliance Pack with Parameter Overrides

Many compliance packs support parameter overrides that allow you to customize the behavior of the rules within the pack. For example, you can adjust thresholds, specify resource types, or configure other rule-specific settings.

module "account" {
  aws_config = {
    enable = true
    compliance_packs = {
      "hipaa-compliance" = {
        template_body = file("${path.module}/templates/hipaa-compliance.yml")
        parameter_overrides = {
          "AccessKeysRotatedParamMaxAccessKeyAge" = "45"
          "PasswordPolicyParamMinimumPasswordLength" = "14"
          "PasswordPolicyParamRequireUppercaseCharacters" = "true"
          "PasswordPolicyParamRequireLowercaseCharacters" = "true"
          "PasswordPolicyParamRequireNumbers" = "true"
          "PasswordPolicyParamRequireSymbols" = "true"
        }
      }
      "pci-dss-compliance" = {
        template_body = file("${path.module}/templates/pci-dss-compliance.yml")
        parameter_overrides = {
          "EncryptedVolumesParamKmsKeyId" = "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
        }
      }
    }
  }
}

Compliance Pack Configuration Options

The compliance pack configuration supports the following options:

  • template_body: (Required) The YAML or JSON template body for the compliance pack. This can be provided directly as a string, loaded from a file using file(), or fetched from a URL using data.http.
  • template_url: (Optional) The URL of the compliance pack template. Note: Either template_body or template_url must be provided, but not both.
  • parameter_overrides: (Optional) A map of parameter overrides to customize the compliance pack rules. The keys should match the parameter names defined in the compliance pack template, and the values are the custom values you want to apply.

Using AWS Managed Compliance Packs

AWS provides several pre-built compliance pack templates that you can use. These templates are available in the AWS Service Catalog and can be referenced by their S3 URLs. Common examples include:

  • Operational Best Practices: General security and operational best practices
  • HIPAA Compliance: Healthcare industry compliance requirements
  • PCI-DSS Compliance: Payment card industry data security standards
  • Security Best Practices: Security-focused configuration rules
  • CIS AWS Foundations Benchmark: Center for Internet Security benchmarks

You can find the complete list of AWS managed compliance pack templates in the AWS Config Conformance Pack Sample Templates documentation.

Example: Multiple Compliance Packs

You can configure multiple compliance packs simultaneously to meet various compliance requirements:

module "account" {
  aws_config = {
    enable = true
    compliance_packs = {
      "operational-best-practices" = {
        template_body = file("${path.module}/templates/operational-best-practices.yml")
      }
      "security-best-practices" = {
        template_body = file("${path.module}/templates/security-best-practices.yml")
        parameter_overrides = {
          "CheckPublicReadAclParam" = "true"
          "CheckPublicWriteAclParam" = "true"
        }
      }
      "cis-aws-foundations-benchmark" = {
        template_body = file("${path.module}/templates/cis-aws-foundations-benchmark.yml")
        parameter_overrides = {
          "AccessKeysRotatedParamMaxAccessKeyAge" = "90"
        }
      }
    }
  }
}

Note: Ensure that AWS Config is enabled (enable = true) in the aws_config variable for compliance packs to be provisioned. The compliance packs will be deployed to the account and will continuously evaluate your resources against the rules defined in the pack.

AWS Config Rules

You can configure additional AWS Config managed rules using the var.aws_config variable. AWS Config rules allow you to evaluate the configuration settings of your AWS resources to ensure they comply with your organization's policies.

module "account" {
  aws_config = {
    enable = true
    rules = {
      "encrypted-volumes" = {
        description = "Checks whether EBS volumes are encrypted"
        identifier = "ENCRYPTED_VOLUMES"
        resource_types = ["AWS::EC2::Volume"]
      }
      "s3-bucket-public-read-prohibited" = {
        description = "Checks that your S3 buckets do not allow public read access"
        identifier = "S3_BUCKET_PUBLIC_READ_PROHIBITED"
        resource_types = ["AWS::S3::Bucket"]
      }
      "rds-instance-public-access-check" = {
        description = "Checks whether the Amazon Relational Database Service instances are not publicly accessible"
        identifier = "RDS_INSTANCE_PUBLIC_ACCESS_CHECK"
        resource_types = ["AWS::RDS::DBInstance"]
        max_execution_frequency = "TwentyFour_Hours"
        inputs = {
          "publicAccessCheckValue" = "true"
        }
      }
      "tagged-resources" = {
        description = "Checks whether resources are properly tagged"
        identifier = "REQUIRED_TAGS"
        resource_types = ["AWS::EC2::Instance"]
        inputs = {
          "tag1Key" = "Environment"
          "tag2Key" = "Owner"
        }
        scope = {
          compliance_resource_types = ["AWS::EC2::Instance"]
          tag_key = "Environment"
          tag_value = "Production"
        }
      }
    }
  }
}

The rules configuration supports the following options:

  • description: A description of what the rule checks
  • identifier: The identifier of the AWS managed Config rule (e.g., ENCRYPTED_VOLUMES, S3_BUCKET_PUBLIC_READ_PROHIBITED)
  • resource_types: A list of resource types that the rule evaluates (for documentation purposes)
  • inputs: (Optional) A map of input parameters for the rule
  • max_execution_frequency: (Optional) The maximum frequency at which the rule runs. Valid values: One_Hour, Three_Hours, Six_Hours, Twelve_Hours, TwentyFour_Hours
  • scope: (Optional) Defines which resources are evaluated by the rule:
    • compliance_resource_types: A list of resource types to scope the rule
    • tag_key: (Optional) The tag key to scope the rule
    • tag_value: (Optional) The tag value to scope the rule

For a complete list of available AWS managed Config rules and their identifiers, see the AWS Config Managed Rules documentation.

IAM Password Policy

The IAM password policy can be configured to enforce password policies on the account. This is useful for ensuring that the account is compliant with the organization's security policies, specific to the accounts requirements.

iam_password_policy = {
  enabled = true
  allow_users_to_change_password = true
  hard_expiry = false
  max_password_age = 90
  minimum_password_length = 8
  password_reuse_prevention = 24
  require_lowercase_characters = true
  require_numbers = true
  require_symbols = true
  require_uppercase_characters = true
}

IAM Access Analyzer

The IAM access analyzer can be configured to analyze access to resources within your account and produce findings related to excessive permissions and or permissions which carry a high risk.

iam_access_analyzer = {
  enabled = true
  analyzer_name = "lza-iam-access-analyzer" # optional
  analyzer_type = "ORGANIZATION" # optional but default
}

AWS Inspector

You can control the enable for disabling of the AWS Inspector service via the var.inspector variable, such as the below example

module "account" {
  inspector = {
    enable = true
    delegate_account_id = "123456789012" # Usually the security account
  }
}

EBS Encryption

The EBS encryption can be configured to encrypt all EBS volumes within the account. The feature ensures all volumes are automatically encrypted.

ebs_encryption = {
  enabled = true
  create_kms_key = true
  key_alias = "lza/ebs/default"
}

S3 Block Public Access

The S3 block public access can be configured to block public access to S3 buckets within the account. The feature ensures all buckets are automatically blocked from public access.

s3_block_public_access = {
  enabled = true
  enable_block_public_policy = true
  enable_block_public_acls = true
  enable_ignore_public_acls = true
  enable_restrict_public_buckets = true
}

IAM Customers Managed Policies

This module can ensure a set of IAM policies are created within the account. This is useful for ensuring that the account is preloaded with any required policy sets.

You can configure additional IAM policies using the var.iam_policies variable, such as the below example

module "account" {
  iam_policies = {
    "deny_s3" = {
      name = "deny-s3"
      description = "Used to deny access to S3"
      policy = data.aws_iam_policy_document.deny_s3.json
    }
    "deny_s3_with_prefix" = {
      name_prefix = "deny-s3-"
      policy = data.aws_iam_policy_document.deny_s3.json
      description = "Used to deny access to S3"
      path   = "/"
    }
  }
}

IAM Roles

This module can ensure a set of IAM roles are created within the account. This is useful for ensuring that the account is compliant with the organization's security policies, specific to the accounts requirements. Note, the IAM role have an automatic dependency on any IAM policies defined above to ensure ordering.

You can configure additional IAM roles using the var.iam_roles variable, such as the below example

module "account" {
  iam_roles = {
    "s3_administrator" = {
      name = "MY_ROLE_NAME"
      assume_roles = ["arn:aws:iam::123456789012:role/role-name"]
      description = "Administrator role for S3"
      path = "/"
      permissions_boundary_arn = null
      permissions_arns = [
        "arn:aws:iam::aws:policy/AmazonS3FullAccess"
      ]
      #policies = [data.aws_iam_policy_document.deny_s3.json]
    }
    "ec2_instance_profile" {
      name = "lza-ssm-instance-profile"
      assume_services = ["ec2.amazonaws.com"]
      description = "Instance profiles for ec2 compute machine"
      path = "/"
      permissions_arns = [
        "arn:aws:iam::aws:policy/AmazonSSMDirectoryServiceAccess",
        "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore",
        "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy",
      ]
    }
    "kms_admin" = {
      name = "kms-admin"
      assume_accounts = ["123456789012"]
      description = "Administrator role for KMS"
      path = "/"
      permissions_arns = [
        "arn:aws:iam::aws:policy/AmazonKMSFullAccess"
      ]
    }
  }
}

RBAC & Identity Center Assignment

This module provides the ability for tenants to manage the assignment of prescribed roles to users and groups within the account. The sso_assignment module is used to manage the assignment of roles to users and groups within the account.

Note, the roles permitted for assignment can be found within local.sso_permitted_permission_sets, an example of the permitted roles can be found below:

sso_permitted_permission_sets = {
  "devops_engineer"   = "DevOpsEngineer"
  "finops_engineer"   = "FinOpsEngineer"
  "network_engineer"  = "NetworkEngineer"
  "network_viewer"    = "NetworkViewer"
  "platform_engineer" = "PlatformEngineer"
  "security_auditor"  = "SecurityAuditor"
}

This maps the exposed name used in the var.rbac to the name of the role within the AWS Identity Center.

Tenants can assign roles to users and groups by providing a map of users and groups to roles within the var.rbac variable. An example of this can be found below:

rbac = {
  "devops_engineer" = {
    users  = ["MY_SSO_USER"]
    groups = ["MY_SSO_GROUP"]
  }
}

Resource Groups

AWS Resource Groups allow you to organize and manage AWS resources by grouping them based on tags, resource types, or other criteria. This module provides the ability to create and manage resource groups within your account, making it easier to organize, discover, and manage related resources.

Basic Resource Group Configuration

You can create resource groups using the var.resource_groups variable. The recommended approach is to use the query object which provides a simpler, more intuitive way to define resource queries.

module "account" {
  resource_groups = {
    "production-ec2-instances" = {
      description = "All EC2 instances in production environment"
      query = {
        resource_type_filters = ["AWS::EC2::Instance"]
        tag_filters           = {
          "Environment" = ["production"]
        }
      }
    }
  }
}

Resource Group with Tag-Based Filtering

Resource groups are commonly used to organize resources by tags, making it easier to manage resources across different environments or applications:

module "account" {
  resource_groups = {
    "production-resources" = {
      description = "All production resources"
      query = {
        resource_type_filters = ["AWS::AllSupported"]
        tag_filters = {
          "Environment" = ["production"]
          "Product"     = ["my-product"]
        }
      }
    }
    "development-resources" = {
      description = "All development resources"
      query = {
        resource_type_filters = ["AWS::AllSupported"]
        tag_filters = {
          "Environment" = ["development"]
        }
      }
    }
  }
}

Resource Group with Resource Type Filtering

You can create resource groups that include only specific resource types. If no tag_filters are specified, the resource group will include all resources of the specified types:

module "account" {
  resource_groups = {
    "s3-buckets" = {
      description = "All S3 buckets in the account"
      query = {
        resource_type_filters = ["AWS::S3::Bucket"]
      }
    }
    "lambda-functions" = {
      description = "All Lambda functions"
      query = {
        resource_type_filters = ["AWS::Lambda::Function"]
      }
    }
    "rds-instances" = {
      description = "All RDS database instances"
      query = {
        resource_type_filters = ["AWS::RDS::DBInstance"]
      }
    }
  }
}

Resource Group with Configuration

Resource groups can include configuration settings for specific use cases, such as AWS Systems Manager maintenance windows or other group-based operations:

module "account" {
  resource_groups = {
    "maintenance-window-targets" = {
      description = "EC2 instances for maintenance windows"
      query = {
        resource_type_filters = ["AWS::EC2::Instance"]
        tag_filters = {
          "MaintenanceWindow" = ["enabled"]
        }
      }
      configuration = {
        type = "AWS::SSM::MaintenanceWindowTarget"
        parameters = [
          {
            name   = "WindowTargetId"
            values = ["target-123456"]
          }
        ]
      }
    }
  }
}

Resource Group Configuration Options

The resource group configuration supports the following options:

  • description: (Required) A description of the resource group
  • query: (Optional) An object that defines the query used to select resources for the group. This is the recommended approach:
    • resource_type_filters: (Optional) A list of AWS resource types (e.g., ["AWS::EC2::Instance"], ["AWS::S3::Bucket"], or ["AWS::AllSupported"]). Defaults to ["AWS::AllSupported"] if not specified.
    • tag_filters: (Optional) A map where keys are tag names and values are lists of tag values. For example: { "Environment" = ["production"], "Product" = ["my-app"] }
  • resource_query: (Optional) A JSON string that defines the query used to select resources for the group. This is an alternative to the query object for advanced use cases or backward compatibility. The query must follow AWS Resource Groups query syntax.
  • type: (Optional) The type of resource query. Defaults to "TAG_FILTERS_1_0" if not specified.
  • configuration: (Optional) Configuration settings for the resource group:
    • type: The type of group configuration (e.g., "AWS::SSM::MaintenanceWindowTarget")
    • parameters: (Optional) A list of parameters for the configuration:
      • name: The parameter name
      • values: A list of parameter values

Using the Query Object (Recommended)

The query object provides a simpler and more intuitive way to define resource group queries:

query = {
  resource_type_filters = ["AWS::EC2::Instance"]  # List of resource types
  tag_filters = {                                  # Map of tag keys to values
    "Environment" = ["production", "staging"]     # Tag key with multiple values
    "Product"     = ["my-app"]                     # Additional tag filter
  }
}

Using Resource Query String (Advanced)

For advanced use cases or backward compatibility, you can provide a raw JSON string:

resource_query = jsonencode({
  ResourceTypeFilters = ["AWS::EC2::Instance"]
  TagFilters = [
    {
      Key    = "Environment"
      Values = ["production", "staging"]
    }
  ]
})

Use Cases

Resource groups are useful for:

  • Environment Management: Group resources by environment (production, staging, development)
  • Application Organization: Group resources belonging to a specific application or service
  • Cost Management: Organize resources for cost allocation and budgeting
  • Security Management: Group resources for security scanning and compliance checks
  • Maintenance Windows: Organize resources for scheduled maintenance operations
  • Resource Discovery: Quickly find and list related resources across your account

Example: Multi-Environment Resource Organization

This example shows how to organize resources across multiple environments using the query object:

module "account" {
  resource_groups = {
    "production-app-resources" = {
      description = "All resources for the production application"
      query = {
        resource_type_filters = ["AWS::AllSupported"]
        tag_filters = {
          "Environment" = ["production"]
          "Application" = ["my-app"]
        }
      }
    }
    "staging-app-resources" = {
      description = "All resources for the staging application"
      query = {
        resource_type_filters = ["AWS::AllSupported"]
        tag_filters = {
          "Environment" = ["staging"]
          "Application" = ["my-app"]
        }
      }
    }
  }
}

Example: Multiple Tag Values

You can specify multiple values for a single tag key to match resources with any of those values:

module "account" {
  resource_groups = {
    "production-and-staging" = {
      description = "All resources in production or staging environments"
      query = {
        resource_type_filters = ["AWS::AllSupported"]
        tag_filters = {
          "Environment" = ["production", "staging"]
        }
      }
    }
  }
}

Note: Resource groups are dynamic and automatically update as resources are created, modified, or deleted based on the resource query criteria. Ensure your resources are properly tagged to be included in the appropriate resource groups. When using the query object, if resource_type_filters is not specified, it defaults to ["AWS::AllSupported"].

GitHub Repository Management

This module includes comprehensive GitHub repository management capabilities through the modules/github_repository module. This allows tenants to create and manage GitHub repositories with enterprise-grade security and compliance features.

GitHub Repository Features

  • Repository Creation: Create GitHub repositories with customizable names, descriptions, and visibility
  • Security & Compliance: Branch protection, required reviews, status checks, vulnerability alerts
  • Collaboration Management: User and team access control, environment protection
  • Automation: Repository templates, merge strategies, and automated workflows

Basic GitHub Repository Usage

module "my_repository" {
  source = "./modules/github_repository"

  repository  = "my-project"
  description = "My awesome project"
  visibility  = "private"
}

Advanced GitHub Repository Configuration

module "enterprise_repository" {
  source = "./modules/github_repository"

  repository  = "enterprise-critical-system"
  description = "Enterprise critical system with strict controls"
  
  # Security settings
  visibility = "private"
  
  # Branch protection
  enforce_branch_protection_for_admins = true
  required_approving_review_count      = 3
  dismiss_stale_reviews                = true
  prevent_self_review                  = true
  
  # Status checks
  required_status_checks = [
    "CI / Build and Test",
    "Security / Security Scan",
    "Compliance / Compliance Check"
  ]
  
  # Environments
  repository_environments          = ["staging", "production"]
  default_environment_review_users = ["senior-dev1", "senior-dev2"]
  
  # Collaborators
  repository_collaborators = [
    {
      username   = "senior-dev1"
      permission = "admin"
    }
  ]
  
  # Topics
  repository_topics = ["enterprise", "terraform", "aws", "critical"]
}

For complete GitHub repository management examples, see the examples/github_repository/ directory.

Cost Management Features

Tenants are able to receive budgets notifications related to the services. Once notifications have been configured they will automatically receive daily, weekly or monthly reports and notifications on where they sit in the budget.

Anomaly Detection

Tenants are able to provision anomaly detection rules within the designated region. This is useful for ensure cost awareness and alerting on any unexpected costs.

cost_anomaly_detection = {
  enabled = true
  monitors = [
    {
      name      = lower("lza-${local.region}")
      frequency = "IMMEDIATE"
      threshold_expression = [
        {
          and = {
            dimension = {
              key           = "ANOMALY_TOTAL_IMPACT_ABSOLUTE"
              match_options = ["GREATER_THAN_OR_EQUAL"]
              values        = ["100"]
            }
          }
        },
        {
          and = {
            dimension = {
              key           = "ANOMALY_TOTAL_IMPACT_PERCENTAGE"
              match_options = ["GREATER_THAN_OR_EQUAL"]
              values        = ["50"]
            }
          }
        }
      ]

      specification = jsonencode({
        "And" : [
          {
            "Dimensions" : {
              "Key" : "REGION"
              "Values" : [local.region]
            }
          }
        ]
      })
    }
  ]
}

CloudWatch Cross-Account Observability

CloudWatch Cross-Account Observability allows you to centralize monitoring and observability data from multiple AWS accounts. This feature supports two configurations:

  • Observability Sink: Configure an account to receive observability data from other accounts
  • Observability Source: Configure an account to send its observability data to a central sink account

Observability Sink Configuration

An observability sink is typically configured in a central monitoring or security account that aggregates observability data from multiple source accounts. The sink allows specified accounts to link their CloudWatch resources.

module "monitoring_account" {
  cloudwatch = {
    observability_sink = {
      enable = true
      identifiers = [
        "123456789012",  # Source account 1
        "234567890123",  # Source account 2
      ]
      resource_types = [
        "AWS::CloudWatch::Metric",
        "AWS::CloudWatch::Dashboard",
        "AWS::CloudWatch::Alarm",
        "AWS::CloudWatch::LogGroup",
        "AWS::CloudWatch::LogStream",
      ]
    }
  }
}

Observability Sink Configuration Options

  • enable: (Required) A flag indicating if the observability sink should be enabled
  • identifiers: (Required) A list of AWS account IDs that are allowed to link their resources to this sink
  • resource_types: (Optional) A list of CloudWatch resource types that can be linked to the sink. Defaults to:
    • AWS::CloudWatch::Metric
    • AWS::CloudWatch::Dashboard
    • AWS::CloudWatch::Alarm
    • AWS::CloudWatch::LogGroup
    • AWS::CloudWatch::LogStream

Observability Source Configuration

An observability source is configured in accounts that need to send their CloudWatch data to a central sink account. This allows centralized monitoring and analysis of observability data across multiple accounts.

module "source_account" {
  cloudwatch = {
    observability_source = {
      enable = true
      account_id = "123456789012"  # The monitoring account ID
      sink_identifier = "arn:aws:oam:us-east-1:123456789012:sink/observability-sink"
      resource_types = [
        "AWS::CloudWatch::Metric",
        "AWS::CloudWatch::Dashboard",
        "AWS::CloudWatch::Alarm",
        "AWS::CloudWatch::LogGroup",
        "AWS::CloudWatch::LogStream",
      ]
    }
  }
}

Observability Source Configuration Options

  • enable: (Required) A flag indicating if the observability source should be enabled
  • account_id: (Required) The AWS account ID of the sink account that will receive the observability data
  • sink_identifier: (Required) The ARN of the OAM sink in the monitoring account (format: arn:aws:oam:region:account-id:sink/sink-id)
  • resource_types: (Optional) A list of CloudWatch resource types to link to the observability sink. Defaults to:
    • AWS::CloudWatch::Metric
    • AWS::CloudWatch::Dashboard
    • AWS::CloudWatch::Alarm
    • AWS::CloudWatch::LogGroup
    • AWS::CloudWatch::LogStream

Complete Example: Centralized Monitoring Setup

This example shows how to set up centralized monitoring with a monitoring account and multiple source accounts:

Monitoring Account (Sink):

module "monitoring_account" {
  cloudwatch = {
    observability_sink = {
      enable = true
      identifiers = [
        "111111111111",  # Production account
        "222222222222",  # Development account
        "333333333333",  # Staging account
      ]
    }
  }
}

Source Account (Production):

module "production_account" {
  cloudwatch = {
    observability_source = {
      enable = true
      account_id = "999999999999"  # Monitoring account ID
      sink_identifier = "arn:aws:oam:us-east-1:999999999999:sink/observability-sink"
    }
  }
}

Note: The sink must be created first in the monitoring account. Once the sink is created, you can obtain its ARN from the AWS Console or Terraform outputs, and use that ARN in the sink_identifier field for all source accounts.

Benefits of CloudWatch Cross-Account Observability

  • Centralized Monitoring: Aggregate metrics, logs, and alarms from multiple accounts in a single location
  • Unified Dashboards: Create dashboards that span multiple accounts without switching contexts
  • Cost Optimization: Reduce duplicate monitoring infrastructure across accounts
  • Security: Centralize security monitoring and alerting in a dedicated security account
  • Compliance: Simplify compliance reporting by centralizing observability data

Account Subscription Filter Policy

CloudWatch Logs Account Subscription Filter Policies allow you to control which log groups can have subscription filters created and what destinations those subscription filters can send logs to. This provides account-level governance for log forwarding and helps ensure compliance with organizational policies.

Basic Account Subscription Filter Policy Configuration

module "account" {
  cloudwatch = {
    account_subscriptions = {
      "lambda-forwarding" = {
        # https://docs.aws.amazon.com/cli/latest/reference/logs/put-account-policy.html
        policy = jsonencode({
          DestinationArn = aws_lambda_function.test.arn
          FilterPattern  = "test"
        })
        selection_criteria = "LogGroupName NOT IN [\"excluded_log_group_name\"]"
      }
    }
  }
}

Account Subscription Filter Policy with Selection Criteria

You can use selection criteria to apply the policy only to specific log groups based on resource attributes:

module "account" {
  cloudwatch = {
    account_subscriptions = {
      "lambda-forwarding" = {
        policy = jsonencode({
          DestinationArn = aws_lambda_function.test.arn
          FilterPattern  = "test"
        })
        selection_criteria = "LogGroupName NOT IN [\"excluded_log_group_name\"]"
      }
    }
  }
}

Multiple Account Subscription Filter Policies

You can configure multiple subscription filter policies for different destinations or log groups:

module "account" {
  cloudwatch = {
    account_subscriptions = {
      "kinesis-streams" = {
        policy = jsonencode({
          Statement = [
            {
              Action = [
                "logs:CreateLogDelivery",
                "logs:GetLogDelivery",
                "logs:UpdateLogDelivery",
                "logs:DeleteLogDelivery",
                "logs:ListLogDeliveries"
              ]
              Effect = "Allow"
              Principal = {
                Service = "logs.amazonaws.com"
              }
              Resource = "arn:aws:logs:*:*:log-delivery:*"
              Condition = {
                StringEquals = {
                  "logs:destinationType" = "KinesisStream"
                }
              }
            }
          ]
          Version = "2012-10-17"
        })
        selection_criteria = "ALL"
      }
      "firehose-delivery" = {
        policy = jsonencode({
          Statement = [
            {
              Action = [
                "logs:CreateLogDelivery",
                "logs:GetLogDelivery",
                "logs:UpdateLogDelivery",
                "logs:DeleteLogDelivery",
                "logs:ListLogDeliveries"
              ]
              Effect = "Allow"
              Principal = {
                Service = "logs.amazonaws.com"
              }
              Resource = "arn:aws:logs:*:*:log-delivery:*"
              Condition = {
                StringEquals = {
                  "logs:destinationType" = "Firehose"
                }
              }
            }
          ]
          Version = "2012-10-17"
        })
        selection_criteria = jsonencode({
          LogGroupName = "/aws/application/*"
        })
      }
    }
  }
}

Account Subscription Filter Policy Configuration Options

  • policy: (Required) The IAM policy document (as JSON string) that defines what actions are allowed for subscription filters. The policy must allow logs:CreateLogDelivery, logs:GetLogDelivery, logs:UpdateLogDelivery, logs:DeleteLogDelivery, and logs:ListLogDeliveries actions.
  • selection_criteria: (Optional) A JSON string that specifies which log groups the policy applies to. Use "ALL" to apply the policy to all log groups, or provide a JSON object with selection criteria such as:
    • LogGroupName: Filter by log group name pattern (e.g., "/aws/lambda/*")
    • ResourceArn: Filter by log group ARN pattern

Supported Destination Types

The subscription filter policy can control access to the following destination types:

  • KinesisStream: Forward logs to Amazon Kinesis Data Streams
  • Firehose: Forward logs to Amazon Kinesis Data Firehose
  • Lambda: Forward logs to AWS Lambda functions

Use Cases

  • Compliance: Ensure only approved destinations can receive log data
  • Security: Control which log groups can forward logs to external systems
  • Cost Management: Restrict log forwarding to specific destinations to control costs
  • Governance: Enforce organizational policies on log data handling

Note: Account subscription filter policies are account-level policies that apply to all log groups in the account (or those matching the selection criteria). They work in conjunction with resource-based policies on individual log groups.

Networking Features

Tenants are able to provision networks within the designated region, while allowing the platform to decide how these are wired up into the network topology of the organization i.e. ensuring the are using IPAM, connected to the transit gateway, egress via the central vpc and so forth.

All networks are defined within the var.networks variable, an example of this can be found below:

networks = {
  my_vpc_name = {
    subnets = {
      private = {
        netmask = 28
      }
      database = {
        netmask = 22
      }
    }

    vpc = {
      availability_zones     = 2
      enable_ipam            = true
      enable_transit_gateway = true
    }
  }

  my_second_vpc = {
    subnets = {
      private = {
        netmask = 28
      }
    }

    vpc = {
      enable_ipam            = true
      enable_transit_gateway = true
    }
  }
}

Transit Gateway Connectivity

When network have defined the enable_transit_gateway boolean it is the responsibility of the consumer of this module to have defined the correct transit gateway id and any default routing requirements.

Assuming the following configuration

module "my_account" {
  ...
  networks = {
    dev = {
      vpc = {
        enable_transit_gateway = true
        ipam_pool_name = "development"
        netmask        = 21
      }

      transit_gateway = {
        gateway_id = "tgw-1234567890"
        gateway_routes = {
          private = "10.0.0.0/8"
        }
      }

      subnets = {
        private = {
          netmask = 24
        }
      }
    },
  }

We can also create transit gateway route table associations by extending the above configuration

module "my_account" {
  ...
  networks = {
    dev = {
      vpc = {
        enable_transit_gateway = true
        ipam_pool_name = "development"
        netmask        = 21
      }

      transit_gateway = {
        gateway_id = "tgw-1234567890"
        gateway_routes = {
          private = "10.0.0.0/8"
        }
        gateway_route_table_id = "rtb-1234567890"
      }
    }
  }
}

Update Documentation

The terraform-docs utility is used to generate this README. Follow the below steps to update:

  1. Make changes to the .terraform-docs.yml file
  2. Fetch the terraform-docs binary (https://terraform-docs.io/user-guide/installation/)
  3. Run terraform-docs markdown table --output-file ${PWD}/README.md --output-mode inject .

Providers

Name Version
aws >= 6.0.0
aws.identity >= 6.0.0
aws.management >= 6.0.0
aws.network >= 6.0.0
aws.tenant >= 6.0.0

Inputs

Name Description Type Default Required
environment The environment in which to provision resources string n/a yes
git_repository The git repository to use for the account string n/a yes
home_region The home region in which to provision global resources string n/a yes
owner The owner of the product, and injected into all resource tags string n/a yes
product The name of the product to provision resources and inject into all resource tags string n/a yes
tags A collection of tags to apply to resources map(string) n/a yes
account_alias The account alias to apply to the account string null no
aws_config Account specific configuration for AWS Config
object({
# A flag indicating if AWS Config should be enabled
enable = optional(bool, false)
# A list of compliance packs to provision in the account
compliance_packs = optional(map(object({
# A map of parameter overrides to apply to the compliance pack
parameter_overrides = optional(map(string), {})
# The URL of the compliance pack
template_url = optional(string, "")
# The body of the compliance pack
template_body = optional(string, "")
})), {})
# A list of managed rules to provision in the account
rules = optional(map(object({
# The list of resource types to scope the rule
resource_types = list(string)
# The description of the rule
description = string
# The identifier of the rule
identifier = string
# The inputs of the rule
inputs = optional(map(string), {})
# The maximum execution frequency of the rule
max_execution_frequency = optional(string, null)
# The scope of the rule
scope = optional(object({
# The list of resource types to scope the rule
compliance_resource_types = optional(list(string), [])
# The key of the tag to scope the rule
tag_key = optional(string, null)
# The value of the tag to scope the rule
tag_value = optional(string, null)
}), null)
})), {})
})
{
"compliance_packs": {},
"enable": false,
"input_parameters": {},
"rules": {},
"scope": null
}
no
budgets A collection of budgets to provision
list(object({
name = string
budget_type = optional(string, "COST")
limit_amount = optional(string, "100.0")
limit_unit = optional(string, "PERCENTAGE")
time_unit = optional(string, "MONTHLY")

notifications = optional(map(object({
comparison_operator = string
notification_type = string
threshold = number
threshold_type = string
})), null)

auto_adjust_data = optional(list(object({
auto_adjust_type = string
})), [])

cost_filter = optional(map(object({
values = list(string)
})), {})

cost_types = optional(object({
include_credit = optional(bool, false)
include_discount = optional(bool, false)
include_other_subscription = optional(bool, false)
include_recurring = optional(bool, false)
include_refund = optional(bool, false)
include_subscription = optional(bool, false)
include_support = optional(bool, false)
include_tax = optional(bool, false)
include_upfront = optional(bool, false)
use_blended = optional(bool, false)
}), {
include_credit = false
include_discount = false
include_other_subscription = false
include_recurring = false
include_refund = false
include_subscription = true
include_support = false
include_tax = false
include_upfront = false
use_blended = false
})

tags = optional(map(string), {})
}))
[] no
central_dns Configuration for the hub used to centrally resolved dns requests
object({
enable = optional(bool, false)
# The domain name to use for the central DNS
vpc_id = optional(string, null)
})
{
"enable": false,
"vpc_id": null
}
no
cloudwatch Configuration for the CloudWatch service
object({
# The observability sink configuration
observability_sink = optional(object({
# A flag indicating if cloudwatch cross-account observability should be enabled
enable = optional(bool, false)
# The AWS Identifier of the accounts that are allowed to access the observability sink
identifiers = optional(list(string), null)
# The AWS resource types that are allowed to be linked to the observability sink
resource_types = optional(list(string), [
"AWS::CloudWatch::Metric",
"AWS::CloudWatch::Dashboard",
"AWS::CloudWatch::Alarm",
"AWS::CloudWatch::LogGroup",
"AWS::CloudWatch::LogStream",
])
}), null)
observability_source = optional(object({
# A flag indicating if cloudwatch cross-account observability should be enabled
enable = optional(bool, false)
# The name of the cloudwatch cross-account observability
account_id = optional(string, null)
# The OAM sink identifier i.e. arn:aws:oam:region:account-id:sink/sink-id
sink_identifier = optional(string, null)
# The resource types to link to the observability source
resource_types = optional(list(string), [
"AWS::CloudWatch::Metric",
"AWS::CloudWatch::Dashboard",
"AWS::CloudWatch::Alarm",
"AWS::CloudWatch::LogGroup",
"AWS::CloudWatch::LogStream",
])
}), null)
## Collection of account subscriptions to provision
account_subscriptions = optional(map(object({
# The policy document to apply to the subscription
policy = optional(string, null)
# The selection criteria to apply to the subscription
selection_criteria = optional(string, null)
})), {})
})
{
"account_subscriptions": {},
"observability_sink": null,
"observability_source": null
}
no
cost_anomaly_detection A collection of cost anomaly detection monitors to apply to the account
object({
enable = optional(bool, true)
# A flag indicating if the default monitors should be enabled
monitors = optional(list(object({
name = string
# The name of the anomaly detection rule
frequency = optional(string, "IMMEDIATE")
# The dimension of the anomaly detection rule, either SERVICE or DIMENSIONAL
threshold_expression = optional(list(object({
and = object({
dimension = object({
key = string
# The key of the dimension
match_options = list(string)
# The match options of the dimension
values = list(string)
# The values of the dimension
})
})
# The expression to apply to the cost anomaly detection monitor
})), [])
# The expression to apply to the anomaly detection rule
# see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ce_anomaly_monitor
specification = optional(string, "")
# The specification to anomaly detection monitor
# see https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ce_anomaly_monitor
})), [])
})
{
"enable": true,
"monitors": []
}
no
cost_center The cost center of the product, and injected into all resource tags string null no
dns A collection of DNS zones to provision and associate with networks
map(object({
comment = optional(string, "Managed by zone created by terraform")
# A comment associated with the DNS zone
network = string
# A list of network names to associate with the DNS zone
private = optional(bool, true)
# A flag indicating if the DNS zone is private or public
}))
{} no
ebs_encryption A collection of EBS encryption settings to apply to the account
object({
enable = optional(bool, false)
# A flag indicating if EBS encryption should be enabled
create_kms_key = optional(bool, true)
# A flag indicating if an EBS encryption key should be created
key_deletion_window_in_days = optional(number, 10)
# The number of days to retain the key before deletion when the key is removed
key_alias = optional(string, "lza/ebs/default")
# The alias of the EBS encryption key when provisioning a new key
key_arn = optional(string, null)
# The ARN of an existing EBS encryption key to use for EBS encryption
})
null no
guardduty A collection of GuardDuty settings to apply to the account
object({
# A flag indicating if GuardDuty should be created
finding_publishing_frequency = optional(string, "FIFTEEN_MINUTES")
# The frequency of finding publishing
detectors = optional(list(object({
name = string
# The name of the detector
enable = optional(bool, true)
# The frequency of finding publishing
additional_configuration = optional(list(object({
name = string
# The name of the additional configuration
enable = optional(bool, true)
# The status of the additional configuration
})), [])
})), [])
# Configuration for the detector
filters = optional(map(object({
# The name of the filter
action = string
# The action of the filter
rank = number
# The rank of the filter
description = string
# The description of the filter
criterion = list(object({
field = string
# The field of the criterion
equals = optional(string, null)
# The equals of the criterion
not_equals = optional(string, null)
# The not equals of the criterion
greater_than = optional(string, null)
# The greater than of the criterion
greater_than_or_equal = optional(string, null)
# The greater than or equal of the criterion
less_than = optional(string, null)
# The less than of the criterion
less_than_or_equal = optional(string, null)
# The less than or equal of the criterion
}))
# The criterion of the filter
})), {})
})
null no
iam_access_analyzer The IAM access analyzer configuration to apply to the account
object({
enable = optional(bool, false)
# A flag indicating if IAM access analyzer should be enabled
analyzer_name = optional(string, "lza-iam-access-analyzer")
# The name of the IAM access analyzer
analyzer_type = optional(string, "ORGANIZATION")
# The type of the IAM access analyzer
})
{
"analyzer_name": "lza-iam-access-analyzer",
"analyzer_type": "ORGANIZATION",
"enable": true
}
no
iam_groups A collection of IAM groups to apply to the account
list(object({
enforce_mfa = optional(bool, true)
# A flag indicating if MFA should be enforced
name = optional(string, null)
# The name prefix of the IAM group
path = optional(string, "/")
# The path of the IAM group
policies = optional(list(string), [])
# A list of policies to apply to the IAM group
users = optional(list(string), [])
# A list of users to apply to the IAM group
}))
[] no
iam_instance_profiles A collection of IAM instance profiles to apply to the account
map(object({
name = optional(string, null)
# The name prefix of the IAM instance profile
path = optional(string, "/")
# The path of the IAM instance profile
permission_arns = optional(list(string), [])
# A list of roles to apply to the IAM instance profile
}))
{} no
iam_password_policy The IAM password policy to apply to the account
object({
enable = optional(bool, false)
# A flag indicating if IAM password policy should be enabled
allow_users_to_change_password = optional(bool, true)
# A flag indicating if users can change their password
hard_expiry = optional(bool, false)
# A flag indicating if a hard expiry should be enforced
max_password_age = optional(number, 90)
# The maximum password age
minimum_password_length = optional(number, 16)
# The minimum password length
password_reuse_prevention = optional(number, 24)
# The number of passwords to prevent reuse
require_lowercase_characters = optional(bool, true)
# A flag indicating if lowercase characters are required
require_numbers = optional(bool, true)
# A flag indicating if numbers are required
require_symbols = optional(bool, true)
# A flag indicating if symbols are required
require_uppercase_characters = optional(bool, true)
# A flag indicating if uppercase characters are required
})
{} no
iam_policies A collection of IAM policies to apply to the account
map(object({
name = optional(string, null)
# The name of the IAM policy
name_prefix = optional(string, null)
# The name prefix of the IAM policy
description = string
# The description of the IAM policy
path = optional(string, "/")
# The path of the IAM policy
policy = string
# The policy document to apply to the IAM policy
}))
{} no
iam_roles A collection of IAM roles to apply to the account
map(object({
name = optional(string, null)
# The name of the IAM role
name_prefix = optional(string, null)
# The name prefix of the IAM role
assume_accounts = optional(list(string), [])
# List of accounts to assume the role
assume_roles = optional(list(string), [])
# List of principals to assume the role
assume_services = optional(list(string), [])
# List of services to assume the role
description = string
# The description of the IAM role
path = optional(string, "/")
# The path of the IAM role
permission_boundary_arn = optional(string, "")
# A collection of tags to apply to the IAM role
permission_arns = optional(list(string), [])
# A list of additional permissions to apply to the IAM role
policies = optional(any, [])
}))
{} no
iam_service_linked_roles A collection of service linked roles to apply to the account list(string)
[
"autoscaling.amazonaws.com",
"spot.amazonaws.com",
"spotfleet.amazonaws.com"
]
no
iam_users A collection of IAM users to apply to the account
list(object({
name = optional(string, null)
# The name of the IAM user
name_prefix = optional(string, null)
# The name prefix of the IAM user
path = optional(string, "/")
# The path of the IAM user
permission_boundary_name = optional(string, null)
# A list of additional permissions to apply to the IAM user
policy_arns = optional(list(string), [])
}))
[] no
identity_center_permitted_roles A map of permitted SSO roles, with the name of the permitted SSO role as the key, and value the permissionset map(string)
{
"network_viewer": "NetworkViewer",
"security_auditor": "SecurityAuditor"
}
no
include_iam_roles Collection of IAM roles to include in the account
object({
security_auditor = optional(object({
enable = optional(bool, false)
name = optional(string, "lza-security-auditor")
}), {})
ssm_instance = optional(object({
enable = optional(bool, false)
name = optional(string, "lza-ssm-instance")
}), {})
})
{
"security_auditor": {
"enable": false,
"name": "lza-security-auditor"
},
"ssm_instance": {
"enable": false,
"name": "lza-ssm-instance"
}
}
no
infrastructure_repository The infrastructure repository provisions and configures a pipeline repository for the landing zone
object({
name = optional(string, null)
# The name prefix of the repository
create = optional(bool, true)
# A flag indicating if the repository should be created
visibility = optional(string, "private")
# The visibility of the repository
default_branch = optional(string, "main")
# home page url of the repository
homepage_url = optional(string, null)
# The home page url of the repository
enable_archived = optional(bool, false)
# A flag indicating if the repository should be archived
enable_discussions = optional(bool, false)
# A flag indicating if the repository should enable discussions
enable_downloads = optional(bool, false)
# A flag indicating if the repository should enable downloads
enable_issues = optional(bool, true)
# A flag indicating if the repository should enable issues
enable_projects = optional(bool, false)
# A flag indicating if the repository should enable projects
enable_wiki = optional(bool, false)
# A flag indicating if the repository should enable wiki
enable_vulnerability_alerts = optional(bool, null)
# A flag indicating if the repository should enable vulnerability alerts
topics = optional(list(string), ["aws", "terraform", "landing-zone"])
# The topics of the repository
collaborators = optional(list(object({
# The username of the collaborator
username = string
# The permission of the collaborator
permission = optional(string, "write")
})), [])
# The collaborators of the repository
template = optional(object({
# The owner of the repository template
owner = string
# The repository template to use for the repository
repository = string
# Include all branches
include_all_branches = optional(bool, false)
}), null)
# Configure webhooks for the repository
webhooks = optional(list(object({
# The content type of the webhook
content_type = optional(string, "json")
# The enable flag of the webhook
enable = optional(bool, true)
# The events of the webhook
events = optional(list(string), ["push", "pull_request"])
# The insecure SSL flag of the webhook
insecure_ssl = optional(bool, false)
# The secret of the webhook
secret = optional(string, null)
# The URL of the webhook
url = string
})), null)
# The branch protection to use for the repository
branch_protection = optional(map(object({
allows_force_pushes = optional(bool, false)
allows_deletions = optional(bool, false)
dismiss_stale_reviews = optional(bool, true)
enforce_admins = optional(bool, true)
lock_branch = optional(bool, false)
require_conversation_resolution = optional(bool, false)
require_last_push_approval = optional(bool, false)
require_signed_commits = optional(bool, true)
required_linear_history = optional(bool, false)

required_status_checks = optional(object({
strict = optional(bool, true)
contexts = optional(list(string), null)
}), null)

required_pull_request_reviews = optional(object({
dismiss_stale_reviews = optional(bool, true)
dismissal_restrictions = optional(list(string), null)
pull_request_bypassers = optional(list(string), null)
require_code_owner_reviews = optional(bool, true)
require_last_push_approval = optional(bool, false)
required_approving_review_count = optional(number, 1)
restrict_dismissals = optional(bool, false)
}), null)
})), {
main = {
allows_force_pushes = false
allows_deletions = false
dismiss_stale_reviews = true
enforce_admins = true
require_conversation_resolution = true
require_signed_commits = true
required_approving_review_count = 2

required_status_checks = {
strict = true
contexts = null
}
}
})

# The branch protection to use for the repository
permissions = optional(object({
read_only_policy_arns = list(string)
# The policy ARNs to associate with the repository
read_write_policy_arns = list(string)
# The policy ARNs to associate with the repository
}), {
read_only_policy_arns = ["arn:aws:iam::aws:policy/ReadOnlyAccess"]
read_write_policy_arns = ["arn:aws:iam::aws:policy/AdministratorAccess"]
})

# The permissions to use for the repository
permissions_boundary = optional(object({
arn = optional(string, null)
# The ARN of the permissions boundary to use for the repository
policy = optional(string, null)
# The policy of the permissions boundary to use for the repository
}), null)
# The permissions boundary to use for the repository
})
null no
inspector Configuration for the AWS Inspector service
object({
enable = optional(bool, false)
# A flag indicating if AWS Inspector should be enabled
delegate_account_id = optional(string, null)
# The account ID we should associate the service to
})
null no
kms_administrator Configuration for the default kms administrator role to use for the account
object({
# The domain name to use for the central DNS
assume_accounts = optional(list(string), [])
# A list of roles to assume the kms administrator role
assume_roles = optional(list(string), [])
# A list of roles to assume the kms administrator role
assume_services = optional(list(string), [])
# A list of services to assume the kms administrator role
description = optional(string, "Provides access to administer the KMS keys for the account")
# The description of the default kms administrator role
enable = optional(bool, false)
# A flag indicating if the default kms administrator role should be enabled
enable_account_root = optional(bool, false)
# A flag indicating if the account root should be enabled
name = optional(string, "lza-kms-adminstrator")
# The name of the default kms administrator role
})
{
"assume_accounts": [],
"assume_roles": [],
"assume_services": [],
"description": "Provides access to administer the KMS keys for the account",
"enable": false,
"enable_account_root": false,
"name": "lza-kms-adminstrator"
}
no
kms_key Configuration for the default kms encryption key to use for the account (per region)
object({
enable = optional(bool, false)
# A flag indicating if account encryption should be enabled
key_deletion_window_in_days = optional(number, 7)
# The number of days to retain the key before deletion when the key is removed
key_alias = optional(string, null)
# The alias of the account encryption key when provisioning a new key
key_administrators = optional(list(string), [])
# A list of ARN of the key administrators
key_owners = optional(list(string), [])
# A list of ARN of the key owners
key_users = optional(list(string), [])
# A list of ARN of the key users - if unset, it will default to the account
})
{
"enable": false,
"key_administrators": [],
"key_alias": "lza/account/default",
"key_deletion_window_in_days": 7,
"key_owners": [],
"key_users": []
}
no
macie A collection of Macie settings to apply to the account
object({
enable = optional(bool, false)
# A flag indicating if Macie should be enabled
frequency = optional(string, "FIFTEEN_MINUTES")
# The frequency of Macie findings
admin_account_id = optional(string, null)
# Is defined the member account will accept any invitations from the management account
})
null no
networks A collection of networks to provision within the designated region
map(object({
firewall = optional(object({
capacity = number
# The capacity of the firewall rule group
rules_source = string
# The content of the suracata rules
ip_sets = map(list(string))
# A map of IP sets to apply to the firewall rule ie. WEBSERVERS = ["100.0.0.0/16"]
port_sets = map(list(number))
# A map of port sets to apply to the firewall rule ie. WEBSERVERS = [80, 443]
domains_whitelist = list(string)
}), null)

private_subnet_tags = optional(map(string), {})
# Additional tags to apply to the private subnet
public_subnet_tags = optional(map(string), {})
# Additional tags to apply to the public subnet

subnets = map(object({
cidr = optional(string, null)
# The CIDR block of the subnet
netmask = optional(number, 0)
# Additional tags to apply to the subnet
tags = optional(map(string), {})
}))

tags = optional(map(string), {})
# A collection of tags to apply to the network - these will be merged with the global tags

transit_gateway = optional(object({
gateway_id = optional(string, null)
# The transit gateway ID to associate with the network
gateway_route_table_id = optional(string, null)
## Optional id of the transit gateway route table to associate with the network
gateway_routes = optional(map(string), null)
# A map used to associate routes with subnets provisioned by the module - i.e ensure
# all private subnets push
}), {
gateway_id = null
gateway_route_table_id = null
gateway_routes = null
})
## Configuration for the transit gateway for this network

vpc = object({
availability_zones = optional(string, 2)
# The availability zone in which to provision the network, defaults to 2
cidr = optional(string, null)
# The CIDR block of the VPC network if not using IPAM
enable_private_endpoints = optional(list(string), [])
# An optional list of private endpoints to associate with the network i.e ["s3", "dynamodb"]
enable_shared_endpoints = optional(bool, true)
# Indicates if the network should accept shared endpoints
enable_transit_gateway = optional(bool, true)
# A flag indicating if the network should be associated with the transit gateway
enable_transit_gateway_appliance_mode = optional(bool, false)
# A flag indicating if the transit gateway should be in appliance mode
enable_default_route_table_association = optional(bool, true)
# A flag indicating if the default route table should be associated with the network
enable_default_route_table_propagation = optional(bool, true)
# A flag indicating if the default route table should be propagated to the network
flow_logs = optional(object({
destination_type = optional(string, "none")
# The destination type of the flow logs
destination_arn = optional(string, null)
# The ARN of the destination of the flow logs
log_format = optional(string, "plain-text")
# The format of the flow logs
traffic_type = optional(string, "ALL")
# The type of traffic to capture
destination_options = optional(object({
file_format = optional(string, "plain-text")
# The format of the flow logs
hive_compatible_partitions = optional(bool, false)
# Whether to use hive compatible partitions
per_hour_partition = optional(bool, false)
# Whether to partition the flow logs per hour
}), null)
# The destination options of the flow logs
}), null)
ipam_pool_name = optional(string, null)
# The name of the IPAM pool to use for the network
nat_gateway_mode = optional(string, "none")
# The NAT gateway mode to use for the network, defaults to none
netmask = optional(number, null)
# The netmask of the VPC network if using IPAM
transit_gateway_routes = optional(map(string), null)
})
}))
{} no
notifications Configuration for the notifications to the owner of the account
object({
email = optional(object({
addresses = optional(list(string), [])
# A list of email addresses to send notifications to
}), {
addresses = []
})

slack = optional(object({
webhook_url = optional(string, "")
# The slack webhook_url to send notifications to
}), {
webhook_url = null
})

teams = optional(object({
webhook_url = optional(string, "")
# The teams webhook_url to send notifications to
}), {
webhook_url = null
})

services = optional(object({
securityhub = object({
enable = optional(bool, false)
# A flag indicating if security hub notifications should be enabled
eventbridge_rule_name = optional(string, "lza-securityhub-eventbridge")
# The sns topic name which is created per region in the account,
# this is used to receive notifications, and forward them on via email or other means.
lambda_name = optional(string, "lza-securityhub-slack-forwarder")
# The name of the lambda which will be used to forward the security hub events to slack
lambda_role_name = optional(string, "lza-securityhub-slack-forwarder")
# The name of the eventbridge rule which is used to forward the security hub events to the lambda
severity = optional(list(string), ["CRITICAL"])
})
}), {
securityhub = {
enable = false
}
})
})
{
"email": {
"addresses": []
},
"services": {
"securityhub": {
"enable": false,
"eventbridge_rule_name": "lza-securityhub-eventbridge",
"lambda_name": "lza-securityhub-slack-forwarder",
"lambda_role_name": "lza-securityhub-slack-forwarder",
"severity": [
"CRITICAL"
]
}
},
"slack": {
"webhook_url": null
},
"teams": {
"webhook_url": null
}
}
no
rbac Provides the ability to associate one of more groups with a sso role in the account
map(object({
users = optional(list(string), [])
# A list of users to associate with the developer role
groups = optional(list(string), [])
# A list of groups to associate with the developer role
}))
{} no
resource_groups Configuration for the resource groups service
map(object({
# The name of the resource group
description = string
# The type of the of group configuration
type = optional(string, "TAG_FILTERS_1_0")
# An optional configuration for the resource group
configuration = optional(object({
# The type of the of group configuration
type = string
# The parameters of the group configuration
parameters = optional(list(object({
# The name of the parameter
name = string
# The list of values for the parameter
values = list(string)
})), [])
}), null)
# The resource query to configure the resource group
query = optional(object({
# A collection of resource types to scope the resource query
resource_type_filters = optional(list(string), ["AWS::AllSupported"])
# A collection of tag filters to scope the resource query
tag_filters = optional(map(list(string)), {})
}), null)
# The resource query in json format https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/resourcegroups_group
resource_query = optional(string, null)
}))
{} no
s3_block_public_access A collection of S3 public block access settings to apply to the account
object({
enable = optional(bool, false)
# A flag indicating if S3 block public access should be enabled
enable_block_public_policy = optional(bool, true)
# A flag indicating if S3 block public policy should be enabled
enable_block_public_acls = optional(bool, true)
# A flag indicating if S3 block public ACLs should be enabled
enable_ignore_public_acls = optional(bool, true)
# A flag indicating if S3 ignore public ACLs should be enabled
enable_restrict_public_buckets = optional(bool, true)
# A flag indicating if S3 restrict public buckets should be enabled
})
{
"enable": false,
"enable_block_public_acls": true,
"enable_block_public_policy": true,
"enable_ignore_public_acls": true,
"enable_restrict_public_buckets": true
}
no
service_control_policies Provides the ability to associate one of more service control policies with an account
map(object({
name = string
# The policy name to associate with the account
policy = string
# The policy document to associate with the account
}))
{} no
ssm Configuration for the SSM service
object({
enable_block_public_sharing = optional(bool, true)
# A flag indicating if SSM public sharing should be blocked
})
{} no

Outputs

Name Description
account_id The account id where the pipeline is running
auditor_account_id The account id for the audit account
environment The environment name for the tenant
infrastructure_repository_git_clone_url The URL of the infrastructure repository for the landing zone
infrastructure_repository_url The SSH URL of the infrastructure repository for the landing zone
ipam_pools_by_name A map of the ipam pool name to id
log_archive_account_id The account id for the log archive account
networks A map of the network name to network details
private_hosted_zones A map of the private hosted zones
private_hosted_zones_by_id A map of the hosted zone name to id
sns_notification_arn The SNS topic ARN for notifications
sns_notification_name Name of the SNS topic used to channel notifications
tags The tags to apply to all resources
tenant_account_id The region of the tenant account
vpc_ids A map of the network name to vpc id

About

Used to provision a landing zone within a tenant account

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 5