Terraform, AWS CloudFormation, and AWS CDK support a broad set of AWS services. Often, users want to automate more and even create resources that are not supported out of the box. In this session, explore how to build CloudFormation custom resources using your chosen programming language or Docker container. This method has been successfully used with CloudFormation and CDK, and it also works well with Terraform. Using Terraform modules, you only have to add a few lines of code to your projects to use a custom provider. Explore how this solution is designed to be secure, simple, future-proof, and reliable.
ABOUT AWS Amazon Web Services (AWS) hosts events, both online and in-person, bringing the cloud computing community together to connect, collaborate, and learn from AWS experts.
AWS is the world’s most comprehensive and broadly adopted cloud platform, offering over 200 fully featured services from data centers globally. Millions of customers—including the fastest-growing startups, largest enterprises, and leading government agencies—are using AWS to lower costs, become more agile, and innovate faster.
#reInvent2022 #AWSreInvent2022 #AWSEvents
Content
0.18 -> - [Martijn] All right.
1.29 -> Welcome everyone to
Terraform providers using
3.717 -> AWS CloudFormation custom resources.
7.59 -> Before I start, quick show of hands,
9.66 -> who has experience with CloudFormation?
13.6 -> Okay, almost everyone. And with Terraform?
17.562 -> Okay.
18.507 -> And who likes the one above the other?
21.9 -> Doesn't matter which one,
22.83 -> but do you have a
preferation on one of those?
25.08 -> - [Audience Member] It depends.
26.493 -> - Okay, it depends. Correct answer. Yeah.
28.65 -> Hopefully after this
talk you will love both
30.978 -> of the applications because
we are using them quite a lot.
36.312 -> Before we start, quick introduction.
38.43 -> My name is Martijn van Dongen.
40.11 -> I come from the Netherlands, Amsterdam.
43.68 -> I'm a Cloud Evangelist
working for Schuberg Philis.
47.04 -> Probably you never heard of our company.
48.93 -> We're just with 400
people in the Netherlands
51.45 -> with basically local customers,
54.12 -> but the customers we also have
55.89 -> are becoming more global customers.
58.26 -> So one of them will be
tomorrow in the keynote.
60.84 -> So I encourage you to visit the keynote.
65.76 -> We are basically a
mission critical company,
69.75 -> so we aim for 100% with everything.
74.52 -> So first place, 100%
customer satisfaction,
78.84 -> but also 100% automation, etc.
81.48 -> So that sets the stage
for this talk, as well.
86.04 -> Next to my day-to-day
job at Schuberg Philis,
88.02 -> I'm also a Cloud Evangelist,
sorry, a community hero,
91.77 -> and that basically because
I organize a lot of meetups
95.67 -> in the Netherlands,
96.93 -> and so they are little bit
smaller than this room,
101.16 -> about 80 people,
102.27 -> so I'm kind of a little
bit nervous right now.
104.85 -> Although we also did a big
event a couple of weeks back,
108.45 -> a community day with 500 people,
110.46 -> and Werner Vogels as the keynote speaker,
112.62 -> so we're really proud,
well, that's a big event.
117.09 -> Next to me is Sohan. Can
you introduce yourself?
119.37 -> - Yeah, of course.
120.203 -> Hi, my name is Sohan Maheshwar.
121.68 -> I'm a Senior Dev Advocate with
AWS based in the Netherlands,
125.13 -> where I work with Martijn
on community events.
128.28 -> And yeah, this is my fifth re:Invent,
130.14 -> so I'm super excited to be here.
132.72 -> Martijn, why don't you take it away?
134.16 -> - Cool.
136.41 -> So our talk is based on a true story
139.29 -> and actually based on two true stories.
142.86 -> The first one is one of our customers,
145.95 -> it's a big lease car company service,
150.03 -> and they have over a thousand engineers
152.85 -> that, of course, have
to automate everything.
155.97 -> And when they were automating
everything they found out,
158.07 -> they discovered,
158.903 -> that there not all the applications
160.53 -> were able to be automated.
162.48 -> So we had to use some kind of
technology to also automate
166.86 -> the parts that are not
native in AWS, for example.
170.64 -> And if we let them go, so
just develop it on their own,
174.6 -> you get all variations of implementations,
177.78 -> and that's what we would like to avoid.
182.16 -> That's what you can see here.
183.54 -> So one team is using CloudFormation.
185.58 -> So they've built their own
custom resource provider,
188.01 -> or their own tooling to
provision those custom resources.
193.383 -> The Terraform implementation
is also different.
195.78 -> They use different tools,
different coding languages,
199.17 -> but also different implementation.
201.39 -> And for CDK and for all
the other infrastructures,
203.76 -> code frameworks, same thing happens.
206.61 -> So what we were looking
for was actually this,
209.1 -> where we can have a common implementation
211.41 -> that all these popular
applications, cloud infrastructure,
216.041 -> configuration languages, can work with.
219.15 -> And we found it using CloudFormation,
221.481 -> that I will show you in this presentation.
225.18 -> The other example came
in a little bit later.
228.24 -> So just a few weeks ago.
230.746 -> I found out that in one
of our customer teams,
233.91 -> they were working with a lot
of breweries around the globe
237.66 -> and they use IoT,
Greengrass, the green icon.
241.89 -> And to provision your IoT Greengrass,
245.25 -> you can use Terraform, you
can use CloudFormation.
250.358 -> And with Terraform, if you
provision something to AWS,
253.853 -> like EC2, it works great.
256.32 -> If you try to do it with
Terraform to Greengrass,
259.32 -> it's not that optimal.
262.05 -> When you use CloudFormation,
it's close to optimal,
264.6 -> but there were still some
things that we would like to do
267.63 -> that was not natively
supported in CloudFormation.
270.57 -> So we resolved the specific
use cases using an AWS Lambda.
278.404 -> - [Sohan] Right.
279.57 -> So essentially Martijn
spoke about two customers.
282.42 -> Now we can't show the demo
of those products, of course,
285.168 -> so we want you to imagine
like a legacy application.
288.21 -> I'm a kid of the nineties,
289.649 -> so this is how most
websites looked back then.
292.65 -> So just think of a legacy application.
295.8 -> Maybe it needs a little
more comic sense, I dunno,
298.001 -> but, you know, so this is
just a visual aid, alright?
301.59 -> And we are gonna talk to you about
303.54 -> how you can use CloudFormation,
305.34 -> specifically custom
resources in CloudFormation.
309.96 -> Now, I know everyone here
knows what CloudFormation is,
312.84 -> but this talk is also being recorded,
314.85 -> so there may be people who might not know.
316.53 -> So just a very, very quick
refresher on CloudFormation.
320.31 -> It lets you model, provision, and manage,
322.65 -> AWS and third-party resources
using infrastructure as code.
327.18 -> It's the OG infrastructures code service,
330.39 -> as I like to call it.
331.5 -> Don't quote me on that
outside of this room, I guess.
334.17 -> But essentially you code
in YAML or JSON directly,
338.01 -> or you can use sample templates.
340.02 -> You can upload these
templates to an S3 bucket,
342.63 -> or to the CloudFormation resource itself.
346.32 -> When you deploy these templates,
it creates a stack, right?
349.77 -> It creates a stack of the
resources that you have deployed
352.86 -> and those stacks and
resources are provisioned
355.35 -> as your running environment.
357.39 -> This way you have represented
358.8 -> your entire infrastructure as code.
360.8 -> So this is repeatable,
it's completely automated,
363.162 -> you can roll back, yada, yada, yada.
366.45 -> What we want to really
focus on is this one feature
370.29 -> of CloudFormation,
called Custom Resources.
373.65 -> We think that this is a
pretty powerful feature
376.41 -> of CloudFormation.
378.09 -> And essentially it allows you to write
380.25 -> custom provisioning logic within
a CloudFormation template.
384.96 -> Now, there are two things
you can specifically do.
387.12 -> One is, there are AWS services
390.03 -> that aren't supported in CloudFormation,
392.73 -> which you could reference
using a custom resource,
395.779 -> or you could also reference
a third-party resource,
399.21 -> using custom resources.
402.15 -> When you're writing CloudFormation code,
404.01 -> this is typically what
it looks like, you know?
406.74 -> Simplest example, you have a
type, which is an EC2 instance,
410.13 -> so there is a particular syntax
411.93 -> that you have to follow, right?
413.46 -> So you say type is equal
to AWS EC2 instance,
417.06 -> and then the properties below,
418.53 -> sort of, describe that EC2 instance.
421.47 -> And that's how when you deploy
this CloudFormation template,
424.35 -> this EC2 instance is up
and running in the cloud.
428.91 -> For custom resource, the
syntax is quite similar.
432.505 -> The difference is, the type is custom,
435.27 -> followed by a name that you can give,
436.98 -> and we are going to call
it a unicorn tracker.
439.41 -> It's a simple application that, you know,
441.3 -> just allows you to add
and delete unicorns,
443.73 -> which you will see very soon.
445.68 -> And essentially in the
properties you have to mention
448.47 -> something called a service token, right?
451.2 -> So how custom resources works,
is first there is something
455.1 -> called a custom resource provider, right?
457.92 -> So someone who's written a template
459.42 -> that provides for the custom resource.
462.06 -> Then, of course, there is
the custom resource itself,
465.21 -> which is what we'll be doing.
466.95 -> And third, CloudFormation
actually runs the two.
470.49 -> Now this provider and the
custom resource, sort of,
473.25 -> talk to each other using this
thing called a service token.
477.6 -> The service token could
either be a lambda function
480.6 -> or it could be an SNS topic.
483.12 -> So essentially when that
custom resource, that template,
485.85 -> starts this lambda
function, or the SNS topic,
488.97 -> is alerted and things
happen behind the scenes,
492.21 -> which I will describe to
you in the next slide.
496.11 -> This is our implementation
for custom resources.
499.56 -> So essentially you have an author
501.27 -> who's written a custom resource provider
504.48 -> in a CloudFormation template.
506.52 -> Again, there is a slight
difference in the syntax,
509.19 -> there is a custom resource provider
512.13 -> and a custom resource itself,
514.5 -> both of them are written as
CloudFormation templates.
518.73 -> The logic of this custom resource
520.92 -> is authored in that lambda function.
523.47 -> So when you deploy the
CloudFormation stack,
525.96 -> it speaks to the lambda function,
527.66 -> which gets the provider
and the custom resources.
531.42 -> What happens behind the
scenes is that this lambda
534.24 -> actually writes to a
pre-signed S3 bucket, right?
537.66 -> So there's a pre-signed
URL with an S3 bucket,
540.39 -> and the results of the deployment
541.86 -> are sent to that S3 bucket.
544.012 -> CloudFormation verifies
looking at that S3 bucket
547.68 -> and then goes ahead and
creates all of your resources.
551.13 -> So all of that is happening
behind the scenes.
553.132 -> We as builders or you as builders,
555.6 -> we don't have to worry
about any of this, right?
558.06 -> You have to focus only
on your custom resources
560.812 -> and the custom resource provider.
564.75 -> So what sort of code do you actually write
567.72 -> in a lambda function
for a custom resource?
570.42 -> Well, it is typical
CRUD operations, right?
572.94 -> Create, Read, Update, and Delete.
575.55 -> So this is sample code
577.41 -> of how your lambda
function would look like.
580.32 -> Again, goes without saying,
but you could write this
582.36 -> in any language supported by lambda.
584.726 -> If you want, you could also
do that in a Docker container.
588.31 -> For this demo we have
written it in Python.
591.21 -> So as you can see, that is an API,
593.25 -> which is the API our ugly
legacy application uses
596.91 -> behind the scenes.
598.59 -> And you can see we've
written a create function,
601.05 -> which just creates a unicorn,
602.79 -> and we've written a delete function,
604.2 -> which just deletes a unicorn, right?
606.03 -> Super simple.
608.94 -> A CloudFormation code looks
a little something like this.
612.66 -> You can see a lambda layer there.
614.52 -> That layer, if you're not
familiar with lambda layers,
617.64 -> it's something that you can use
619.17 -> within your lambda functions,
620.82 -> if your lambda function has
things like dependencies.
623.7 -> So for instance, if you
have some Python libraries
626.4 -> that you want to have as
part of your lambda function,
629.01 -> you can put that in a lambda
layer, which can be shared.
631.978 -> Then of course we describe
the lambda function itself
635.307 -> and you know, you see all of that,
637.29 -> and right at the bottom is the endpoint,
639.75 -> which again I said is the API endpoint
641.85 -> for our legacy application.
644.7 -> So let's actually take a look
at how all of this end-to-end
647.94 -> will look in a demo.
649.41 -> We have, of course, prerecorded it
651.12 -> because waiting for CloudFormation
to, you know, think,
654.54 -> isn't the most fun demo.
655.83 -> So we'll go through it rather quickly.
657.78 -> All of this code is available on GitHub
659.277 -> and we will be sharing the
link at the end, right?
661.5 -> So all of it's there, so
don't worry about that.
665.622 -> Right, so essentially we haven't
spoken yet about Terraform,
670.74 -> but the room seems very familiar.
672.81 -> We're gonna use just
two Terraform commands
674.91 -> for this part of the demo.
676.278 -> Terraform init, initializes Terraform.
678.252 -> Terraform apply, goes ahead
and creates cloud resources.
682.74 -> Right? That's pretty much it.
685.14 -> So hopefully you can see
the screen at the back,
688.71 -> but we are essentially
initializing Terraform,
691.05 -> and we are going to initialize
our legacy application.
694.56 -> Shout out to Dennis,
695.46 -> and a couple of folks
here on the front row,
696.96 -> who helped us write that application.
699.42 -> And this is going ahead and creating...
704.25 -> Yeah, I'm just gonna pause that there.
706.53 -> Yeah, this has created
resources in the cloud
709.29 -> and it's created an endpoint,
711.18 -> which is the API gateway URL, right?
713.79 -> This is the endpoint for our legacy app.
717 -> We will be using this
in the rest of our demo.
720.45 -> Again, this API is an open
API as you will see right now,
724.5 -> but if you want to add
authorization or anything to it,
727.2 -> you can do that via open API gateway,
730.56 -> for the purposes of the demo,
731.79 -> this is right now an open API, right?
734.13 -> As you can see, there's
nothing in the API,
736.56 -> in the backend right now, no items.
738.18 -> So we will be adding it using
our lambda function very soon.
745.17 -> Okay. So yeah.
749.25 -> So now we're gonna create
the custom provider itself
752.64 -> using this particular
CloudFormation template, right?
756.48 -> And as you can see in this template,
758.46 -> this is the code that
I showed you earlier,
759.99 -> there is a lambda layer
761.49 -> and there is an endpoint
right at the bottom.
764.04 -> So we are going to deploy
765.63 -> this CloudFormation template very quickly.
768.84 -> You can see the resources
that are mentioned in this
772.05 -> particular CloudFormation template.
775.74 -> And this is written in
Python 3.9, but like I said,
778.53 -> you can write it in any
language that lambda supports.
786.12 -> Right?
787.2 -> So what we are going to do now
788.4 -> is also very quickly look at
790.29 -> the lambda function itself, right?
793.26 -> The one thing I want you to look at
795.51 -> is line number three, right?
797.07 -> That's the hidden hero
of this entire demo,
799.26 -> which we didn't speak about in the slides,
801.51 -> but it says from CR Helper, right?
803.64 -> So that is short for
Custom Resource Helper,
807.12 -> it's an open source module
developed by AWS itself.
811.29 -> It's very, very helpful
812.61 -> when you want to reroute
requests from custom resources
815.97 -> because it takes care of the mapping.
817.83 -> So when CloudFormation
818.82 -> sends an event to your lambda function,
821.1 -> this takes care of the
mapping automatically, right?
823.56 -> Otherwise you have to
do all of that manually.
825.81 -> So as you can see in the code itself,
827.64 -> you see @helper.create,
@helper.read, @helper.delete,
832.62 -> this is CR Helper doing
all of that work for you.
835.65 -> So if you're using custom resources,
838.41 -> I highly recommend you check
out this particular module.
842.94 -> The code in itself is trivial,
845.22 -> all it does is add and delete unicorns,
848.55 -> so don't worry too much about it.
850.2 -> You can see a backend API mentioned there
853.02 -> and the rest is typical CRUD code.
855.24 -> We haven't written the update function.
857.46 -> That's an exercise for the audience
859.17 -> or we just ran out time, one of the two.
860.895 -> Yeah, you understand.
862.176 -> Yeah, and as you can see, you
can delete unicorn as well.
869.73 -> So what we are going to do now is
871.26 -> we have the cloud formation template,
874.091 -> we have the lambda code.
876.6 -> So yeah, let's just deploy it.
879.15 -> So to deploy it first we need an S3 bucket
882.21 -> and I'm gonna create an S3 bucket,
884.04 -> which is imaginatively named
some-random-bucket-1234.
888.06 -> Honestly, it was a miracle
that this worked, right?
890.04 -> I was convinced that someone's
taken that name before,
892.68 -> apparently not.
894.39 -> So yeah, this is the
end point we got earlier
897.75 -> from our API gateway, right?
900.18 -> This is gonna be the
API for our legacy app.
904.02 -> I created this bucket from which
906.63 -> our CloudFormation template will be read.
910.71 -> That bucket's created
and now I'm just going to