Infrastructure as Code for Beginners (AWS CDK)

AWS Developers · Beginner ·☁️ DevOps & Cloud ·1mo ago

Key Takeaways

Builds a serverless REST API on AWS using the Cloud Development Kit with Python

Full Transcript

It all clicked for me once I just stopped clicking. And here's why. And finally done. Three AWS services, a DynamoDB table, lambda function, API Gateway, all configured, all connected, all deployed, and it took a lot of clicking and scrolling, but hey, at least I don't have to do all that again. >> Hey Rob, the client just signed off on a staging environment. Can you spin up the same API stack in US West 2? So, the same stack, the same config, same permissions, just by the end of day. Thanks. >> Wow, so I get to do all of that again in a different region from memory that I didn't even write down what I clicked on the first time because past me assumed future me probably wouldn't have to do that again and had a photographic memory. Well, past me was wrong about lots of things including this one. You see, this intern was clicking through the AWS console to set up a serverless REST API using DynamoDB, lambda, API Gateway, all the IAM wiring between them. A lot of clicking and it worked great that one time. But then when he needed to do it again, that's when the console stopped being a tool and started becoming a punishment. But then he learned infrastructure as code and that's when it clicked. Side note on the clicking thing. In the industry, it's called click ops. The more you know. Back to the show. >> Hey Rob, the client just signed off on a staging environment. So, can you spin up the same API stack in US West 2 with the same config, same permissions? They just need it by end of day. >> Easy peasy lemon squeezy. >> Great. >> How's that it? Same API in a new region, end of day. CDK deploy, that's all I need. Done. Oh, you need more regions, other configurations? Test me. I'm deploying to places that don't even exist yet. Just kidding. I deploy everywhere. Mr. International, yeah. Infrastructure as code, let's get it. All right, the intern is more productive now and a little bit too excited, but that's okay because in this video you're going to learn infrastructure as code so that you can deploy your cloud setup with one command, replicated across regions, roll back when something breaks because something will break eventually, and tear it all down so AWS doesn't send you a surprise love letter at the end of the month. Once you know this, you're not just the person who builds the API, you're the person everyone slacks when something goes wrong, which sounds like a curse, but on a resume, it looks like a promotion. So, here's what we're building so you have the map. A serverless REST API, three AWS services, each doing one job. API Gateway receives HTTP requests and routes them to your Lambda function. It's the front door to your app. Lambda runs your Python code without you managing a server, aka serverless. You pay per request, not per hour, and DynamoDB stores your data, which is a NoSQL database that scales without you configuring capacity. You'll deploy each piece one at a time, verify it works, and then add the next. You'll have a working CRUD API you can hit from your terminal, and you can use the blog linked in the description to find out all the commands I run so that you can reuse those commands and the code that I use so that you can follow along. I'm going to build that API through the console, every step, every click, not because I want you to do it this way, but because I want you to feel the pain that I'm feeling before we fix it. I'm also going to be putting a click counter in the corner somewhere so that we can watch my dignity decrease in real time. I'm in my AWS console. I'm going to go search DynamoDB. So, that works for one person, one time. Now, picture the Monday morning version. Your teammate needs the same API. You send him a Slack message, go to DynamoDB, click create table, partition key is ID, go to Lambda, blah blah blah. That's a long Slack message. By the third paragraph, they stop reading. They create the table with a partition key named ID uppercase capital letters and spend an hour wondering why the Lambda returns empty results. Because DynamoDB is case sensitive and friendships are not case sensitive. You need a staging environment, click ops your way through the same steps. You get most of the settings right. The ones you got wrong don't surface until a week later when staging behaves differently from production and nobody can figure out why. Then somebody deletes the API Gateway. The Lambda function's IAM role had a custom policy wrote three weeks ago. You didn't save it anywhere because past you assumed future you would remember. Past you was wrong about a lot of things. A manager asks, "Which environments have encryption enabled?" Three environments, four services each, and you've burned 20 minutes answering a yes or no question. Same pattern every time. The console works fine until a second person or a second environment enters the picture. So, those clicks could be a file. A file you commit to get, hand to a teammate, or rerun after a disaster. That is infrastructure as code. You write a file that describes your AWS resources. You have a tool that reads it, and everything gets created for you. Same file, same result every time. Your teammate needs the same API? They clone the repo and run one command. Same table, name casing, same timeout, same permissions. You need a staging environment? Deploy the same code with a different stack name. Somebody deletes the API Gateway? Run the deploy command again. The tool detects the missing resource and recreates it. The custom IAM policy you wrote 3 weeks ago is in the stack file, version controlled with a Git history showing who wrote it and why. Recovery takes minutes instead of an afternoon. Cost control. When you're done with an environment, one command tears everything down in the right order. No forgotten DynamoDB tables quietly billing you for months. I once found a table from a tutorial I did in 2023. It had zero items in it, but it cost me $4.73, and it made me question my budgeting skills. Infrastructure as code solves these problems. The next question is which tool? Three main options for AWS, and I want to be clear about the tradeoffs, though a full comparison deserves its own video. So, CloudFormation is the foundation. You write YAML or JSON, hand it to AWS, and it creates everything. CDK is what we're going to be using, which stands for Cloud Developer Kit. It generates CloudFormation templates underneath the hood, and CloudFormation is powerful, but very verbose. You'd have to specify every detail yourself about what each resource will look like, but CDK fills in the blanks. Another option is Terraform, and Terraform uses its own language called HCL, and works across multiple cloud providers. It calls AWS APIs directly, not through CloudFormation. If you're building on AWS, Azure, GCP at the same time, Terraform is a good multi-cloud choice. If you're AWS only, CDK, Cloud Developer Kit, removes all that friction. AWS CDK lets you write infrastructure in Python, TypeScript, JavaScript, Java, C#, or Go. It converts your code into CloudFormation templates and deploys it. You get things that you're familiar with in programming like loops, conditionals, autocomplete, real programming instead of just hand formatting a YAML document. This video uses Python CDK. Oh, and quick cost check before we start building. Lambda's always free tier covers 1 million requests per month. DynamoDB's always free tier covers 25 GB of storage, 25 read write capacity units in provision mode. Basically, this tutorial costs nothing to recreate on your free tier account. I'll show you how to tear everything down at the end so nothing keeps running. Oh, and side note, you'll need a AWS free tier account and have AWS CLI configured to follow along. If you don't have a free tier account, check out this video on how to set up your AWS free tier account properly, and then check out this video to see how to configure your AWS CLI. And one more thing, one last thing before we start building, there's a single line of code in this project that if you forget it, every API call fails with an access denied exception. I'll show you what that line is when we get to it. I forgot it in my first project and spent 2 hours staring at error logs. 2 hours. I could have learned how to juggle in that time, or anything. Would have been more productive, that's for sure. Time to set up the project. Let's get started. In order to get started, we need a couple of prerequisites, so I will open up my terminal, but make sure you have your free tier account and an the user instead of your root user and then here we go. So, here I have my terminal window open and just want to confirm these prerequisites before you get started. So, you need Python Python 3 {dash}{dash} version. You should get a Python version output. Another thing we need is we want to make sure we have Node. Because Node is what's going to allow us to install the NPM package for the Cloud Development Kit in the future step. And lastly, we want to make sure that your laptop is hooked up to your AWS account, which you will be provisioning resources to. So, AWS STS. So, this is the AWS CLI, uh which allows us to type in the AWS command. STS get caller identity. And it should show you who you are as an output here. So, let me go ahead and clear out the terminal now. So, that's the prerequisite check. All right. So, first we're going to scaffold our CDK project. So, where do I want to put my project? I like to put it in the desktop, so I'll CD into desktop and then I will go ahead um Before I start though, there's one thing I need it's a package called AWS CDK. Type in NPM install {dash}g AWS CDK. And this is the Cloud Development Kit package that allows you to use the CDK to provision resources to the cloud. I already have it installed, so my output might look a little bit different than yours. All right. So, you can verify it by doing CDK {dash}{dash} version. And you'll see that you should have a version output here. Now, let's create a directory for our project and initialize a CDK app with Python. So, I'm in my desktop. I'll create a directory called CRUD API in my desktop, so MKDIR CRUD API CD into the directory that I just created, CRUD API. I should be in that folder called CRUD API now. And this is where I will initialize a CDK project. In order to do that, this is the command. CDK init app {dash} {dash} language Python. And now it's creating all of the boilerplate file structure and boilerplate code and configuration requirements that's needed for us to get started. Let's see what that looks like. And in order to actually see what it looks like, I like to be in an IDE. My preferred IDE is Kiro, so I'll just open this folder up in Kiro. All right, here you should see my Kiro IDE. I'll just click open project to the most recent one, CRUD API. Perfect. Now, you'll see a lot of files and folders. It should look like this for you. Should have a test folder. You should have a CRUD API folder. So, another folder that's named the same as your parent folder name. So, this is the base boilerplate where we're going to be working off of. CDK generated project structure for us. Now, let's activate the virtual environment in Python and install the dependencies. And instead of using this terminal that I was using before, I'll just use the terminal that's also here in Kiro, so. Terminal, new terminal. And I'll just work from here. Cool. So, in this terminal I will type in source. I'll type in this command to activate the virtual environment. And then I will install all the requirements that come in this requirements.txt file. So, it comes with these requirements, these Python packages that it's going to install into my virtual environment. I'll use pip to install all the requirements listed in the requirements.txt file. Perfect. Now I have an Python virtual environment that has all the packages needed in order for us to start building. So, let's go ahead and go to the next step. So, the important file here is the CRUD API stack.py py file. Inside the CRUD API folder is where it's located. Uh that's where you define your AWS resources. You'll notice the directory is CRUD API. Yeah, you'll notice that it is named after your folder name, CRUD API stack. So, a stack is just a set of related resources that are going to be deployed together into the cloud for your app. So, this is where we're going to start defining our resources instead of clicking. So, whatever we clicked to do, we're going to be representing that in Python code here. That's how this works. This is a great boilerplate, but we're going to need to for each Lambda function that we're going to be building, we're going to need to create a folder to organize all those Lambda functions. So, let me just create a directory here called lambda. And this is where I'm going to be defining my Lambda functions. In this case, we only have one Lambda function, so this is where I'll put it. One set of step that catches people. CDK needs a one-time bootstrap. This creates an S3 bucket with some IAM roles in your AWS account that CDK uses to deploy. In this case, your packaged Lambda function will be stored in a S3 as a zip file. You run it once per account per region. You will forget you already did it, run it again 6 months later, see the already bootstrap message, and feel a brief but intense joy. All right, let's run this command. So, here I would type in CDK bootstrap. All right, looks like it's bootstrapped and ready to go. It has secured the S3 bucket and other resources it needs in order to properly deploy resources that we are going to define now. Project is scaffolded, bootstrap is done, now we build. We're starting with the database, DynamoDB. In the CDK, you define it with a few lines of Python and CDK handles the rest. After you write the code, we deploy it and verify it's live on AWS. Build, deploy, verify. Build, deploy, verify. That's the workflow for the rest of this video. All right, first I'm going to open up the CRUD API folder. And there's a file called CRUD API stack, and that's the file I'm interested in. Just minimize the terminal here for now. And this is where you will be defining your infrastructure as code inside of this function. In Python, it's written like this with def keyword. So, it even shows you an example of deploying a SQS resource here. Um so, let's do that with our DynamoDB. So, what I'm going to do is I'm going to replace everything in this file with this code, which you will find in the linked blog in the description, so that you can also copy and paste it. And let's go over this code here. So, we're importing from the AWS CDK some different classes. So, stack, which allows us to group AWS resources together, a removal policy controls what happens when we delete the stack. Um I wouldn't worry about CFN output for now, we'll see that later, but it it us to print the values of the resources that have been deployed once we deploy them. And uh here we're also importing the DynamoDB module uh and aliasing it with the name just DynamoDB. So, it's a little bit less to type. And in this base class uh CRUD API stack, this is where we're going to define our database. So, here we have defined the DynamoDB table, named it with a regular variable. You can name this variable whatever you'd like, but we're using this DynamoDB keyword, which comes from here. And it has a method called dot table. So, we can use DynamoDB.table and print in the parentheses we can give it information about what table to create. So, this line right here will create the DynamoDB table. And here we're giving it the name of the table, the partition key, which is the unique ID, the primary key for the table, and the billing mode, removal policy. So, all these pieces of information that we would like to configure when we're clicking, doing click ops, well, we can do that right here using code. And so, that's our definition for building and deploying the DynamoDB table. And in this part right here where we see outputs, the CFN output, that comes from this import right here, which just allows us to print the names of the resources that were created. So, in this case, since this function is related to the database table, we're assigning it a print statement that it will print after it's deployed that tells us the table name and the description of the table as well. Anytime before you deploy, best practice is to see the difference between what you are about to deploy, which is currently the code that we have, versus what's actually on your AWS account, so that you can see a difference of what is going to be added or inserted or removed from your cloud account. So, I'll type in Let me clear up the terminal here first, and I'll just type in CDK uh CDK diff. And it's going to see what the difference would be in our cloud account if we were to deploy this. All right, it shows us a lot of things that are a little bit hard to read right now, but the main point you need to focus on right here where it says resources and the plus sign. So, we're adding a new resource called DynamoDB, and the name of the table is going to be items table. And yeah, that there you go. So, now we're ready to deploy. This is one command, CDK deploy. And it's going to ask you for permission on if everything looks good before it presses the final button, which you don't have to press, cuz it's going to do it for you. Oh, actually didn't ask me for permission this time. Interesting. All right, cool. So, uh it is deploying currently, and once it finishes deploying, we can verify on our console. All right, so it says it finished deploying. 25 seconds, right? So, let's take a look at our console. So, here I'm back in my console. Let's verify in DynamoDB. If I go to tables, there it is. And it's going to be named according to the name of your stack, so there are going to be some random uniquely generated characters here, but that's the name of our table right here. So, we configured all of this without having to click ops our way through it. So, we just built our first piece of infrastructure deployed from code. Uh we now have a database that we deploy to the cloud using infrastructure as code. Now we need code in Python that will talk to that database in query create, read, update, delete operations from that database. The Lambda function is the Python that handles API requests and we're going to be writing it down here and defining the Lambda as infrastructure as code. So now let's go ahead and create the Lambda function and that's going to be in our folder called Lambda. So in Lambda, I'm going to do new file index.py. And I will go ahead and paste in some code already that I have ready. Once again, you can find all of this code in the blog linked in the description below. So here is just a Python script for a Lambda function that's going to import the necessary packages so that it can communicate with DynamoDB properly such as generating a unique ID and reading from environment variables, what the table name is. In this case, it was the items table. You don't want to hardcode your table name cuz what if you want to reuse this Lambda function for other DynamoDB tables and you want it to dynamically understand which table you're working with, right? Uh so boto3 is going to be a package that allows us to programmatically talk to different resources in Python. So here in this case, we're connecting to DynamoDB using boto3 and then uh that table variable is going to be assigned It's going to use the DynamoDB variable to be assigned a DynamoDB table and it's going to use the table that we have in our environment variables. And this is just a basic Python code for the name of our Lambda function here. So Lambda function is going to hit this handler and then it's going to try, depending on if we're trying to get, post, put, or delete items in the database. It will perform the relevant operations on the database. And here's the code for getting all the items from a DynamoDB table. table.scan Right. So, basically, we have all of our CRUD operations written out in Python here. This is not infrastructure as code, though. This is just a Lambda function's Python code. Typical code that you'd have in a maybe like a Flask app or a serverless app that would just query from a database. So, just want to be clear, this Lambda code right here is not infrastructure as code. What is infrastructure as code is in your CRUD API stack file. So, that means, even though we've defined our Lambda function, like the content, like the code that the Lambda function is going to have, we haven't explicitly told our CDK that we're going to want to provision a Lambda function that will have this code in our cloud environment. So, we go to CRUD API stack here, and then we're going to need to add infrastructure as code here, in this file, in order to represent the Lambda function. Now, we define the Lambda function in CDK and wire it to the database that we've already deployed. Remember that line of code I mentioned earlier in this video? The one thing that breaks everything if you forget it? Yeah. We're about to write it. All right. So, back in this file, CRUD API stack, where we define our resources, I'm just going to replace everything here with this new block of code, which you'll also find in the blog, in the appropriate step, where the only difference is, actually, we have create We have imported some new imports here, such as duration for Lambda timeouts. Lambda, Remember, it's serverless, so you can set a timeout for how long the lambda function runs for. And also importing this lambda resource here so that we can define our lambda resources. And I'm using this alias underscore lambda just because Python actually has a conflicting keyword called lambda in Python. So, yeah, fun fact. So, I'm using underscore lambda as the alias of the AWS lambda. Or you can just not alias it at all and just leave it as AWS lambda if you'd like. But if you want to follow along, I'm using this alias here. So, what's new here? Lambda function is being defined here. And that's our API handler, right? It's just the function that runs when we make a request to the API, and it's going to trigger this lambda function. So, this dot function keyword is going to be what's going to create a lambda function. Similar to DynamoDB.table, this dot table method here allows you to enter some parameters to define your DynamoDB table. Same here. Same concept here. Dot function method here allows you to specify some parameters for your lambda. We're going to call our lambda function API handler. It's going to use Python 3.12. And it's going to be using code from the asset in the file called lambda in the folder called lambda. So, it zips the lambda folder and uploads it. You're going to need a folder for each lambda function that you have. In this case, we only have one, so we have one folder with the index.py. So that it knows which DynamoDB table to query cuz what if I have lots of DynamoDB tables? Well, it's going to use the environment variable called table name, and it's going to take the items table, this variable, items table, and assign the name of that table that we have already deployed as the environment variable for this lambda to use when it needs to connect to a DynamoDB table. We're setting the timeout duration and the memory size. Basic configuration steps for lambda. Now, good thing is you can reuse this template. The first time, you know, it looks like a lot, but each new time, you don't have to write all this again. You just maybe change if I need to change the Python 3.14, I just do this. Right, I don't need to go into the console and click through a bunch of stuff. That's click ops. And for uh permissions, this is the one line. Super important. This is the one line. Super important. Items table {dot} grant read write data to the API handler. What does that mean? So, this DynamoDB table called which is which has the variable name items table going to go here. It's going to be very strict as to what services can query from query data from it. So, by default, it doesn't allow any services to query data from it. Security is day zero. And in this case, we're allowing it to grant the permission to our API handler. Now, what is API handler? It's this variable. What is this variable? This variable represents our lambda function that we are going to be creating. So, we're letting the DynamoDB table know that it can grant read and write data access to our API handler. Now, this grant read write data, that's a built-in method that comes with the CDK so that you can add permissions for one service to allow access from another service. So, then in our outputs, stays the same here. Make sure your file is saved, and let's go ahead I'm going to clear out my terminal here. And I'm going to type in CDK diff so that I can see the difference between what I'm proposing to deploy to the cloud. Basically, the difference in this file versus what's actually in my cloud environment. So, there should be a difference in the fact that there is no lambda function actually in my cloud environment. Let's see. All right, there we go. It's giving us some information here. Let's take a look at this information right here. So, for the credit API stack, if I were to deploy this, it would be creating a couple couple new things here. Updating the role for the lambda so that it can access the DynamoDB. Also, in terms of resources, it's adding a new role and a new policy. So, for the lambda, it's it would be adding a new role and policy. That lambda would also have the policy, meaning the permissions to talk to DynamoDB and create data into it or query data from it. Uh we're also going to be adding a new resource called which is the lambda function called API handler. That's the function that we defined. And by default, um when you create a lambda function, it also creates a log group for CloudWatch so that you can monitor the status of your lambdas. Okay, cool. Now, let's go ahead and deploy. All right, it's going to ask me for permissions this time. Uh require approval is enabled and stack includes security sensitive updates. Now, why is that? Well, because it's security sensitive because I'm allowing one service, the lambda, to access another service's data and like to communicate with another service. So, that's a change in security policy. So, it's asking me, "Are you sure you want to deploy these changes?" I'm going to say yes because I do want my lambda to have more access. In this case, just access to the DynamoDB table that we have created. And once it's finished deploying, we can verify on our console. All right, looks like everything is deployed here so far. Let's double-check and verify. So, I'm back in the console and I'll just search Lambda. Let's see what we got. There we go. There's our Lambda function. And if I click on it, I should see all that code that we just had. There we go. Here's all that code. And it configures the environment variable for this Lambda. You can see that here in the configuration for environment variables, table name. That's where if I go back to the terminal, you can see Yeah, we created environment variable called table name. That's going to be assigned to the name of the table of our DynamoDB table. In this case, that's the name of the table. Cool. So, we were able to do that with just a couple lines of code. Two resources deployed. The database and the function are wired together with the right permissions so that they can communicate with one another. And now we got one more piece to go, the API Gateway for us to actually access this whole application. API Gateway receives HTTP requests from the internet and forwards them to your Lambda function. After this deploy, you'll have a live URL to that API that you can hit from your terminal so that you can actually modify the data that's in your database. Here I am in the crud api stack.py file. And we're going to need one more resource. Just like we had Lambda function with our most previous resource, we're going to need another one right here. What I'm going to do is I'm going to replace all the existing code with the updated code so that we can see the difference here. All right, so I have pasted I've replaced everything that we just had with this new code, which you'll also find in the blog in the corresponding step. And the new things that were added here are some imports here, AWS API Gateway. That's the import, and we're just aliasing it as API GW for short. Here, nothing has changed. But here, you'll see a new resource. So, we're creating a variable called API. Name it anything you want, but it's using this import alias API GW. Here, you'll see this variable called API, which is equal to the API Gateway resource .RestApi method call. So, in this method call, uh similar to the method call here, like Lambda has a function.function method call, or DynamoDB has a .table method call, the method calls are where you specify the configuration parameters for the resource. In this case, for the table, table name, right? Things like that. So, for Lambda function, we have this information. In this case, for REST API, we're telling it the name of our API is items API. And uh item service is the REST API name, and we're going to we're going to give it a description. And then the API Gateway needs to forward all requests to our Lambda function as well. So, if I make a curl request to http:// API Gateway URL/items, for example, it needs to know that there's a Lambda function in our cloud environment that it needs to call and invoke. So, this is where we tell it that. We're creating a variable called items integration, and all it is is doing right now is it's integrating the API Gateway, which is this alias right here. So, integrating from the API Gateway to have a Lambda integration with our API handler. That's our Lambda function that we're integrating it with. So, forward all requests to our Lambda function. That's what it's doing. And our Lambda of the is called API handler. And now we have certain routes for our API. The base name in our URL. So, if our URL is like http:// blah blah blah blah blah blah dot com {slash} items, right? This {slash} items, this first part of our path, that's what we're defining here. Just so our URL has more organization. And then in this URL, we can make a get, post, put, or delete request. So, that's all that is here. We're adding methods. So, for this URL {slash} items, we're adding a get and post method, but we're also adding a resource in the put and delete requests because uh sometimes in your API, you want to tell it which specific item to update or which specific item to get from the table or which specific item to delete. So, this ID uh resource right here, it'll hold the ID of the resource that you want to get by one or update or delete. Uh these are the built-in methods called add method. Where does single item come from? Single item is just the name of our API Gateway resource that will have the {slash} ID attached. So, it would look like http:// blah blah blah blah That's our API Gateway URL. dot com {slash} items. That's where this comes from. That's one resource. And our other resource is {slash} ID. So, in this case, if I query item {slash} maybe an ID of one item from our database, then it'll query our database for a primary key that has this particular ID to either update or delete it with delete it or uh just to retrieve the information from it. So, let's go here. Um in the outputs, you should see also this new line where we're going to specify the API Gateway endpoint URL in our output once we deploy. So, outputs are just the pieces of information that your CDK will display to you once you have finished deploying it on your terminal. So, that's helpful because then we can just find out our API Gateway URL right away after deploying and then test and verify that it works. So, let's go ahead and do that. Now, before we deploy, best practice is to do Well, first let me clear the screen and do CDK diff. And let me make the terminal bigger here. So, we can see the difference between our stack locally versus what's actually in the cloud. All right, here's some output here that we see. So, what it's doing is it's going to allow the Lambda to invoke to be invoked by the API Gateway. So, some policy changes are happening. And then we also have the resources that are going to be built. So, our REST API, the stages, so in this case the prod stage, some new permissions that are going to be deployed so that I think some permissions related to the API Gateway being allowed to trigger a Lambda function. So, those are the types of things that you'll see with CDK diff. Overall in the outputs, you can also see that we're outputting more information now because we also want the API Gateway URL shown. So, now if I do CDK deploy, it's going to go ahead and deploy these resources. All right, it's asking me for permission to deploy these changes because this involves some more security sensitive updates again because we're modifying some IAM policies, in this case for the API Gateway and the Lambda function for them to be able to talk to each other. So, I'll press Y. It looks like it's creating the API Gateway. And once everything's done, we'll be ready to test it. All right. So, the API is live. You can see your endpoint right here. Uh you can click on either one of these. They're both the same. So, copy. I'll just right-click and copy. And we can actually make curl requests to this. So, let's take a look. In order to start testing it to see if it actually works, I'll create some environment variable here just to set the a variable to equal to this particular URL. So, I'll type in API I'm creating a variable called API URL in my shell environment to be equal to this URL. And now if I do echo URL, I should see that URL. Perfect. So, it knows this variable to be equal to this URL that we have deployed. And now we can test it using a curl command. So, first, let's uh our database is empty because we just created it. So, let's go ahead and send a post request using this curl command, which you'll also find in the blog linked in the description. So, I'm sending a curl command with a um post request to that API URL {slash} items. Because remember, um items is what we called one of the our endpoint URL resources right here. So, {slash} items. That's where we can do the get and post request to get all items or to post an item into the database. Cool. So, if I run this, uh what I'm doing is I'm passing it in JSON data that it would insert into the DynamoDB table. In this case, uh the item is called the item name is buy oat milk. Description is the fancy barista edition. Cool. So, it looks like it successfully inserted it, and we can verify that. Uh let me insert another item here real quick. Let's do one for a basketball jersey. Okay. So, I inserted two items into the database by sending two curl post requests and now let me do a get request to see if we get all of those items. I'll copy and paste this command curl the API URL /items which is you know that URL that we have. So yeah, we got two items that we just inserted into DynamoDB table. So it was able to go from API Gateway to our lambda handler and determine which crud operation to use and in this case it was a get request and it got me all the pieces of information, all the table items from the DynamoDB table. We can also do our typical get one single item by ID command. So in this case we see we see these IDs that were automatically generated in DynamoDB, right? So let's take one of these IDs and what I can do is I can type in this command curl -s API URL /items / the unique item name of one of the items in my in our DynamoDB table. In this URL right here. So if I make a curl request, I get back the data for that particular item that has this ID. So our API Gateway, that's where this is coming from by the way, right? So it wasn't just /items as our only path. We also had a URL path for /items / a dynamic variable called ID. So in this case we passed in a unique ID name from one of our database table items and it was able to give us all the information just about that one particular item. In this case that was the basketball jersey item. So we can verify that everything works and now we have a full crud API. There you go. So let's go ahead and recap. And there you have it, a full crud API running on AWS, deployed from code, zero console clicks. The first time this worked for me, I stared at the terminal for a solid 30 seconds. Then I deployed it again just to make sure it wasn't a fluke cuz I was in disbelief. That's the API working. Before we clean up, I want to peek behind the curtain. Remember, CDK generates CloudFormation under the hood. How much CloudFormation did it write for us? Well, we can verify by taking a look at this command right here. CDK synth. So, this command shows us what CloudFormation template was generated from our CDK files. So, this is what CloudFormation looks like. You see right here. Let me just make my terminal bigger here. So, let's start from top. And what is this? So, it is a YAML configuration, which is basically just like key-value pairs. And it talks about every single resource and every configuration that we've set up so far line by line. So, items table, it's a DynamoDB table, some properties that it has. It has these properties and configurations. Um API handler, that's our lambda. Uh well, this is actually our role for our lambda. So, this is a IAM policy role uh that allows the lambda to have some permissions to be executed and invoked by certain entities. And also, in this case, we have the DynamoDB policy document. So, it's a lot. It's a lot of information here, right? Um and you don't have to manually type and configure every single piece of information about every single resource in the cloud that you have deployed. CDK allows us to do that with very little amount of typing relatively. And it'll fill in the rest of the details for us. So, this CDK in Python generated this YAML template here, this CloudFormation template that you see right here. And this is what is going to be used by AWS eventually to directly generate the resources. So, CDK handled the IAM roles, the lambda permission for API Gateway, the deployment stage configuration, and a dozen other details you'd otherwise piece together from documentation and hope nothing changed since the last time you check the documentation. All right, one last thing before we wrap up, cost control. We need to tear down all the resources that we've created so far so that we don't incur any charges. This was just for demo purposes, so we definitely don't need any of these running after the demo. And same with you for your tutorial as you're learning and following along with the blog. So, here's the command that's going to do it for us. Instead of going through the console and going to lambda, DynamoDB, and delete, and confirming, I can just type in CDK destroy, press enter. It's going to ask me, "Are you sure you want to delete?" Yes. And I can be rest assured now that every resource that we have provisioned using this stack is going to be gone. And we will not be incurring any charges or any responsibilities from that. All right, there you go. Everything is removed from our cloud environment. CDK deletes the API Gateway, the lambda function, the DynamoDB table, and all the IAM roles and policies in the right order, handling all the dependencies. No guessing about which to delete first, no surprise bills, no $4.73 worth of DynamoDB tables hunting you from 2023. But one thing to know, CDK destroy deletes everything. So, in the case where if you feel like you made a bad change and you want to undo it without tearing down the whole stack, make sure you're version controlling, you're using Git to version control your code so that you can do Git revert on your last commit and run CDK deploy again after fixing whatever issues you were unsure of. CDK compares your code to what's deployed and rolls back the changes. Destroy is for when you're done. Revert plus deploy is for when you messed up. And after the CDK destroy command finishes, I always do a quick check in the CloudFormation console to make sure the stack is gone. Takes 5 seconds and saves you from a surprise bill on next month's invoice. I could do that actually through the AWS CLI right here. So, let me just show you how to do that. So, the name of our stack is this, right? CRUD API stack. So, I'll just type in this command, AWS CloudFormation describe stacks, stack name CRUD API stack. So, it's saying I need to configure I need to specify region. Okay. Let's uh do that. I could do AWS configure. And just go with everything here. Default re- region name. So, in this case uh US East 1. All right. So, now that I did AWS configure here with the region, it needed that, I can run this command again. Yeah, that stack does not exist. So, in this case, an error is a good thing. Error. That stack does not exist because we we got rid of it. So, we don't want it to exist. So, yeah, good error to have. All right. So, before we wrap up, I want to cover some common errors beginners usually encounter when doing infrastructure as code with CDK. So, I want to save you some debugging time. Four errors trip up most CDK beginners. I know because I've hit all four. I've hit all 10. Even though we're talking about four. Um in my first project, some of them I've hit them multiple times. So, we don't need to get into how many times I've hit these errors. Let's just talk about what you might see as an error. So, one error you might see says, "This stack uses assets, so the toolkit stack must be deployed." If you see that, that just means you forgot to bootstrap. Run CDK bootstrap once and it goes away. This is the CDK equivalent of "Did you try turning it off and on again?" Next error is user is not authorized. Anytime you see something that says user is not authorized, that means your AWS CLI user doesn't have the permissions CDK needs. For a personal learning account, you can use administrator access. Never do this in production. I'm looking at you. You know who you are. Another error is unable to import module index. So, in this case, your lambda code isn't where the CDK expects it. Check that your lambda folder has a file named exactly index.py, not handler.py, not mycode demo.py, not copy of index from tutorial.py. Literally index.py, just like you saw in our folder structure. Last error that's really common is access denied exception. For example, DynamoDB put item access denied exception. Remember that line of code where we grant the read and write data to the lambda function, the lambda handler, to be able to write to DynamoDB and read from it? If you forgot that step, this is what happens. So, make sure you have that step in there. So, that's the whole project. Started with an empty terminal, scaffolded a CDK project, deployed a Dynamo DB table, and verified it was live. Wrote a Lambda function and deployed it with the right permissions. Added API Gateway, deployed it, tested a full CRUD API from your terminal, and tore it all down with one command. And if someone asked us to provision that environment again, we just do CDK deploy. One command. Easy peasy. So, a couple things to note. In the age of AI, you can leverage your AI coding assistant. In my case, I I'm using Kiro to help you write the functions and the CDK code, the infrastructure as code that you need by just describing to the agent in plain English what you want, and it'll actually generate the code needed for that. So, you can play around with that so that you don't have to guess every single piece of variable names, import names, or method names from certain imports that you would need to use in order to accomplish certain things. You can just ask the AI, and it'll generate the infrastructure as code for you, and you can review over that and deploy it that way. Another thing to note is each piece, I deployed it and verified on my own before adding the next piece. So, notice I didn't deploy all of the infrastructure at once. I went step by step just so I knew that I was going the right direction. That's good for a beginner to do. Now, let's talk about three things that you can build on top of this if you wanted to explore further and add to it. So, you can add authentication with Amazon Cognito, so only logged in users can create, update, or delete items. Or, you could also deploy separate stacks for dev, staging, and production from the same codebase without having to rewrite everything. You can also add CloudWatch alarms so that you get notified when your Lambda errors spike. The blog post linked in the description has the complete code, all the commands, the architecture diagrams, and if you found this useful, subscribe. I'm building many more of these types of videos to help you get ramped up in terms of beginner fundamentals when it comes to cloud and software development. Happy coding, happy building, and thank you for watching.

