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
    }
  }
}

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
}

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"]
  }
}

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]
            }
          }
        ]
      })
    }
  ]
}

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
archive ~> 2.0
aws >= 5.0.0
aws.identity >= 5.0.0
aws.management >= 5.0.0
aws.network >= 5.0.0
aws.tenant >= 5.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
region The region we are provisioning the resources for the landing zone string n/a yes
tags A collection of tags to apply to resources map(string) n/a yes
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
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
})), [])
})
{
"enabled": 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
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",
"enabled": false
}
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, 8)
# 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
})
{
"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
}
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
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
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, null)
# 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": [],
"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
})
{
"enabled": false,
"key_administrators": [],
"key_alias": "lza/account/default",
"key_deletion_window_in_days": 10
}
no
macie A collection of Macie settings to apply to the account
object({
enable = optional(bool, false)
})
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)

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

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
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)
# A list of routes to associate with the transit gateway, optional
})
}))
{} 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
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_block_public_acls": true,
"enable_block_public_policy": true,
"enable_ignore_public_acls": true,
"enable_restrict_public_buckets": true,
"enabled": false
}
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

Outputs

Name Description
account_id The account id where the pipeline is running
environment The environment name for the tenant
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
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

Releases

No releases published

Packages

No packages published