Skip to main content
Control Tower 0.8.1

Control Tower Landing Zone

View Source Release Notes

This is a Terraform module that deploys the AWS Control Tower Landing Zone in the management account.

Usage

module "control_tower_landing_zone" {
source = "git::git@github.com:gruntwork-io/terraform-aws-control-tower//modules/landingzone/control-tower-landing-zone?ref=v0.7.6"

email_address_account_log_archiver = "log-archiver-email@example.com"
email_address_account_audit = "audit-email@example.com"
}

How to upgrade the landing zone

AWS releases updates to the Control Tower Landing Zone on a regular basis. To upgrade Landing Zone to the latest version, you can simply update the landing_zone_version input variable to the desired version.

Check the Configuration update management in AWS Control Tower documentation for more information.

How to enable Region deny control

The Region deny control can't be enabled via the API/IaC. To enable it you need to follow the steps below:

  1. Open the AWS Control Tower console at https://console.aws.amazon.com/controltower/
  2. In the navigation pane, choose Settings.
  3. In the Region deny control section, choose Edit.
  4. Select the Enable region deny control check box.
  5. Choose Save.

How to import existing Landing Zones

If you are setting up the Landing Zone on a new installation no module is required. This section is for importing existing Landing Zones into infrastructure as code.

The goal of this section is to import the existing Landing Zone into Terraform so that it can be managed as code with no changes to the existing Landing Zone.

To achieve this you can use one of the following import methods:

  • [RECOMANDED] Using the import blocks available in OpenTofu and Terraform
    import {
    to = module.<resource_type>.<resource_name>
    id = "<resource_id>"
    }
  • Import resources using the CLI import command available in OpenTofu and Terraform
    tofu import "module.<resource_type>.<resource_name>" "<resource_id>"

Regardless of the method you choose, you will need find the resources that need to be imported using the AWS Console or the AWS CLI.

In the following table you can find examples for all the resources that need to be imported:

Resource TargetHow to find the Resource ID
module.<module_name>.aws_organizations_account.log_archiveaws organizations list-accounts --query "Accounts[?Name=='Logs'].Id"
module.<module_name>.aws_organizations_account.auditaws organizations list-accounts --query "Accounts[?Name=='Security'].Id"
module.<module_name>.aws_iam_role.controltower_adminIn this case the ID is static AWSControlTowerAdmin
module.<module_name>.aws_iam_role.controltower_executionIn this case the ID is static AWSControlTowerExecution (depending on the version this one may not exist)
module.<module_name>.aws_iam_policy.controltower_admin_policyaws iam list-policies --no-paginate --query "Policies[?PolicyName=='AWSControlTowerAdminPolicy'].Arn"
module.<module_name>.aws_iam_role.cloudtrailIn this case the ID is static AWSControlTowerCloudTrailRole
module.<module_name>.aws_iam_policy.cloudtrail_policyaws iam list-policies --no-paginate --query "Policies[?PolicyName=='AWSControlTowerCloudTrailRolePolicy'].Arn"
module.<module_name>.aws_iam_role.stacksetIn this case the ID is static AWSControlTowerStackSetRole
module.<module_name>.aws_iam_policy.stackset_policyaws iam list-policies --no-paginate --query "Policies[?PolicyName=='AWSControlTowerStackSetRolePolicy'].Arn"
module.<module_name>.aws_iam_role.config_aggregatorIn this case the ID is static AWSControlTowerConfigAggregatorRoleForOrganizations
module.<module_name>.aws_kms_key.controltoweraws kms list-aliases --no-paginate --query "Aliases[?AliasName=='alias/control_tower_key'].TargetKeyId"
module.<module_name>.aws_kms_alias.controltowerIn this case the ID is static alias/control_tower_key
module.<module_name>.aws_controltower_landing_zone.zoneaws controltower list-landing-zones --no-paginate

After you have found the resource IDs you can import the resources. The CLI commands assume a default configuration, if you have a different configuration you will need to adjust the commands accordingly.

The resources aws_iam_role_policy_attachment are safe to be re-created. We also recommend using the variable existing_key_arn to use the KSM key. Especially if you have a dedicated policy attached to the key.

The goal of the import is to have a plan with no operations to be performed. If you see any operations to be performed, you should stop and investigate the issue before applying the plan.

Sample Usage

main.tf

# ------------------------------------------------------------------------------------------------------
# DEPLOY GRUNTWORK'S CONTROL-TOWER-LANDING-ZONE MODULE
# ------------------------------------------------------------------------------------------------------

