Bridging the terraform - CloudFormation gap
This content is more than 4 years old and the cloud moves fast so some information may be slightly out of date.
CloudFormation does not cover all AWS Resource types. Terraform does a better job in covering resource types just in time.
So if you want to use a resource type which CloudFormation does not support yet, but you want to use CloudFormation, you have to build a Custom Resource with an own Lambda Function. CDK to the rescue: use AwsCustomResource
.
AwsCustomResource
gives you an easy tool to implement these resources - Without programming a Custom Resource Lambda.
Analyse
At first, you have to check whether CloudFormation does support the Resource Type. The CloudFormation doc is the right place to start.
In this example, I want to create an SES EmailIdentity. We see at SES Resource Type Reference - AWS CloudFormation, that this resource type is not supported.
Cloudformation does not support that. Terraform has this resource type: AWS: aws_ses_email_identity - Terraform by HashiCorp. So, let`s bridge the gap.
How do we use the type with CDK?
At first, you have to look at the API for this resource and determine the CRUD lifecycle, or just CUD (Create/Update/Delete).
If we look at the SES API or in the Node CDK we see:
VerifyEmailIdentity
creates a EmailIdentity and sends out a verification mail, deleteIdentity
deletes it.
Caution: real emails are sent out, so I will notice in my own domain when you try this…
CLI test create
Sometimes its good to test this with the CLI first, so we try:
aws ses verify-email-identity --email-address "info@mydomain.com"
Replace “info@mydomain.com” with you domain/email!
Now check in the AWS Console whether the Resource is created.
CLI test delete
Now we try the delete:
aws ses delete-identity --identity info@mydomain.com
Replace “info@mydomain.com” with you domain/email!
And check that it`s deleted. An Update is a delete and re-create in this case.
Implement Resource in CDK
With the AwsCustomResource you get the “CUD” properties:
onCreate, onDelete, onUpdate.
With our recherche from the SES API we get:
Call ______ | Property |
---|---|
onCreate |
verifyEmailIdentity |
onDelete |
deleteIdentity |
The first AwsCustomResource
The code for the custom create now look like this:
new AwsCustomResource(this, 'Identity', {
onCreate: {
service: 'SES',
action: 'verifyEmailIdentity',
parameters: {
EmailAddress: emailIdentity,
},
physicalResourceId: PhysicalResourceId.of("Identity"),
// PhysicalResourceId.fromResponse('VerificationToken') // Use the token returned by the call as physical id
},
One thing to mention is that with the example from the CDK documentation, which uses PhysicalResourceId.fromResponse('VerificationToken')
you get an error from CloudFormation about Identity/Resource/Default (Identity2D60E2CC) Invalid PhysicalResourceId
. So I changed to a static value.
The custom resource is backed by Lambda. The CDK creates a particular function, which interprets the service
and action
parameters.
This is the simple architecture:
CDK adds the managed policy AWSLambdaBasicExecutionRole
to the Lambda function.
To give the function the additional rights it needs, we have to add some rights:
policy: AwsCustomResourcePolicy.fromStatements(
[ new PolicyStatement({
actions: ['ses:verifyEmailIdentity',
'ses:deleteIdentity'],
effect: Effect.ALLOW,
resources: ['*']
})]
)
});
The actions in the policy are just the action from the onCreate
and the other hooks.
When we look at the terraform aws provider code, we see the similarities:
resource_aws_ses_email_identity
func resourceAwsSesEmailIdentityCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).sesconn
email := d.Get("email").(string)
email = strings.TrimSuffix(email, ".")
createOpts := &ses.VerifyEmailIdentityInput{
EmailAddress: aws.String(email),
}
_, err := conn.VerifyEmailIdentity(createOpts)
if err != nil {
return fmt.Errorf("Error requesting SES email identity verification: %s", err)
}
d.SetId(email)
return resourceAwsSesEmailIdentityRead(d, meta)
}
We see that terraform is doing the same. The CDK AwsCustomResource
is just a simple generic way of doing this.
Summary
If you use CDK and a resource is missing, consider the AwsCustomResource
to create it in an easy way.
The code for this is available in our cdk-template repository on github.
Conclusion
Thanks to
Photo by Alex Radelich on Unsplash