Some notes I took while working through the AWS Modern Application Workshop. Plus a visualization of the architecture that I drew myself.
#aws #cloud #docker #container
What started as “just follow the lab” turned into a really good walkthrough of how different AWS services fit together in a real microservice setup.
For me, this workshop connected a lot of pieces:
Give it a try: github/aws-modern-application-workshop
While drawing the architecture, the whole stack started to make sense to me. I tried to include all the actual resources we created during the deployment.
bucket name: cldinf25-24a52511v
Preparation
bc=cldinf25-24a52511v
aws s3 mb s3://$bc
aws s3 website s3://$bc --index-document index.html
In s3 bucket > permissions > Allow public access
Edit …/module-1/aws-cli/website-bucket-policy.json
aws s3api put-bucket-policy --bucket $bc --policy file://~/environment/01-aws/module-1/aws-cli/website-bucket-policy.json
aws s3 cp ~/environment/01-aws/module-1/web/index.html s3://$bc/index.html
aws s3 cp ~/environment/01-aws/module-1/web/images s3://$bc/images --recursive
{
"Stacks": [
{
"StackId": "arn:aws:cloudformation:us-east-1:730335656277:stack/MythicalMysfitsCoreStack/c53eaff0-9a01-11f0-848a-0e3b010e3503",
"StackName": "MythicalMysfitsCoreStack",
"Description": "This stack deploys the core network infrastructure and IAM resources to be used for a service hosted in Amazon ECS using AWS Fargate.",
"CreationTime": "2025-09-25T11:21:24.381000+00:00",
"RollbackConfiguration": {},
"StackStatus": "CREATE_COMPLETE",
"DisableRollback": false,
"NotificationARNs": [],
"Capabilities": [
"CAPABILITY_NAMED_IAM"
],
"Outputs": [
{
"OutputKey": "CurrentAccount",
"OutputValue": "730335656277",
"Description": "The ID of the Account being used.",
"ExportName": "MythicalMysfitsCoreStack:CurrentAccount"
},
{
"OutputKey": "FargateContainerSecurityGroup",
"OutputValue": "sg-09e3ee8e30eae3827",
"Description": "A security group used to allow Fargate containers to receive traffic",
"ExportName": "MythicalMysfitsCoreStack:FargateContainerSecurityGroup"
},
{
"OutputKey": "PublicSubnetOne",
"OutputValue": "subnet-0a0c03a59ff05ae0c",
"Description": "Public subnet one",
"ExportName": "MythicalMysfitsCoreStack:PublicSubnetOne"
},
{
"OutputKey": "PrivateSubnetTwo",
"OutputValue": "subnet-0d9f8ff73f358e7b1",
"Description": "Private subnet two",
"ExportName": "MythicalMysfitsCoreStack:PrivateSubnetTwo"
},
{
"OutputKey": "CurrentRegion",
"OutputValue": "us-east-1",
"Description": "The string representation of the region being used.",
"ExportName": "MythicalMysfitsCoreStack:CurrentRegion"
},
{
"OutputKey": "VPCId",
"OutputValue": "vpc-053aa64e93ea535b5",
"Description": "The ID of the VPC that this stack is deployed in",
"ExportName": "MythicalMysfitsCoreStack:VPCId"
},
{
"OutputKey": "PublicSubnetTwo",
"OutputValue": "subnet-0931096e9df7ba5a0",
"Description": "Public subnet two",
"ExportName": "MythicalMysfitsCoreStack:PublicSubnetTwo"
},
{
"OutputKey": "PrivateSubnetOne",
"OutputValue": "subnet-0f869c7f81c6ab1d6",
"Description": "Private subnet one",
"ExportName": "MythicalMysfitsCoreStack:PrivateSubnetOne"
}
],
"Tags": [],
"EnableTerminationProtection": false,
"DriftInformation": {
"StackDriftStatus": "NOT_CHECKED"
}
}
]
}
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 730335656277.dkr.ecr.us-east-1.amazonaws.com
cd ~/environment/01-aws/module-2/app
docker build -t mythicalmysfits/service .
docker tag mythicalmysfits/service:latest 730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest
# Testrun
docker run -p 8080:8080 mythicalmysfits/service:latest
[!Tag] We tag the image so we can push it to the right ECR repository.
aws ecr create-repository --repository-name mythicalmysfits/service
{
"repository": {
"repositoryArn": "arn:aws:ecr:us-east-1:730335656277:repository/mythicalmysfits/service",
"registryId": "730335656277",
"repositoryName": "mythicalmysfits/service",
"repositoryUri": "730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service",
"createdAt": "2025-09-25T13:12:04.089000+00:00",
"imageTagMutability": "MUTABLE",
"imageScanningConfiguration": {
"scanOnPush": false
},
"encryptionConfiguration": {
"encryptionType": "AES256"
}
}
}
docker push 730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest
The push refers to repository [730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service]
aws ecr describe-images --repository-name mythicalmysfits/service
{ "imageDetails": [
{
"registryId": "730335656277",
"repositoryName": "mythicalmysfits/service",
"imageDigest": "sha256:ca6c215d986ac13459857656a9739e942f898a2d8567470fba60ba64c29443f7",
"imageTags": [
"latest"
],
"imageSizeInBytes": 8307143,
"imagePushedAt": "2025-09-25T13:14:52.788000+00:00",
"imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json",
"artifactMediaType": "application/vnd.docker.container.image.v1+json"
}
]
}
{
"imageDetails": [
{
"registryId": "730335656277",
"repositoryName": "mythicalmysfits/service",
"imageDigest": "sha256:ca6c215d986ac13459857656a9739e942f898a2d8567470fba60ba64c29443f7",
"imageTags": [
"latest"
],
"imageSizeInBytes": 8307143,
"imagePushedAt": "2025-09-25T13:14:52.788000+00:00",
"imageManifestMediaType": "application/vnd.docker.distribution.manifest.v2+json",
"artifactMediaType": "application/vnd.docker.container.image.v1+json"
}]}
Amazaon Elastic Container Service: Cluster of “servers” that the service container will be deployed. We will be using Fargate, so we dont have to provision the server by ourselves.
aws ecs create-cluster --cluster-name MythicalMysfits-Cluster
{ "cluster": {
"clusterArn": "arn:aws:ecs:us-east-1:730335656277:cluster/MythicalMysfits-Cluster",
"clusterName": "MythicalMysfits-Cluster",
"status": "ACTIVE",
"registeredContainerInstancesCount": 0,
"runningTasksCount": 0,
"pendingTasksCount": 0,
"activeServicesCount": 0,
"statistics": [],
"tags": [],
"settings": [
{
"name": "containerInsights",
"value": "disabled"
}
],
"capacityProviders": [],
"defaultCapacityProviderStrategy": []
}
}
Needed so we can access our Logs, since we dont have access to the server infrasructure (because we are using AWS Fargate) The logs that the container generate will be pushed automatically to AWS CloudWatch.
aws logs create-log-group --log-group-name mythicalmysfits-logs
RoleArn is the LabRole in the Amazon Web Interface: “IAM > Roles > LabRole”
{ "family": "mythicalmysfitsservice",
"cpu": "256",
"memory": "512",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"FARGATE"
],
"executionRoleArn": "arn:aws:iam::730335656277:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
"taskRoleArn": "arn:aws:iam::730335656277:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
"containerDefinitions": [
{
"name": "MythicalMysfits-Service",
"image": "730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest",
"portMappings": [
{
"containerPort": 8080,
"protocol": "http"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "mythicalmysfits-logs",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "awslogs-mythicalmysfits-service"
}
},
"essential": true
}
]
}
aws ecs register-task-definition --cli-input-json file://~/environment/01-aws/module-2/aws-cli/task-definition.json
{
"taskDefinition": {
"taskDefinitionArn": "arn:aws:ecs:us-east-1:730335656277:task-definition/mythicalmysfitsservice:1",
"containerDefinitions": [
{
"name": "MythicalMysfits-Service",
"image": "730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest",
"cpu": 0,
{
"taskDefinition": {
"taskDefinitionArn": "arn:aws:ecs:us-east-1:730335656277:task-definition/mythicalmysfitsservice:1",
"containerDefinitions": [
{
"name": "MythicalMysfits-Service",
"image": "730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest",
"cpu": 0,
"portMappings": [
{
"containerPort": 8080,
"hostPort": 8080,
"protocol": "tcp"
}
],
"essential": true,
"environment": [],
"mountPoints": [],
"volumesFrom": [],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "mythicalmysfits-logs",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "awslogs-mythicalmysfits-service"
}
},
"systemControls": []
}
],
"family": "mythicalmysfitsservice",
"taskRoleArn": "arn:aws:iam::730335656277:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
"executionRoleArn": "arn:aws:iam::730335656277:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
"networkMode": "awsvpc",
"revision": 1,
"volumes": [],
"status": "ACTIVE",
"requiresAttributes": [
{
"name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
},
{
"name": "ecs.capability.execution-role-awslogs"
},
{
"name": "com.amazonaws.ecs.capability.ecr-auth"
},
{
"name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
},
{
"name": "com.amazonaws.ecs.capability.task-iam-role"
},
{
"name": "ecs.capability.execution-role-ecr-pull"
},
{
"name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
},
{
"name": "ecs.capability.task-eni"
}
],
"placementConstraints": [],
"compatibilities": [
"EC2",
"MANAGED_INSTANCES",
"FARGATE"
],
"requiresCompatibilities": [
"FARGATE"
],
"cpu": "256",
"memory": "512",
"registeredAt": "2025-09-25T19:02:38.456000+00:00",
"registeredBy": "arn:aws:sts::730335656277:assumed-role/voclabs/user4400849=agron.makolli@ost.ch"
}
}
We place a Network Load Balancer (NLB) in front of our service to make sure the service is not directly exposed to the internet. With this setup we also make sure that the frontend can always communicate to the same DNS. Which in this case ist the NLB and the service can scale up or down on demand.
aws elbv2 create-load-balancer --name mysfits-nlb --scheme internet-facing --type network --subnets subnet-0a0c03a59ff05ae0c subnet-0931096e9df7ba5a0 > ~/environment/nlb-output.json
{
"LoadBalancers": [
{
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/930f117e55fa705b",
"DNSName": "mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com",
"CanonicalHostedZoneId": "Z26RNL4JYFTOTI",
"CreatedTime": "2025-09-25T20:59:56.174000+00:00",
"LoadBalancerName": "mysfits-nlb",
"Scheme": "internet-facing",
"VpcId": "vpc-053aa64e93ea535b5",
"State": {
"Code": "active"
},
"Type": "network",
"AvailabilityZones": [
{
"ZoneName": "us-east-1a",
"SubnetId": "subnet-0a0c03a59ff05ae0c",
"LoadBalancerAddresses": []
},
{
"ZoneName": "us-east-1b",
"SubnetId": "subnet-0931096e9df7ba5a0",
"LoadBalancerAddresses": []
}
],
"IpAddressType": "ipv4",
"EnablePrefixForIpv6SourceNat": "off"
}
]
}
List current load balancer
aws elbv2 describe-load-balancers --names mysfits-nlb
{
"LoadBalancers": [
{
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/ca927f4ee9b8bda0",
"DNSName": "mysfits-nlb-ca927f4ee9b8bda0.elb.us-east-1.amazonaws.com",
"CanonicalHostedZoneId": "Z26RNL4JYFTOTI",
"CreatedTime": "2025-09-25T16:42:32.242000+00:00",
"LoadBalancerName": "mysfits-nlb",
"Scheme": "internet-facing",
"VpcId": "vpc-053aa64e93ea535b5",
"State": {
"Code": "active"
},
"Type": "network",
"AvailabilityZones": [
{
"ZoneName": "us-east-1a",
"SubnetId": "subnet-0a0c03a59ff05ae0c",
"LoadBalancerAddresses": []
},
{
"ZoneName": "us-east-1b",
"SubnetId": "subnet-0d9f8ff73f358e7b1",
"LoadBalancerAddresses": []
}
],
"IpAddressType": "ipv4",
"EnablePrefixForIpv6SourceNat": "off"
}
]
}
aws elbv2 delete-load-balancer --load-balancer-arn <LOAD-BALANCER-ARN>
Services/containers can register themselves in a target group so the load balancer can forward requests to them.
aws elbv2 create-target-group --name MythicalMysfits-TargetGroup --port 8080 --protocol TCP --target-type ip --vpc-id vpc-053aa64e93ea535b5 --health-check-interval-seconds 10 --health-check-path / --health-check-protocol HTTP --healthy-threshold-count 3 --unhealthy-threshold-count 3 > ~/environment/target-group-output.json
{
"TargetGroups": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85",
"TargetGroupName": "MythicalMysfits-TargetGroup",
"Protocol": "TCP",
"Port": 8080,
"VpcId": "vpc-053aa64e93ea535b5",
"HealthCheckProtocol": "HTTP",
"HealthCheckPort": "traffic-port",
"HealthCheckEnabled": true,
"HealthCheckIntervalSeconds": 10,
"HealthCheckTimeoutSeconds": 6,
"HealthyThresholdCount": 3,
"UnhealthyThresholdCount": 3,
"HealthCheckPath": "/",
"Matcher": {
"HttpCode": "200-399"
},
"TargetType": "ip",
"IpAddressType": "ipv4"
}
]
}
Informs Load Balancer to listen to a specific port and tells him where to forward it
aws elbv2 create-listener --default-actions TargetGroupArn=arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85,Type=forward --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/930f117e55fa705b --port 80 --protocol TCP
{
"Listeners": [
{
"ListenerArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:listener/net/mysfits-nlb/ca927f4ee9b8bda0/260227d61d4f4fe8",
"LoadBalancerArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/ca927f4ee9b8bda0",
"Port": 80,
"Protocol": "TCP",
"DefaultActions": [
{
"Type": "forward",
"TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85",
"ForwardConfig": {
"TargetGroups": [
{
"TargetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85"
}
]
}
}
]
}
]
}
{
"serviceName": "MythicalMysfits-Service",
"cluster": "MythicalMysfits-Cluster",
"launchType": "FARGATE",
"deploymentConfiguration": {
"maximumPercent": 200,
"minimumHealthyPercent": 0
},
"desiredCount": 1,
"networkConfiguration": {
"awsvpcConfiguration": {
"assignPublicIp": "DISABLED",
"securityGroups": [
"sg-09e3ee8e30eae3827"
],
"subnets": [
"subnet-0f869c7f81c6ab1d6",
"subnet-0d9f8ff73f358e7b1"
]
}
},
"taskDefinition": "mythicalmysfitsservice",
"loadBalancers": [
{
"containerName": "MythicalMysfits-Service",
"containerPort": 8080,
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85"
}
]
}
aws ecs create-service --cli-input-json file://~/environment/01-aws/module-2/aws-cli/service-definition.json
{
"service": {
"serviceArn": "arn:aws:ecs:us-east-1:730335656277:service/MythicalMysfits-Cluster/MythicalMysfits-Service",
"serviceName": "MythicalMysfits-Service",
"clusterArn": "arn:aws:ecs:us-east-1:730335656277:cluster/MythicalMysfits-Cluster",
"loadBalancers": [
{
"targetGroupArn": "arn:aws:elasticloadbalancing:us-east-1:730335656277:targetgroup/MythicalMysfits-TargetGroup/fcaa0e3888345e85",
"containerName": "MythicalMysfits-Service",
"containerPort": 8080
}
],
"serviceRegistries": [],
"status": "ACTIVE",
"desiredCount": 1,
"runningCount": 0,
"pendingCount": 0,
"launchType": "FARGATE",
"platformVersion": "LATEST",
"platformFamily": "Linux",
"taskDefinition": "arn:aws:ecs:us-east-1:730335656277:task-definition/mythicalmysfitsservice:1",
"deploymentConfiguration": {
"deploymentCircuitBreaker": {
"enable": false,
"rollback": false
},
"maximumPercent": 200,
"minimumHealthyPercent": 0,
"strategy": "ROLLING",
"bakeTimeInMinutes": 0
},
"deployments": [
{
"id": "ecs-svc/6349396091140787692",
"status": "PRIMARY",
"taskDefinition": "arn:aws:ecs:us-east-1:730335656277:task-definition/mythicalmysfitsservice:1",
"desiredCount": 0,
"pendingCount": 0,
"runningCount": 0,
"failedTasks": 0,
"createdAt": "2025-09-25T19:04:50.056000+00:00",
"updatedAt": "2025-09-25T19:04:50.056000+00:00",
"launchType": "FARGATE",
"platformVersion": "1.4.0",
"platformFamily": "Linux",
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"subnet-0d9f8ff73f358e7b1",
"subnet-0a0c03a59ff05ae0c"
],
"securityGroups": [
"sg-09e3ee8e30eae3827"
],
"assignPublicIp": "DISABLED"
}
},
"rolloutState": "IN_PROGRESS",
"rolloutStateReason": "ECS deployment ecs-svc/6349396091140787692 in progress."
}
],
"roleArn": "arn:aws:iam::730335656277:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS",
"events": [],
"createdAt": "2025-09-25T19:04:50.056000+00:00",
"placementConstraints": [],
"placementStrategy": [],
"networkConfiguration": {
"awsvpcConfiguration": {
"subnets": [
"subnet-0d9f8ff73f358e7b1",
"subnet-0a0c03a59ff05ae0c"
],
"securityGroups": [
"sg-09e3ee8e30eae3827"
],
"assignPublicIp": "DISABLED"
}
},
"healthCheckGracePeriodSeconds": 0,
"schedulingStrategy": "REPLICA",
"deploymentController": {
"type": "ECS"
},
"createdBy": "arn:aws:iam::730335656277:role/voclabs",
"enableECSManagedTags": false,
"propagateTags": "NONE",
"enableExecuteCommand": false,
"availabilityZoneRebalancing": "ENABLED"
}
}
Replace API Endpoint
var mysfitsApiEndpoint = 'http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com'; // example: 'http://mythi-publi-abcd12345-01234567890123.elb.us-east-1.amazonaws.com'
aws s3 cp ~/environment/01-aws/module-2/web/index.html s3://cldinf25-24a52511v/index.html
aws dynamodb create-table --cli-input-json file://~/environment/01-aws/module-3/aws-cli/dynamodb-table.json
aws dynamodb describe-table --table-name MysfitsTable
aws dynamodb scan --table-name MysfitsTable
{
"Items": [],
"Count": 0,
"ScannedCount": 0,
"ConsumedCapacity": null
}
aws dynamodb batch-write-item --request-items file://~/environment/01-aws/module-3/aws-cli/populate-dynamodb.json
Change CORS Header Attribute in mythicalMysfitsService.go:
(*w).Header().Set("Access-Control-Allow-Origin", "http://cldinf25-24a52511v.s3-website-us-east-1.amazonaws.com")
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 730335656277.dkr.ecr.us-east-1.amazonaws.com
cd ~/environment/01-aws/module-3/app
docker build -t mythicalmysfits/service .
docker tag mythicalmysfits/service:latest 730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest
docker push 730335656277.dkr.ecr.us-east-1.amazonaws.com/mythicalmysfits/service:latest
aws ecr describe-images --repository-name mythicalmysfits/service
aws ecs update-service \
--cluster MythicalMysfits-Cluster \
--service MythicalMysfits-Service \
--force-new-deployment
aws s3 cp --recursive ~/environment/01-aws/module-3/web/ s3://cldinf25-24a52511v/
aws cognito-idp create-user-pool --pool-name MysfitsUserPool --auto-verified-attributes email
{
"UserPool": {
"Id": "us-east-1_p2O5mtJQx",
"Name": "MysfitsUserPool",
"Policies": {
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": true,
"TemporaryPasswordValidityDays": 7
},
"SignInPolicy": {
"AllowedFirstAuthFactors": [
"PASSWORD"
]
}
},
"DeletionProtection": "INACTIVE",
"LambdaConfig": {},
"LastModifiedDate": "2025-09-29T17:08:11.958000+00:00",
"CreationDate": "2025-09-29T17:08:11.958000+00:00",
"SchemaAttributes": [
{
"Name": "sub",
"AttributeDataType": "String",
"DeveloperOnlyAttribute": false,
"Mutable": false,
"Required": true,
"StringAttributeConstraints": {
"MinLength": "1",
"MaxLength": "2048"
}
},
],
"AutoVerifiedAttributes": [
"email"
],
"VerificationMessageTemplate": {
"DefaultEmailOption": "CONFIRM_WITH_CODE"
},
"UserAttributeUpdateSettings": {
"AttributesRequireVerificationBeforeUpdate": []
},
"MfaConfiguration": "OFF",
"EstimatedNumberOfUsers": 0,
"EmailConfiguration": {
"EmailSendingAccount": "COGNITO_DEFAULT"
},
"AdminCreateUserConfig": {
"AllowAdminCreateUserOnly": false,
"UnusedAccountValidityDays": 7
},
"Arn": "arn:aws:cognito-idp:us-east-1:730335656277:userpool/us-east-1_p2O5mtJQx",
"AccountRecoverySetting": {
"RecoveryMechanisms": [
{
"Priority": 1,
"Name": "verified_email"
},
{
"Priority": 2,
"Name": "verified_phone_number"
}
]
},
"UserPoolTier": "ESSENTIALS"
}
}
aws cognito-idp create-user-pool-client --user-pool-id us-east-1_p2O5mtJQx --client-name MysfitsUserPoolClient
{
"UserPoolClient": {
"UserPoolId": "us-east-1_p2O5mtJQx",
"ClientName": "MysfitsUserPoolClient",
"ClientId": "3og76rh5f1pgtpub4vn59qhthd",
"LastModifiedDate": "2025-09-29T17:15:39.520000+00:00",
"CreationDate": "2025-09-29T17:15:39.520000+00:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"AllowedOAuthFlowsUserPoolClient": false,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}
aws apigateway create-vpc-link --name MysfitsApiVpcLink --target-arns arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/930f117e55fa705b
{
"id": "8i80kp",
"name": "MysfitsApiVpcLink",
"targetArns": [
"arn:aws:elasticloadbalancing:us-east-1:730335656277:loadbalancer/net/mysfits-nlb/930f117e55fa705b"
],
"status": "PENDING"
}
{
"swagger": 2.0,
"info": {
"title": "MysfitsApi"
},
"securityDefinitions": {
"MysfitsUserPoolAuthorizer": {
"type": "apiKey",
"name": "Authorization",
"in": "header",
"x-amazon-apigateway-authtype": "cognito_user_pools",
"x-amazon-apigateway-authorizer": {
"type": "COGNITO_USER_POOLS",
"providerARNs": [
"arn:aws:cognito-idp:us-east-1:730335656277:userpool/us-east-1_p2O5mtJQx"
]
}
}
},
"paths": {
"/": {
"get": {
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"connectionType": "VPC_LINK",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"connectionId": "8i80kp",
"httpMethod": "GET",
"type": "HTTP_PROXY",
"uri": "http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com/"
}
},
"options": {
"summary": "CORS support",
"description": "Enable CORS by returning correct headers\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CORS"
],
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{}\n"
}
}
}
},
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
}
}
},
"/mysfits": {
"get": {
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"connectionType": "VPC_LINK",
"connectionId": "8i80kp",
"httpMethod": "GET",
"type": "HTTP_PROXY",
"uri": "http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com/mysfits",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
}
}
},
"options": {
"summary": "CORS support",
"description": "Enable CORS by returning correct headers\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CORS"
],
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{}\n"
}
}
}
},
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
}
}
},
"/mysfits/{mysfitId}": {
"get": {
"parameters": [{
"name": "mysfitId",
"in": "path",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"x-amazon-apigateway-integration": {
"requestParameters": {
"integration.request.path.mysfitId": "method.request.path.mysfitId"
},
"connectionType": "VPC_LINK",
"connectionId": "8i80kp",
"httpMethod": "GET",
"type": "HTTP_PROXY",
"uri": "http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com/mysfits/{mysfitId}",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
}
}
},
"options": {
"summary": "CORS support",
"description": "Enable CORS by returning correct headers\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CORS"
],
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{}\n"
}
}
}
},
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
}
}
},
"/mysfits/{mysfitId}/adopt": {
"post": {
"parameters": [{
"name": "mysfitId",
"in": "path",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"security": [{
"MysfitsUserPoolAuthorizer": [
]
}],
"x-amazon-apigateway-integration": {
"requestParameters": {
"integration.request.path.mysfitId": "method.request.path.mysfitId"
},
"connectionType": "VPC_LINK",
"connectionId": "8i80kp",
"httpMethod": "POST",
"type": "HTTP_PROXY",
"uri": "http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com/mysfits/{mysfitId}/adopt",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
}
}
},
"options": {
"summary": "CORS support",
"description": "Enable CORS by returning correct headers\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CORS"
],
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{}\n"
}
}
}
},
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
}
}
},
"/mysfits/{mysfitId}/like": {
"post": {
"parameters": [{
"name": "mysfitId",
"in": "path",
"required": true,
"type": "string"
}],
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
},
"security": [{
"MysfitsUserPoolAuthorizer": [
]
}],
"x-amazon-apigateway-integration": {
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
},
"requestParameters": {
"integration.request.path.mysfitId": "method.request.path.mysfitId"
},
"connectionType": "VPC_LINK",
"connectionId": "8i80kp",
"httpMethod": "POST",
"security": [{
"MysfitsUserPoolAuthorizer": [
]
}],
"type": "HTTP_PROXY",
"uri": "http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com/mysfits/{mysfitId}/like",
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
}
}
}
},
"options": {
"summary": "CORS support",
"description": "Enable CORS by returning correct headers\n",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"CORS"
],
"x-amazon-apigateway-integration": {
"type": "mock",
"requestTemplates": {
"application/json": "{\n \"statusCode\" : 200\n}\n"
},
"responses": {
"default": {
"statusCode": "200",
"responseParameters": {
"method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'",
"method.response.header.Access-Control-Allow-Methods": "'*'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
},
"responseTemplates": {
"application/json": "{}\n"
}
}
}
},
"responses": {
"200": {
"description": "Default response for CORS method",
"headers": {
"Access-Control-Allow-Headers": {
"type": "string"
},
"Access-Control-Allow-Methods": {
"type": "string"
},
"Access-Control-Allow-Origin": {
"type": "string"
}
}
}
}
}
}
}
}
Following the tutorial i got the error:
aws apigateway import-rest-api --parameters endpointConfigurationTypes=REGIONAL --body file://~/environment/01-aws/module-4/aws-cli/api-swagger.json --fail-on-warnings
Invalid base64: "{
"swagger": 2.0,
"info": {
To use JSON files you need to specify it with -body fileb://
aws apigateway import-rest-api --parameters endpointConfigurationTypes=REGIONAL --body fileb://~/environment/01-aws/module-4/aws-cli/api-swagger.json --fail-on-warnings
{
"id": "vuj4n7mexi",
"name": "MysfitsApi",
"createdDate": "2025-09-29T17:41:04+00:00",
"apiKeySource": "HEADER",
"endpointConfiguration": {
"types": [
"REGIONAL"
],
"ipAddressType": "ipv4"
},
"disableExecuteApiEndpoint": false,
"rootResourceId": "crradh7hd6"
}
aws apigateway create-deployment --rest-api-id vuj4n7mexi --stage-name prod
{
"id": "yjy8g3",
"createdDate": "2025-09-29T17:49:46+00:00"
}
https://vuj4n7mexi.execute-api.us-east-1.amazonaws.com/prod
var mysfitsApiEndpoint = 'http://mysfits-nlb-930f117e55fa705b.elb.us-east-1.amazonaws.com'; // example: 'https://abcd12345.execute-api.us-east-1.amazonaws.com/prod'
var cognitoUserPoolId = 'us-east-1_p2O5mtJQx'; // example: 'us-east-1_abcd12345'
var cognitoUserPoolClientId = '3og76rh5f1pgtpub4vn59qhthd'; // example: 'abcd12345abcd12345abcd12345'
var awsRegion = 'us-east-1'; // example: 'us-east-1' or 'eu-west-1' etc.
confirm.html & register.html
var cognitoUserPoolId = 'us-east-1_p2O5mtJQx'; // example: 'us-east-1_abcd12345'
var cognitoUserPoolClientId = '3og76rh5f1pgtpub4vn59qhthd'; // example: 'abcd12345abcd12345abcd12345'
aws s3 cp --recursive ~/environment/01-aws/module-4/web/ s3://cldinf25-24a52511v/