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:
- Create a Customer Managed Key (CMK)
- Build the AMI using the key
- 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 Hashicorp’s Packer AMI Builder. The following packer snippet shows you the
essential parts to create an encrypted image:
... "builders": [{ "encrypt_boot": true, "kms_key_id": "{{user <code>kms_key
}}", "ami_regions": ["eu-central-1"], "region_kms_key_ids": { "eu-central-1": "{{userkms_key
}}" }, "ami_users": [ "111111111111", "222222222222" ], "snapshot_users":[ "111111111111", "222222222222" ], ...
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:
CustomAMI:
Type: Custom::AMI
Properties:
Filters:
name: your-custom-ami-name
Owners:
- '0000000000' # source account of the ami
ExpectedNumberOfKmsKeys: 1
ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:binxio-cfn-ami-provider'
KMSGrant:
Type: Custom::KMSGrant
Properties:
Name: autoscaling-access-to-encrypted-custom-ami
KeyId: !GetAtt CustomAMI.KmsKeyId
GranteePrincipal: !Sub 'arn:aws:iam::${AWS::AccountId}:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling'
Operations:
- Encrypt
- Decrypt
- ReEncryptFrom
- ReEncryptTo
- GenerateDataKey
- GenerateDataKeyWithoutPlaintext
- DescribeKey
- CreateGrant
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.