AWS Cloud Operations Blog

Manage AWS account alternate contacts with Terraform

Managing AWS billing, support and service team notifications, and potential security events are critical for customers to ensure security, cost optimization and operational monitoring for their AWS deployments. Alternate contacts allow us to contact another person about issues with your account at the right time, even if you’re unavailable. AWS will send you operational notifications such as impacted service availability to your operations alternate contact and important security related email notifications to both your primary account holder as well as the alternate security contact (if provided) in your AWS account.

Terraform is an open-source software used by customers to provision and manage infrastructure and services on AWS Cloud. Terraform lets developers and infrastructure architects build scalable and manageable infrastructure as code.

A previous blog post outline how you can programmatically manage alternate contacts in member accounts within AWS Organizations using the AWS Command Line Interface (CLI) in the AWS CloudShell. In this post, I will demonstrate how you can automate the set up and management of alternate contacts across all of your member accounts within an AWS Organizations using Terraform’s infrastructure-as-code module. Using this solution, you can receive important security, billing, and operations notifications by managing organization-wide alternate contacts across all of your accounts, including new accounts that you provision in your organization.

Prerequisites

To complete the steps described in this post, you will need the following:

  1. Download and set up Terraform. You can see these instructions to get started with Terraform on AWS.
  2. Make sure that your Terraform environment can assume an administrative IAM role to implement the resources described in this post across your management and delegated administrator accounts. Refer to the next section for a list of resources created by Terraform. See the Terraform documentation on how to use AssumeRole to provision AWS resources across accounts.

Here is a sample Terraform AssumeRole policy to access an AWS account:

provider "aws" {
  region  = “us-east-1” 
   assume_role {
     role_arn = "<ROLE_ARN>”
   }
}
  1. In your management account, enable the AWS Account Management service for your organization so that you can centrally manage alternate contacts. Do this by using the AWS CLI or CloudShell.
aws organizations enable-aws-service-access --service-principal account.amazonaws.com
  1. Register a delegated administrator so that users don’t need access to the management account to manage alternate contacts. Make sure that you replace <YOUR-CHOSEN-ACCOUNT-ID> in the following code snippet with the account ID of your delegated administrator.
aws organizations register-delegated-administrator --account-id <YOUR-CHOSEN-ACCOUNT-ID> --service-principal account.amazonaws.com

You only need to perform Steps 3 and 4 once throughout the lifecycle of your AWS account. You can use the AWS CLI or a Terraform resource.

Solution overview

The solution uses a serverless and event-driven architecture consisting of Amazon EventBridge, AWS Lambda, AWS Identity and Access Management roles, AWS Terraform provider, and AWS Organizations.

The following steps describe the workflow of the solution discussed in this blog post:

  1. In the designated administrator account, the Terraform module implements a custom Amazon EventBridge event bus, EventBridge Rule, IAM roles, and AWS Lambda function to programmatically configure your alternate contact.
  2. In the Management account, the Terraform module implements an IAM role and EventBridge rule attached to the Default event bus. The rule consists of a cross-account event trigger that invokes the Lambda function in your delegated account when a new account is added to your AWS Organizations.
  3. When a new account is added to AWS Organizations, the default event bus in the Management account receives the CreateAccountResult API event in AWS CloudTrail to acknowledge the account creation event. Then, it routes the event to a local EventBridge rule and forwards the event to a custom event bus in the delegated administrator account using a cross-account event bus.
  4. The custom event bus routes the event to an EventBridge rule that triggers the Lambda function in the delegated administrator account.
  5. The function obtains the list of member accounts, and configures the alternate contact in each account.

Note: The alternate contacts for the management account can only be modified using the standalone context, not the organization context. The Lambda function does not configure the alternate contact in the management account.

The following architecture diagram illustrates the solution discussed in this post:

The administrator uses Terraform to deploy the components required by the solution across the delegated and management accounts, including AWS EventBridge rules and buses, AWS IAM Roles, and AWS Lambda function.

By default, this terraform module creates a deployment package and uses it to create or update the Lambda Function or Layer in the delegated administrator account.

