Creating a multi-region key using terraform
Tags: terraform, aws, kms, keys
Published: 2022-03-17 18:41:41

For organizations to encrypt their data in a cloud-native approach AWS provides a fully managed service [AWS KMS](https://aws.amazon.com/kms/), a high-performance key management system with the “pay as you go” model to lower costs and reduce their administration burden compared to self-managed [hardware security module (HSM)](https://csrc.nist.gov/glossary/term/Hardware_Security_Module_HSM).

By choosing AWS KMS organizations get three options for encryption key management:
* AWS KMS with customer or AWS-managed keys
* AWS KMS with BYOK 
* AWS KMS with a KMS custom key store key management backed by [CloudHSM](https://aws.amazon.com/cloudhsm/)

[Terraform AWS provider](https://github.com/hashicorp/terraform-provider-aws) version [3.64.0](https://github.com/hashicorp/terraform-provider-aws/releases/tag/v3.64.0) introduced new resource `aws_kms_replica_key` by which we can create [Customer Managed Key (CMK)](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#key-mgmt). 

In this blog post, we will walkthrough the steps for creating a *multi-region* CMK using the resource `aws_kms_replica_key` which was introduced newly in [Terraform AWS provider](https://github.com/hashicorp/terraform-provider-aws) version [3.64.0](https://github.com/hashicorp/terraform-provider-aws/releases/tag/v3.64.0).

Multi-Region keys come in handy for data security scenarios - Disaster recovery, Global data management, Distributed signing applications, Active-active applications that spun multiple regions.

As we need the resource type `aws_kms_replica_key` from Terraform AWS provider the below block helps to add this to our project. Make sure you have atleast 3.64.0 version to achieve this.


```
terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 3.64.0"
    }
  }
}
```
Multi-Region keys are not global. You create a multi-Region primary key and then replicate it into regions that you select within an AWS partition. Then you manage the Multi-Region key in each region independently.

In our case we will have the primary key created in Singapore region while the replicas in Sydney and Jakarta respectively.

```
# Singapore
provider "aws" {
  region = "ap-southeast-1"
}

# Sydney
provider "aws" {
  alias  = "secondary"
  region = "ap-southeast-2"
}

# Jakarta
# 3.70.0 Terraform AWS Provider release will use AWS SDK v1.42.23 
# which adds ap-southeast-3 to the list of regions for the standard AWS partition.
# https://github.com/hashicorp/terraform-provider-aws/issues/22252
provider "aws" {
  alias  = "tertiary"
  region = "ap-southeast-3"
  skip_region_validation = true
}
```

> **_NOTE:_** If you are trying to create a KMS replica in `JAKARTA` region you will encounter an error as below
  Error: Invalid AWS Region: ap-southeast-3
  with provider["registry.terraform.io/hashicorp/aws"].tertiary,

This is becuase support for `ap-southeast-3` was added in AWS SDK v1.42.23 and used in Terraform AWS Provider v3.70.0. The temporary solution for this would be to add `skip_region_validation = true` statement in the provider block.

Unlike other AWS resource policies, a AWS KMS key policy does not automatically give permission to the account or any of its users. To give permission to account administrators, the key policy must include an explicit statement that provides this permission, like this one.

```
data "aws_iam_policy_document" "kms" {
  # Allow root users full management access to key
  statement {
    effect = "Allow"
    actions = [
      "kms:*"
    ]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]
    }
  }

  # Allow other accounts limited access to key
  statement {
    effect = "Allow"
    actions = [
      "kms:CreateGrant",
      "kms:Encrypt",
      "kms:Decrypt",
      "kms:ReEncrypt*",
      "kms:GenerateDataKey*",
      "kms:DescribeKey",
    ]

    resources = ["*"]

    # AWS account IDs that need access to this key
    principals {
      type        = "AWS"
      identifiers = var.account_ids
    }
  }
}
```
### Creating multi-region primary key

```
resource "aws_kms_key" "primary" {
  description         = "CMK for AWS CB Blog"
  enable_key_rotation = true
  policy              = data.aws_iam_policy_document.kms.json
  multi_region        = true
}
```
**Resource** :`aws_kms_key` is used to create a single-region OR multi-region primary KMS key.

As this is a multi-region key the `id` & `key_id` has _mrk-_ as prefix.


```
terraform show -json terraform.tfstate | jq '.values.root_module.resources[0].values.id'
"mrk-01641fdcadec421f9ed2665c7d78ef9c"

terraform show -json terraform.tfstate | jq '.values.root_module.resources[0].values.key_id'
"mrk-01641fdcadec421f9ed2665c7d78ef9c"
```

You can also refer to KMS key using its alias. Resource : `aws_kms_alias` is used to create an alias.

```
resource "aws_kms_alias" "alias" {
  target_key_id = aws_kms_key.primary.id
  name          = format("alias/%s", lower("AWS_CB_CMK"))
}
```

> **_NOTE:_** "name" must begin with 'alias/' and be comprised of only [a-zA-Z0-9:/_-]

![AWS KMS CMK](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iok7uemk2v6dfkrho5cm.png)


### Creating multi-region replica keys
```
resource "aws_kms_replica_key" "secondary" {
  provider = aws.secondary

  description             = "Multi-Region replica key"
  deletion_window_in_days = 7
  primary_key_arn         = aws_kms_key.primary.arn
}

resource "aws_kms_replica_key" "tertiary" {
  provider = aws.tertiary

  description             = "Multi-Region replica key"
  deletion_window_in_days = 7
  primary_key_arn         = aws_kms_key.primary.arn
}
```

Resource :`aws_kms_replica_key` is used to create a multi-region replica key. Here we are passing explicitly the provider alias (aws.secondary & aws.tertiary) to create the keys in Sydney & Jakarta region.


![MRK](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1pk3hu1k1lvc27qdkw4x.png)

You need to set a waiting period of 7 (min) - 30 (max, default) days for deleting the KMS key.

We thus have a primary key in Singapore region with its replica in Sydney & Jakarta respectively. 


> 
While you implement this and in continuation of this blog we will see 

> - [How we can consume this key in encrypting an S3 bucket to configure AWS CloudTrail](https://dev.to/aws-builders/encrypt-cloudtrail-logs-with-multi-region-key-with-terraform-1hln)
> - [Configure an SQS](https://dev.to/aws-builders/configuring-amazon-sqs-queues-using-terraform-9g2)
> - Integrating Azure Sentinel to consume AWS CloudTrail data.

Source Code for above setup available [here](https://github.com/sujaypillai/awscb001/tree/mrk)