Original Description

In this beginner friendly tutorial, you'll learn Infrastructure as Code (IaC) by building a real project: a serverless REST API on AWS using the Cloud Development Kit (CDK) with Python. We'll start by manually clicking through the AWS console to set up DynamoDB, Lambda, and API Gateway (yes, there's a click counter) so you can feel the pain. Then we rebuild the entire thing using AWS CDK, deploy it with one command, test it live, and tear it all down when we're done. AWS blog post with code: https://go.aws/3PJYfdi AWS CDK Documentation: https://go.aws/433Nw0i AWS CDK Python Reference: https://go.aws/4tWh7Uj Follow AWS Developers! 🆇 X: https://go.aws/4tWsDir 💼 LinkedIn: https://go.aws/43xc166 0:00 The ClickOps Problem 1:41 Infrastructure as Code to the Rescue 2:58 Architecture Overview: Serverless REST API 3:44 Building via Console (The Pain) 5:49 What is Infrastructure as Code? 7:02 IaC Tools: CloudFormation vs CDK vs Terraform 8:01 Why AWS CDK? 9:40 Project Setup & Prerequisites 10:36 Scaffolding the CDK Project 13:50 Understanding the Stack File 15:45 Step 1: Deploy DynamoDB Table 21:06 Step 2: Write the Lambda Function 23:45 Step 3: Define Lambda as Infrastructure as Code 31:52 Step 4: Deploy API Gateway 38:23 Testing the Live CRUD API 42:26 What CDK Generates Under the Hood 44:50 Cost Control 47:55 Common CDK Errors & Fixes 49:56 Recap & Next Steps #AWS #AWSCDK #Serverless
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Related Reads

📰
AWS Self-Healing Infrastructure: Catching and Fixing Server Crashes with AI Autopilot
Learn how to use AWS Self-Healing Infrastructure with AI Autopilot to catch and fix server crashes automatically
Medium · DevOps
📰
Understanding a Real AWS CodePipeline CI/CD Flow: Source Build Deploy"
Learn how to set up a CI/CD pipeline using AWS CodePipeline, covering source, build, and deploy stages
Dev.to · Guna SantoshDeep Srivastava
📰
How to Use Terraform with Python for Infrastructure
Learn to manage infrastructure with Terraform and Python for efficient and automated deployment
Dev.to · qing
📰
Interactive containers and exec: get inside, run commands, get out
Learn to interact with Docker containers using exec, cp, and shells to run commands and manage files
Dev.to · Javi Palacios

Chapters (18)

The ClickOps Problem
1:41 Infrastructure as Code to the Rescue
2:58 Architecture Overview: Serverless REST API
3:44 Building via Console (The Pain)
5:49 What is Infrastructure as Code?
7:02 IaC Tools: CloudFormation vs CDK vs Terraform
8:01 Why AWS CDK? 9:40 Project Setup & Prerequisites
10:36 Scaffolding the CDK Project
13:50 Understanding the Stack File
15:45 Step 1: Deploy DynamoDB Table
21:06 Step 2: Write the Lambda Function
23:45 Step 3: Define Lambda as Infrastructure as Code
31:52 Step 4: Deploy API Gateway
38:23 Testing the Live CRUD API
42:26 What CDK Generates Under the Hood
44:50 Cost Control
47:55 Common CDK Errors & Fixes
49:56 Recap & Next Steps
Up next
AWS, Azure, GCP: The One Thing Every Business Gets Wrong
AI Daily
Watch →