Kanji
・ Cloud engineer / freelance ・ Born in 1993 ・ Born in Ehime Prefecture / Lives in Shibuya-ku, Tokyo ・ AWS history 5 years Profile details
Table of Contents
LocalStack is a tool for emulating AWS cloud services in a local environment. By mimicking AWS services locally, developers can use the AWS CLI and SDKs for testing without an internet connection.
With the Free plan, you can emulate some AWS services locally, but not all services are available. Major services like EC2, VPC, Lambda, S3, DynamoDB, SNS, and SQS are supported.
LocalStack is provided as a Docker container, making setup easy with Docker.
Former2 is a tool for generating AWS CloudFormation templates. Using IAM user or STS credentials, it scans resources in your AWS account and generates IaC (Infrastructure as Code) templates for CloudFormation, CDK, Terraform, Pulumi, and more. It also supports Draw.io (Beta), but in my experience, it did not work for VPC and EC2 creation.
Former2, like LocalStack, is available as a Docker container and can be easily set up with Docker.
This article introduces a LocalStack + Former2 setup with the following three key points:
LocalStack accounts have a 14-day trial period, during which you can use the LocalStack resource browser to view AWS resources via GUI. After the trial, you need a paid plan to use the GUI. By using Former2, you can visualize and check AWS resource states without worrying about the trial period.
While Former2 is available as a web service, the OSS version is provided as a Docker container, which can be orchestrated with LocalStack using Docker Compose.
You can clone the GitHub repository and start it with Docker Compose as shown below:
Cloning the repository or creating a Docker Compose file is not difficult, but this article introduces a method using DinD (Docker-in-Docker) to build and run Docker commands within a single container.
Former2 saves settings in the browser’s local storage, so clearing the browser cache will erase your settings. Even when running in a Docker container, there is no default way to persist settings, so you must reconfigure them manually if they are reset. This article introduces a workaround: writing Former2 settings in a JavaScript file and applying them at container startup.
Before proceeding, please note the following. LocalStack may behave differently from actual AWS environments, so it is recommended for self-learning purposes only.
Here are some behaviors I observed:
--capabilities CAPABILITY_NAMED_IAM
Even if deployment works in LocalStack, issues may arise when deploying to production. Use this environment for learning AWS CLI or CloudFormation, or for internal workshops, understanding these limitations.
This article assumes the following prerequisites:
First, create the following three files in your working directory:
${WORK_DIRECTORY}/ ${WORK_DIRECTORY}/docker-compose.yml ${WORK_DIRECTORY}/Dockerfile ${WORK_DIRECTORY}/setting.js # Example directory structure localstack-with-former2/ localstack-with-former2/docker-compose.yml localstack-with-former2/Dockerfile localstack-with-former2/setting.js
You can create the working directory anywhere. For example:
WORK_DIRECTORY=$(pwd)/localstack-with-former2 mkdir -p ${WORK_DIRECTORY} cd ${WORK_DIRECTORY}
Create the Dockerfile with the following content. Key points:
Dockerfile
ARG
setting.js
index.html
/root/former2
/volume
docker-compose up
# REF: https://hub.docker.com/r/docker/compose FROM docker/compose:alpine-1.29.1 WORKDIR /root RUN apk add --no-cache git RUN git clone https://github.com/iann0036/former2.git COPY docker-compose.yml . # WORKAROUND: https://stackoverflow.com/questions/37013947/force-a-docker-build-to-rebuild-a-single-step ARG SETTING_TIMESTAMP=YYYYMMDDHHMMSS COPY setting.js . RUN cp setting.js former2/lib/setting.js && \ sed -i '/<\/head>/i <script src="lib/setting.js"></script>' former2/index.html CMD cp -R /root/former2 /volume && \ docker-compose up
Create the docker-compose.yml file as follows. Key points:
docker-compose.yml
docker.sock
/var/run/docker.sock
volume
EXTRA_CORS_ALLOWED_ORIGINS
# REF: https://docs.docker.jp/compose/compose-file/compose-versioning.html version: "3.8" services: former2: container_name: former2 image: nginx:stable-alpine ports: - "127.0.0.1:8089:80" volumes: - ${WORK_DIRECTORY}/volume/former2:/usr/share/nginx/html - "/var/run/docker.sock:/var/run/docker.sock" # REF: https://docs.localstack.cloud/getting-started/installation/#starting-localstack-with-docker localstack: container_name: localstack image: localstack/localstack ports: - "4566:4566" - "4510-4559:4510-4559" environment: - DEBUG=${DEBUG-} - DOCKER_HOST=unix:///var/run/docker.sock # REF: https://github.com/iann0036/former2 - EXTRA_CORS_ALLOWED_ORIGINS=chrome-extension://fhejmeojlbhfhjndnkkleooeejklmigi volumes: - ${WORK_DIRECTORY}/volume/localstack:/var/lib/localstack - "/var/run/docker.sock:/var/run/docker.sock"
Create the setting.js file as follows. These settings are based on former2/js/app.js at master · iann0036/former2 . Only the default region, CloudFormation template indentation, and LocalStack endpoint usage are set here.
window.localStorage.setItem('region', "ap-northeast-1"); window.localStorage.setItem('cfnspacing',"2"); window.localStorage.setItem('uselocalstackendpoint', "true"); window.localStorage.setItem('logicalidstrategy', "longtypeprefixoptionalindexsuffix"); // default window.localStorage.setItem('defaultoutput', "cloudformation"); // default window.localStorage.setItem('iaclangselect', "typescript"); // default window.localStorage.setItem('skipirrelevantresources', "true"); // default window.localStorage.setItem('relatedresourcessetting', ""); // default window.localStorage.setItem('includedefaultresources', ""); // default
Using the files from “Preparation Step 1,” build the Docker image:
--platform=linux/amd64
--build-arg SETTING_TIMESTAMP=$(date +%Y%m%d%H%M%S)
docker build \ --platform=linux/amd64 \ --build-arg SETTING_TIMESTAMP=$(date +%Y%m%d%H%M%S) \ -t localstack-with-former2:latest \ .
Start LocalStack and Former2 with:
--detach
docker run \ --name localstack-with-former2 \ --platform=linux/amd64 \ --privileged \ --detach \ --rm \ -it \ -e WORK_DIRECTORY=$(pwd) \ -v /var/run/docker.sock:/var/run/docker.sock \ -v $(pwd)/volume:/volume \ localstack-with-former2:latest
Note: Run these commands in the directory where you built the Docker image.
In docker-compose.yml , the Former2 container mounts ${WORK_DIRECTORY}/volume/former2:/usr/share/nginx/html . Since docker-compose up runs inside the container, you might expect ${WORK_DIRECTORY}/volume/former2 inside the container to be mounted, but actually, the source is the host directory. Therefore, mounting /root/former2 (which exists only inside the container) is not possible. See: linux - Docker in Docker cannot mount volume - Stack Overflow
${WORK_DIRECTORY}/volume/former2:/usr/share/nginx/html
${WORK_DIRECTORY}/volume/former2
Thus, the process is:
-v $(pwd)/volume:/volume
${WORK_DIRECTORY}/volume
CMD
First, configure the AWS CLI to use LocalStack.
You can use environment variables or an AWS CLI profile. With environment variables, you must set them each time. With a profile, you configure once and specify the profile for commands.
aws configure set aws_access_key_id localstack --profile localstack aws configure set aws_secret_access_key localstack --profile localstack aws configure set region ap-northeast-1 --profile localstack aws configure set aws_endpoint_url http://localhost:4566 --profile localstack
export AWS_ACCESS_KEY_ID=localstack export AWS_SECRET_ACCESS_KEY=localstack export AWS_REGION=ap-northeast-1 export AWS_ENDPOINT_URL=http://localhost:4566
Assuming you configured the AWS CLI with the profile method, once LocalStack is running, you can use the AWS CLI to interact with resources.
First, verify AWS CLI access to LocalStack:
aws sts \ get-caller-identity \ --profile localstack
If you see a response like below, your configuration is correct (account ID will be 000000000000 ):
000000000000
# Example output % aws sts get-caller-identity \ --profile localstack { "UserId": "AKIAIOSFODNN7EXAMPLE", "Account": "000000000000", "Arn": "arn:aws:iam::000000000000:root" }
Next, create a sample S3 bucket (bucket name does not need to be globally unique):
aws s3api \ create-bucket \ --bucket hoge-foo-bar \ --create-bucket-configuration LocationConstraint=ap-northeast-1 \ --profile localstack
# Example output % aws s3api \ create-bucket \ --bucket hoge-foo-bar \ --create-bucket-configuration LocationConstraint=ap-northeast-1 \ --profile localstack { "Location": "http://hoge-foo-bar.s3.localhost.localstack.cloud:4566/" }
Note: Resources are deleted when you stop the container. To retain resources, keep the container running.
Check the bucket you created using Former2. Access http://127.0.0.1:8089/ , then go to “Storage” -> “S3” -> “Buckets”. You should see your bucket displayed as below.
For configuration values not visible in Former2, use the AWS CLI. For example, to check the status of a CloudFormation stack, use the AWS CLI. Below is a command example for a stack named sample-vpc-stack . The output is formatted into a table using jq.
sample-vpc-stack
# Stack List aws cloudformation describe-stacks \ --profile localstack \ --query "Stacks[].{StackName: StackName, StackStatus: StackStatus, CreationTime: CreationTime, LastUpdatedTime: LastUpdatedTime}" \ --output json \ | jq -r '["StackName","StackStatus","CreationTime","LastUpdatedTime"], (.[] | [.StackName, .StackStatus, .CreationTime, .LastUpdatedTime]) | @tsv' \ | column -t -s $'\t' # List of Stack Resources stack_name="sample-vpc-stack" aws cloudformation list-stack-resources \ --stack-name "${stack_name}" \ --profile localstack \ --output json \ | jq -r '["LogicalResourceId","PhysicalResourceId","ResourceType","ResourceStatus"], (.StackResourceSummaries[] | [.LogicalResourceId, .PhysicalResourceId, .ResourceType, .ResourceStatus]) | @tsv' \ | column -t -s $'\t' # Stack Events List stack_name="sample-vpc-stack" aws cloudformation describe-stack-events \ --stack-name "${stack_name}" \ --query "StackEvents[].{Timestamp: Timestamp, LogicalResourceId: LogicalResourceId, ResourceType: ResourceType, ResourceStatus: ResourceStatus, ResourceStatusReason: ResourceStatusReason}" \ --profile localstack \ --output json \ | jq -r '["Timestamp","LogicalResourceId","ResourceType","ResourceStatus","ResourceStatusReason"], (.[] | [.Timestamp, .LogicalResourceId, .ResourceType, .ResourceStatus, .ResourceStatusReason]) | @tsv' \ | column -t -s $'\t' # Stack Outputs List stack_name="sample-vpc-stack" aws cloudformation describe-stacks \ --stack-name "${stack_name}" \ --query "Stacks[].Outputs[].{OutputKey: OutputKey, OutputValue: OutputValue, ExportName: ExportName}" \ --profile localstack \ --output json \ | jq -r '["OutputKey","OutputValue"], (.[] | [.OutputKey, .OutputValue, .ExportName]) | @tsv' \ | column -t -s $'\t'
Below is an example of the execution results. The CloudFormation template for the VPC used in this example will be introduced in a separate article.
# Execution Result - Stack List % aws cloudformation describe-stacks \ --profile localstack \ --query "Stacks[].{StackName: StackName, StackStatus: StackStatus, CreationTime: CreationTime, LastUpdatedTime: LastUpdatedTime}" \ --output json \ | jq -r '["StackName","StackStatus","CreationTime","LastUpdatedTime"], (.[] | [.StackName, .StackStatus, .CreationTime, .LastUpdatedTime]) | @tsv' \ | column -t -s $'\t' StackName StackStatus CreationTime LastUpdatedTime sample-vpc-stack CREATE_COMPLETE 2025-05-25T01:49:43.238000+00:00 2025-05-25T01:49:43.238000+00:00 # Execution Result - Stack Resource List % stack_name="sample-vpc-stack" aws cloudformation list-stack-resources \ --stack-name "${stack_name}" \ --profile localstack \ --output json \ | jq -r '["LogicalResourceId","PhysicalResourceId","ResourceType","ResourceStatus"], (.StackResourceSummaries[] | [.LogicalResourceId, .PhysicalResourceId, .ResourceType, .ResourceStatus]) | @tsv' \ | column -t -s $'\t' LogicalResourceId PhysicalResourceId ResourceType ResourceStatus VPC1 vpc-4bd2b9a09d788f172 AWS::EC2::VPC CREATE_COMPLETE InternetGateway igw-9bbb17a1eaf64992b AWS::EC2::InternetGateway CREATE_COMPLETE FlowLogsIAMRole AWS::IAM::Role CREATE_COMPLETE PublicSubnet1 subnet-c0c96c0f53f7f1cd7 AWS::EC2::Subnet CREATE_COMPLETE PublicSubnet2 subnet-836137197cc3767a2 AWS::EC2::Subnet CREATE_COMPLETE PrivateSubnet1 subnet-197c985876fcbfdac AWS::EC2::Subnet CREATE_COMPLETE PrivateSubnet2 subnet-a059fb7bd23898fd7 AWS::EC2::Subnet CREATE_COMPLETE ProtectedSubnet1 subnet-1ca041ff52b2493fb AWS::EC2::Subnet CREATE_COMPLETE ProtectedSubnet2 subnet-eff4ebad7049f6c6f AWS::EC2::Subnet CREATE_COMPLETE PublicRouteTable rtb-6f5f999ed1a475a4e AWS::EC2::RouteTable CREATE_COMPLETE PrivateSubnet1RouteTable AWS::EC2::RouteTable CREATE_COMPLETE PrivateSubnet2RouteTable AWS::EC2::RouteTable CREATE_COMPLETE ProtectedRouteTable rtb-453340a863a7a1059 AWS::EC2::RouteTable CREATE_COMPLETE CustomNetworkAcl acl-b0001fb613e8118d2 AWS::EC2::NetworkAcl CREATE_COMPLETE VPCFlowLog1 AWS::EC2::FlowLog CREATE_COMPLETE VPCGatewayAttachment AWS::EC2::VPCGatewayAttachment CREATE_COMPLETE PublicSubnet1RouteTableAssociation rtbassoc-c838b296a6edb9a37 AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE PublicSubnet2RouteTableAssociation rtbassoc-5fe392e039de6701d AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE PrivateSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE PrivateSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE ProtectedSubnet1RouteTableAssociation rtbassoc-896b8d426f6c0d5e4 AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE ProtectedSubnet2RouteTableAssociation rtbassoc-8e3ec29094055a5bc AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE CustomNetworkAclEntryInbound unknown AWS::EC2::NetworkAclEntry CREATE_COMPLETE CustomNetworkAclEntryOutbound unknown AWS::EC2::NetworkAclEntry CREATE_COMPLETE NaclAssociationPublicSubnet1 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NaclAssociationPublicSubnet2 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NaclAssociationPrivateSubnet1 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NaclAssociationPrivateSubnet2 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NaclAssociationProtectedSubnet1 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NaclAssociationProtectedSubnet2 unknown AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE NATGateway1EIP AWS::EC2::EIP CREATE_COMPLETE NATGateway2EIP AWS::EC2::EIP CREATE_COMPLETE PublicRoute rtb-6f5f999ed1a475a4e~0.0.0.0/0 AWS::EC2::Route CREATE_COMPLETE NatGateway1 AWS::EC2::NatGateway CREATE_COMPLETE NatGateway2 AWS::EC2::NatGateway CREATE_COMPLETE PrivateSubnet2Route AWS::EC2::Route CREATE_COMPLETE PrivateSubnet1Route AWS::EC2::Route CREATE_COMPLETE # Execution Result - Stack Events List % stack_name="sample-vpc-stack" aws cloudformation describe-stack-events \ --stack-name "${stack_name}" \ --query "StackEvents[].{Timestamp: Timestamp, LogicalResourceId: LogicalResourceId, ResourceType: ResourceType, ResourceStatus: ResourceStatus, ResourceStatusReason: ResourceStatusReason}" \ --profile localstack \ --output json \ | jq -r '["Timestamp","LogicalResourceId","ResourceType","ResourceStatus","ResourceStatusReason"], (.[] | [.Timestamp, .LogicalResourceId, .ResourceType, .ResourceStatus, .ResourceStatusReason]) | @tsv' \ | column -t -s $'\t' Timestamp LogicalResourceId ResourceType ResourceStatus ResourceStatusReason 2025-05-25T01:49:44.440000+00:00 sample-vpc-stack AWS::CloudFormation::Stack CREATE_COMPLETE 2025-05-25T01:49:44.412000+00:00 PrivateSubnet1Route AWS::EC2::Route CREATE_COMPLETE 2025-05-25T01:49:44.411000+00:00 PrivateSubnet2Route AWS::EC2::Route CREATE_COMPLETE 2025-05-25T01:49:44.411000+00:00 NatGateway2 AWS::EC2::NatGateway CREATE_COMPLETE 2025-05-25T01:49:44.411000+00:00 NatGateway1 AWS::EC2::NatGateway CREATE_COMPLETE 2025-05-25T01:49:44.411000+00:00 PublicRoute AWS::EC2::Route CREATE_COMPLETE 2025-05-25T01:49:44.408000+00:00 PublicRoute AWS::EC2::Route CREATE_IN_PROGRESS 2025-05-25T01:49:44.398000+00:00 NATGateway2EIP AWS::EC2::EIP CREATE_COMPLETE 2025-05-25T01:49:44.398000+00:00 NATGateway1EIP AWS::EC2::EIP CREATE_COMPLETE 2025-05-25T01:49:44.398000+00:00 NaclAssociationProtectedSubnet2 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.391000+00:00 NaclAssociationProtectedSubnet1 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.384000+00:00 NaclAssociationPrivateSubnet2 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.377000+00:00 NaclAssociationPrivateSubnet1 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.370000+00:00 NaclAssociationPublicSubnet2 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.363000+00:00 NaclAssociationPublicSubnet1 AWS::EC2::SubnetNetworkAclAssociation CREATE_COMPLETE 2025-05-25T01:49:44.356000+00:00 CustomNetworkAclEntryOutbound AWS::EC2::NetworkAclEntry CREATE_COMPLETE 2025-05-25T01:49:44.346000+00:00 CustomNetworkAclEntryInbound AWS::EC2::NetworkAclEntry CREATE_COMPLETE 2025-05-25T01:49:44.336000+00:00 ProtectedSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.333000+00:00 ProtectedSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_IN_PROGRESS 2025-05-25T01:49:44.326000+00:00 ProtectedSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.323000+00:00 ProtectedSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_IN_PROGRESS 2025-05-25T01:49:44.316000+00:00 PrivateSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.316000+00:00 PrivateSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.316000+00:00 PublicSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.313000+00:00 PublicSubnet2RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_IN_PROGRESS 2025-05-25T01:49:44.306000+00:00 PublicSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_COMPLETE 2025-05-25T01:49:44.299000+00:00 PublicSubnet1RouteTableAssociation AWS::EC2::SubnetRouteTableAssociation CREATE_IN_PROGRESS 2025-05-25T01:49:44.292000+00:00 VPCGatewayAttachment AWS::EC2::VPCGatewayAttachment CREATE_COMPLETE 2025-05-25T01:49:44.290000+00:00 VPCGatewayAttachment AWS::EC2::VPCGatewayAttachment CREATE_IN_PROGRESS 2025-05-25T01:49:44.283000+00:00 VPCFlowLog1 AWS::EC2::FlowLog CREATE_COMPLETE 2025-05-25T01:49:44.283000+00:00 CustomNetworkAcl AWS::EC2::NetworkAcl CREATE_COMPLETE 2025-05-25T01:49:44.280000+00:00 CustomNetworkAcl AWS::EC2::NetworkAcl CREATE_IN_PROGRESS 2025-05-25T01:49:44.269000+00:00 ProtectedRouteTable AWS::EC2::RouteTable CREATE_COMPLETE 2025-05-25T01:49:44.267000+00:00 ProtectedRouteTable AWS::EC2::RouteTable CREATE_IN_PROGRESS 2025-05-25T01:49:44.256000+00:00 PrivateSubnet2RouteTable AWS::EC2::RouteTable CREATE_COMPLETE 2025-05-25T01:49:44.256000+00:00 PrivateSubnet1RouteTable AWS::EC2::RouteTable CREATE_COMPLETE 2025-05-25T01:49:44.256000+00:00 PublicRouteTable AWS::EC2::RouteTable CREATE_COMPLETE 2025-05-25T01:49:44.253000+00:00 PublicRouteTable AWS::EC2::RouteTable CREATE_IN_PROGRESS 2025-05-25T01:49:44.242000+00:00 ProtectedSubnet2 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.239000+00:00 ProtectedSubnet2 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.218000+00:00 ProtectedSubnet1 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.215000+00:00 ProtectedSubnet1 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.193000+00:00 PrivateSubnet2 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.190000+00:00 PrivateSubnet2 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.168000+00:00 PrivateSubnet1 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.164000+00:00 PrivateSubnet1 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.143000+00:00 PublicSubnet2 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.140000+00:00 PublicSubnet2 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.109000+00:00 PublicSubnet1 AWS::EC2::Subnet CREATE_COMPLETE 2025-05-25T01:49:44.103000+00:00 PublicSubnet1 AWS::EC2::Subnet CREATE_IN_PROGRESS 2025-05-25T01:49:44.042000+00:00 FlowLogsIAMRole AWS::IAM::Role CREATE_COMPLETE 2025-05-25T01:49:44.042000+00:00 InternetGateway AWS::EC2::InternetGateway CREATE_COMPLETE 2025-05-25T01:49:44.040000+00:00 InternetGateway AWS::EC2::InternetGateway CREATE_IN_PROGRESS 2025-05-25T01:49:44.031000+00:00 VPC1 AWS::EC2::VPC CREATE_COMPLETE 2025-05-25T01:49:43.320000+00:00 VPC1 AWS::EC2::VPC CREATE_IN_PROGRESS 2025-05-25T01:49:43.246000+00:00 sample-vpc-stack AWS::CloudFormation::Stack CREATE_IN_PROGRESS 2025-05-25T01:49:43.238000+00:00 sample-vpc-stack AWS::CloudFormation::Stack REVIEW_IN_PROGRESS # Execution Result - Stack Outputs List % stack_name="sample-vpc-stack" aws cloudformation describe-stacks \ --stack-name "${stack_name}" \ --query "Stacks[].Outputs[].{OutputKey: OutputKey, OutputValue: OutputValue, ExportName: ExportName}" \ --profile localstack \ --output json \ | jq -r '["OutputKey","OutputValue"], (.[] | [.OutputKey, .OutputValue, .ExportName]) | @tsv' \ | column -t -s $'\t' OutputKey OutputValue VPCId vpc-4bd2b9a09d788f172 sample-vpc-id PublicSubnet1Id subnet-c0c96c0f53f7f1cd7 sample-public-subnet1-id PublicSubnet2Id subnet-836137197cc3767a2 sample-public-subnet2-id PrivateSubnet1Id subnet-197c985876fcbfdac sample-private-subnet1-id PrivateSubnet2Id subnet-a059fb7bd23898fd7 sample-private-subnet2-id ProtectedSubnet1Id subnet-1ca041ff52b2493fb sample-protected-subnet1-id ProtectedSubnet2Id subnet-eff4ebad7049f6c6f sample-protected-subnet2-id