module "control_tower_landing_zone" {

source = "git::git@github.com:gruntwork-io/terraform-aws-control-tower.git//modules/landingzone/control-tower-landing-zone?ref=v0.8.1"

# ----------------------------------------------------------------------------------------------------
# REQUIRED VARIABLES
# ----------------------------------------------------------------------------------------------------

# The email address to use for the account to use for audit.
email_address_account_audit = <string>

# The email address to use for the account to use for centralized logging.
email_address_account_log_archiver = <string>

# ----------------------------------------------------------------------------------------------------
# OPTIONAL VARIABLES
# ----------------------------------------------------------------------------------------------------

# The number of days to retain log objects in the centralized access logging
# bucket.
access_logging_bucket_retention_days = 3650

# The name of the account to use for audit.
account_name_audit = "Security"

# The name of the account to use for centralized logging.
account_name_log_archiver = "Logs"

# The name of an additional organizational unit to create in AWS Control
# Tower.
additional_organizational_unit_name = "Pre-prod"

# The amount of time allowed for the create operation to take before being
# considered to have failed.
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts
create_operation_timeout = "60m"

# The amount of time allowed for the delete operation to take before being
# considered to have failed.
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts
delete_operation_timeout = "60m"

# Whether to enable access management in AWS Control Tower.
enable_access_management = true

# The ARN of an existing KMS key to use for AWS Control Tower.
existing_key_arn = ""

# The name of the foundational organizational unit to create in AWS Control
# Tower.
foundational_organizational_unit_name = "Security"

# A list of AWS regions to govern with AWS Control Tower. The region where you
# deploy the landing zone MUST always be included in this list.
governed_regions = ["us-east-1","us-west-2"]

# A list of IAM users or roles that should be granted administrative access to
# the KMS key.
kms_key_admins = []

# The alias to use for the KMS key used by AWS Control Tower.
kms_key_alias_name = "control_tower_key"

# A list of IAM users or roles that should be granted user access to the KMS
# key.
kms_key_users = []

# The version of the AWS Control Tower landing zone to deploy.
landing_zone_version = "3.3"

# The number of days to retain log objects in the centralized logging bucket.
logging_bucket_retention_days = 365

# Whether to provision the production organizational unit.
provision_prod_ou = false

# If you want to overwrite the auto discovery of the root OU, you can provide
# the ID here.
root_ou_id = ""

# A map of tags to apply to the AWS Control Tower landing zone.
tags = {}

# The amount of time allowed for the update operation to take before being
# considered to have failed.
# https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts
update_operation_timeout = "60m"

}


Reference

Required

The email address to use for the account to use for audit.

The email address to use for the account to use for centralized logging.

Optional

The number of days to retain log objects in the centralized access logging bucket.

3650
account_name_auditstringoptional

The name of the account to use for audit.

"Security"

The name of the account to use for centralized logging.

"Logs"

The name of an additional organizational unit to create in AWS Control Tower.

"Pre-prod"

The amount of time allowed for the create operation to take before being considered to have failed. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts

"60m"

The amount of time allowed for the delete operation to take before being considered to have failed. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts

"60m"

Whether to enable access management in AWS Control Tower.

true
existing_key_arnstringoptional

The ARN of an existing KMS key to use for AWS Control Tower.

""

The name of the foundational organizational unit to create in AWS Control Tower.

"Security"
governed_regionslist(string)optional

A list of AWS regions to govern with AWS Control Tower. The region where you deploy the landing zone MUST always be included in this list.

[
"us-east-1",
"us-west-2"
]
kms_key_adminslist(string)optional

A list of IAM users or roles that should be granted administrative access to the KMS key.

[]
kms_key_alias_namestringoptional

The alias to use for the KMS key used by AWS Control Tower.

"control_tower_key"
kms_key_userslist(string)optional

A list of IAM users or roles that should be granted user access to the KMS key.

[]
landing_zone_versionstringoptional

The version of the AWS Control Tower landing zone to deploy.

"3.3"

The number of days to retain log objects in the centralized logging bucket.

365
provision_prod_oubooloptional

Whether to provision the production organizational unit.

false
root_ou_idstringoptional

If you want to overwrite the auto discovery of the root OU, you can provide the ID here.

""
tagsmap(string)optional

A map of tags to apply to the AWS Control Tower landing zone.

{}

The amount of time allowed for the update operation to take before being considered to have failed. https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/servicecatalog_provisioned_product#timeouts

"60m"