AWS with LocalStack
Written by: Tom Spencer
Sep 28, 2024 โ 8 min readOverview
I liked this article from Brian Rinaldi for using AWS as a frontend developer. I liked his points about using AWS primitives i.e. that they can help save money and increase understanding of web development. I also loved the description of IAM:
working with IAM is like untangling a string of Christmas lights only to find that, once untangled, the lights aren't working because one single light, which you can't reliably locate, is not working. It's that fun.
A fellow AWS Community Builder Sedat Salman has also written a nice article is useful for further understanding of IAM: https://dev.to/aws-builders/a-beginners-guide-to-aws-identity-and-access-management-iam-4j5c
I decided to run some of the code described in the article and share my experience here.
Setting up AWS CLI
If you are not using AWS locally frequently a common gotcha is setting up AWS CLI. There are three steps here:
To configure CLI locally we can use aws configure. This command is used to configure the AWS CLI for general authentication and interaction with AWS services. It sets up credentials for access using access keys, which typically consist of an Access Key ID and a Secret Access Key. What it Configures:
- Access Key ID
- Secret Access Key
- Default region (e.g., us-east-1)
- Output format (e.g. json, text, yaml, etc.)
We can also use aws configure sso. This command is used to configure AWS CLI with Single Sign-On (SSO), which allows users to authenticate using AWS IAM Identity Center (formerly AWS SSO) or an identity provider such as Microsoft Active Directory, Okta, or others.
Article Overview
Brian's article starts with an overview of AWS Infrastructure and AWS Security Basics.
AWS Infrastructure
The main points about the AWS infrastructure are that AWS has regions e.g. us-east-1
. Each region has 3 or more availability zones (AZ). These are separate data centers within a region to make the zones resilient.
AWS Security Basics
The main vehicle for security in AWS is AWS Identity and Access Management. When we build applications each application will need to have specific access rights. IAM is made of four key parts:
- Users = people who need to interact with AWS services
- User groups = tool to assign permissions to multiple users
- Roles = temporary grant of rights to access AWS services
- Policies = JSON documents that define permitted actions on services
The article then goes into detail for the website building blocks and the web application building blocks. The website represents the html or visible documents. The web application is the backend services that service the resources.
Website
For the website we see:
- Amazon S3 = store website assets
- CloudFront = edge caching of static web assets (Content Delivery Network)
Web Applications
For the web application building blocks we see:
- Lambda = function for running some form of backend processing for the web application
- API Gateway = helps organise the backend APIs through restful endpoints
- DynamoDB = database for storing data perhaps displayed on the website or saved from the lambda function run
Running the code
For the infrastructure setup for the services described above, Brian used the AWS CDK stack. Brian included the code for the article here: https://github.com/localstack-samples/aws-for-frontend-devs The README is quite clear. We will clone the code and try running it to see if it works for us. Mainly we just follow the README: To install all the prerequisites, you can use the Makefile:
make install
I need LocalStack so just download from here: https://docs.localstack.cloud/getting-started/installation/
brew install localstack/tap/localstack-cli
To install cdklocal on npm we can usee this command: https://docs.localstack.cloud/user-guide/integrations/aws-cdk/
# Install globally
npm install -g aws-cdk-local aws-cdk
# Verify it installed correctly
cdklocal --version
# e.g. 1.65.5
```bash
Localstack now works:
```bash
โ aws-for-frontend-devs git:(master) โ localstack start
__ _______ __ __
/ / ____ _________ _/ / ___// /_____ ______/ /__
/ / / __ \/ ___/ __ `/ /\__ \/ __/ __ `/ ___/ //_/
/ /___/ /_/ / /__/ /_/ / /___/ / /_/ /_/ / /__/ ,<
/_____/\____/\___/\__,_/_//____/\__/\__,_/\___/_/|_|
๐ป LocalStack CLI 3.7.2
๐ค Profile: default
[04:38:41] starting LocalStack in Docker mode ๐ณ localstack.py:503
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ LocalStack Runtime Log (press CTRL-C to quit) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
LocalStack version: 3.0.3.dev
LocalStack Docker container id: 511e3f4c7e1c
LocalStack build date: 2023-12-29
LocalStack build git hash: 563edf7a
2024-09-28T03:38:42.727 INFO --- [-functhread6] hypercorn.error : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-09-28T03:38:42.727 INFO --- [-functhread6] hypercorn.error : Running on https://0.0.0.0:4566 (CTRL + C to quit)
2024-09-28T03:38:42.856 INFO --- [ MainThread] localstack.utils.bootstrap : Execution of "start_runtime_components" took 611.63ms
Ready.
cdk bootstrap works well:
โ s3 git:(master) โ cdklocal bootstrap
โณ Bootstrapping environment aws://000000000000/us-east-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.
CDKToolkit: creating CloudFormation changeset...
โ
Environment aws://000000000000/us-east-1 bootstrapped.
cdklocal deploy works:
โ s3 git:(master) โ cdklocal deploy
โจ Synthesis time: 7.1s
S3Stack: start: Building 3322b7049fb0ed2b7cbb644a2ada8d1116ff80c32dca89e6ada846b5de26f961:current_account-current_region
S3Stack: success: Built 3322b7049fb0ed2b7cbb644a2ada8d1116ff80c32dca89e6ada846b5de26f961:current_account-current_region
S3Stack: start: Building 2d56e153cac88d3e0c2f842e8e6f6783b8725bf91f95e0673b4725448a56e96d:current_account-current_region
S3Stack: success: Built 2d56e153cac88d3e0c2f842e8e6f6783b8725bf91f95e0673b4725448a56e96d:current_account-current_region
S3Stack: start: Publishing 3322b7049fb0ed2b7cbb644a2ada8d1116ff80c32dca89e6ada846b5de26f961:current_account-current_region
S3Stack: start: Building 02b3e28b9bf2893c77bb213a44995ab8ac1d9224ab74c400ab1a51d5f5fb4bc7:current_account-current_region
S3Stack: success: Built 02b3e28b9bf2893c77bb213a44995ab8ac1d9224ab74c400ab1a51d5f5fb4bc7:current_account-current_region
S3Stack: start: Publishing 2d56e153cac88d3e0c2f842e8e6f6783b8725bf91f95e0673b4725448a56e96d:current_account-current_region
S3Stack: start: Building e9830ceade25b6c2605fad111a59b934a6f3b0896226c71f839794fe1bb0c2b3:current_account-current_region
S3Stack: success: Built e9830ceade25b6c2605fad111a59b934a6f3b0896226c71f839794fe1bb0c2b3:current_account-current_region
S3Stack: start: Publishing 02b3e28b9bf2893c77bb213a44995ab8ac1d9224ab74c400ab1a51d5f5fb4bc7:current_account-current_region
S3Stack: start: Publishing e9830ceade25b6c2605fad111a59b934a6f3b0896226c71f839794fe1bb0c2b3:current_account-current_region
S3Stack: success: Published e9830ceade25b6c2605fad111a59b934a6f3b0896226c71f839794fe1bb0c2b3:current_account-current_region
S3Stack: success: Published 2d56e153cac88d3e0c2f842e8e6f6783b8725bf91f95e0673b4725448a56e96d:current_account-current_region
S3Stack: success: Published 02b3e28b9bf2893c77bb213a44995ab8ac1d9224ab74c400ab1a51d5f5fb4bc7:current_account-current_region
S3Stack: success: Published 3322b7049fb0ed2b7cbb644a2ada8d1116ff80c32dca89e6ada846b5de26f961:current_account-current_region
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:
IAM Statement Changes
โโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Resource โ Effect โ Action โ Principal โ Condition โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ + โ ${Custom::CDKBucketDep โ Allow โ sts:AssumeRole โ Service:lambda.amazona โ โ
โ โ loyment8693BB64968944B โ โ โ ws.com โ โ
โ โ 69AAFB0CC9EB8756C/Serv โ โ โ โ โ
โ โ iceRole.Arn} โ โ โ โ โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ + โ ${MyWebAppBucket.Arn} โ Deny โ s3:* โ AWS:* โ "Bool": { โ
โ โ ${MyWebAppBucket.Arn}/ โ โ โ โ "aws:SecureTransport": โ
โ โ * โ โ โ โ "false" โ
โ โ โ โ โ โ } โ
โ + โ ${MyWebAppBucket.Arn} โ Allow โ s3:Abort* โ AWS:${Custom::CDKBucke โ โ
โ โ ${MyWebAppBucket.Arn}/ โ โ s3:DeleteObject* โ tDeployment8693BB64968 โ โ
โ โ * โ โ s3:GetBucket* โ 944B69AAFB0CC9EB8756C/ โ โ
โ โ โ โ s3:GetObject* โ ServiceRole} โ โ
โ โ โ โ s3:List* โ โ โ
โ โ โ โ s3:PutObject โ โ โ
โ โ โ โ s3:PutObjectLegalHold โ โ โ
โ โ โ โ s3:PutObjectRetention โ โ โ
โ โ โ โ s3:PutObjectTagging โ โ โ
โ โ โ โ s3:PutObjectVersionTag โ โ โ
โ โ โ โ ging โ โ โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ + โ ${MyWebAppBucket.Arn}/ โ Allow โ s3:GetObject โ AWS:* โ โ
โ โ * โ โ โ โ โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ + โ arn:${AWS::Partition}: โ Allow โ s3:GetBucket* โ AWS:${Custom::CDKBucke โ โ
โ โ s3:::{"Fn::Sub":"cdk-h โ โ s3:GetObject* โ tDeployment8693BB64968 โ โ
โ โ nb659fds-assets-${AWS: โ โ s3:List* โ 944B69AAFB0CC9EB8756C/ โ โ
โ โ :AccountId}-${AWS::Reg โ โ โ ServiceRole} โ โ
โ โ ion}"} โ โ โ โ โ
โ โ arn:${AWS::Partition}: โ โ โ โ โ
โ โ s3:::{"Fn::Sub":"cdk-h โ โ โ โ โ
โ โ nb659fds-assets-${AWS: โ โ โ โ โ
โ โ :AccountId}-${AWS::Reg โ โ โ โ โ
โ โ ion}"}/* โ โ โ โ โ
โโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
IAM Policy Changes
โโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ Resource โ Managed Policy ARN โ
โโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ + โ ${Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC โ arn:${AWS::Partition}:iam::aws:policy/service-role/AW โ
โ โ 9EB8756C/ServiceRole} โ SLambdaBasicExecutionRole โ
โโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)? y
S3Stack: deploying... [1/1]
S3Stack: creating CloudFormation changeset...
โ
S3Stack
โจ Deployment time: 35.37s
Outputs:
S3Stack.BucketWebsiteURL = http://s3stack-mywebappbucketb9f20f45-8911936a.s3-website.localhost.localstack.cloud:4566
Stack ARN:
arn:aws:cloudformation:us-east-1:000000000000:stack/S3Stack/3a83232a
โจ Total time: 42.47s
It is nice that I don't have to deploy to AWS. LocalStack is very useful for checking local deployments. A fun test might be to check how to push to the S3 bucket locally. I can see that localstack is running a docker container locally:
โ s3 git:(master) โ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
511e3f4c7e1c localstack/localstack "docker-entrypoint.sh" 6 minutes ago Up 6 minutes (healthy) 127.0.0.1:4510-4560->4510-4560/tcp, 127.0.0.1:4566->4566/tcp, 5678/tcp localstack-main
I can view the S3 bucket I have created here:
โ s3 git:(master) โ aws --endpoint-url=http://localhost:4566 s3 ls
2024-09-28 04:40:09 cdk-hnb659fds-assets-000000000000-us-east-1
2024-09-28 04:41:13 s3stack-mywebappbucketb9f20f45-8911936a
We can also check other deployments:
aws --endpoint-url=http://localhost:4566 dynamodb list-tables
aws --endpoint-url=http://localhost:4566 lambda list-functions
Conclusion
I very much enjoyed reading the localstack article and also really liked the time saving that LocalStack offers for running CDK deploys locally. This is a great tool and David's article has provided a nice introduction to Frontend development with AWS Recommended.