×

How to start an autoscaling group using a cross-account encrypted AMI

Using encrypted Amazon machine images from another account in an autoscaling group does not work out of the box. You need to create an explicit KMS grant to make it work. In this blog, I will show you how to configure this in CloudFormation using our custom KMS grant provider in CloudFormation.

To create autoscaling groups based upon an encrypted AMI from another AWS account, you need to take the following three steps:

  1. Create a Customer Managed Key (CMK)
  2. Build the AMI using the key
  3. Grant autoscaling service access to the key

Create a Customer Managed KMS Key

To create an Amazon machine image which can be used across different accounts, you need to use a customer managed KMS key. The reason for this is that AWS managed keys can not be used outside the account they were created in.

The following CloudFormation resource defines a KMS encryption key policy which allows encrypted machine images to be used in other accounts:

EncryptionKey:
  Type: AWS::KMS::Key
  Properties:
    Description: 'EBS encryption key'
    EnableKeyRotation: true
    KeyPolicy:
      Version: '2012-10-17'
      Statement:
	- Sid: Enable IAM User Permissions
	  Effect: Allow
	  Principal:
	    AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
	  Action: kms:*
	  Resource: '*'
	- Sid: Allow use of the key
	  Effect: Allow
	  Principal:
	    AWS:
	      - arn:aws:iam::111111111111:root
	      - arn:aws:iam::222222222222:root
	  Action:
	    - kms:Encrypt
	    - kms:Decrypt
	    - kms:ReEncrypt*
	    - kms:GenerateDataKey*
	    - kms:DescribeKey
	  Resource: '*'
	- Sid: Allow attachment of persistent resources
	  Effect: Allow
	  Principal:
	    AWS:
	      - arn:aws:iam::111111111111:root
	      - arn:aws:iam::222222222222:root
	  Action:
	    - kms:ListGrants
	    - kms:CreateGrant
	    - kms:RevokeGrant
	  Resource: '*'

This key policy allows the key to be used in the target accounts 111111111111 and 222222222222. Now you can build an encrypted image with this key.

Build the AMI using the key

To build the Amazon machine image with the key, you can use Hashicorps Packer AMI Builder. The following packer snippet shows you the essential parts to create an encrypted image:

 1  ...
 2  "builders": [{
 3      "encrypt_boot": true,
 4      "kms_key_id": "{{user `kms_key`}}",
 5      "ami_regions": ["eu-central-1"],
 6      "region_kms_key_ids": {
 7        "eu-central-1": "{{user `kms_key`}}"
 8      },
 9      "ami_users": [ "111111111111", "222222222222" ],
10      "snapshot_users":[ "111111111111", "222222222222" ], 
11      ...

Lines 3-7 encrypts the image using our customer managed key. line 9 shares the AMI with our target accounts. line 10 ensures that the target account can read the snapshot too.

This image can already be used to start virtual machine instances in the target accounts. But any attempt to start an autoscaling group will fail with the following error message:

Client.InternalError: Client error on launch.

To resolve this, you need to grant the AWS autoscaling service access to the KMS key.

Grant the autoscaling service access to the key

To grant the autoscaling service in the target account access to the key, you create a KMS grant as follows:

 1  CustomAMI:
 2    Type: Custom::AMI
 3    Properties:
 4      Filters:
 5        name: your-custom-ami-name
 6      Owners:
 7        - '0000000000'    # source account of the ami
 8      ExpectedNumberOfKmsKeys: 1
 9      ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:binxio-cfn-ami-provider'
10
11  KMSGrant:
12  Type: Custom::KMSGrant
13  Properties:
14    Name: autoscaling-access-to-encrypted-custom-ami
15    KeyId: !GetAtt CustomAMI.KmsKeyId
16    GranteePrincipal: !Sub 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling'
17    Operations:
18      - Encrypt
19      - Decrypt
20      - ReEncryptFrom
21      - ReEncryptTo
22      - GenerateDataKey
23      - GenerateDataKeyWithoutPlaintext
24      - DescribeKey
25      - CreateGrant
26    ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:binxio-cfn-kms-provider'

The KMS encryption key id of the machine image is retrieved using the Custom::AMI at line 1-9. The KMS grant to the key is defined in line 11-26 by the Custom::KMSGrant.

Now the autoscaling group will be able to start instances from your encrypted machine image. That is all there is to it. If you want to use these custom providers too, you need to install it.

Installation

To install the custom KMS grant provider, type:

git clone https://github.com/binxio/cfn-kms-provider.git
cd cfn-kms-provider
aws cloudformation deploy \
        --capabilities CAPABILITY_IAM \
        --stack-name cfn-kms-provider \
        --template-file ./cloudformation/cfn-resource-provider.yaml

To install the custom AMI provider, type:

git clone https://github.com/binxio/cfn-ami-provider.git
cd cfn-ami-provider
aws cloudformation deploy \
        --capabilities CAPABILITY_IAM \
        --stack-name cfn-ami-provider \
        --template-file ./cloudformation/cfn-resource-provider.yaml

This will install our pre-packaged provider from s3://binxio-public-${AWS_REGION}/lambdas/cfn-ami-provider-latest.zip.

Conclusion

If you want to use an encrypted machine image from another account in an autoscaling group, you need to create a KMS grant for the autoscaling service linked role. Using the Custom::AMI and Custom::KMSGrant you can deploy the required KMS grant in a CloudFormation template using our custom provider.

Picture of Mark van Holsteijn
Mark van Holsteijn
CTO