The source code for this blog can be downloaded from this GitHub repository.

Walkthrough

In this section, I highlight a method to deploy a sample AWS account alternate security, billing, and operations contacts using a Terraform variable definition (.tfvars) file. You can customize the deployment method using alternative approaches, such as CI/CD pipelines, Customizations for AWS Control Tower, etc., as applicable to your organization. The GitHub repository consists of a root module- main.tf Terraform script that implements two child modules dedicated_account and management_account in the delegated administrator and management accounts respectively.

Implementation Steps

  1. Validate that your AWS CLI profile is configured in your terminal and your Terraform environment can assume an administrative role in the delegated and management account.
  2. Clone the alternate contact repository:

git clone https://github.com/aws-samples/aws-account-alternate-contact-with-terraform

  1. Change into the downloaded directory:
$ cd aws-account-alternate-contact-with-terraform
  1. Open the providers.tf file and update it with your Terraform provider configuration. You can define your AWS CLI profiles or input your assume_role IAM role ARNs for the management and delegated accounts. See the Terraform Provider configuration for information on how to use providers to interact with remote systems.
provider "aws" {
  region  = var.region
  alias   = "delegated_account"
    assume_role {
    role_arn = "arn:aws:iam::<INSERT-DELEGATED-ACCOUNT-ID>:role/<INSERT-ASSUME-ROLE-NAME>"
    }
}

provider "aws" {
  region  = var.region
  alias   = "mgmt_account"
    assume_role {
    role_arn = "arn:aws:iam::<INSERT-MANAGEMENT-ACCOUNT-ID>:role/<INSERT-ASSUME-ROLE-NAME>"
    }
}
  1. Now you will create a file titled terraform.tfvars to define your variable arguments:
$ touch terraform.tfvars
  1. Open terraform.tfvars in your text editor, and paste in the following Terraform configuration file. Modify the file with your variable parameters, and save the file.
region = "us-east-1"
management_account_id  = "<INSERT-YOUR-MANAGEMENT-ACCOUNT-ID>"
security_alternate_contact = CONTACT_TYPE=SECURITY; EMAIL_ADDRESS=john@example.com; CONTACT_NAME=John Bob; PHONE_NUMBER=1234567890; CONTACT_TITLE=Risk Manager
billing_alternate_contact = CONTACT_TYPE=BILLING; EMAIL_ADDRESS=alice@example.com; CONTACT_NAME=Alice Doe; PHONE_NUMBER=1234567890; CONTACT_TITLE=Finance Manager
operations_alternate_contact = CONTACT_TYPE=OPERATIONS; EMAIL_ADDRESS=bob@example.com; CONTACT_NAME=Bob Smith; PHONE_NUMBER=1234567890; CONTACT_TITLE=Operations Manager  
invoke_lambda = true
tags = {
  Project     = "AWS-Alternate-Contact"
  Environment = "Dev"
}

The following variables are required:

  • region: The home AWS Region of your AWS Organizations management account.
  • management_account_id: The account ID of your AWS Organizations Management account.
  • security_alternate_contact: The security alternate contact details. Note: the valid value for CONTACT_TYPE is SECURITY.
  • billing_alternate_contact: The billing alternate contact details. Note: the valid value for CONTACT_TYPE is BILLING.
  • operations_alternate_contact: The operations alternate contact details. Note: the valid value for CONTACT_TYPE is OPERATIONS.
  • invoke_lambda: Controls if Lambda function should be invoked (true or false).
  • tags: A map of tags to assign to the resource.

Note: The root module must be deployed in the same region across your management and designated administrator accounts since EventBridge is a regional AWS service. This is automatically handled by the script and you should only include one argument for the region variable. You can make the Terraform module invoke the Lambda function by setting the invoke_lambda variable as true before running terraform apply.

The module uses some default parameters, such as event bus name, CloudWatch log group, and Lambda function name, to simplify the implementation described in this post. You can modify these default parameters by modifying the variables.tf file in the child module directories.

  1. Modify the main.tf file using a text editor and update the source variable parameter. If you are deploying the Terraform script from a local folder, then your source should be the local directory. For example:
