Deploying CloudFormation resource rype
Resource types are like custom resources but treated as first-class citizens within CloudFormation; you can use CloudFormation capabilities to create, provision, and manage these custom resources in a safe and repeatable manner, just as you would any AWS resource.
AWS offers CloudFormation CLI to develop, test, and deploy resource types.
During first time deployment, the CLI will create CloudFormationManagedUploadInfrastructure
stack with necessary resources: S3 artifact bucket, KMS key, and logging & metrics role. The template is part of the CLI code. And this poses a problem: we need all buckets to have versioning and all roles to have PermissionBoundary
and Path
. I found one GitHub issue that mentions the same thing — https://github.com/aws-cloudformation/cloudformation-cli/issues/466, but there is no solution at the time of writing.
Luckily for us, we can replicate cfn submit
functionality using aws cli.
The first step is to use cfn submit
with --dry-run
option. It will package the code but won’t do anything else. The output of this command would be zip archive located in the current directory.
Next, we need to create our own template, which can be a copy of original managed-upload-infrastructure.yaml template.
Afterward, the process is as follows:
- Upload zip to the S3 bucket.
- Call
aws cloudformation register-type
to register the type with code located in S3. - Call
aws cloudformation describe-type-registration
to get the latest version ARN. - Call
aws cloudformation set-type-default-version
to make the latest version default.
The code bellow assumed the following env vars:
- S3_TYPE_ARTIFACT_BUCKET: artifact bucket name.
- EXECUTION_ROLE_ARN: role ARN to allow AWS API calls; can be empty if your resource does not interact with AWS Services. This role is created by the CLI after inspecting permissions in definitions file, the template is located in resource-role.yaml file.
- LOG_ROLE_ARN: role ARN to allow CloudWatch logging.
#for versioning purposes
resource_type_prefix=$(date '+%Y%m%d%H%M')
aws s3 cp myORG-myService-myResource.zip s3://$S3_TYPE_ARTIFACT_BUCKET/myORG-myService-myResource-$resource_type_prefix.zip
registration_token=$(aws cloudformation register-type \
--type-name myORG::myService::myResource \
--schema-handler-package s3://$S3_TYPE_ARTIFACT_BUCKET/myORG-myService-myResource-$resource_type_prefix.zip \
--type RESOURCE \
--execution-role-arn $EXECUTION_ROLE_ARN \
--logging-config LogRoleArn=$LOG_ROLE_ARN,LogGroupName=myORG-myService-myResource \
--output text \
--query 'RegistrationToken')
#wait register-type to complete
sleep 60
#grab the latest version arn
type_version_arn=$(aws cloudformation describe-type-registration \
--registration-token $registration_token \
--output text \
--query 'TypeVersionArn')
aws cloudformation set-type-default-version \
--type RESOURCE \
--type-name myORG::myService::myResource \
--version-id "${type_version_arn: -8}"