module "delegated_admin_account" {
  source = "./modules/delegated_account"
module "management_account" {
  source = "./modules/management_account"

The source argument in a module block tells Terraform where to find the source code for the desired child module. Typical sources include Terraform Registry, local paths, Amazon Simple Storage Service (S3) Buckets, and many more. See the documentation on Terraform Module Sources for additional information.

  1. Now that you have added the script with your variable configuration, you can initialize the directory. Initializing a configuration directory downloads and installs the AWS provider, which is defined in the configuration:
$ terraform init

You should see a message that says “Terraform has been successfully initialized!" and the version of the provider that was installed.

  1. You should format and validate your configuration. The terraform fmt command automatically updates configurations in the current directory for readability and consistency. You can also make sure that your configuration is syntactically valid and consistent using the terraform validate command:
$ terraform fmt
$ terraform validate

You should now see a success message, which confirms that your template configuration is valid:

Success! The configuration is valid.
  1. You will now apply the configuration to create the infrastructure:
$ terraform apply

Before applying any configuration changes, Terraform prints out the execution plan to describe the actions that Terraform will take to update your infrastructure.

Once prompted, you will need to type “yes” to confirm that the plan can be run.

Plan: 13 to add, 0 to change, 0 to destroy.

Changes to Outputs:
+ delegated_account_event_bus = (known after apply)
+ delegated_account_event_rule = (known after apply)
+ delegated_account_lambda_function = (known after apply)
+ management_account_event_bus_role = (known after apply)

Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.

Enter a value: yes

After a successful deployment, you will see Terraform outputs similar to the following messages:

Apply complete! Resources: 13 added, 0 changed, 0 destroyed.

Outputs:

delegated_account_event_bus = "arn:aws:events:us-east-1:<ACCOUNT-ID>:event-bus/aws-alternate-contact"
delegated_account_event_rule = "arn:aws:events:us-east-1:<ACCOUNT-ID>:rule/aws-alternate-contact/aws-alternate-contact-rule"
delegated_account_lambda_function = "arn:aws:lambda:us-east-1:<ACCOUNT-ID>:function:aws-alternate-contact"
management_account_event_bus_role = "arn:aws:iam::<ACCOUNT-ID>:role/aws-eventbridge-alternate-contact-role"

Congratulations! You have now deployed the alternate security contact using Terraform.

Monitor alternate security contact configuration

To confirm that your alternate security contact has been deployed, you can perform the following steps:

  1. Log in to any of your member accounts within your AWS Organizations, and navigate to the account management/billing console.
  2. You should now see that the alternate security has been successfully configured:

The Billing console page in the AWS Management console displays the Security alternate contact details implemented by the Terraform solution described in this post.

Cleaning up

To avoid incurring future charges and undo the deployment, run the following terraform command:

$ terraform destroy

Running “terraform destroy” will completely remove all of the code, and future accounts won’t get the alternate contacts information. Before destroying all of your managed resources, terraform prints out the execution plan to describe the actions that Terraform will take to update your infrastructure. Once prompted, you will need to type “yes” to confirm that the plan can be run:

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes

Once the resources are removed, you will see a Terraform output similar to the following message:

Destroy complete! Resources: 13 destroyed.

Conclusion

This post demonstrates how to implement AWS account alternate contacts at scale using Terraform. Using infrastructure as code, you can programmatically set the alternate contacts across all of your accounts, including new accounts that you create or add to your organization. The example used in this post demonstrates the set-up of alternate security, billing, and operations contacts. You should make sure that the primary and alternate account contact details across your organization are set up for the workload owners and any additional teams that must receive billing, operations, and security notifications.

To learn more about AWS account alternate contacts, visit our AWS documentation.

About the author

Ibukun Oyewumi

Ibukun Oyewumi is a Security Assurance Consultant at AWS. He focuses on helping customers architect, build, scale, and optimize security controls, risk management, and compliance.

Sean Cai

Sean Cai is a Security Consultant at AWS focused on SecDevOps and designing optimized Security, Risk, and Compliance solutions.