├── 0003-terraform-gha ├── .terraform-version ├── config │ ├── dev │ │ ├── terraform.tfvars │ │ └── backend.hcl │ ├── prod │ │ ├── terraform.tfvars │ │ └── backend.hcl │ └── local │ │ ├── terraform.tfvars │ │ └── backend.hcl ├── ec2.tf ├── main.tf ├── variables.tf └── .terraform.lock.hcl ├── 0011-billing-alarm ├── .terraform-version ├── outputs.tf ├── sns.tf ├── main.tf ├── variables.tf ├── cloudwatch.tf └── .terraform.lock.hcl ├── 0006-middy-js ├── terraform │ ├── .terraform-version │ ├── outputs.tf │ ├── ssm.tf │ ├── cloudwatch.tf │ ├── modules │ │ └── lambda-layer │ │ │ ├── outputs.tf │ │ │ ├── main.tf │ │ │ └── variables.tf │ ├── lambda-layers.tf │ ├── main.tf │ ├── variables.tf │ ├── dynamodb.tf │ ├── locals.tf │ ├── api.tf │ ├── lambda.tf │ ├── .terraform.lock.hcl │ └── iam.tf └── lambdas │ ├── layers │ ├── utils │ │ └── nodejs │ │ │ └── response.js │ ├── middy │ │ └── nodejs │ │ │ ├── package.json │ │ │ └── package-lock.json │ └── middlewares │ │ └── nodejs │ │ ├── error-handler.js │ │ ├── ssm.js │ │ ├── debugger.js │ │ └── api-event-normalizer.js │ └── todos │ ├── delete.js │ ├── post.js │ ├── put.js │ └── get.js ├── 0008-terraform-new-s3 ├── .terraform-version ├── outputs.tf ├── website │ ├── error.html │ └── index.html ├── main.tf ├── s3.tf └── .terraform.lock.hcl ├── 0002-terraform-lambda-layer ├── .terraform-version ├── variables.tf ├── locals.tf ├── network.tf ├── main.tf ├── lambdas │ └── cat-api │ │ ├── package.json │ │ ├── index.js │ │ └── package-lock.json ├── layers │ └── got │ │ └── nodejs │ │ ├── package.json │ │ └── package-lock.json ├── lambda.tf ├── outputs.tf ├── layer.tf ├── iam.tf └── .terraform.lock.hcl ├── 0009-terraform-optional-vars ├── .terraform-version ├── terraform.tf ├── modules │ └── s3-hosting-static-website │ │ ├── www │ │ ├── error.html │ │ └── index.html │ │ ├── outputs.tf │ │ ├── variables.tf │ │ └── main.tf ├── providers.tf ├── outputs.tf ├── main.tf ├── blog │ ├── blog.rtl.css │ ├── blog.css │ └── index.html └── .terraform.lock.hcl ├── 0010-s3-trigger-lambda ├── terraform │ ├── .terraform-version │ ├── cloudwatch.tf │ ├── variables.tf │ ├── locals.tf │ ├── outputs.tf │ ├── main.tf │ ├── lambda.tf │ ├── s3.tf │ ├── iam.tf │ └── .terraform.lock.hcl ├── README.md ├── .DS_Store └── lambdas │ ├── .DS_Store │ └── s3-trigger │ ├── .DS_Store │ ├── package.json │ └── index.js ├── 0001-terraform-time-and-date-functions ├── .terraform-version └── tf │ ├── main.tf │ └── .terraform.lock.hcl ├── 0005-terraform-serverless-pattern-rest-api ├── terraform │ ├── .terraform-version │ ├── outputs.tf │ ├── ssm.tf │ ├── cloudwatch.tf │ ├── main.tf │ ├── variables.tf │ ├── dynamodb.tf │ ├── locals.tf │ ├── api.tf │ ├── lambda.tf │ ├── .terraform.lock.hcl │ └── iam.tf └── lambdas │ ├── layers │ └── utils │ │ └── nodejs │ │ ├── response.js │ │ └── normalizer.js │ └── todos │ ├── delete.js │ ├── post.js │ ├── get.js │ └── put.js ├── 0007-aws-ecs-fargate └── app │ ├── .dockerignore │ ├── diagram.jpg │ ├── diagram-vpce.jpg │ ├── db │ └── index.js │ ├── package.json │ ├── .editorconfig │ ├── Dockerfile │ ├── README.md │ └── index.js ├── 0014-terraform-import ├── locals.tf ├── imports.tf ├── s3 │ └── main.tf ├── main.tf ├── .terraform.lock.hcl └── generated.tf ├── 0013-terraform-test ├── outputs.tf ├── tests │ ├── final │ │ └── main.tf │ ├── setup │ │ └── main.tf │ └── website.tftest.hcl ├── www │ ├── error.html │ └── index.html ├── variables.tf ├── main.tf ├── s3.tf └── .terraform.lock.hcl ├── 0004-serverless-pattern-rest-api ├── response.js ├── normalizer.js ├── delete.js ├── post.js ├── get.js └── put.js ├── 0012-dynamo-s3-athena └── lambdas │ └── seed-database │ ├── package.json │ ├── queries.sql │ └── index.js ├── .gitignore ├── LICENSE ├── README.md └── .github └── workflows ├── 0003_tf_apply_prod.yml ├── 0003_tf_apply_dev.yml ├── 0003_tf_destroy.yml └── 0003_tf_plan.yml /0003-terraform-gha/.terraform-version: -------------------------------------------------------------------------------- 1 | 0.15.5 2 | -------------------------------------------------------------------------------- /0011-billing-alarm/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.3 2 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.0.5 2 | -------------------------------------------------------------------------------- /0008-terraform-new-s3/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.2 2 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/.terraform-version: -------------------------------------------------------------------------------- 1 | 0.15.4 2 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.2 2 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.3.2 2 | -------------------------------------------------------------------------------- /0001-terraform-time-and-date-functions/.terraform-version: -------------------------------------------------------------------------------- 1 | 0.15.0 2 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/.terraform-version: -------------------------------------------------------------------------------- 1 | 1.0.5 2 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/dev/terraform.tfvars: -------------------------------------------------------------------------------- 1 | environment = "dev" 2 | instance_type = "t3.small" 3 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/prod/terraform.tfvars: -------------------------------------------------------------------------------- 1 | environment = "prod" 2 | instance_type = "t3.medium" 3 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "api_url" { 2 | value = aws_apigatewayv2_stage.this.invoke_url 3 | } 4 | -------------------------------------------------------------------------------- /0011-billing-alarm/outputs.tf: -------------------------------------------------------------------------------- 1 | output "billing_sns_topic_arn" { 2 | value = aws_sns_topic.billing_alarm.arn 3 | } 4 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | .editorconfig -------------------------------------------------------------------------------- /0008-terraform-new-s3/outputs.tf: -------------------------------------------------------------------------------- 1 | output "url" { 2 | value = aws_s3_bucket_website_configuration.website.website_endpoint 3 | } 4 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/README.md: -------------------------------------------------------------------------------- 1 | ## Como disparar a lambda 2 | 3 | 1. Suba um arquivo no formato `.jpg` na pasta `input/images` 4 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/local/terraform.tfvars: -------------------------------------------------------------------------------- 1 | aws_profile = "tf014" 2 | environment = "local" 3 | instance_type = "t3.small" 4 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgasparoto/youtube-cleber-gasparoto/HEAD/0010-s3-trigger-lambda/.DS_Store -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "api_url" { 2 | value = aws_apigatewayv2_stage.this.invoke_url 3 | } 4 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgasparoto/youtube-cleber-gasparoto/HEAD/0007-aws-ecs-fargate/app/diagram.jpg -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/lambdas/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgasparoto/youtube-cleber-gasparoto/HEAD/0010-s3-trigger-lambda/lambdas/.DS_Store -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/diagram-vpce.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgasparoto/youtube-cleber-gasparoto/HEAD/0007-aws-ecs-fargate/app/diagram-vpce.jpg -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/lambdas/s3-trigger/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chgasparoto/youtube-cleber-gasparoto/HEAD/0010-s3-trigger-lambda/lambdas/s3-trigger/.DS_Store -------------------------------------------------------------------------------- /0014-terraform-import/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | imports = { 3 | bucket_name = "bucket-do-cleber-criado-no-console-da-aws" 4 | dynamodb_table_name = "users" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/db/index.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require('pg') 2 | 3 | const pool = new Pool() 4 | 5 | module.exports = { 6 | query: (text, params) => pool.query(text, params), 7 | } -------------------------------------------------------------------------------- /0006-middy-js/terraform/ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "dynamodb_table" { 2 | name = "${local.namespaced_service_name}-dynamodb-table" 3 | type = "String" 4 | value = aws_dynamodb_table.this.name 5 | } 6 | -------------------------------------------------------------------------------- /0013-terraform-test/outputs.tf: -------------------------------------------------------------------------------- 1 | output "website_endpoint" { 2 | description = "Website endpoint URL" 3 | value = "http://${aws_s3_bucket_website_configuration.s3_bucket.website_endpoint}/index.html" 4 | } 5 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "s3_lambda" { 2 | name = "/aws/lambda/${aws_lambda_function.s3_trigger.function_name}" 3 | retention_in_days = 3 4 | } 5 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | type = string 3 | default = "eu-central-1" 4 | } 5 | 6 | variable "aws_profile" { 7 | type = string 8 | default = "tf014" 9 | } 10 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/dev/backend.hcl: -------------------------------------------------------------------------------- 1 | bucket = "tfstate-968339500772" 2 | key = "0003-terraform-gha/dev/terraform.tfstate" 3 | region = "eu-central-1" 4 | dynamodb_table = "tflock-tfstate-968339500772" 5 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/prod/backend.hcl: -------------------------------------------------------------------------------- 1 | bucket = "tfstate-968339500772" 2 | key = "0003-terraform-gha/prod/terraform.tfstate" 3 | region = "eu-central-1" 4 | dynamodb_table = "tflock-tfstate-968339500772" 5 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "this" { 2 | for_each = aws_lambda_function.todos 3 | 4 | name = "/aws/lambda/${each.value["function_name"]}" 5 | retention_in_days = 3 6 | } 7 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/ssm.tf: -------------------------------------------------------------------------------- 1 | resource "aws_ssm_parameter" "dynamodb_table" { 2 | name = "${local.namespaced_service_name}-dynamodb-table" 3 | type = "String" 4 | value = aws_dynamodb_table.this.name 5 | } 6 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_log_group" "this" { 2 | for_each = aws_lambda_function.todos 3 | 4 | name = "/aws/lamda/${each.value["function_name"]}" 5 | retention_in_days = 3 6 | } 7 | -------------------------------------------------------------------------------- /0003-terraform-gha/config/local/backend.hcl: -------------------------------------------------------------------------------- 1 | bucket = "tfstate-968339500772" 2 | key = "0003-terraform-gha/local/terraform.tfstate" 3 | region = "eu-central-1" 4 | profile = "tf014" 5 | dynamodb_table = "tflock-tfstate-968339500772" 6 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/response.js: -------------------------------------------------------------------------------- 1 | module.exports = function (status, body) { 2 | return { 3 | statusCode: status, 4 | body: JSON.stringify(body), 5 | headers: { 6 | 'Content-Type': 'application/json', 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/modules/lambda-layer/outputs.tf: -------------------------------------------------------------------------------- 1 | output "id" { 2 | value = aws_lambda_layer_version.this.id 3 | } 4 | 5 | output "arn" { 6 | value = aws_lambda_layer_version.this.arn 7 | } 8 | 9 | output "version" { 10 | value = aws_lambda_layer_version.this.version 11 | } 12 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/utils/nodejs/response.js: -------------------------------------------------------------------------------- 1 | module.exports = function (status, body) { 2 | return { 3 | statusCode: status, 4 | body: JSON.stringify(body), 5 | headers: { 6 | 'Content-Type': 'application/json', 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "AWS region to deploy to" 3 | type = string 4 | } 5 | 6 | variable "aws_profile" { 7 | description = "AWS profile to run Terraform with" 8 | type = string 9 | default = "default" 10 | } 11 | -------------------------------------------------------------------------------- /0011-billing-alarm/sns.tf: -------------------------------------------------------------------------------- 1 | resource "aws_sns_topic" "billing_alarm" { 2 | name = "billing-alarm-notification" 3 | } 4 | 5 | resource "aws_sns_topic_subscription" "billing_alarm_email_target" { 6 | topic_arn = aws_sns_topic.billing_alarm.arn 7 | protocol = "email" 8 | endpoint = var.email 9 | } 10 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/lambda-layers.tf: -------------------------------------------------------------------------------- 1 | module "layers" { 2 | for_each = local.lambda_layers 3 | 4 | source = "./modules/lambda-layer" 5 | 6 | name = each.key 7 | description = each.value 8 | output_path = "files/${each.key}-layer.zip" 9 | source_dir = "${local.layers_path}/${each.key}" 10 | } 11 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/layers/utils/nodejs/response.js: -------------------------------------------------------------------------------- 1 | module.exports = function (status, body) { 2 | return { 3 | statusCode: status, 4 | body: JSON.stringify(body), 5 | headers: { 6 | 'Content-Type': 'application/json', 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | account_id = data.aws_caller_identity.current.account_id 3 | component_name = "tf-s3-lambda" 4 | 5 | lambdas_path = "${path.module}/../lambdas" 6 | 7 | readme_file = "README.md" 8 | readme_file_path = "${path.module}/../${local.readme_file}" 9 | } 10 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/terraform.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.3.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.34.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /0013-terraform-test/tests/final/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | http = { 4 | source = "hashicorp/http" 5 | version = "3.4.0" 6 | } 7 | } 8 | } 9 | 10 | variable "endpoint" { 11 | type = string 12 | } 13 | 14 | data "http" "index" { 15 | url = var.endpoint 16 | method = "GET" 17 | } 18 | -------------------------------------------------------------------------------- /0013-terraform-test/www/error.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | Error 11 | 12 | 13 |

Something is amiss.

14 | 15 | 16 | -------------------------------------------------------------------------------- /0013-terraform-test/tests/setup/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | random = { 4 | source = "hashicorp/random" 5 | version = "3.5.1" 6 | } 7 | } 8 | } 9 | 10 | resource "random_pet" "bucket_prefix" { 11 | length = 6 12 | } 13 | 14 | output "bucket_prefix" { 15 | value = random_pet.bucket_prefix.id 16 | } 17 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | lambdas_path = "${path.module}/lambdas" 3 | layers_path = "${path.module}/layers" 4 | 5 | common_tags = { 6 | Project = "Lambda Layers with Terraform" 7 | CreatedAt = formatdate("YYYY-MM-DD", timestamp()) 8 | ManagedBy = "Terraform" 9 | Owner = "Cleber Gasparoto" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/network.tf: -------------------------------------------------------------------------------- 1 | //resource "aws_vpc" "example" { 2 | // cidr_block = "10.1.0.0/16" 3 | // tags = { 4 | // Name = "my-vpc-resource" 5 | // } 6 | //} 7 | //resource "aws_subnet" "example" { 8 | // cidr_block = "10.1.1.0/24" 9 | // vpc_id = aws_vpc.example.id 10 | // tags = { 11 | // Name = "my-subnet-resource" 12 | // } 13 | //} 14 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/normalizer.js: -------------------------------------------------------------------------------- 1 | const normalizeEvent = event => { 2 | return { 3 | method: event['requestContext']['http']['method'], 4 | data: event['body'] ? JSON.parse(event['body']) : {}, 5 | querystring: event['queryStringParameters'] || {}, 6 | pathParameters: event['pathParameters'] || {}, 7 | }; 8 | }; 9 | 10 | module.exports = normalizeEvent; 11 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/lambdas/s3-trigger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "s3-trigger", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@aws-sdk/client-rekognition": "^3.188.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "bucket" { 2 | value = { 3 | arn = aws_s3_bucket.lambda.arn 4 | name = aws_s3_bucket.lambda.id 5 | } 6 | } 7 | 8 | output "lambda" { 9 | value = { 10 | arn = aws_lambda_function.s3_trigger.arn 11 | name = aws_lambda_function.s3_trigger.function_name 12 | invoke_arn = aws_lambda_function.s3_trigger.invoke_arn 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /0003-terraform-gha/ec2.tf: -------------------------------------------------------------------------------- 1 | data "aws_ami" "ubuntu" { 2 | owners = ["amazon"] 3 | most_recent = true 4 | name_regex = "Ubuntu" 5 | } 6 | 7 | resource "aws_instance" "web" { 8 | ami = data.aws_ami.ubuntu.id 9 | instance_type = var.instance_type 10 | 11 | tags = { 12 | Name = "${var.environment}: EC2 created by GHA" 13 | Env = var.environment 14 | Type = var.instance_type 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.15.4" 3 | } 4 | 5 | provider "aws" { 6 | region = var.aws_region 7 | profile = var.aws_profile 8 | 9 | default_tags { 10 | tags = { 11 | Project = "Lambda Layers with Terraform" 12 | CreatedAt = formatdate("YYYY-MM-DD", timestamp()) 13 | ManagedBy = "Terraform" 14 | Owner = "Cleber Gasparoto" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "1.0.5" 3 | } 4 | 5 | provider "aws" { 6 | region = var.aws_region 7 | profile = var.aws_profile 8 | 9 | default_tags { 10 | tags = { 11 | Project = "Serverless REST API Tutorial" 12 | CreatedAt = "2021-09-05" 13 | ManagedBy = "Terraform" 14 | Owner = "Cleber Gasparoto" 15 | Env = var.env 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/layers/utils/nodejs/normalizer.js: -------------------------------------------------------------------------------- 1 | const normalizeEvent = event => { 2 | return { 3 | method: event['requestContext']['http']['method'], 4 | data: event['body'] ? JSON.parse(event['body']) : {}, 5 | querystring: event['queryStringParameters'] || {}, 6 | pathParameters: event['pathParameters'] || {}, 7 | }; 8 | }; 9 | 10 | module.exports = normalizeEvent; 11 | -------------------------------------------------------------------------------- /0008-terraform-new-s3/website/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Error page 9 | 10 | 11 |

Essa é uma página de erro

12 | 13 | 14 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/lambdas/cat-api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cat-api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "Cleber Gasparoto (https://github.com/chgasparoto)", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "got": "^11.8.2" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "1.0.5" 3 | } 4 | 5 | provider "aws" { 6 | region = var.aws_region 7 | profile = var.aws_profile 8 | 9 | default_tags { 10 | tags = { 11 | Project = "Serverless REST API Tutorial" 12 | CreatedAt = "2021-09-05" 13 | ManagedBy = "Terraform" 14 | Owner = "Cleber Gasparoto" 15 | Env = var.env 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/middy/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@middy/core": "^2.5.1", 4 | "@middy/http-json-body-parser": "^2.5.1" 5 | }, 6 | "name": "nodejs", 7 | "version": "1.0.0", 8 | "main": "index.js", 9 | "devDependencies": {}, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "description": "" 17 | } 18 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "env" { 2 | type = string 3 | default = "dev" 4 | } 5 | 6 | variable "aws_region" { 7 | type = string 8 | default = "eu-central-1" 9 | } 10 | 11 | variable "aws_profile" { 12 | type = string 13 | default = "tf014" 14 | } 15 | 16 | variable "aws_account_id" { 17 | type = string 18 | default = "968339500772" 19 | } 20 | 21 | variable "service_name" { 22 | type = string 23 | default = "todos" 24 | } 25 | -------------------------------------------------------------------------------- /0008-terraform-new-s3/website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Home 9 | 10 | 11 |

Esse website foi criado utilizando AWS S3 e Terraform

12 | 13 | 14 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/layers/got/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": { 7 | "got": "^11.8.2" 8 | }, 9 | "devDependencies": {}, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [], 14 | "author": "Cleber Gasparoto (https://github.com/chgasparoto)", 15 | "license": "ISC" 16 | } 17 | -------------------------------------------------------------------------------- /0012-dynamo-s3-athena/lambdas/seed-database/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seed-database" 3 | "version": "1.0.0" 4 | "description": "" 5 | "main": "index.js" 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | } 9 | "keywords": [] 10 | "author": "" 11 | "license": "ISC" 12 | "dependencies": { 13 | "@aws-sdk/client-dynamodb": "^3.204.0" 14 | "@aws-sdk/util-dynamodb": "^3.204.0" 15 | "@ngneat/falso": "^6.1.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/middlewares/nodejs/error-handler.js: -------------------------------------------------------------------------------- 1 | module.exports = () => { 2 | const errorHandler = async (request) => { 3 | console.error(request.error || ''); 4 | return { 5 | statusCode: 500, 6 | body: 'Something went wrong', 7 | headers: { 8 | 'Content-Type': 'application/json', 9 | }, 10 | }; 11 | } 12 | 13 | return { 14 | onError: errorHandler, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/modules/s3-hosting-static-website/www/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Error page 9 | 10 | 11 |

Error page (Terraform and S3)

12 | 13 | 14 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/modules/s3-hosting-static-website/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | Index page 9 | 10 | 11 |

Index page (Terraform and S3)

12 | 13 | 14 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "env" { 2 | type = string 3 | default = "dev" 4 | } 5 | 6 | variable "aws_region" { 7 | type = string 8 | default = "eu-central-1" 9 | } 10 | 11 | variable "aws_profile" { 12 | type = string 13 | default = "tf014" 14 | } 15 | 16 | variable "aws_account_id" { 17 | type = string 18 | default = "968339500772" 19 | } 20 | 21 | variable "service_name" { 22 | type = string 23 | default = "todos" 24 | } 25 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/modules/lambda-layer/main.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "artefact" { 2 | output_path = var.output_path 3 | type = "zip" 4 | source_dir = var.source_dir 5 | } 6 | 7 | resource "aws_lambda_layer_version" "this" { 8 | layer_name = var.name 9 | description = var.description 10 | filename = data.archive_file.artefact.output_path 11 | source_code_hash = data.archive_file.artefact.output_base64sha256 12 | compatible_runtimes = var.compatible_runtimes 13 | } 14 | -------------------------------------------------------------------------------- /0011-billing-alarm/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.3.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.34.0" 8 | } 9 | } 10 | } 11 | 12 | provider "aws" { 13 | region = "us-east-1" 14 | profile = var.aws_profile 15 | 16 | default_tags { 17 | tags = { 18 | Project = "Cloudwatch Billing Alarm" 19 | ManagedBy = "Terraform" 20 | CreatedAt = "October 2022" 21 | Environment = "dev" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-ecs-fargate-nodejs-app", 3 | "version": "1.0.0", 4 | "description": "Simple Node.js app running on AWS ECS Fargate", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [ 10 | "serverless", 11 | "ecs", 12 | "fargate" 13 | ], 14 | "author": "Cleber Gasparoto", 15 | "license": "ISC", 16 | "dependencies": { 17 | "axios": "^1.2.2", 18 | "express": "^4.18.2", 19 | "pg": "^8.8.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "eu-central-1" 3 | profile = "mac_cgasparoto" 4 | 5 | default_tags { 6 | tags = { 7 | Project = "Terraform Module Object Attributes" 8 | ManagedBy = "Terraform" 9 | CreatedAt = "October 2022" 10 | Environment = "dev" 11 | } 12 | } 13 | } 14 | 15 | data "aws_caller_identity" "current" {} 16 | 17 | resource "random_pet" "bucket_name" { 18 | prefix = data.aws_caller_identity.current.account_id 19 | length = 2 20 | } 21 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/middlewares/nodejs/ssm.js: -------------------------------------------------------------------------------- 1 | const SSM = require('aws-sdk/clients/ssm'); 2 | const ssm = new SSM(); 3 | 4 | const defaults = {}; 5 | 6 | module.exports = (opts = {}) => { 7 | const options = { ...defaults, ...opts } 8 | 9 | const getParameter = async (request) => { 10 | const { Parameter: { Value } } = await ssm.getParameter({ Name: options.parameterName }).promise(); 11 | request.event.table_name = Value; 12 | } 13 | 14 | return { 15 | before: getParameter, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /0003-terraform-gha/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "0.15.5" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "3.44.0" 8 | } 9 | } 10 | 11 | backend "s3" {} 12 | } 13 | 14 | provider "aws" { 15 | region = var.aws_region 16 | profile = var.aws_profile 17 | 18 | default_tags { 19 | tags = { 20 | Project = "Terraform GHA setup" 21 | CreatedAt = "2021-06-05" 22 | ManagedBy = "Terraform" 23 | Owner = "Cleber Gasparoto" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/middlewares/nodejs/debugger.js: -------------------------------------------------------------------------------- 1 | const defaults = { 2 | debug: true 3 | } 4 | 5 | module.exports = (opts = {}) => { 6 | const options = { ...defaults, ...opts } 7 | 8 | const debugEvent = async (request) => { 9 | if (process.env.DEBUG && options.debug) { 10 | console.log({ 11 | message: 'Received event', 12 | data: JSON.stringify(request.event), 13 | }); 14 | } 15 | } 16 | 17 | return { 18 | before: debugEvent, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /0014-terraform-import/imports.tf: -------------------------------------------------------------------------------- 1 | import { 2 | to = aws_dynamodb_table.users 3 | id = local.imports.dynamodb_table_name 4 | } 5 | 6 | import { 7 | to = module.s3.aws_s3_bucket.users 8 | id = local.imports.bucket_name 9 | } 10 | 11 | import { 12 | to = aws_s3_bucket_versioning.users 13 | id = local.imports.bucket_name 14 | } 15 | 16 | import { 17 | to = aws_s3_bucket_lifecycle_configuration.users 18 | id = local.imports.bucket_name 19 | } 20 | 21 | module "s3" { 22 | source = "./s3" 23 | 24 | bucket_name = local.imports.bucket_name 25 | } 26 | -------------------------------------------------------------------------------- /0014-terraform-import/s3/main.tf: -------------------------------------------------------------------------------- 1 | variable "bucket_name" { 2 | description = "Name of the s3 bucket. Must be unique." 3 | default = null 4 | type = string 5 | 6 | validation { 7 | condition = can(regex("^[a-z0-9.-]{3,63}$", var.bucket_name)) 8 | error_message = "Invalid S3 bucket name" 9 | } 10 | } 11 | 12 | resource "aws_s3_bucket" "users" { 13 | bucket = var.bucket_name 14 | } 15 | 16 | output "name" { 17 | value = aws_s3_bucket.users.bucket 18 | } 19 | 20 | output "arn" { 21 | value = aws_s3_bucket.users.arn 22 | } 23 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/lambdas/cat-api/index.js: -------------------------------------------------------------------------------- 1 | const got = require('got'); 2 | 3 | exports.handler = async (event, context) => { 4 | console.log('Received event', event); 5 | console.log('Context', context); 6 | 7 | try { 8 | const response = await got('https://aws.random.cat/meow'); 9 | console.log(response.body); 10 | 11 | return { 12 | status: 200, 13 | body: JSON.parse(response.body), 14 | }; 15 | } catch (error) { 16 | console.error(error); 17 | throw new Error(error); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /0011-billing-alarm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_profile" { 2 | description = "AWS profile to run Terraform with" 3 | type = string 4 | default = "default" 5 | } 6 | 7 | variable "threshold" { 8 | description = "The threshold that will trigger the metric alarm" 9 | type = string 10 | } 11 | 12 | variable "currency" { 13 | description = "Currency type (e.g. USD, CAD, EUR)" 14 | type = string 15 | default = "USD" 16 | } 17 | 18 | variable "email" { 19 | description = "The email to subscribe to the SNS topic" 20 | type = string 21 | } 22 | -------------------------------------------------------------------------------- /0003-terraform-gha/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | type = string 3 | description = "The AWS region to deploy to" 4 | default = "eu-central-1" 5 | } 6 | 7 | variable "aws_profile" { 8 | type = string 9 | description = "The AWS profile to use to execute the commands" 10 | default = "default" 11 | } 12 | 13 | variable "environment" { 14 | type = string 15 | description = "The environment to deploy to" 16 | default = "dev" 17 | } 18 | 19 | variable "instance_type" { 20 | type = string 21 | description = "The instance power" 22 | } 23 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/dynamodb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "this" { 2 | name = local.namespaced_service_name 3 | hash_key = "id" 4 | billing_mode = "PAY_PER_REQUEST" 5 | 6 | attribute { 7 | name = "id" 8 | type = "N" 9 | } 10 | } 11 | 12 | resource "aws_dynamodb_table_item" "this" { 13 | table_name = aws_dynamodb_table.this.name 14 | hash_key = aws_dynamodb_table.this.hash_key 15 | 16 | item = < { 2 | 3 | const eventNormalizer = async (request) => { 4 | request.event.normalized = { 5 | method: request.event['requestContext']['http']['method'] || 'GET', 6 | data: request.event['body'] ? JSON.parse(request.event['body']) : {}, 7 | querystring: request.event['queryStringParameters'] || {}, 8 | pathParameters: request.event['pathParameters'] || {}, 9 | } 10 | } 11 | 12 | return { 13 | before: eventNormalizer, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /0014-terraform-import/main.tf: -------------------------------------------------------------------------------- 1 | # https://developer.hashicorp.com/terraform/language/import 2 | # https://developer.hashicorp.com/terraform/language/import/generating-configuration 3 | 4 | terraform { 5 | required_version = "~> 1.6" 6 | 7 | required_providers { 8 | aws = { 9 | source = "hashicorp/aws" 10 | version = "~> 5.19" 11 | } 12 | } 13 | } 14 | 15 | provider "aws" { 16 | region = "eu-central-1" 17 | 18 | default_tags { 19 | tags = { 20 | "Video" = "Terraform Import" 21 | "CreatedAt" = "2023-10-11" 22 | "ManagedBy" = "Terraform" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | quote_type = single 11 | 12 | [vcbuild.bat] 13 | end_of_line = crlf 14 | 15 | [Makefile] 16 | indent_size = 8 17 | indent_style = tab 18 | 19 | [{deps}/**] 20 | charset = unset 21 | end_of_line = unset 22 | indent_size = unset 23 | indent_style = unset 24 | trim_trailing_whitespace = unset 25 | 26 | [{test/fixtures,deps,tools/node_modules,tools/gyp,tools/icu,tools/msvs}/**] 27 | insert_final_newline = false -------------------------------------------------------------------------------- /0009-terraform-optional-vars/modules/s3-hosting-static-website/outputs.tf: -------------------------------------------------------------------------------- 1 | output "arn" { 2 | description = "ARN of the bucket" 3 | value = aws_s3_bucket.website.arn 4 | } 5 | 6 | output "name" { 7 | description = "Name (id) of the bucket" 8 | value = aws_s3_bucket.website.id 9 | } 10 | 11 | output "domain" { 12 | description = "Domain name of the bucket" 13 | value = aws_s3_bucket_website_configuration.website.website_domain 14 | } 15 | 16 | output "endpoint" { 17 | description = "Endpoint of the bucket" 18 | value = aws_s3_bucket_website_configuration.website.website_endpoint 19 | } 20 | -------------------------------------------------------------------------------- /0011-billing-alarm/cloudwatch.tf: -------------------------------------------------------------------------------- 1 | resource "aws_cloudwatch_metric_alarm" "billing" { 2 | alarm_name = "Billing Alarm" 3 | alarm_description = "It triggers when the threshold is >= ${var.threshold} ${var.currency}" 4 | comparison_operator = "GreaterThanOrEqualToThreshold" 5 | evaluation_periods = "1" 6 | metric_name = "EstimatedCharges" 7 | namespace = "AWS/Billing" 8 | period = "21600" # 6 hours 9 | statistic = "Maximum" 10 | threshold = var.threshold 11 | alarm_actions = [aws_sns_topic.billing_alarm.arn] 12 | 13 | dimensions = { 14 | Currency = var.currency 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /0013-terraform-test/variables.tf: -------------------------------------------------------------------------------- 1 | variable "aws_region" { 2 | description = "The region to deploy the infra to" 3 | default = "eu-central-1" # Frankfurt 4 | type = string 5 | 6 | validation { 7 | condition = can(regex("^[a-z][a-z]-[a-z]+-[0-9]+$", var.aws_region)) 8 | error_message = "Invalid AWS region name" 9 | } 10 | } 11 | 12 | variable "bucket_name" { 13 | description = "Name of the s3 bucket. Must be unique." 14 | default = null 15 | type = string 16 | 17 | validation { 18 | condition = can(regex("^[a-z0-9.-]{3,63}$", var.bucket_name)) 19 | error_message = "Invalid S3 bucket name" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/dynamodb.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "this" { 2 | name = local.namespaced_service_name 3 | hash_key = "id" 4 | billing_mode = "PAY_PER_REQUEST" 5 | 6 | attribute { 7 | name = "id" 8 | type = "N" 9 | } 10 | } 11 | 12 | resource "aws_dynamodb_table_item" "this" { 13 | table_name = aws_dynamodb_table.this.name 14 | hash_key = aws_dynamodb_table.this.hash_key 15 | 16 | item = jsonencode({ 17 | id = { 18 | N = "1" 19 | } 20 | task = { 21 | S = "dar like no video" 22 | } 23 | done = { 24 | S = "false" 25 | } 26 | created_at = { 27 | S = timestamp() 28 | } 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/main.tf: -------------------------------------------------------------------------------- 1 | module "website_s3_bucket" { 2 | source = "./modules/s3-hosting-static-website" 3 | 4 | bucket_name = "${random_pet.bucket_name.id}.com" 5 | 6 | files = { 7 | terraform_managed = true 8 | error_document_key = "index.html" 9 | www_path = "${path.module}/blog" 10 | } 11 | 12 | cors_rules = [ 13 | { 14 | allowed_headers = ["*"], 15 | allowed_methods = ["PUT", "POST"], 16 | allowed_origins = ["http:localhost:3000"], 17 | exposed_headers = ["Etag"], 18 | max_aged_seconds = 3000 19 | }, 20 | { 21 | allowed_origins = ["*"], 22 | allowed_methods = ["GET"], 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.3.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.34.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | 16 | provider "aws" { 17 | region = var.aws_region 18 | profile = var.aws_profile 19 | 20 | default_tags { 21 | tags = { 22 | Project = "Trigger Lamba with S3 Bucket" 23 | ManagedBy = "Terraform" 24 | CreatedAt = "October 2022" 25 | Environment = "dev" 26 | } 27 | } 28 | } 29 | 30 | data "aws_caller_identity" "current" {} 31 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/lambda.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "cat_api_artefact" { 2 | output_path = "files/cat-api-artefact.zip" 3 | type = "zip" 4 | source_file = "${local.lambdas_path}/cat-api/index.js" 5 | } 6 | 7 | resource "aws_lambda_function" "cat_api" { 8 | function_name = "cat_api" 9 | handler = "index.handler" 10 | description = "Gets a random cat image" 11 | role = aws_iam_role.cat_api_lambda.arn 12 | runtime = "nodejs14.x" 13 | 14 | filename = data.archive_file.cat_api_artefact.output_path 15 | source_code_hash = data.archive_file.cat_api_artefact.output_base64sha256 16 | 17 | timeout = 5 18 | memory_size = 128 19 | 20 | layers = [aws_lambda_layer_version.got.arn] 21 | } 22 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | namespaced_service_name = "${var.service_name}-${var.env}" 3 | 4 | lambdas_path = "${path.module}/../lambdas" 5 | layers_path = "${local.lambdas_path}/layers" 6 | 7 | lambdas = { 8 | get = { 9 | description = "Get todos" 10 | memory = 256 11 | timeout = 10 12 | } 13 | delete = { 14 | description = "Delete given todo" 15 | memory = 128 16 | timeout = 5 17 | } 18 | put = { 19 | description = "Update given todo" 20 | memory = 128 21 | timeout = 5 22 | } 23 | post = { 24 | description = "Create new todo" 25 | memory = 128 26 | timeout = 5 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/outputs.tf: -------------------------------------------------------------------------------- 1 | output "layers" { 2 | value = [{ 3 | got = { 4 | arn = aws_lambda_layer_version.got.arn 5 | name = aws_lambda_layer_version.got.layer_name 6 | version = aws_lambda_layer_version.got.version 7 | description = aws_lambda_layer_version.got.description 8 | created_at = aws_lambda_layer_version.got.created_date 9 | } 10 | }] 11 | } 12 | 13 | output "lambdas" { 14 | value = [{ 15 | arn = aws_lambda_function.cat_api.arn 16 | name = aws_lambda_function.cat_api.function_name 17 | description = aws_lambda_function.cat_api.description 18 | version = aws_lambda_function.cat_api.version 19 | last_modified = aws_lambda_function.cat_api.last_modified 20 | }] 21 | } 22 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/modules/lambda-layer/variables.tf: -------------------------------------------------------------------------------- 1 | variable "name" { 2 | type = string 3 | description = "Layer name" 4 | } 5 | 6 | variable "description" { 7 | type = string 8 | description = "Layer description. If it is using a npm package it is a good practice to put the version of it" 9 | default = "" 10 | } 11 | 12 | variable "output_path" { 13 | type = string 14 | description = "The path where the zipped layer will be saved in the local machine" 15 | } 16 | 17 | variable "source_dir" { 18 | type = string 19 | description = "The source directory where the layer files are" 20 | } 21 | 22 | variable "compatible_runtimes" { 23 | type = list(string) 24 | description = "The list of compatible runtimes" 25 | default = ["nodejs14.x"] 26 | } 27 | -------------------------------------------------------------------------------- /0013-terraform-test/main.tf: -------------------------------------------------------------------------------- 1 | # https://www.hashicorp.com/blog/terraform-1-6-adds-a-test-framework-for-enhanced-code-validation 2 | # https://developer.hashicorp.com/terraform/language/tests 3 | # https://developer.hashicorp.com/terraform/cli/test 4 | # https://developer.hashicorp.com/terraform/tutorials/configuration-language/test 5 | # http://terraform-test-intro-09877890.s3-website.eu-central-1.amazonaws.com/index.html 6 | 7 | terraform { 8 | required_version = "~> 1.6" 9 | 10 | required_providers { 11 | aws = { 12 | source = "hashicorp/aws" 13 | version = "~> 5.19" 14 | } 15 | } 16 | } 17 | 18 | provider "aws" { 19 | region = var.aws_region 20 | 21 | default_tags { 22 | tags = { 23 | "Video" = "Terraform Test" 24 | "CreatedAt" = "2023-10-08" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/lambda.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "s3_trigger" { 2 | type = "zip" 3 | source_dir = "${local.lambdas_path}/s3-trigger" 4 | output_path = "files/${local.component_name}-artefact.zip" 5 | } 6 | 7 | resource "aws_lambda_function" "s3_trigger" { 8 | function_name = local.component_name 9 | role = aws_iam_role.s3_lambda.arn 10 | runtime = "nodejs16.x" 11 | architectures = ["arm64"] 12 | handler = "index.handler" 13 | 14 | filename = data.archive_file.s3_trigger.output_path 15 | source_code_hash = data.archive_file.s3_trigger.output_base64sha256 16 | } 17 | 18 | resource "aws_lambda_permission" "s3" { 19 | action = "lambda:InvokeFunction" 20 | function_name = aws_lambda_function.s3_trigger.function_name 21 | principal = "s3.amazonaws.com" 22 | source_arn = aws_s3_bucket.lambda.arn 23 | } 24 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/layer.tf: -------------------------------------------------------------------------------- 1 | resource "null_resource" "install_layer_deps" { 2 | triggers = { 3 | layer_build = filemd5("${local.layers_path}/got/nodejs/package.json") 4 | } 5 | 6 | provisioner "local-exec" { 7 | working_dir = "${local.layers_path}/got/nodejs" 8 | command = "npm install --production" 9 | } 10 | } 11 | 12 | data "archive_file" "got_layer" { 13 | output_path = "files/got-layer.zip" 14 | type = "zip" 15 | source_dir = "${local.layers_path}/got" 16 | 17 | depends_on = [null_resource.install_layer_deps] 18 | } 19 | 20 | resource "aws_lambda_layer_version" "got" { 21 | layer_name = "got-layer" 22 | description = "got: ^11.8.2" 23 | filename = data.archive_file.got_layer.output_path 24 | source_code_hash = data.archive_file.got_layer.output_base64sha256 25 | compatible_runtimes = ["nodejs14.x"] 26 | } 27 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/s3.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "lambda" { 2 | bucket = "${local.account_id}-${local.component_name}" 3 | } 4 | 5 | resource "aws_s3_bucket_acl" "lambda" { 6 | bucket = aws_s3_bucket.lambda.id 7 | acl = "private" 8 | } 9 | 10 | resource "aws_s3_object" "readme" { 11 | bucket = aws_s3_bucket.lambda.id 12 | key = "input/images/${local.readme_file}" 13 | content_type = "text/markdown; charset=UTF-8" 14 | source = local.readme_file_path 15 | etag = filemd5(local.readme_file_path) 16 | } 17 | 18 | resource "aws_s3_bucket_notification" "s3_trigger" { 19 | bucket = aws_s3_bucket.lambda.id 20 | 21 | lambda_function { 22 | events = ["s3:ObjectCreated:*"] 23 | filter_prefix = "input/images" 24 | filter_suffix = ".jpg" 25 | lambda_function_arn = aws_lambda_function.s3_trigger.arn 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # Crash log files 9 | crash.log 10 | 11 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 12 | # .tfvars files are managed as part of configuration and so should be included in 13 | # version control. 14 | # 15 | # example.tfvars 16 | 17 | # Ignore override files as they are usually used to override resources locally and so 18 | # are not checked in 19 | override.tf 20 | override.tf.json 21 | *_override.tf 22 | *_override.tf.json 23 | 24 | # Include override files you do wish to add to version control using negated pattern 25 | # 26 | # !example_override.tf 27 | 28 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 29 | # example: *tfplan* 30 | 31 | .idea 32 | *.zip 33 | node_modules 34 | .terraform 35 | .DS_STORE -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/lambdas/s3-trigger/index.js: -------------------------------------------------------------------------------- 1 | const { RekognitionClient, DetectFacesCommand } = require("@aws-sdk/client-rekognition"); 2 | const rekoClient = new RekognitionClient(); 3 | 4 | exports.handler = async (event) => { 5 | console.log(JSON.stringify(event)); 6 | 7 | const bucketName = event.Records[0].s3.bucket.name; 8 | const objectName = event.Records[0].s3.object.key; 9 | 10 | const detectFacesCommand = new DetectFacesCommand({ 11 | Image: { 12 | S3Object: { 13 | Bucket: bucketName, 14 | Name: objectName 15 | } 16 | }, 17 | Attributes: ['ALL'] 18 | }); 19 | 20 | try { 21 | const response = await rekoClient.send(detectFacesCommand); 22 | console.log(JSON.stringify(response)); 23 | } catch (e) { 24 | console.log(e); 25 | throw new Error(e.message); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /0001-terraform-time-and-date-functions/tf/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 0.15" 3 | } 4 | 5 | resource "random_pet" "bucket" {} 6 | 7 | locals { 8 | now = timestamp() 9 | 10 | brasilia_tz = timeadd(local.now, "-3h") 11 | amsterdam_tz = timeadd(local.now, "2h") 12 | 13 | date_br = formatdate("DD/MM/YYYY", local.brasilia_tz) 14 | date_nl = formatdate("D-MM-YYYY", local.amsterdam_tz) 15 | 16 | date_utc = formatdate("YYYY-MM-DD", local.now) 17 | } 18 | 19 | output "bucket_name" { 20 | value = "${random_pet.bucket.id}-${local.date_utc}" 21 | } 22 | 23 | output "locals" { 24 | value = { 25 | "now" = local.now, 26 | "brasilia_tz" = local.brasilia_tz, 27 | "amsterdam_tz" = local.amsterdam_tz, 28 | "date_br" = local.date_br, 29 | "date_nl" = local.date_nl, 30 | "date_utc" = local.date_utc 31 | } 32 | } 33 | 34 | output "pet" { 35 | value = random_pet.bucket.id 36 | } 37 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/locals.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | namespaced_service_name = "${var.service_name}-${var.env}" 3 | 4 | lambdas_path = "${path.module}/../lambdas" 5 | layers_path = "${local.lambdas_path}/layers" 6 | 7 | lambda_layers = { 8 | utils = "Utils for response and event normalization", 9 | middy = "Middy core 2.5.1", 10 | middlewares = "Custom middy middlewares" 11 | } 12 | 13 | lambdas = { 14 | get = { 15 | description = "Get todos" 16 | memory = 256 17 | timeout = 10 18 | } 19 | delete = { 20 | description = "Delete given todo" 21 | memory = 128 22 | timeout = 5 23 | } 24 | put = { 25 | description = "Update given todo" 26 | memory = 128 27 | timeout = 5 28 | } 29 | post = { 30 | description = "Create new todo" 31 | memory = 128 32 | timeout = 5 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /0012-dynamo-s3-athena/lambdas/seed-database/queries.sql: -------------------------------------------------------------------------------- 1 | -- create table sql 2 | CREATE EXTERNAL TABLE IF NOT EXISTS user_comments_table_2022_11_06_001 ( 3 | Item struct < 4 | pk:struct 5 | sk:struct 6 | active:struct 7 | createdAt:struct 8 | email:struct 9 | fullname:struct 10 | username:struct 11 | comment:struct 12 | > 13 | ) 14 | ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 15 | LOCATION 's3://968339500772-dynamodb-s3-athena/AWSDynamoDB/01667735083131-0dfc8cf2/data/' 16 | TBLPROPERTIES ('has_encrypted_data'='true'); 17 | 18 | -- filter by email and active field 19 | SELECT Item.pk.S as id 20 | Item.email.S as email 21 | from_unixtime(cast(Item.createdAt.N as bigint) / 1000) 22 | Item.active.BOOL as active 23 | FROM user_comments_table_2022_11_06_001 24 | WHERE Item.email.S LIKE '%.com' 25 | AND Item.active.BOOL = true; -------------------------------------------------------------------------------- /0008-terraform-new-s3/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 1.3.2" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = "~> 4.34.0" 8 | } 9 | random = { 10 | source = "hashicorp/random" 11 | version = "~> 3.4.3" 12 | } 13 | } 14 | } 15 | 16 | provider "aws" { 17 | region = "eu-central-1" 18 | profile = "mac_cgasparoto" 19 | 20 | default_tags { 21 | tags = { 22 | Project = "Terraform new S3 Bucket" 23 | ManagedBy = "Terraform" 24 | CreatedAt = "October 2022" 25 | Environment = "dev" 26 | } 27 | } 28 | } 29 | 30 | # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity 31 | data "aws_caller_identity" "current" {} 32 | 33 | # https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet 34 | resource "random_pet" "bucket_name" { 35 | prefix = data.aws_caller_identity.current.account_id 36 | length = 4 37 | } 38 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/modules/s3-hosting-static-website/variables.tf: -------------------------------------------------------------------------------- 1 | variable "bucket_name" { 2 | description = "Bucket name, must be unique" 3 | type = string 4 | 5 | validation { 6 | condition = length(var.bucket_name) > 10 && endswith(var.bucket_name, ".com") 7 | error_message = "The given bucket name is invalid. It must have more than 10 chars and ends with '.com'" 8 | } 9 | } 10 | 11 | variable "files" { 12 | description = "Configuration for website files" 13 | type = object({ 14 | terraform_managed = bool 15 | error_document_key = optional(string, "error.html") 16 | index_document_suffix = optional(string, "index.html") 17 | www_path = optional(string) 18 | }) 19 | } 20 | 21 | variable "cors_rules" { 22 | description = "List of CORS rules" 23 | type = list(object({ 24 | allowed_headers = optional(set(string)), 25 | allowed_methods = set(string), 26 | allowed_origins = set(string), 27 | expose_headers = optional(set(string)), 28 | max_age_seconds = optional(number) 29 | })) 30 | default = [] 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Cleber Gasparoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/api.tf: -------------------------------------------------------------------------------- 1 | resource "aws_apigatewayv2_api" "this" { 2 | name = "${local.namespaced_service_name}-api" 3 | protocol_type = "HTTP" 4 | } 5 | 6 | resource "aws_apigatewayv2_stage" "this" { 7 | api_id = aws_apigatewayv2_api.this.id 8 | name = "$default" 9 | auto_deploy = true 10 | } 11 | 12 | resource "aws_apigatewayv2_integration" "todos" { 13 | for_each = local.lambdas 14 | 15 | api_id = aws_apigatewayv2_api.this.id 16 | integration_type = "AWS_PROXY" 17 | integration_method = "POST" 18 | payload_format_version = "2.0" 19 | integration_uri = aws_lambda_function.todos[each.key].invoke_arn 20 | } 21 | 22 | resource "aws_apigatewayv2_route" "todos" { 23 | for_each = local.lambdas 24 | 25 | api_id = aws_apigatewayv2_api.this.id 26 | route_key = "${upper(each.key)} /v1/todos" 27 | target = "integrations/${aws_apigatewayv2_integration.todos[each.key].id}" 28 | } 29 | 30 | resource "aws_apigatewayv2_route" "todos_get" { 31 | api_id = aws_apigatewayv2_api.this.id 32 | route_key = "GET /v1/todos/{todoId}" 33 | target = "integrations/${aws_apigatewayv2_integration.todos["get"].id}" 34 | } 35 | -------------------------------------------------------------------------------- /0001-terraform-time-and-date-functions/tf/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/random" { 5 | version = "3.1.0" 6 | hashes = [ 7 | "h1:rKYu5ZUbXwrLG1w81k7H3nce/Ys6yAxXhWcbtk36HjY=", 8 | "zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc", 9 | "zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626", 10 | "zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff", 11 | "zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2", 12 | "zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992", 13 | "zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427", 14 | "zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc", 15 | "zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f", 16 | "zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b", 17 | "zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7", 18 | "zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a", 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /0003-terraform-gha/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "3.44.0" 6 | constraints = "3.44.0" 7 | hashes = [ 8 | "h1:VOVZWybe1x0E4qyawTwt7jXVBRUplTrzVFHim217DqI=", 9 | "zh:0680315b29a140e9b7e4f5aeed3f2445abdfab31fc9237f34dcad06de4f410df", 10 | "zh:13811322a205fb4a0ee617f0ae51ec94176befdf569235d0c7064db911f0acc7", 11 | "zh:25e427a1cfcb1d411bc12040cf0684158d094416ecf18889a41196bacc761729", 12 | "zh:40cd6acd24b060823f8d116355d8f844461a11925796b1757eb2ee18abc0bc7c", 13 | "zh:94e2463eef555c388cd27f6e85ad803692d6d80ffa621bdc382ab119001d4de4", 14 | "zh:aadc3bc216b14839e85b463f07b8507920ace5f202a608e4a835df23711c8a0d", 15 | "zh:ab50dc1242af5a8fcdb18cf89beeaf2b2146b51ecfcecdbea033913a5f4c1c14", 16 | "zh:ad48bbf4af66b5d48ca07c5c558d2f5724311db4dd943c1c98a7f3f107e03311", 17 | "zh:ad76796c2145a7aaec1970a5244f5c0a9d200556121e2c5b382f296597b1a03c", 18 | "zh:cf0a2181356598f8a2abfeaf0cdf385bdeea7f2e52821c850a2a08b60c26b9f6", 19 | "zh:f76801af6bc34fe4a5bf1c63fa0204e24b81691049efecd6baa1526593f03935", 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/api.tf: -------------------------------------------------------------------------------- 1 | resource "aws_apigatewayv2_api" "this" { 2 | name = "${local.namespaced_service_name}-api" 3 | protocol_type = "HTTP" 4 | } 5 | 6 | resource "aws_apigatewayv2_stage" "this" { 7 | api_id = aws_apigatewayv2_api.this.id 8 | name = "$default" 9 | auto_deploy = true 10 | } 11 | 12 | resource "aws_apigatewayv2_integration" "todos" { 13 | for_each = local.lambdas 14 | 15 | api_id = aws_apigatewayv2_api.this.id 16 | integration_type = "AWS_PROXY" 17 | integration_method = "POST" 18 | payload_format_version = "2.0" 19 | integration_uri = aws_lambda_function.todos[each.key].invoke_arn 20 | } 21 | 22 | resource "aws_apigatewayv2_route" "todos" { 23 | for_each = local.lambdas 24 | 25 | api_id = aws_apigatewayv2_api.this.id 26 | route_key = "${upper(each.key)} /v1/todos" 27 | target = "integrations/${aws_apigatewayv2_integration.todos[each.key].id}" 28 | } 29 | 30 | resource "aws_apigatewayv2_route" "todos_get" { 31 | api_id = aws_apigatewayv2_api.this.id 32 | route_key = "GET /v1/todos/{todoId}" 33 | target = "integrations/${aws_apigatewayv2_integration.todos["get"].id}" 34 | } 35 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/Dockerfile: -------------------------------------------------------------------------------- 1 | ################### 2 | # BUILD FOR LOCAL DEVELOPMENT 3 | ################### 4 | 5 | FROM node:18-alpine As development 6 | 7 | WORKDIR /usr/src/app 8 | 9 | COPY --chown=node:node index.js package*.json ./ 10 | 11 | RUN npm install 12 | 13 | COPY --chown=node:node . . 14 | 15 | USER node 16 | 17 | ################### 18 | # BUILD FOR PRODUCTION 19 | ################### 20 | 21 | FROM node:18-alpine As build 22 | 23 | WORKDIR /usr/src/app 24 | 25 | COPY --chown=node:node index.js package*.json ./ 26 | COPY --chown=node:node --from=development /usr/src/app/db ./db 27 | COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules 28 | 29 | COPY --chown=node:node . . 30 | 31 | ENV NODE_ENV production 32 | 33 | RUN npm ci --omit=dev && npm cache clean --force 34 | 35 | USER node 36 | 37 | ################### 38 | # PRODUCTION 39 | ################### 40 | 41 | FROM node:18-alpine As production 42 | 43 | COPY --chown=node:node --from=build /usr/src/app/index.js ./index.js 44 | COPY --chown=node:node --from=build /usr/src/app/db ./db 45 | COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules 46 | 47 | EXPOSE 3000 48 | 49 | CMD [ "node", "index.js" ] 50 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/delete.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | 4 | const normalizeEvent = require('./normalizer'); 5 | const response = require('./response'); 6 | 7 | exports.handler = async event => { 8 | if (process.env.DEBUG) { 9 | console.log({ 10 | message: 'Received event', 11 | data: JSON.stringify(event), 12 | }); 13 | } 14 | 15 | const table = event.table || process.env.TABLE; 16 | if (!table) { 17 | throw new Error('No table name defined.'); 18 | } 19 | 20 | const { 21 | data: { id }, 22 | } = normalizeEvent(event); 23 | 24 | const params = { 25 | TableName: table, 26 | Key: { 27 | id: parseInt(id, 10), 28 | }, 29 | }; 30 | 31 | try { 32 | await dynamo.delete(params).promise(); 33 | 34 | console.log({ 35 | message: 'Record has been deleted', 36 | data: JSON.stringify(params), 37 | }); 38 | 39 | return response(200, `Record ${id} has been deleted`); 40 | } catch (err) { 41 | console.error(err); 42 | return response(500, 'Somenthing went wrong'); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/post.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | 4 | const normalizeEvent = require('./normalizer'); 5 | const response = require('./response'); 6 | 7 | exports.handler = async event => { 8 | if (process.env.DEBUG) { 9 | console.log({ 10 | message: 'Received event', 11 | data: JSON.stringify(event), 12 | }); 13 | } 14 | 15 | const table = event.table || process.env.TABLE; 16 | if (!table) { 17 | throw new Error('No table name defined.'); 18 | } 19 | 20 | const { data } = normalizeEvent(event); 21 | 22 | const params = { 23 | TableName: table, 24 | Item: { 25 | ...data, 26 | created_at: new Date().toISOString(), 27 | }, 28 | }; 29 | 30 | try { 31 | await dynamo.put(params).promise(); 32 | 33 | console.log({ 34 | message: 'Record has been created', 35 | data: JSON.stringify(params), 36 | }); 37 | 38 | return response(201, `Record ${data.id} has been created`); 39 | } catch (err) { 40 | console.error(err); 41 | return response(500, 'Somenthing went wrong'); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /0011-billing-alarm/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.34.0" 6 | constraints = "~> 4.34.0" 7 | hashes = [ 8 | "h1:JRqeU/5qR61U+z86mC68C5hp0XHZXxmRK9dupTIAhGg=", 9 | "zh:2bdc9b908008c1e874d8ba7e2cfabd856cafb63c52fef51a1fdeef2f5584bffd", 10 | "zh:43c5364e3161be3856e56494cbb8b21d513fc05875f1b40e66e583602154dd0a", 11 | "zh:44e763adae92489f223f65866c1f8b5342e7e85b95daa8d1f483a2afb47f7db3", 12 | "zh:62bfabb3a1a31814cb3fadc356539d8253b95abacfffd90984affb54c9a53a86", 13 | "zh:6495ce67897d2d5648d466c09e8588e837c2878322988738a95c06926044b05d", 14 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 15 | "zh:b1546b2ac61d7cc27a8eba160ae1b6ac581d2c4af824a400d6511e4998da398a", 16 | "zh:c8c362c5527f0533bde581e41cdb2bdf42aea557762f326dbfa122fdf001fb10", 17 | "zh:c8cc28fb41f73ca09f590bace2204ea325f6116be0bbce6abfebd393d028f12c", 18 | "zh:db0601c9bd12ca028d60ac87e85320285ebc64857715f7908dd6a283e5f44d45", 19 | "zh:e64d2193236d05348ba2e8b99650d9274e5af80be39b3ee28bbe442b0684d8a3", 20 | "zh:ff6228f3751e1f0ee7dc086d09e32d39ca6556f0b5267f36aae67881d29ace94", 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Youtube: Cleber Gasparoto 2 | 3 | All related code to my [Youtube channel](https://www.youtube.com/channel/UCnP-0M4m5peN-7aLfs4pScA). 4 | 5 | Latest videos: 6 | - [AWS ECS Fargate: Criando e deployando uma aplicação Node.js](https://www.youtube.com/playlist?list=PLWQmZVQayUUI5RinDqpoIXiRYWy5YZKjs) 7 | - [Middy.js: Tutorial de como utilizar AWS Lambda com Middlewares](https://youtu.be/HZS6CKeFo8w) 8 | - [Terraform: AWS Serverless REST API](https://www.youtube.com/playlist?list=PLWQmZVQayUUK5581j2xYO6iTzooId50Lz) 9 | - [Serverless (Node.js) REST API Tutorial - API Gateway, Lambda e DynamoDB](https://youtu.be/dRndyTrbL60) 10 | - [O que é e para o que serve o Terraform?](https://youtu.be/bIPF_hzmQGE) 11 | - [Como configurar Github Actions para rodar Terraform com um backend S3](https://youtu.be/2McC52KuWh0) 12 | - [Terraform: Como configurar as default tags dentro do AWS provider](https://youtu.be/f7Rz4p5UOQI) 13 | - [Como criar AWS Lambda com Terraform](https://youtu.be/_KnpfcjdyOo) 14 | - [Manipulação de data e hora com Terraform](https://youtu.be/Kb7Td9h6H8Y) 15 | - [Como publicar um package no npm de forma automatizada - Curso Completo em Português](https://youtu.be/Er6TedCiTMs) 16 | - [Terraform com AWS - Curso em Português](https://www.youtube.com/playlist?list=PLWQmZVQayUUIgSmOj3GPH2BJcn0hOzIaP) 17 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/todos/delete.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | const ssm = new AWS.SSM(); 4 | 5 | const normalizeEvent = require('/opt/nodejs/normalizer'); 6 | const response = require('/opt/nodejs/response'); 7 | 8 | exports.handler = async event => { 9 | if (process.env.DEBUG) { 10 | console.log({ 11 | message: 'Received event', 12 | data: JSON.stringify(event), 13 | }); 14 | } 15 | 16 | try { 17 | const { Parameter: { Value: table } } = await ssm.getParameter({ Name: process.env.TABLE }).promise(); 18 | const { 19 | data: { id }, 20 | } = normalizeEvent(event); 21 | 22 | const params = { 23 | TableName: table, 24 | Key: { 25 | id: parseInt(id, 10), 26 | }, 27 | }; 28 | 29 | await dynamo.delete(params).promise(); 30 | 31 | console.log({ 32 | message: 'Record has been deleted', 33 | data: JSON.stringify(params), 34 | }); 35 | 36 | return response(200, `Record ${id} has been deleted`); 37 | } catch (err) { 38 | console.error(err); 39 | return response(500, 'Somenthing went wrong'); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/todos/delete.js: -------------------------------------------------------------------------------- 1 | const DynamoDB = require('aws-sdk/clients/dynamodb'); 2 | const dynamo = new DynamoDB.DocumentClient(); 3 | const middy = require('@middy/core'); 4 | 5 | const response = require('/opt/nodejs/response'); 6 | const debugMiddleware = require('/opt/nodejs/debugger'); 7 | const ssmMiddleware = require('/opt/nodejs/ssm'); 8 | const errorHandlerMiddleware = require('/opt/nodejs/error-handler'); 9 | const eventNormalizerMiddleware = require('/opt/nodejs/api-event-normalizer'); 10 | 11 | const baseHandler = async event => { 12 | const { 13 | data: { id }, 14 | } = event.normalized; 15 | 16 | const params = { 17 | TableName: event.table_name, 18 | Key: { 19 | id: parseInt(id, 10), 20 | }, 21 | }; 22 | 23 | await dynamo.delete(params).promise(); 24 | 25 | console.log({ 26 | message: 'Record has been deleted', 27 | data: JSON.stringify(params), 28 | }); 29 | 30 | return response(200, `Record ${id} has been deleted`); 31 | }; 32 | 33 | const handler = middy(baseHandler) 34 | .use(debugMiddleware({ debug: true })) 35 | .use(eventNormalizerMiddleware()) 36 | .use(ssmMiddleware({ parameterName: process.env.TABLE })) 37 | .use(errorHandlerMiddleware()); 38 | 39 | module.exports = { handler }; 40 | -------------------------------------------------------------------------------- /.github/workflows/0003_tf_apply_prod.yml: -------------------------------------------------------------------------------- 1 | name: Apply terraform resources on folder 0003-terraform-gha with prod config 2 | 3 | on: 4 | push: 5 | tags: 6 | - v[12].[0-9]+.[0-9]+ 7 | 8 | env: 9 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 10 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 11 | 12 | jobs: 13 | apply-prod: 14 | name: 'terraform apply prod' 15 | runs-on: ubuntu-latest 16 | env: 17 | TF_WORKING_DIR: '0003-terraform-gha' 18 | 19 | defaults: 20 | run: 21 | shell: bash 22 | working-directory: ${{ env.TF_WORKING_DIR }} 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v2 27 | 28 | - name: Setup Terraform 29 | uses: hashicorp/setup-terraform@v1 30 | with: 31 | terraform_version: 0.15.5 32 | 33 | - name: Terraform fmt 34 | id: fmt 35 | run: terraform fmt -check 36 | 37 | - name: Terraform Init 38 | id: init 39 | run: terraform init -backend=true -backend-config="config/prod/backend.hcl" 40 | 41 | - name: Terraform Validate 42 | id: validate 43 | run: terraform validate -no-color 44 | 45 | - name: Terraform Apply 46 | run: terraform apply -auto-approve -var-file="config/prod/terraform.tfvars" 47 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/todos/post.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | const ssm = new AWS.SSM(); 4 | 5 | const normalizeEvent = require('/opt/nodejs/normalizer'); 6 | const response = require('/opt/nodejs/response'); 7 | 8 | exports.handler = async event => { 9 | if (process.env.DEBUG) { 10 | console.log({ 11 | message: 'Received event', 12 | data: JSON.stringify(event), 13 | }); 14 | } 15 | 16 | try { 17 | const { Parameter: { Value: table } } = await ssm.getParameter({ Name: process.env.TABLE }).promise(); 18 | const { data } = normalizeEvent(event); 19 | 20 | const params = { 21 | TableName: table, 22 | Item: { 23 | ...data, 24 | created_at: new Date().toISOString(), 25 | }, 26 | }; 27 | 28 | await dynamo.put(params).promise(); 29 | 30 | console.log({ 31 | message: 'Record has been created', 32 | data: JSON.stringify(params), 33 | }); 34 | 35 | return response(201, `Record ${data.id} has been created`); 36 | } catch (err) { 37 | console.error(err); 38 | return response(500, 'Somenthing went wrong'); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/todos/post.js: -------------------------------------------------------------------------------- 1 | const DynamoDB = require('aws-sdk/clients/dynamodb'); 2 | const dynamo = new DynamoDB.DocumentClient(); 3 | const middy = require('@middy/core'); 4 | 5 | const response = require('/opt/nodejs/response'); 6 | const debugMiddleware = require('/opt/nodejs/debugger'); 7 | const ssmMiddleware = require('/opt/nodejs/ssm'); 8 | const errorHandlerMiddleware = require('/opt/nodejs/error-handler'); 9 | const eventNormalizerMiddleware = require('/opt/nodejs/api-event-normalizer'); 10 | 11 | const baseHandler = async event => { 12 | const { 13 | data 14 | } = event.normalized; 15 | 16 | const params = { 17 | TableName: event.table_name, 18 | Item: { 19 | ...data, 20 | created_at: new Date().toISOString(), 21 | }, 22 | }; 23 | 24 | await dynamo.put(params).promise(); 25 | 26 | console.log({ 27 | message: 'Record has been created', 28 | data: JSON.stringify(params), 29 | }); 30 | 31 | return response(201, `Record ${data.id} has been created`); 32 | }; 33 | 34 | const handler = middy(baseHandler) 35 | .use(debugMiddleware({ debug: true })) 36 | .use(eventNormalizerMiddleware()) 37 | .use(ssmMiddleware({ parameterName: process.env.TABLE })) 38 | .use(errorHandlerMiddleware()); 39 | 40 | module.exports = { handler }; 41 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "lambda_assume_role" { 2 | statement { 3 | actions = ["sts:AssumeRole"] 4 | 5 | principals { 6 | type = "Service" 7 | identifiers = ["lambda.amazonaws.com"] 8 | } 9 | } 10 | } 11 | 12 | resource "aws_iam_role" "cat_api_lambda" { 13 | name = "cat-api-lambda-role" 14 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 15 | } 16 | 17 | data "aws_iam_policy_document" "create_logs_cloudwatch" { 18 | statement { 19 | sid = "AllowCreatingLogGroups" 20 | effect = "Allow" 21 | resources = ["arn:aws:logs:*:*:*"] 22 | actions = ["logs:CreateLogGroup"] 23 | } 24 | 25 | statement { 26 | sid = "AllowWritingLogs" 27 | effect = "Allow" 28 | resources = ["arn:aws:logs:*:*:log-group:/aws/lambda/*:*"] 29 | 30 | actions = [ 31 | "logs:CreateLogStream", 32 | "logs:PutLogEvents", 33 | ] 34 | } 35 | } 36 | 37 | resource "aws_iam_policy" "create_logs_cloudwatch" { 38 | name = "create-cw-logs-policy" 39 | policy = data.aws_iam_policy_document.create_logs_cloudwatch.json 40 | } 41 | 42 | resource "aws_iam_role_policy_attachment" "cat_api_cloudwatch" { 43 | policy_arn = aws_iam_policy.create_logs_cloudwatch.arn 44 | role = aws_iam_role.cat_api_lambda.name 45 | } 46 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/blog/blog.rtl.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-list-comma-newline-after */ 2 | 3 | .blog-header { 4 | border-bottom: 1px solid #e5e5e5; 5 | } 6 | 7 | .blog-header-logo { 8 | font-family: Amiri, Georgia, "Times New Roman", serif; 9 | font-size: 2.25rem; 10 | } 11 | 12 | .blog-header-logo:hover { 13 | text-decoration: none; 14 | } 15 | 16 | h1, h2, h3, h4, h5, h6 { 17 | font-family: Amiri, Georgia, "Times New Roman", serif; 18 | } 19 | 20 | .display-4 { 21 | font-size: 2.5rem; 22 | } 23 | @media (min-width: 768px) { 24 | .display-4 { 25 | font-size: 3rem; 26 | } 27 | } 28 | 29 | .flex-auto { 30 | flex: 0 0 auto; 31 | } 32 | 33 | .h-250 { height: 250px; } 34 | @media (min-width: 768px) { 35 | .h-md-250 { height: 250px; } 36 | } 37 | 38 | /* Pagination */ 39 | .blog-pagination { 40 | margin-bottom: 4rem; 41 | } 42 | 43 | /* 44 | * Blog posts 45 | */ 46 | .blog-post { 47 | margin-bottom: 4rem; 48 | } 49 | .blog-post-title { 50 | font-size: 2.5rem; 51 | } 52 | .blog-post-meta { 53 | margin-bottom: 1.25rem; 54 | color: #727272; 55 | } 56 | 57 | /* 58 | * Footer 59 | */ 60 | .blog-footer { 61 | padding: 2.5rem 0; 62 | color: #727272; 63 | text-align: center; 64 | background-color: #f9f9f9; 65 | border-top: .05rem solid #e5e5e5; 66 | } 67 | .blog-footer p:last-child { 68 | margin-bottom: 0; 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/0003_tf_apply_dev.yml: -------------------------------------------------------------------------------- 1 | name: Apply terraform resources on folder 0003-terraform-gha with dev config 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | paths: 9 | - '0003-terraform-gha/**' 10 | 11 | env: 12 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 13 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 14 | 15 | jobs: 16 | apply-dev: 17 | name: 'terraform apply dev' 18 | runs-on: ubuntu-latest 19 | env: 20 | TF_WORKING_DIR: '0003-terraform-gha' 21 | 22 | defaults: 23 | run: 24 | shell: bash 25 | working-directory: ${{ env.TF_WORKING_DIR }} 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v2 30 | 31 | - name: Setup Terraform 32 | uses: hashicorp/setup-terraform@v1 33 | with: 34 | terraform_version: 0.15.5 35 | 36 | - name: Terraform fmt 37 | id: fmt 38 | run: terraform fmt -check 39 | 40 | - name: Terraform Init 41 | id: init 42 | run: terraform init -backend=true -backend-config="config/dev/backend.hcl" 43 | 44 | - name: Terraform Validate 45 | id: validate 46 | run: terraform validate -no-color 47 | 48 | - name: Terraform Apply 49 | run: terraform apply -auto-approve -var-file="config/dev/terraform.tfvars" 50 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/blog/blog.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable selector-list-comma-newline-after */ 2 | 3 | .blog-header { 4 | border-bottom: 1px solid #e5e5e5; 5 | } 6 | 7 | .blog-header-logo { 8 | font-family: "Playfair Display", Georgia, "Times New Roman", serif/*rtl:Amiri, Georgia, "Times New Roman", serif*/; 9 | font-size: 2.25rem; 10 | } 11 | 12 | .blog-header-logo:hover { 13 | text-decoration: none; 14 | } 15 | 16 | h1, h2, h3, h4, h5, h6 { 17 | font-family: "Playfair Display", Georgia, "Times New Roman", serif/*rtl:Amiri, Georgia, "Times New Roman", serif*/; 18 | } 19 | 20 | .display-4 { 21 | font-size: 2.5rem; 22 | } 23 | @media (min-width: 768px) { 24 | .display-4 { 25 | font-size: 3rem; 26 | } 27 | } 28 | 29 | .flex-auto { 30 | flex: 0 0 auto; 31 | } 32 | 33 | .h-250 { height: 250px; } 34 | @media (min-width: 768px) { 35 | .h-md-250 { height: 250px; } 36 | } 37 | 38 | /* Pagination */ 39 | .blog-pagination { 40 | margin-bottom: 4rem; 41 | } 42 | 43 | /* 44 | * Blog posts 45 | */ 46 | .blog-post { 47 | margin-bottom: 4rem; 48 | } 49 | .blog-post-title { 50 | font-size: 2.5rem; 51 | } 52 | .blog-post-meta { 53 | margin-bottom: 1.25rem; 54 | color: #727272; 55 | } 56 | 57 | /* 58 | * Footer 59 | */ 60 | .blog-footer { 61 | padding: 2.5rem 0; 62 | color: #727272; 63 | text-align: center; 64 | background-color: #f9f9f9; 65 | border-top: .05rem solid #e5e5e5; 66 | } 67 | .blog-footer p:last-child { 68 | margin-bottom: 0; 69 | } 70 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/get.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | 4 | const normalizeEvent = require('./normalizer'); 5 | const response = require('./response'); 6 | 7 | exports.handler = async event => { 8 | if (process.env.DEBUG) { 9 | console.log({ 10 | message: 'Received event', 11 | data: JSON.stringify(event), 12 | }); 13 | } 14 | 15 | const table = event.table || process.env.TABLE; 16 | if (!table) { 17 | throw new Error('No table name defined.'); 18 | } 19 | 20 | const { pathParameters } = normalizeEvent(event); 21 | 22 | const params = { 23 | TableName: table, 24 | }; 25 | 26 | try { 27 | let data = {}; 28 | if (pathParameters && pathParameters['todoId']) { 29 | data = await dynamo 30 | .get({ 31 | ...params, 32 | Key: { 33 | id: parseInt(pathParameters['todoId'], 10), 34 | }, 35 | }) 36 | .promise(); 37 | } else { 38 | data = await dynamo.scan(params).promise(); 39 | } 40 | 41 | console.log({ 42 | message: 'Records found', 43 | data: JSON.stringify(data), 44 | }); 45 | 46 | return response(200, data); 47 | } catch (err) { 48 | console.error(err); 49 | return response(500, 'Somenthing went wrong'); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /0014-terraform-import/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.20.1" 6 | constraints = "~> 5.19" 7 | hashes = [ 8 | "h1:8fm4qyHhF6vQ6m4MkgOavcxN7DACfnXsvO+ASwgg4as=", 9 | "zh:0aa027e8b4aaa6e1263a7a1908f203021137f014c8f915c923288395648bf0d9", 10 | "zh:1ea0b90799119e5fc6ea56d640e287c3ec61bb1ac7ec88cf5a54b5e215634cda", 11 | "zh:26c55bd353dd3a7e80473bcc854ca7801abc79227566164ea0009dc3d5817029", 12 | "zh:32dfd9a6dbe6975cdfbf4ace73fa849a1af12fd18a77f06a8c3a1cf5b59d6b50", 13 | "zh:339ed356f9ff7ecc4846074013db5e9b9b2d7c568b0276f0e6e2f20b3521e598", 14 | "zh:39ec81b2dc48e616a411b22bf5539e2a7e2a4dfd68c78c6ff2e2bf5c16e4b787", 15 | "zh:656d57b778485016055eae1cdfe96ae90181ac32bd37ea479ac186b5cc7de29f", 16 | "zh:6f47b12891330955eb5bcaca259e45c47afb2cfb7daec34f89d816bd64d96bf6", 17 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 18 | "zh:a83cf7937cf4435ebeb535a57b43180e211ece4c9b89c6dbfa159c4c19b6cd5b", 19 | "zh:b86278d5a31b6ae9ee354ab2722ee06931cd9db569883ab46b6cd80c7557b152", 20 | "zh:bc2150dfb653141b5a3b32b22c95541321e555b58cb7bac2763efa196f1f4e39", 21 | "zh:c9e99bdd066e48492f7fbb116450726e37835ca6dcf428c2e08a82832bb9769b", 22 | "zh:f46459a130c716b1ec9f146ebf4045947c1532f75fb01b2088b8a1d8df98436c", 23 | "zh:fbd78c63eb69982573631912bcb9eeed63eee32d3c7087a076af8f3d9e8c60cd", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /0004-serverless-pattern-rest-api/put.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | 4 | const normalizeEvent = require('./normalizer'); 5 | const response = require('./response'); 6 | 7 | exports.handler = async event => { 8 | if (process.env.DEBUG) { 9 | console.log({ 10 | message: 'Received event', 11 | data: JSON.stringify(event), 12 | }); 13 | } 14 | 15 | const table = event.table || process.env.TABLE; 16 | if (!table) { 17 | throw new Error('No table name defined.'); 18 | } 19 | 20 | const { data } = normalizeEvent(event); 21 | 22 | const params = { 23 | TableName: table, 24 | Key: { 25 | id: parseInt(data.id, 10), 26 | }, 27 | UpdateExpression: 'set #a = :x, #b = :d', 28 | ExpressionAttributeNames: { 29 | '#a': 'done', 30 | '#b': 'updated_at', 31 | }, 32 | ExpressionAttributeValues: { 33 | ':x': data.done, 34 | ':d': new Date().toISOString(), 35 | }, 36 | }; 37 | 38 | try { 39 | await dynamo.update(params).promise(); 40 | 41 | console.log({ 42 | message: 'Record has been update', 43 | data: JSON.stringify(params), 44 | }); 45 | 46 | return response(200, `Record ${data.id} has been updated`); 47 | } catch (err) { 48 | console.error(err); 49 | return response(500, 'Somenthing went wrong'); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "lambda_assume_role" { 2 | statement { 3 | actions = ["sts:AssumeRole"] 4 | 5 | principals { 6 | type = "Service" 7 | identifiers = ["lambda.amazonaws.com"] 8 | } 9 | } 10 | } 11 | 12 | resource "aws_iam_role" "s3_lambda" { 13 | name = "${local.component_name}-lambda-role" 14 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 15 | } 16 | 17 | data "aws_iam_policy_document" "s3_lambda" { 18 | statement { 19 | effect = "Allow" 20 | resources = ["arn:aws:logs:*:*"] 21 | actions = ["logs:CreateLogGroup"] 22 | } 23 | statement { 24 | sid = "AllowWritingLogs" 25 | effect = "Allow" 26 | resources = ["arn:aws:logs:*:*:log-group:/aws/lambda/*:*"] 27 | 28 | actions = [ 29 | "logs:CreateLogStream", 30 | "logs:PutLogEvents", 31 | ] 32 | } 33 | 34 | statement { 35 | effect = "Allow" 36 | resources = ["arn:aws:s3:::${aws_s3_bucket.lambda.id}/*"] 37 | actions = ["s3:GetObject"] 38 | } 39 | 40 | statement { 41 | effect = "Allow" 42 | resources = ["*"] 43 | actions = ["rekognition:DetectFaces"] 44 | } 45 | } 46 | 47 | resource "aws_iam_policy" "s3_lambda" { 48 | name = "${local.component_name}-policy" 49 | policy = data.aws_iam_policy_document.s3_lambda.json 50 | } 51 | 52 | resource "aws_iam_role_policy_attachment" "s3_trigger" { 53 | policy_arn = aws_iam_policy.s3_lambda.arn 54 | role = aws_iam_role.s3_lambda.name 55 | } 56 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/todos/get.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | const ssm = new AWS.SSM(); 4 | 5 | const normalizeEvent = require('/opt/nodejs/normalizer'); 6 | const response = require('/opt/nodejs/response'); 7 | 8 | exports.handler = async event => { 9 | if (process.env.DEBUG) { 10 | console.log({ 11 | message: 'Received event', 12 | data: JSON.stringify(event), 13 | }); 14 | } 15 | 16 | try { 17 | const { Parameter: { Value: table } } = await ssm.getParameter({ Name: process.env.TABLE }).promise(); 18 | const { pathParameters } = normalizeEvent(event); 19 | 20 | const params = { 21 | TableName: table, 22 | }; 23 | let data = {}; 24 | if (pathParameters && pathParameters['todoId']) { 25 | data = await dynamo 26 | .get({ 27 | ...params, 28 | Key: { 29 | id: parseInt(pathParameters['todoId'], 10), 30 | }, 31 | }) 32 | .promise(); 33 | } else { 34 | data = await dynamo.scan(params).promise(); 35 | } 36 | 37 | console.log({ 38 | message: 'Records found', 39 | data: JSON.stringify(data), 40 | }); 41 | 42 | return response(200, data); 43 | } catch (err) { 44 | console.error(err); 45 | return response(500, 'Somenthing went wrong'); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/todos/put.js: -------------------------------------------------------------------------------- 1 | const DynamoDB = require('aws-sdk/clients/dynamodb'); 2 | const dynamo = new DynamoDB.DocumentClient(); 3 | const middy = require('@middy/core'); 4 | 5 | const response = require('/opt/nodejs/response'); 6 | const debugMiddleware = require('/opt/nodejs/debugger'); 7 | const ssmMiddleware = require('/opt/nodejs/ssm'); 8 | const errorHandlerMiddleware = require('/opt/nodejs/error-handler'); 9 | const eventNormalizerMiddleware = require('/opt/nodejs/api-event-normalizer'); 10 | 11 | const baseHandler = async event => { 12 | const { data } = event.normalized; 13 | const params = { 14 | TableName: event.table_name, 15 | Key: { 16 | id: parseInt(data.id, 10), 17 | }, 18 | UpdateExpression: 'set #a = :x, #b = :d', 19 | ExpressionAttributeNames: { 20 | '#a': 'done', 21 | '#b': 'updated_at', 22 | }, 23 | ExpressionAttributeValues: { 24 | ':x': data.done, 25 | ':d': new Date().toISOString(), 26 | }, 27 | }; 28 | 29 | await dynamo.update(params).promise(); 30 | 31 | console.log({ 32 | message: 'Record has been update', 33 | data: JSON.stringify(params), 34 | }); 35 | 36 | return response(200, `Record ${data.id} has been updated`); 37 | }; 38 | 39 | const handler = middy(baseHandler) 40 | .use(debugMiddleware({ debug: true })) 41 | .use(eventNormalizerMiddleware()) 42 | .use(ssmMiddleware({ parameterName: process.env.TABLE })) 43 | .use(errorHandlerMiddleware()); 44 | 45 | module.exports = { handler }; 46 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/lambdas/todos/put.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | const dynamo = new AWS.DynamoDB.DocumentClient(); 3 | const ssm = new AWS.SSM(); 4 | 5 | const normalizeEvent = require('/opt/nodejs/normalizer'); 6 | const response = require('/opt/nodejs/response'); 7 | 8 | exports.handler = async event => { 9 | if (process.env.DEBUG) { 10 | console.log({ 11 | message: 'Received event', 12 | data: JSON.stringify(event), 13 | }); 14 | } 15 | 16 | try { 17 | const { Parameter: { Value: table } } = await ssm.getParameter({ Name: process.env.TABLE }).promise(); 18 | const { data } = normalizeEvent(event); 19 | const params = { 20 | TableName: table, 21 | Key: { 22 | id: parseInt(data.id, 10), 23 | }, 24 | UpdateExpression: 'set #a = :x, #b = :d', 25 | ExpressionAttributeNames: { 26 | '#a': 'done', 27 | '#b': 'updated_at', 28 | }, 29 | ExpressionAttributeValues: { 30 | ':x': data.done, 31 | ':d': new Date().toISOString(), 32 | }, 33 | }; 34 | 35 | await dynamo.update(params).promise(); 36 | 37 | console.log({ 38 | message: 'Record has been update', 39 | data: JSON.stringify(params), 40 | }); 41 | 42 | return response(200, `Record ${data.id} has been updated`); 43 | } catch (err) { 44 | console.error(err); 45 | return response(500, 'Somenthing went wrong'); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/lambda.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "todos" { 2 | for_each = local.lambdas 3 | 4 | output_path = "files/${each.key}-todo-artefact.zip" 5 | type = "zip" 6 | source_file = "${local.lambdas_path}/todos/${each.key}.js" 7 | } 8 | 9 | resource "aws_lambda_function" "todos" { 10 | for_each = local.lambdas 11 | 12 | function_name = "dynamodb-${each.key}-item" 13 | handler = "${each.key}.handler" 14 | description = each.value["description"] 15 | role = aws_iam_role.rest_api_role.arn 16 | runtime = "nodejs14.x" 17 | 18 | filename = data.archive_file.todos[each.key].output_path 19 | source_code_hash = data.archive_file.todos[each.key].output_base64sha256 20 | 21 | timeout = each.value["timeout"] 22 | memory_size = each.value["memory"] 23 | 24 | layers = [ 25 | module.layers["utils"].arn, 26 | module.layers["middy"].arn, 27 | module.layers["middlewares"].arn, 28 | ] 29 | 30 | tracing_config { 31 | mode = "Active" 32 | } 33 | 34 | environment { 35 | variables = { 36 | TABLE = aws_ssm_parameter.dynamodb_table.name 37 | DEBUG = var.env == "dev" 38 | AWS_NODEJS_CONNECTION_REUSE_ENABLED = 1 39 | } 40 | } 41 | } 42 | 43 | resource "aws_lambda_permission" "api" { 44 | for_each = local.lambdas 45 | 46 | action = "lambda:InvokeFunction" 47 | function_name = aws_lambda_function.todos[each.key].arn 48 | principal = "apigateway.amazonaws.com" 49 | source_arn = "arn:aws:execute-api:${var.aws_region}:${var.aws_account_id}:*/*" 50 | } 51 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/todos/get.js: -------------------------------------------------------------------------------- 1 | const DynamoDB = require('aws-sdk/clients/dynamodb'); 2 | const dynamo = new DynamoDB.DocumentClient(); 3 | const middy = require('@middy/core'); 4 | const httpJsonBodyParser = require('@middy/http-json-body-parser'); 5 | 6 | const response = require('/opt/nodejs/response'); 7 | const debugMiddleware = require('/opt/nodejs/debugger'); 8 | const ssmMiddleware = require('/opt/nodejs/ssm'); 9 | const errorHandlerMiddleware = require('/opt/nodejs/error-handler'); 10 | const eventNormalizerMiddleware = require('/opt/nodejs/api-event-normalizer'); 11 | 12 | const baseHandler = async event => { 13 | let data; 14 | const { pathParameters } = event.normalized; 15 | const params = { 16 | TableName: event.table_name, 17 | }; 18 | 19 | if (pathParameters && pathParameters['todoId']) { 20 | data = await dynamo 21 | .get({ 22 | ...params, 23 | Key: { 24 | id: parseInt(pathParameters['todoId'], 10), 25 | }, 26 | }) 27 | .promise(); 28 | } else { 29 | data = await dynamo.scan(params).promise(); 30 | } 31 | 32 | console.log({ 33 | message: 'Records found', 34 | data: JSON.stringify(data), 35 | }); 36 | 37 | return response(200, data); 38 | }; 39 | 40 | const handler = middy(baseHandler) 41 | .use(debugMiddleware({ debug: true })) 42 | .use(httpJsonBodyParser()) 43 | .use(eventNormalizerMiddleware()) 44 | .use(ssmMiddleware({ parameterName: process.env.TABLE })) 45 | .use(errorHandlerMiddleware()); 46 | 47 | module.exports = { handler }; 48 | -------------------------------------------------------------------------------- /0008-terraform-new-s3/s3.tf: -------------------------------------------------------------------------------- 1 | # https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket#example-usage 2 | resource "aws_s3_bucket" "website" { 3 | bucket = random_pet.bucket_name.id 4 | } 5 | 6 | resource "aws_s3_bucket_acl" "website" { 7 | bucket = aws_s3_bucket.website.id 8 | acl = "public-read" 9 | } 10 | 11 | resource "aws_s3_bucket_versioning" "website" { 12 | bucket = aws_s3_bucket.website.id 13 | versioning_configuration { 14 | status = "Enabled" 15 | } 16 | } 17 | 18 | resource "aws_s3_bucket_policy" "website" { 19 | bucket = aws_s3_bucket.website.id 20 | policy = data.aws_iam_policy_document.allow_public_access.json 21 | } 22 | 23 | data "aws_iam_policy_document" "allow_public_access" { 24 | statement { 25 | principals { 26 | type = "*" 27 | identifiers = ["*"] 28 | } 29 | 30 | actions = [ 31 | "s3:GetObject" 32 | ] 33 | 34 | resources = [ 35 | "${aws_s3_bucket.website.arn}/*", 36 | ] 37 | } 38 | } 39 | 40 | resource "aws_s3_bucket_website_configuration" "website" { 41 | bucket = aws_s3_bucket.website.bucket 42 | 43 | index_document { 44 | suffix = "index.html" 45 | } 46 | 47 | error_document { 48 | key = "error.html" 49 | } 50 | } 51 | 52 | resource "aws_s3_object" "index_page" { 53 | bucket = aws_s3_bucket.website.id 54 | key = "index.html" 55 | content_type = "text/html; charset=UTF-8" 56 | source = "website/index.html" 57 | etag = filemd5("website/index.html") 58 | } 59 | 60 | resource "aws_s3_object" "error_page" { 61 | bucket = aws_s3_bucket.website.id 62 | key = "error.html" 63 | content_type = "text/html; charset=UTF-8" 64 | source = "website/error.html" 65 | etag = filemd5("website/error.html") 66 | } 67 | -------------------------------------------------------------------------------- /0013-terraform-test/tests/website.tftest.hcl: -------------------------------------------------------------------------------- 1 | # variables 0-1 2 | # provider 0-n 3 | # run 1-n 4 | 5 | variables { 6 | aws_region = "eu-west-3" # Paris 7 | bucket_name = "este-e-um-nome-de-balde-aleatorio-1237890" 8 | } 9 | 10 | provider "aws" { 11 | region = var.aws_region 12 | } 13 | 14 | # unit test 15 | run "validate_inputs" { 16 | # tfstate in-memory global - para todos os blocos run sem module 17 | 18 | command = plan 19 | 20 | variables { 21 | aws_region = "europe-west-3" 22 | bucket_name = "Nome Inválido" 23 | } 24 | 25 | expect_failures = [ 26 | var.aws_region, 27 | var.bucket_name 28 | ] 29 | } 30 | 31 | run "setup" { 32 | # tfstate in-memory - module setup 33 | 34 | module { 35 | source = "./tests/setup" 36 | } 37 | } 38 | 39 | # integration test 40 | run "create_buckets" { 41 | # tfstate in-memory global 42 | 43 | variables { 44 | bucket_name = run.setup.bucket_prefix 45 | } 46 | 47 | assert { 48 | condition = aws_s3_bucket.s3_bucket.bucket == var.bucket_name 49 | error_message = "Invalid bucket name" 50 | } 51 | 52 | assert { 53 | condition = aws_s3_object.index.etag == filemd5("./www/index.html") 54 | error_message = "Invalid eTag for index.html" 55 | } 56 | 57 | assert { 58 | condition = aws_s3_object.error.etag == filemd5("./www/error.html") 59 | error_message = "Invalid eTag for error.html" 60 | } 61 | } 62 | 63 | run "website_is_running" { 64 | command = plan 65 | 66 | module { 67 | source = "./tests/final" 68 | } 69 | 70 | variables { 71 | endpoint = run.create_buckets.website_endpoint 72 | } 73 | 74 | assert { 75 | condition = data.http.index.status_code == 200 76 | error_message = "Website responded with HTTP status ${data.http.index.status_code}" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /0012-dynamo-s3-athena/lambdas/seed-database/index.js: -------------------------------------------------------------------------------- 1 | const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb"); 2 | const { marshall } = require("@aws-sdk/util-dynamodb"); 3 | const { 4 | randUuid, 5 | randEmail, 6 | randFullName, 7 | randUserName, 8 | randBoolean, 9 | randParagraph 10 | } = require("@ngneat/falso"); 11 | 12 | const client = new DynamoDBClient({ 13 | region: process.env.AWS_REGION || "eu-central-1" 14 | }); 15 | 16 | exports.handler = async (event) => { 17 | console.log(JSON.stringify(event)); 18 | 19 | try { 20 | for (let i = 0; i < event.createUsersNumber; i++) { 21 | const userId = randUuid(); 22 | 23 | await client.send( 24 | new PutItemCommand({ 25 | TableName: process.env.DYNAMODB_TABLE_NAME, 26 | Item: marshall({ 27 | pk: userId, 28 | sk: "USERDATA", 29 | fullname: randFullName(), 30 | email: randEmail(), 31 | username: randUserName(), 32 | active: randBoolean(), 33 | createdAt: Date.now(), 34 | }) 35 | }) 36 | ); 37 | 38 | for (let j = 0; j < event.createCommentsNumber; j++) { 39 | await client.send( 40 | new PutItemCommand({ 41 | TableName: process.env.DYNAMODB_TABLE_NAME, 42 | Item: marshall({ 43 | pk: userId, 44 | sk: `USERCOMMENT_${randUuid()}`, 45 | createdAt: Date.now(), 46 | comment: randParagraph() 47 | }) 48 | }) 49 | ); 50 | } 51 | } 52 | 53 | const message = `${event.createUsersNumber} users and ${event.createCommentsNumber} users comments for each user have been inserted successfully`; 54 | 55 | console.log(message); 56 | 57 | return message; 58 | } catch (e) { 59 | console.log(e); 60 | throw new Error(e.message); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/lambda.tf: -------------------------------------------------------------------------------- 1 | data "archive_file" "utils_layer" { 2 | output_path = "files/utils-layer.zip" 3 | type = "zip" 4 | source_dir = "${local.layers_path}/utils" 5 | } 6 | 7 | resource "aws_lambda_layer_version" "utils" { 8 | layer_name = "utils-layer" 9 | description = "Utils for response and event normalization" 10 | filename = data.archive_file.utils_layer.output_path 11 | source_code_hash = data.archive_file.utils_layer.output_base64sha256 12 | compatible_runtimes = ["nodejs14.x"] 13 | } 14 | 15 | data "archive_file" "todos" { 16 | for_each = local.lambdas 17 | 18 | output_path = "files/${each.key}-todo-artefact.zip" 19 | type = "zip" 20 | source_file = "${local.lambdas_path}/todos/${each.key}.js" 21 | } 22 | 23 | resource "aws_lambda_function" "todos" { 24 | for_each = local.lambdas 25 | 26 | function_name = "dynamodb-${each.key}-item" 27 | handler = "${each.key}.handler" 28 | description = each.value["description"] 29 | role = aws_iam_role.rest_api_role.arn 30 | runtime = "nodejs14.x" 31 | 32 | filename = data.archive_file.todos[each.key].output_path 33 | source_code_hash = data.archive_file.todos[each.key].output_base64sha256 34 | 35 | timeout = each.value["timeout"] 36 | memory_size = each.value["memory"] 37 | 38 | layers = [aws_lambda_layer_version.utils.arn] 39 | 40 | tracing_config { 41 | mode = "Active" 42 | } 43 | 44 | environment { 45 | variables = { 46 | TABLE = aws_ssm_parameter.dynamodb_table.name 47 | DEBUG = var.env == "dev" 48 | } 49 | } 50 | } 51 | 52 | resource "aws_lambda_permission" "api" { 53 | for_each = local.lambdas 54 | 55 | action = "lambda:InvokeFunction" 56 | function_name = aws_lambda_function.todos[each.key].arn 57 | principal = "apigateway.amazonaws.com" 58 | source_arn = "arn:aws:execute-api:${var.aws_region}:${var.aws_account_id}:*/*" 59 | } 60 | -------------------------------------------------------------------------------- /0013-terraform-test/s3.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "s3_bucket" { 2 | bucket = var.bucket_name 3 | } 4 | 5 | resource "aws_s3_bucket_website_configuration" "s3_bucket" { 6 | bucket = aws_s3_bucket.s3_bucket.id 7 | 8 | index_document { 9 | suffix = "index.html" 10 | } 11 | 12 | error_document { 13 | key = "error.html" 14 | } 15 | } 16 | 17 | resource "aws_s3_bucket_public_access_block" "s3_bucket" { 18 | bucket = aws_s3_bucket.s3_bucket.id 19 | 20 | block_public_acls = false 21 | block_public_policy = false 22 | ignore_public_acls = false 23 | restrict_public_buckets = false 24 | } 25 | 26 | resource "aws_s3_bucket_ownership_controls" "s3_bucket" { 27 | bucket = aws_s3_bucket.s3_bucket.id 28 | 29 | rule { 30 | object_ownership = "BucketOwnerPreferred" 31 | } 32 | } 33 | 34 | resource "aws_s3_bucket_acl" "s3_bucket" { 35 | depends_on = [ 36 | aws_s3_bucket_public_access_block.s3_bucket, 37 | aws_s3_bucket_ownership_controls.s3_bucket, 38 | ] 39 | 40 | bucket = aws_s3_bucket.s3_bucket.id 41 | 42 | acl = "public-read" 43 | } 44 | 45 | resource "aws_s3_bucket_policy" "s3_bucket" { 46 | depends_on = [ 47 | aws_s3_bucket_public_access_block.s3_bucket 48 | ] 49 | 50 | bucket = aws_s3_bucket.s3_bucket.id 51 | 52 | policy = jsonencode({ 53 | Version = "2012-10-17" 54 | Statement = [ 55 | { 56 | Sid = "PublicReadGetObject" 57 | Effect = "Allow" 58 | Principal = "*" 59 | Action = "s3:GetObject" 60 | Resource = [ 61 | aws_s3_bucket.s3_bucket.arn, 62 | "${aws_s3_bucket.s3_bucket.arn}/*", 63 | ] 64 | }, 65 | ] 66 | }) 67 | } 68 | 69 | resource "aws_s3_object" "index" { 70 | bucket = aws_s3_bucket.s3_bucket.id 71 | key = "index.html" 72 | source = "www/index.html" 73 | content_type = "text/html" 74 | } 75 | 76 | resource "aws_s3_object" "error" { 77 | bucket = aws_s3_bucket.s3_bucket.id 78 | key = "error.html" 79 | source = "www/error.html" 80 | content_type = "text/html" 81 | } 82 | -------------------------------------------------------------------------------- /0014-terraform-import/generated.tf: -------------------------------------------------------------------------------- 1 | # __generated__ by Terraform 2 | # Please review these resources and move them into your main configuration files. 3 | 4 | # __generated__ by Terraform from "users" 5 | resource "aws_dynamodb_table" "users" { 6 | billing_mode = "PAY_PER_REQUEST" 7 | deletion_protection_enabled = false 8 | hash_key = "id" 9 | name = "users" 10 | range_key = null 11 | read_capacity = 0 12 | restore_date_time = null 13 | restore_source_name = null 14 | restore_to_latest_time = null 15 | stream_enabled = false 16 | stream_view_type = null 17 | table_class = "STANDARD" 18 | tags = {} 19 | tags_all = { 20 | CreatedAt = "2023-10-11" 21 | Video = "Terraform Import" 22 | } 23 | write_capacity = 0 24 | attribute { 25 | name = "id" 26 | type = "S" 27 | } 28 | point_in_time_recovery { 29 | enabled = false 30 | } 31 | ttl { 32 | attribute_name = "" 33 | enabled = false 34 | } 35 | } 36 | 37 | # __generated__ by Terraform from "bucket-do-cleber-criado-no-console-da-aws" 38 | resource "aws_s3_bucket_versioning" "users" { 39 | bucket = "bucket-do-cleber-criado-no-console-da-aws" 40 | expected_bucket_owner = null 41 | mfa = null 42 | versioning_configuration { 43 | mfa_delete = null 44 | status = "Enabled" 45 | } 46 | } 47 | 48 | # __generated__ by Terraform from "bucket-do-cleber-criado-no-console-da-aws" 49 | resource "aws_s3_bucket_lifecycle_configuration" "users" { 50 | bucket = "bucket-do-cleber-criado-no-console-da-aws" 51 | expected_bucket_owner = null 52 | rule { 53 | id = "move-to-archive" 54 | status = "Enabled" 55 | filter { 56 | object_size_greater_than = null 57 | object_size_less_than = null 58 | prefix = "logs" 59 | } 60 | transition { 61 | date = null 62 | days = 30 63 | storage_class = "STANDARD_IA" 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/archive" { 5 | version = "2.2.0" 6 | hashes = [ 7 | "h1:2K5LQkuWRS2YN1/YoNaHn9MAzjuTX8Gaqy6i8Mbfv8Y=", 8 | "zh:06bd875932288f235c16e2237142b493c2c2b6aba0e82e8c85068332a8d2a29e", 9 | "zh:0c681b481372afcaefddacc7ccdf1d3bb3a0c0d4678a526bc8b02d0c331479bc", 10 | "zh:100fc5b3fc01ea463533d7bbfb01cb7113947a969a4ec12e27f5b2be49884d6c", 11 | "zh:55c0d7ddddbd0a46d57c51fcfa9b91f14eed081a45101dbfc7fd9d2278aa1403", 12 | "zh:73a5dd68379119167934c48afa1101b09abad2deb436cd5c446733e705869d6b", 13 | "zh:841fc4ac6dc3479981330974d44ad2341deada8a5ff9e3b1b4510702dfbdbed9", 14 | "zh:91be62c9b41edb137f7f835491183628d484e9d6efa82fcb75cfa538c92791c5", 15 | "zh:acd5f442bd88d67eb948b18dc2ed421c6c3faee62d3a12200e442bfff0aa7d8b", 16 | "zh:ad5720da5524641ad718a565694821be5f61f68f1c3c5d2cfa24426b8e774bef", 17 | "zh:e63f12ea938520b3f83634fc29da28d92eed5cfbc5cc8ca08281a6a9c36cca65", 18 | "zh:f6542918faa115df46474a36aabb4c3899650bea036b5f8a5e296be6f8f25767", 19 | ] 20 | } 21 | 22 | provider "registry.terraform.io/hashicorp/aws" { 23 | version = "3.57.0" 24 | hashes = [ 25 | "h1:H6JCnoa3swF3rgHL0ys9KNArffU+IEGPvhQ6JnfQY/c=", 26 | "zh:241a4203078ea35f63202b613f0e4b428a842734ded62d9f487cdf7c2a66d639", 27 | "zh:2c1cbf3cd03a2a7ff267be09cedf1698738c372b1411ca74cfcb3bf4b0846f27", 28 | "zh:318ad2331f60e03d284f90f728486b9df7ac9570af641c43b56216357e624b52", 29 | "zh:43ff96b34b4829a34693281492786b9ca6dd06870dd45b0ae82ea352c33353d7", 30 | "zh:6c36b874622603793fc637272742d84ecbf68dfe4c8d8148bb6e9b733cd0e216", 31 | "zh:7a1aaac01c82d06f9ebc997ae2094a7d96e7a467aaaeaa1cda64ee952f3144d8", 32 | "zh:9b917b03b8771f87a021fe7aa9fd00ae06cc455a1eaa1fb748930182617b2772", 33 | "zh:bd90550e6d9311092170f4935e42e91e6d8bed5241e41eca39fa4aeca28d9c6f", 34 | "zh:be5076ea705c174581fd616b118e0c17d15bd8ab0da1b3eee4f3fb6b11e78f2c", 35 | "zh:f4f0d13414c932ecf65ba92daab6e755c244dcb77b4be59a3ac18ba2f56cdc00", 36 | "zh:fa3575a23fd20ce00658977374491022c4c0c36a00260ebeebb0c3f3af4824aa", 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/modules/s3-hosting-static-website/main.tf: -------------------------------------------------------------------------------- 1 | resource "aws_s3_bucket" "website" { 2 | bucket = var.bucket_name 3 | } 4 | 5 | resource "aws_s3_bucket_acl" "website" { 6 | bucket = aws_s3_bucket.website.id 7 | acl = "public-read" 8 | } 9 | 10 | resource "aws_s3_bucket_policy" "website" { 11 | bucket = aws_s3_bucket.website.id 12 | policy = jsonencode({ 13 | Version = "2012-10-17" 14 | Statement = [ 15 | { 16 | Sid = "PublicReadGetObject" 17 | Effect = "Allow" 18 | Principal = "*" 19 | Action = "s3:GetObject" 20 | Resource = [ 21 | aws_s3_bucket.website.arn, 22 | "${aws_s3_bucket.website.arn}/*", 23 | ] 24 | }, 25 | ] 26 | }) 27 | } 28 | 29 | resource "aws_s3_bucket_website_configuration" "website" { 30 | bucket = aws_s3_bucket.website.bucket 31 | 32 | index_document { 33 | suffix = var.files.index_document_suffix 34 | } 35 | 36 | error_document { 37 | key = var.files.error_document_key 38 | } 39 | } 40 | 41 | module "website_files" { 42 | source = "hashicorp/dir/template" 43 | version = "1.0.2" 44 | 45 | base_dir = var.files.www_path != null ? var.files.www_path : "${path.module}/www" 46 | } 47 | 48 | resource "aws_s3_object" "website" { 49 | for_each = var.files.terraform_managed ? module.website_files.files : {} 50 | 51 | bucket = aws_s3_bucket.website.id 52 | 53 | key = each.key 54 | source = each.value.source_path 55 | content = each.value.content 56 | content_type = each.value.content_type 57 | etag = each.value.digests.md5 58 | } 59 | 60 | resource "aws_s3_bucket_cors_configuration" "website" { 61 | count = length(var.cors_rules) > 0 ? 1 : 0 62 | 63 | bucket = aws_s3_bucket.website.id 64 | 65 | dynamic "cors_rule" { 66 | for_each = var.cors_rules 67 | 68 | content { 69 | allowed_headers = cors_rule.value["allowed_headers"] 70 | allowed_methods = cors_rule.value["allowed_methods"] 71 | allowed_origins = cors_rule.value["allowed_origins"] 72 | expose_headers = cors_rule.value["expose_headers"] 73 | max_age_seconds = cors_rule.value["max_age_seconds"] 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/archive" { 5 | version = "2.2.0" 6 | hashes = [ 7 | "h1:2K5LQkuWRS2YN1/YoNaHn9MAzjuTX8Gaqy6i8Mbfv8Y=", 8 | "zh:06bd875932288f235c16e2237142b493c2c2b6aba0e82e8c85068332a8d2a29e", 9 | "zh:0c681b481372afcaefddacc7ccdf1d3bb3a0c0d4678a526bc8b02d0c331479bc", 10 | "zh:100fc5b3fc01ea463533d7bbfb01cb7113947a969a4ec12e27f5b2be49884d6c", 11 | "zh:55c0d7ddddbd0a46d57c51fcfa9b91f14eed081a45101dbfc7fd9d2278aa1403", 12 | "zh:73a5dd68379119167934c48afa1101b09abad2deb436cd5c446733e705869d6b", 13 | "zh:841fc4ac6dc3479981330974d44ad2341deada8a5ff9e3b1b4510702dfbdbed9", 14 | "zh:91be62c9b41edb137f7f835491183628d484e9d6efa82fcb75cfa538c92791c5", 15 | "zh:acd5f442bd88d67eb948b18dc2ed421c6c3faee62d3a12200e442bfff0aa7d8b", 16 | "zh:ad5720da5524641ad718a565694821be5f61f68f1c3c5d2cfa24426b8e774bef", 17 | "zh:e63f12ea938520b3f83634fc29da28d92eed5cfbc5cc8ca08281a6a9c36cca65", 18 | "zh:f6542918faa115df46474a36aabb4c3899650bea036b5f8a5e296be6f8f25767", 19 | ] 20 | } 21 | 22 | provider "registry.terraform.io/hashicorp/aws" { 23 | version = "3.57.0" 24 | hashes = [ 25 | "h1:H6JCnoa3swF3rgHL0ys9KNArffU+IEGPvhQ6JnfQY/c=", 26 | "zh:241a4203078ea35f63202b613f0e4b428a842734ded62d9f487cdf7c2a66d639", 27 | "zh:2c1cbf3cd03a2a7ff267be09cedf1698738c372b1411ca74cfcb3bf4b0846f27", 28 | "zh:318ad2331f60e03d284f90f728486b9df7ac9570af641c43b56216357e624b52", 29 | "zh:43ff96b34b4829a34693281492786b9ca6dd06870dd45b0ae82ea352c33353d7", 30 | "zh:6c36b874622603793fc637272742d84ecbf68dfe4c8d8148bb6e9b733cd0e216", 31 | "zh:7a1aaac01c82d06f9ebc997ae2094a7d96e7a467aaaeaa1cda64ee952f3144d8", 32 | "zh:9b917b03b8771f87a021fe7aa9fd00ae06cc455a1eaa1fb748930182617b2772", 33 | "zh:bd90550e6d9311092170f4935e42e91e6d8bed5241e41eca39fa4aeca28d9c6f", 34 | "zh:be5076ea705c174581fd616b118e0c17d15bd8ab0da1b3eee4f3fb6b11e78f2c", 35 | "zh:f4f0d13414c932ecf65ba92daab6e755c244dcb77b4be59a3ac18ba2f56cdc00", 36 | "zh:fa3575a23fd20ce00658977374491022c4c0c36a00260ebeebb0c3f3af4824aa", 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /0006-middy-js/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "lambda_assume_role" { 2 | statement { 3 | actions = ["sts:AssumeRole"] 4 | 5 | principals { 6 | type = "Service" 7 | identifiers = ["lambda.amazonaws.com"] 8 | } 9 | } 10 | } 11 | 12 | data "aws_iam_policy_document" "lambda" { 13 | statement { 14 | sid = "AllowCreatingLogGroups" 15 | effect = "Allow" 16 | resources = ["arn:aws:logs:*:*:*"] 17 | actions = ["logs:CreateLogGroup"] 18 | } 19 | 20 | statement { 21 | sid = "AllowWritingLogs" 22 | effect = "Allow" 23 | resources = ["arn:aws:logs:*:*:log-group:/aws/lambda/*:*"] 24 | 25 | actions = [ 26 | "logs:CreateLogStream", 27 | "logs:PutLogEvents", 28 | ] 29 | } 30 | 31 | statement { 32 | effect = "Allow" 33 | resources = ["*"] 34 | actions = [ 35 | "dynamodb:ListTables", 36 | "ssm:DescribeParameters", 37 | "xray:PutTraceSegments" 38 | ] 39 | } 40 | 41 | statement { 42 | effect = "Allow" 43 | resources = ["arn:aws:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.this.name}"] 44 | actions = [ 45 | "dynamodb:PutItem", 46 | "dynamodb:DescribeTable", 47 | "dynamodb:DeleteItem", 48 | "dynamodb:GetItem", 49 | "dynamodb:Scan", 50 | "dynamodb:Query", 51 | "dynamodb:UpdateItem" 52 | ] 53 | } 54 | 55 | statement { 56 | effect = "Allow" 57 | resources = ["arn:aws:ssm:${var.aws_region}:${var.aws_account_id}:parameter/${aws_ssm_parameter.dynamodb_table.name}"] 58 | actions = [ 59 | "ssm:GetParameters", 60 | "ssm:GetParameter", 61 | ] 62 | } 63 | } 64 | 65 | resource "aws_iam_role" "rest_api_role" { 66 | name = "${local.namespaced_service_name}-lambda-role" 67 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 68 | } 69 | 70 | resource "aws_iam_policy" "lambda" { 71 | name = "${local.namespaced_service_name}-policy" 72 | policy = data.aws_iam_policy_document.lambda.json 73 | } 74 | 75 | resource "aws_iam_role_policy_attachment" "cloudwatch" { 76 | policy_arn = aws_iam_policy.lambda.arn 77 | role = aws_iam_role.rest_api_role.name 78 | } 79 | -------------------------------------------------------------------------------- /0005-terraform-serverless-pattern-rest-api/terraform/iam.tf: -------------------------------------------------------------------------------- 1 | data "aws_iam_policy_document" "lambda_assume_role" { 2 | statement { 3 | actions = ["sts:AssumeRole"] 4 | 5 | principals { 6 | type = "Service" 7 | identifiers = ["lambda.amazonaws.com"] 8 | } 9 | } 10 | } 11 | 12 | resource "aws_iam_role" "rest_api_role" { 13 | name = "${local.namespaced_service_name}-lambda-role" 14 | assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json 15 | } 16 | 17 | data "aws_iam_policy_document" "create_logs_cloudwatch" { 18 | statement { 19 | sid = "AllowCreatingLogGroups" 20 | effect = "Allow" 21 | resources = ["arn:aws:logs:*:*:*"] 22 | actions = ["logs:CreateLogGroup"] 23 | } 24 | 25 | statement { 26 | sid = "AllowWritingLogs" 27 | effect = "Allow" 28 | resources = ["arn:aws:logs:*:*:log-group:/aws/lambda/*:*"] 29 | 30 | actions = [ 31 | "logs:CreateLogStream", 32 | "logs:PutLogEvents", 33 | ] 34 | } 35 | 36 | statement { 37 | effect = "Allow" 38 | resources = ["*"] 39 | actions = [ 40 | "dynamodb:ListTables", 41 | "ssm:DescribeParameters", 42 | "xray:PutTraceSegments" 43 | ] 44 | } 45 | 46 | statement { 47 | effect = "Allow" 48 | resources = ["arn:aws:dynamodb:${var.aws_region}:${var.aws_account_id}:table/${aws_dynamodb_table.this.name}"] 49 | actions = [ 50 | "dynamodb:PutItem", 51 | "dynamodb:DescribeTable", 52 | "dynamodb:DeleteItem", 53 | "dynamodb:GetItem", 54 | "dynamodb:Scan", 55 | "dynamodb:Query", 56 | "dynamodb:UpdateItem" 57 | ] 58 | } 59 | 60 | statement { 61 | effect = "Allow" 62 | resources = ["arn:aws:ssm:${var.aws_region}:${var.aws_account_id}:parameter/${aws_ssm_parameter.dynamodb_table.name}"] 63 | actions = [ 64 | "ssm:GetParameters", 65 | "ssm:GetParameter", 66 | ] 67 | } 68 | } 69 | 70 | resource "aws_iam_policy" "create_logs_cloudwatch" { 71 | name = "${local.namespaced_service_name}-policy" 72 | policy = data.aws_iam_policy_document.create_logs_cloudwatch.json 73 | } 74 | 75 | resource "aws_iam_role_policy_attachment" "cat_api_cloudwatch" { 76 | policy_arn = aws_iam_policy.create_logs_cloudwatch.arn 77 | role = aws_iam_role.rest_api_role.name 78 | } 79 | -------------------------------------------------------------------------------- /0008-terraform-new-s3/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.34.0" 6 | constraints = "~> 4.34.0" 7 | hashes = [ 8 | "h1:JRqeU/5qR61U+z86mC68C5hp0XHZXxmRK9dupTIAhGg=", 9 | "zh:2bdc9b908008c1e874d8ba7e2cfabd856cafb63c52fef51a1fdeef2f5584bffd", 10 | "zh:43c5364e3161be3856e56494cbb8b21d513fc05875f1b40e66e583602154dd0a", 11 | "zh:44e763adae92489f223f65866c1f8b5342e7e85b95daa8d1f483a2afb47f7db3", 12 | "zh:62bfabb3a1a31814cb3fadc356539d8253b95abacfffd90984affb54c9a53a86", 13 | "zh:6495ce67897d2d5648d466c09e8588e837c2878322988738a95c06926044b05d", 14 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 15 | "zh:b1546b2ac61d7cc27a8eba160ae1b6ac581d2c4af824a400d6511e4998da398a", 16 | "zh:c8c362c5527f0533bde581e41cdb2bdf42aea557762f326dbfa122fdf001fb10", 17 | "zh:c8cc28fb41f73ca09f590bace2204ea325f6116be0bbce6abfebd393d028f12c", 18 | "zh:db0601c9bd12ca028d60ac87e85320285ebc64857715f7908dd6a283e5f44d45", 19 | "zh:e64d2193236d05348ba2e8b99650d9274e5af80be39b3ee28bbe442b0684d8a3", 20 | "zh:ff6228f3751e1f0ee7dc086d09e32d39ca6556f0b5267f36aae67881d29ace94", 21 | ] 22 | } 23 | 24 | provider "registry.terraform.io/hashicorp/random" { 25 | version = "3.4.3" 26 | hashes = [ 27 | "h1:tL3katm68lX+4lAncjQA9AXL4GR/VM+RPwqYf4D2X8Q=", 28 | "zh:41c53ba47085d8261590990f8633c8906696fa0a3c4b384ff6a7ecbf84339752", 29 | "zh:59d98081c4475f2ad77d881c4412c5129c56214892f490adf11c7e7a5a47de9b", 30 | "zh:686ad1ee40b812b9e016317e7f34c0d63ef837e084dea4a1f578f64a6314ad53", 31 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 32 | "zh:84103eae7251384c0d995f5a257c72b0096605048f757b749b7b62107a5dccb3", 33 | "zh:8ee974b110adb78c7cd18aae82b2729e5124d8f115d484215fd5199451053de5", 34 | "zh:9dd4561e3c847e45de603f17fa0c01ae14cae8c4b7b4e6423c9ef3904b308dda", 35 | "zh:bb07bb3c2c0296beba0beec629ebc6474c70732387477a65966483b5efabdbc6", 36 | "zh:e891339e96c9e5a888727b45b2e1bb3fcbdfe0fd7c5b4396e4695459b38c8cb1", 37 | "zh:ea4739860c24dfeaac6c100b2a2e357106a89d18751f7693f3c31ecf6a996f8d", 38 | "zh:f0c76ac303fd0ab59146c39bc121c5d7d86f878e9a69294e29444d4c653786f8", 39 | "zh:f143a9a5af42b38fed328a161279906759ff39ac428ebcfe55606e05e1518b93", 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/README.md: -------------------------------------------------------------------------------- 1 | # AWS ECS Fargate Tutorial: Criando e deployando uma aplicação Node.js 2 | 3 | Série de vídeos no Youtube explicando como construir e deployar uma aplicação Node.js usando o AWS ECS Fargate. 4 | 5 | ## Vídeos no Youtube 6 | 1. [Entendendo o que será construído](https://youtu.be/SwUC4sXEcqE) 7 | 2. [Implementando aplicação Node.js](https://youtu.be/UuSM26i39Fk) 8 | 3. [Criando imagem do Docker](https://youtu.be/MM-UTLKQ2Xc) 9 | 4. [Configurando AWS ECR](https://youtu.be/NF8iZp6rqps) 10 | 5. [Configurando VPC, Internet Gateway, Subnets e Route Tables](https://youtu.be/9x_GQanUWJM) 11 | 6. [Configurando o Load Balancer](https://youtu.be/IM6k8gM7co8) 12 | 7. [Finalizando a configuração do cluster do ECS e acessando a aplicação](https://youtu.be/UQnJyLnfbgw) 13 | 8. [Alternativa ao NAT Gateway - Subnet Públicas](https://youtu.be/_eEZhXsUL0g) 14 | 9. [Alternativa ao NAT Gateway - VPC Endpoints](https://youtu.be/68cERUTP2kE) 15 | 10. [Conectando o serviço à um banco de dados no AWS RDS](https://youtu.be/IFovNm2Rm8M) 16 | 11. [Alternativa ao NAT Gateway - EC2 NAT Instances](https://youtu.be/sYqNcH9dD0M) 17 | 12. [Subindo uma nova versão da nossa aplicação no ECR e ECS](https://youtu.be/_eyU6A7bCqA) 18 | 13. [Deletando todos os recursos da nossa aplicação](https://youtu.be/erdoH56RarM) 19 | 20 | ## Diagrama de arquitetura 21 | 22 | ![App Screenshot](diagram.jpg) 23 | 24 | ## Links 25 | 26 | - [Playlist dos vídeos](https://www.youtube.com/playlist?list=PLWQmZVQayUUI5RinDqpoIXiRYWy5YZKjs) 27 | - [Ferramenta CIDR](https://cidr.xyz/) 28 | - [O que é CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) 29 | - [Fargate Network Mode](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/AWS_Fargate.html#fargate-tasks-networkmode) 30 | - [Analogia de uma VPC completa](https://start.jcolemorrison.com/aws-vpc-core-concepts-analogy-guide/) 31 | - [Post oficial da AWS sobre a rede de uma task no Fargate](https://aws.amazon.com/blogs/compute/task-networking-in-aws-fargate/) 32 | 33 | 34 | ## Ordem de deleção dos recursos na AWS 35 | 1. ECS Service 36 | 2. Task definition 37 | 3. Load balancer 38 | 4. Target group 39 | 5. Service security group 40 | 6. Load balancer security group 41 | 7. NAT Gateways 42 | 8. Subnets 43 | 9. Dettach Internet Gateway 44 | 10. Delete Internet Gateway 45 | 11. Route tables 46 | 12. VPC 47 | 13. ECS Cluster 48 | 14. ECR 49 | 15. IAM 50 | 16. Cloudwatch 51 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "4.34.0" 6 | constraints = "~> 4.34.0" 7 | hashes = [ 8 | "h1:JRqeU/5qR61U+z86mC68C5hp0XHZXxmRK9dupTIAhGg=", 9 | "zh:2bdc9b908008c1e874d8ba7e2cfabd856cafb63c52fef51a1fdeef2f5584bffd", 10 | "zh:43c5364e3161be3856e56494cbb8b21d513fc05875f1b40e66e583602154dd0a", 11 | "zh:44e763adae92489f223f65866c1f8b5342e7e85b95daa8d1f483a2afb47f7db3", 12 | "zh:62bfabb3a1a31814cb3fadc356539d8253b95abacfffd90984affb54c9a53a86", 13 | "zh:6495ce67897d2d5648d466c09e8588e837c2878322988738a95c06926044b05d", 14 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 15 | "zh:b1546b2ac61d7cc27a8eba160ae1b6ac581d2c4af824a400d6511e4998da398a", 16 | "zh:c8c362c5527f0533bde581e41cdb2bdf42aea557762f326dbfa122fdf001fb10", 17 | "zh:c8cc28fb41f73ca09f590bace2204ea325f6116be0bbce6abfebd393d028f12c", 18 | "zh:db0601c9bd12ca028d60ac87e85320285ebc64857715f7908dd6a283e5f44d45", 19 | "zh:e64d2193236d05348ba2e8b99650d9274e5af80be39b3ee28bbe442b0684d8a3", 20 | "zh:ff6228f3751e1f0ee7dc086d09e32d39ca6556f0b5267f36aae67881d29ace94", 21 | ] 22 | } 23 | 24 | provider "registry.terraform.io/hashicorp/random" { 25 | version = "3.4.3" 26 | constraints = "~> 3.4.3" 27 | hashes = [ 28 | "h1:tL3katm68lX+4lAncjQA9AXL4GR/VM+RPwqYf4D2X8Q=", 29 | "zh:41c53ba47085d8261590990f8633c8906696fa0a3c4b384ff6a7ecbf84339752", 30 | "zh:59d98081c4475f2ad77d881c4412c5129c56214892f490adf11c7e7a5a47de9b", 31 | "zh:686ad1ee40b812b9e016317e7f34c0d63ef837e084dea4a1f578f64a6314ad53", 32 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 33 | "zh:84103eae7251384c0d995f5a257c72b0096605048f757b749b7b62107a5dccb3", 34 | "zh:8ee974b110adb78c7cd18aae82b2729e5124d8f115d484215fd5199451053de5", 35 | "zh:9dd4561e3c847e45de603f17fa0c01ae14cae8c4b7b4e6423c9ef3904b308dda", 36 | "zh:bb07bb3c2c0296beba0beec629ebc6474c70732387477a65966483b5efabdbc6", 37 | "zh:e891339e96c9e5a888727b45b2e1bb3fcbdfe0fd7c5b4396e4695459b38c8cb1", 38 | "zh:ea4739860c24dfeaac6c100b2a2e357106a89d18751f7693f3c31ecf6a996f8d", 39 | "zh:f0c76ac303fd0ab59146c39bc121c5d7d86f878e9a69294e29444d4c653786f8", 40 | "zh:f143a9a5af42b38fed328a161279906759ff39ac428ebcfe55606e05e1518b93", 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /.github/workflows/0003_tf_destroy.yml: -------------------------------------------------------------------------------- 1 | name: Destroy terraform resources on the given folder 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | dir: 7 | description: 'Directory with resources to be destroyed' 8 | required: true 9 | default: '0003-terraform-gha' 10 | 11 | env: 12 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 13 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 14 | 15 | jobs: 16 | destroy-dev: 17 | name: 'terraform destroy dev' 18 | runs-on: ubuntu-latest 19 | env: 20 | TF_WORKING_DIR: ${{ github.event.inputs.dir }} 21 | 22 | defaults: 23 | run: 24 | shell: bash 25 | working-directory: ${{ env.TF_WORKING_DIR }} 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v2 30 | 31 | - name: Setup Terraform 32 | uses: hashicorp/setup-terraform@v1 33 | with: 34 | terraform_version: 0.15.5 35 | 36 | - name: Terraform fmt 37 | id: fmt 38 | run: terraform fmt -check 39 | 40 | - name: Terraform Init 41 | id: init 42 | run: terraform init -backend=true -backend-config="config/dev/backend.hcl" 43 | 44 | - name: Terraform Validate 45 | id: validate 46 | run: terraform validate -no-color 47 | 48 | - name: Terraform Destroy 49 | run: terraform destroy -auto-approve -var-file="config/dev/terraform.tfvars" 50 | 51 | destroy-prod: 52 | name: 'terraform destroy prod' 53 | runs-on: ubuntu-latest 54 | env: 55 | TF_WORKING_DIR: ${{ github.event.inputs.dir }} 56 | 57 | defaults: 58 | run: 59 | shell: bash 60 | working-directory: ${{ env.TF_WORKING_DIR }} 61 | 62 | steps: 63 | - name: Checkout 64 | uses: actions/checkout@v2 65 | 66 | - name: Setup Terraform 67 | uses: hashicorp/setup-terraform@v1 68 | with: 69 | terraform_version: 0.15.5 70 | 71 | - name: Terraform fmt 72 | id: fmt 73 | run: terraform fmt -check 74 | 75 | - name: Terraform Init 76 | id: init 77 | run: terraform init -backend=true -backend-config="config/prod/backend.hcl" 78 | 79 | - name: Terraform Validate 80 | id: validate 81 | run: terraform validate -no-color 82 | 83 | - name: Terraform Destroy 84 | run: terraform destroy -auto-approve -var-file="config/prod/terraform.tfvars" 85 | -------------------------------------------------------------------------------- /0006-middy-js/lambdas/layers/middy/nodejs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "@middy/core": "^2.5.1", 12 | "@middy/http-json-body-parser": "^2.5.1" 13 | }, 14 | "devDependencies": {} 15 | }, 16 | "node_modules/@middy/core": { 17 | "version": "2.5.1", 18 | "resolved": "https://registry.npmjs.org/@middy/core/-/core-2.5.1.tgz", 19 | "integrity": "sha512-kAZMhjcZRv2eqBd6uFBeaCuaSvhV0gb8XNl8kw/SiqbRptVh5FOb425gK3AtJda4VZPsQb4HeVN3G4Wzdb9o3g==", 20 | "engines": { 21 | "node": ">=12" 22 | } 23 | }, 24 | "node_modules/@middy/http-json-body-parser": { 25 | "version": "2.5.1", 26 | "resolved": "https://registry.npmjs.org/@middy/http-json-body-parser/-/http-json-body-parser-2.5.1.tgz", 27 | "integrity": "sha512-KPMtWS9wiD2kZu5fQBVRh0DBp2/lHttZBAKeE3sYZMm29MWcUuX6QibotXkwI2f20Rs5JigvzqY6CCMAEDHUPQ==", 28 | "dependencies": { 29 | "@middy/util": "^2.5.1" 30 | }, 31 | "engines": { 32 | "node": ">=12" 33 | } 34 | }, 35 | "node_modules/@middy/util": { 36 | "version": "2.5.1", 37 | "resolved": "https://registry.npmjs.org/@middy/util/-/util-2.5.1.tgz", 38 | "integrity": "sha512-Tpx97CpE2GlgDdaCav+IZzEV2dSmb4hJj57r1xIOiRctYyYYhS6o4jOjfLBWAcDxbWs0WZb/kvv58p0O+1hRqw==", 39 | "engines": { 40 | "node": ">=12" 41 | } 42 | } 43 | }, 44 | "dependencies": { 45 | "@middy/core": { 46 | "version": "2.5.1", 47 | "resolved": "https://registry.npmjs.org/@middy/core/-/core-2.5.1.tgz", 48 | "integrity": "sha512-kAZMhjcZRv2eqBd6uFBeaCuaSvhV0gb8XNl8kw/SiqbRptVh5FOb425gK3AtJda4VZPsQb4HeVN3G4Wzdb9o3g==" 49 | }, 50 | "@middy/http-json-body-parser": { 51 | "version": "2.5.1", 52 | "resolved": "https://registry.npmjs.org/@middy/http-json-body-parser/-/http-json-body-parser-2.5.1.tgz", 53 | "integrity": "sha512-KPMtWS9wiD2kZu5fQBVRh0DBp2/lHttZBAKeE3sYZMm29MWcUuX6QibotXkwI2f20Rs5JigvzqY6CCMAEDHUPQ==", 54 | "requires": { 55 | "@middy/util": "^2.5.1" 56 | } 57 | }, 58 | "@middy/util": { 59 | "version": "2.5.1", 60 | "resolved": "https://registry.npmjs.org/@middy/util/-/util-2.5.1.tgz", 61 | "integrity": "sha512-Tpx97CpE2GlgDdaCav+IZzEV2dSmb4hJj57r1xIOiRctYyYYhS6o4jOjfLBWAcDxbWs0WZb/kvv58p0O+1hRqw==" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/archive" { 5 | version = "2.2.0" 6 | hashes = [ 7 | "h1:2K5LQkuWRS2YN1/YoNaHn9MAzjuTX8Gaqy6i8Mbfv8Y=", 8 | "zh:06bd875932288f235c16e2237142b493c2c2b6aba0e82e8c85068332a8d2a29e", 9 | "zh:0c681b481372afcaefddacc7ccdf1d3bb3a0c0d4678a526bc8b02d0c331479bc", 10 | "zh:100fc5b3fc01ea463533d7bbfb01cb7113947a969a4ec12e27f5b2be49884d6c", 11 | "zh:55c0d7ddddbd0a46d57c51fcfa9b91f14eed081a45101dbfc7fd9d2278aa1403", 12 | "zh:73a5dd68379119167934c48afa1101b09abad2deb436cd5c446733e705869d6b", 13 | "zh:841fc4ac6dc3479981330974d44ad2341deada8a5ff9e3b1b4510702dfbdbed9", 14 | "zh:91be62c9b41edb137f7f835491183628d484e9d6efa82fcb75cfa538c92791c5", 15 | "zh:acd5f442bd88d67eb948b18dc2ed421c6c3faee62d3a12200e442bfff0aa7d8b", 16 | "zh:ad5720da5524641ad718a565694821be5f61f68f1c3c5d2cfa24426b8e774bef", 17 | "zh:e63f12ea938520b3f83634fc29da28d92eed5cfbc5cc8ca08281a6a9c36cca65", 18 | "zh:f6542918faa115df46474a36aabb4c3899650bea036b5f8a5e296be6f8f25767", 19 | ] 20 | } 21 | 22 | provider "registry.terraform.io/hashicorp/aws" { 23 | version = "3.42.0" 24 | hashes = [ 25 | "h1:C6/yDp6BhuDFx0qdkBuJj/OWUJpAoraHTJaU6ac38Rw=", 26 | "zh:126c856a6eedddd8571f161a826a407ba5655a37a6241393560a96b8c4beca1a", 27 | "zh:1a4868e6ac734b5fc2e79a4a889d176286b66664aad709435aa6acee5871d5b0", 28 | "zh:40fed7637ab8ddeb93bef06aded35d970f0628025b97459ae805463e8aa0a58a", 29 | "zh:68def3c0a5a1aac1db6372c51daef858b707f03052626d3427ac24cba6f2014d", 30 | "zh:6db7ec9c8d1803a0b6f40a664aa892e0f8894562de83061fa7ac1bc51ff5e7e5", 31 | "zh:7058abaad595930b3f97dc04e45c112b2dbf37d098372a849081f7081da2fb52", 32 | "zh:8c25adb15a19da301c478aa1f4a4d8647cabdf8e5dae8331d4490f80ea718c26", 33 | "zh:8e129b847401e39fcbc54817726dab877f36b7f00ff5ed76f7b43470abe99ff9", 34 | "zh:d268bb267a2d6b39df7ddee8efa7c1ef7a15cf335dfa5f2e64c9dae9b623a1b8", 35 | "zh:d6eeb3614a0ab50f8e9ab5666ae5754ea668ce327310e5b21b7f04a18d7611a8", 36 | "zh:f5d3c58055dff6e38562b75d3edc908cb2f1e45c6914f6b00f4773359ce49324", 37 | ] 38 | } 39 | 40 | provider "registry.terraform.io/hashicorp/null" { 41 | version = "3.1.0" 42 | hashes = [ 43 | "h1:xhbHC6in3nQryvTQBWKxebi3inG5OCgHgc4fRxL0ymc=", 44 | "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2", 45 | "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515", 46 | "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521", 47 | "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2", 48 | "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e", 49 | "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53", 50 | "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d", 51 | "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8", 52 | "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70", 53 | "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b", 54 | "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e", 55 | ] 56 | } 57 | -------------------------------------------------------------------------------- /0010-s3-trigger-lambda/terraform/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/archive" { 5 | version = "2.2.0" 6 | hashes = [ 7 | "h1:2K5LQkuWRS2YN1/YoNaHn9MAzjuTX8Gaqy6i8Mbfv8Y=", 8 | "zh:06bd875932288f235c16e2237142b493c2c2b6aba0e82e8c85068332a8d2a29e", 9 | "zh:0c681b481372afcaefddacc7ccdf1d3bb3a0c0d4678a526bc8b02d0c331479bc", 10 | "zh:100fc5b3fc01ea463533d7bbfb01cb7113947a969a4ec12e27f5b2be49884d6c", 11 | "zh:55c0d7ddddbd0a46d57c51fcfa9b91f14eed081a45101dbfc7fd9d2278aa1403", 12 | "zh:73a5dd68379119167934c48afa1101b09abad2deb436cd5c446733e705869d6b", 13 | "zh:841fc4ac6dc3479981330974d44ad2341deada8a5ff9e3b1b4510702dfbdbed9", 14 | "zh:91be62c9b41edb137f7f835491183628d484e9d6efa82fcb75cfa538c92791c5", 15 | "zh:acd5f442bd88d67eb948b18dc2ed421c6c3faee62d3a12200e442bfff0aa7d8b", 16 | "zh:ad5720da5524641ad718a565694821be5f61f68f1c3c5d2cfa24426b8e774bef", 17 | "zh:e63f12ea938520b3f83634fc29da28d92eed5cfbc5cc8ca08281a6a9c36cca65", 18 | "zh:f6542918faa115df46474a36aabb4c3899650bea036b5f8a5e296be6f8f25767", 19 | ] 20 | } 21 | 22 | provider "registry.terraform.io/hashicorp/aws" { 23 | version = "4.34.0" 24 | constraints = "~> 4.34.0" 25 | hashes = [ 26 | "h1:JRqeU/5qR61U+z86mC68C5hp0XHZXxmRK9dupTIAhGg=", 27 | "zh:2bdc9b908008c1e874d8ba7e2cfabd856cafb63c52fef51a1fdeef2f5584bffd", 28 | "zh:43c5364e3161be3856e56494cbb8b21d513fc05875f1b40e66e583602154dd0a", 29 | "zh:44e763adae92489f223f65866c1f8b5342e7e85b95daa8d1f483a2afb47f7db3", 30 | "zh:62bfabb3a1a31814cb3fadc356539d8253b95abacfffd90984affb54c9a53a86", 31 | "zh:6495ce67897d2d5648d466c09e8588e837c2878322988738a95c06926044b05d", 32 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 33 | "zh:b1546b2ac61d7cc27a8eba160ae1b6ac581d2c4af824a400d6511e4998da398a", 34 | "zh:c8c362c5527f0533bde581e41cdb2bdf42aea557762f326dbfa122fdf001fb10", 35 | "zh:c8cc28fb41f73ca09f590bace2204ea325f6116be0bbce6abfebd393d028f12c", 36 | "zh:db0601c9bd12ca028d60ac87e85320285ebc64857715f7908dd6a283e5f44d45", 37 | "zh:e64d2193236d05348ba2e8b99650d9274e5af80be39b3ee28bbe442b0684d8a3", 38 | "zh:ff6228f3751e1f0ee7dc086d09e32d39ca6556f0b5267f36aae67881d29ace94", 39 | ] 40 | } 41 | 42 | provider "registry.terraform.io/hashicorp/random" { 43 | version = "3.4.3" 44 | constraints = "~> 3.4.3" 45 | hashes = [ 46 | "h1:tL3katm68lX+4lAncjQA9AXL4GR/VM+RPwqYf4D2X8Q=", 47 | "zh:41c53ba47085d8261590990f8633c8906696fa0a3c4b384ff6a7ecbf84339752", 48 | "zh:59d98081c4475f2ad77d881c4412c5129c56214892f490adf11c7e7a5a47de9b", 49 | "zh:686ad1ee40b812b9e016317e7f34c0d63ef837e084dea4a1f578f64a6314ad53", 50 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 51 | "zh:84103eae7251384c0d995f5a257c72b0096605048f757b749b7b62107a5dccb3", 52 | "zh:8ee974b110adb78c7cd18aae82b2729e5124d8f115d484215fd5199451053de5", 53 | "zh:9dd4561e3c847e45de603f17fa0c01ae14cae8c4b7b4e6423c9ef3904b308dda", 54 | "zh:bb07bb3c2c0296beba0beec629ebc6474c70732387477a65966483b5efabdbc6", 55 | "zh:e891339e96c9e5a888727b45b2e1bb3fcbdfe0fd7c5b4396e4695459b38c8cb1", 56 | "zh:ea4739860c24dfeaac6c100b2a2e357106a89d18751f7693f3c31ecf6a996f8d", 57 | "zh:f0c76ac303fd0ab59146c39bc121c5d7d86f878e9a69294e29444d4c653786f8", 58 | "zh:f143a9a5af42b38fed328a161279906759ff39ac428ebcfe55606e05e1518b93", 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /0007-aws-ecs-fargate/app/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const axios = require('axios'); 3 | 4 | const db = require('./db'); 5 | 6 | const app = express(); 7 | const port = 3000; 8 | 9 | app.use(express.json()); 10 | app.use(express.urlencoded({ extended: true })); 11 | 12 | const indexPage = ` 13 |

Hello from a Node.js Application running on AWS ECS Fargate

14 |

What would you like to see?

15 | 21 | `; 22 | 23 | app.get('/', (req, res) => res.send(indexPage)); 24 | app.get('/healthcheck', (req, res) => { 25 | try { 26 | res.sendStatus(204); 27 | } catch (error) { 28 | res.sendStatus(500); 29 | } 30 | }); 31 | 32 | app.get('/dogs', async (req, res) => { 33 | try { 34 | const response = await axios.get('https://dog.ceo/api/breeds/image/random'); 35 | 36 | console.log(JSON.stringify(response.data)); 37 | 38 | const { message: dogImage } = response.data; 39 | res.send( 40 | `random dog` 41 | ); 42 | } catch (error) { 43 | console.error(JSON.stringify(error)); 44 | res.status(500); 45 | res.send(error.message); 46 | } 47 | }); 48 | 49 | app.get('/cats', async (req, res) => { 50 | try { 51 | const response = await axios.get('https://aws.random.cat/meow'); 52 | 53 | console.log(JSON.stringify(response.data)); 54 | 55 | const { file: catImage } = response.data; 56 | res.send( 57 | `random cat` 58 | ); 59 | } catch (error) { 60 | console.error(JSON.stringify(error)); 61 | res.status(500); 62 | res.send(error.message); 63 | } 64 | }); 65 | 66 | app.get('/create-tables', async (req, res) => { 67 | const createTableSql = ` 68 | CREATE EXTENSION IF NOT EXISTS "pgcrypto"; 69 | 70 | CREATE TABLE IF NOT EXISTS todos ( 71 | id UUID PRIMARY KEY DEFAULT gen_random_uuid(), 72 | todo VARCHAR (255) NOT NULL, 73 | done BOOLEAN DEFAULT false, 74 | created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP 75 | );` 76 | 77 | try { 78 | await db.query(createTableSql); 79 | res.send('Todo table has been created'); 80 | } catch (error) { 81 | console.error(JSON.stringify(error)); 82 | res.status(500); 83 | res.send(error.message); 84 | } 85 | }); 86 | 87 | app.get('/todos', async(req, res) => { 88 | try { 89 | const result = await db.query('SELECT * FROM todos'); 90 | 91 | console.log({ result }); 92 | 93 | res.send(result.rows); 94 | } catch (error) { 95 | console.error(JSON.stringify(error)); 96 | res.status(500); 97 | res.send(error.message); 98 | } 99 | }); 100 | 101 | app.post('/todos', async(req, res) => { 102 | try { 103 | const params = req.body; 104 | 105 | console.log({ params }); 106 | 107 | const result = await db.query('INSERT INTO todos(todo) VALUES($1) RETURNING *', [params.todo]); 108 | res.send(result.rows[0]); 109 | } catch (error) { 110 | console.error(JSON.stringify(error)); 111 | res.status(500); 112 | res.send(error.message); 113 | } 114 | }); 115 | 116 | app.listen(port, () => { 117 | console.log(`App running on port ${port}`); 118 | }); 119 | -------------------------------------------------------------------------------- /0013-terraform-test/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "terraform init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.terraform.io/hashicorp/aws" { 5 | version = "5.19.0" 6 | constraints = "~> 5.19" 7 | hashes = [ 8 | "h1:MJclj56jijp7T4V4g5tzHXS3M8vUdJAcBRjEstBh0Hc=", 9 | "zh:03aa0f857c6dfce5f46c9bf3aad45534b9421e68983994b6f9dd9812beaece9c", 10 | "zh:0639818c5bf9f9943667f39ec38bb945c9786983025dff407390133fa1ca5041", 11 | "zh:0b82ad42ced8fb4a138eaf2fd37cf6059ca0bb482114b35fb84f22fc1500324a", 12 | "zh:173e8c19a9f1d8f6457c80f4a73a92f420a81d650fc4ad0f97a5dc4b9485bba8", 13 | "zh:42913a40ddfe9b4f3c78ad2e3cdc1dcfd48151bc132dc6b49fc32cd6da79db21", 14 | "zh:452db5caca2e53d5f7090979d518e77aa5fd98385514b11ee2ce76a46e89cb53", 15 | "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", 16 | "zh:a12377ade89ee18d9be116436e411e8396898bd70b21ab027c161c785e86238d", 17 | "zh:aa9e4746ba49044ad5b4dda57fcdba7bc16fe65f696766fb2c55c30a27abf844", 18 | "zh:adfaee76d283f1c321fad2e4154be88d57da8c2ecfdca9516c8920bd2ece36ed", 19 | "zh:bf6fbc6d60661c03ed2214173c1deced908dc62480dd41e67ac399fa4abd7467", 20 | "zh:cb685da03ad00d1a27891f3d366d75e8795ac81f1b427888b434e6832ca40633", 21 | "zh:e0432c78dfaf2baebe2bf5c0ad8087f547c69c2c5a00e4c1dcd5a6344ce726df", 22 | "zh:e0ec9ccb8d34d6d0d8bf7f8628c223951832b4d50ea8887fc711fa854b3a28b4", 23 | "zh:f274397ada4ef3c1dce2f70e719c8ccf19fc4e7a2e3f45d018764c6267fd7157", 24 | ] 25 | } 26 | 27 | provider "registry.terraform.io/hashicorp/http" { 28 | version = "3.4.0" 29 | constraints = "3.4.0" 30 | hashes = [ 31 | "h1:m0d6+9xK/9TJSE9Z6nM4IwHXZgod4/jkdsf7CZSpUvo=", 32 | "zh:56712497a87bc4e91bbaf1a5a2be4b3f9cfa2384baeb20fc9fad0aff8f063914", 33 | "zh:6661355e1090ebacab16a40ede35b029caffc279d67da73a000b6eecf0b58eba", 34 | "zh:67b92d343e808b92d7e6c3bbcb9b9d5475fecfed0836963f7feb9d9908bd4c4f", 35 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 36 | "zh:86ebb9be9b685c96dbb5c024b55d87526d57a4b127796d6046344f8294d3f28e", 37 | "zh:902be7cfca4308cba3e1e7ba6fc292629dfd150eb9a9f054a854fa1532b0ceba", 38 | "zh:9ba26e0215cd53b21fe26a0a98c007de1348b7d13a75ae3cfaf7729e0f2c50bb", 39 | "zh:a195c941e1f1526147134c257ff549bea4c89c953685acd3d48d9de7a38f39dc", 40 | "zh:a7967b3d2a8c3e7e1dc9ae381ca753268f9fce756466fe2fc9e414ca2d85a92e", 41 | "zh:bde56542e9a093434d96bea21c341285737c6d38fea2f05e12ba7b333f3e9c05", 42 | "zh:c0306f76903024c497fd01f9fd9bace5854c263e87a97bc2e89dcc96d35ca3cc", 43 | "zh:f9335a6c336171e85f8e3e99c3d31758811a19aeb21fa8c9013d427e155ae2a9", 44 | ] 45 | } 46 | 47 | provider "registry.terraform.io/hashicorp/random" { 48 | version = "3.5.1" 49 | constraints = "3.5.1" 50 | hashes = [ 51 | "h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=", 52 | "zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64", 53 | "zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d", 54 | "zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831", 55 | "zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3", 56 | "zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f", 57 | "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", 58 | "zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b", 59 | "zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2", 60 | "zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865", 61 | "zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03", 62 | "zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602", 63 | "zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014", 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /.github/workflows/0003_tf_plan.yml: -------------------------------------------------------------------------------- 1 | name: Create terraform plan on folder 0003-terraform-gha 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '0003-terraform-gha/**' 7 | 8 | env: 9 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 10 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 11 | 12 | jobs: 13 | plan-dev: 14 | name: 'terraform plan dev' 15 | runs-on: ubuntu-latest 16 | env: 17 | TF_WORKING_DIR: '0003-terraform-gha' 18 | 19 | defaults: 20 | run: 21 | shell: bash 22 | working-directory: ${{ env.TF_WORKING_DIR }} 23 | 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v2 27 | 28 | - name: Setup Terraform 29 | uses: hashicorp/setup-terraform@v1 30 | with: 31 | terraform_version: 0.15.5 32 | 33 | - name: Terraform fmt 34 | id: fmt 35 | run: terraform fmt -check 36 | 37 | - name: Terraform Init 38 | id: init 39 | run: terraform init -backend=true -backend-config="config/dev/backend.hcl" 40 | 41 | - name: Terraform Validate 42 | id: validate 43 | run: terraform validate -no-color 44 | 45 | - name: Terraform Plan 46 | id: plandev 47 | run: terraform plan -no-color -var-file="config/dev/terraform.tfvars" 48 | 49 | - uses: actions/github-script@0.9.0 50 | if: github.event_name == 'pull_request' 51 | env: 52 | PLAN: "terraform\n${{ steps.plandev.outputs.stdout }}" 53 | with: 54 | github-token: ${{ secrets.GITHUB_TOKEN }} 55 | script: | 56 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` 57 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` 58 | #### Terraform Validation 🤖${{ steps.validate.outputs.stdout }} 59 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` 60 |
Show Plan 61 | \`\`\`${process.env.PLAN}\`\`\` 62 |
63 | *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.TF_WORKING_DIR }}\`, Workflow: \`${{ github.workflow }}\`*`; 64 | github.issues.createComment({ 65 | issue_number: context.issue.number, 66 | owner: context.repo.owner, 67 | repo: context.repo.repo, 68 | body: output 69 | }) 70 | 71 | 72 | plan-prod: 73 | name: 'terraform plan prod' 74 | runs-on: ubuntu-latest 75 | env: 76 | TF_WORKING_DIR: '0003-terraform-gha' 77 | 78 | defaults: 79 | run: 80 | shell: bash 81 | working-directory: ${{ env.TF_WORKING_DIR }} 82 | 83 | steps: 84 | - name: Checkout 85 | uses: actions/checkout@v2 86 | 87 | - name: Setup Terraform 88 | uses: hashicorp/setup-terraform@v1 89 | with: 90 | terraform_version: 0.15.5 91 | 92 | - name: Terraform fmt 93 | id: fmt 94 | run: terraform fmt -check 95 | 96 | - name: Terraform Init 97 | id: init 98 | run: terraform init -backend=true -backend-config="config/prod/backend.hcl" 99 | 100 | - name: Terraform Validate 101 | id: validate 102 | run: terraform validate -no-color 103 | 104 | - name: Terraform Plan 105 | id: planprod 106 | run: terraform plan -no-color -var-file="config/prod/terraform.tfvars" 107 | 108 | - uses: actions/github-script@0.9.0 109 | if: github.event_name == 'pull_request' 110 | env: 111 | PLAN: "terraform\n${{ steps.planprod.outputs.stdout }}" 112 | with: 113 | github-token: ${{ secrets.GITHUB_TOKEN }} 114 | script: | 115 | const output = `#### Terraform Format and Style 🖌\`${{ steps.fmt.outcome }}\` 116 | #### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\` 117 | #### Terraform Validation 🤖${{ steps.validate.outputs.stdout }} 118 | #### Terraform Plan 📖\`${{ steps.plan.outcome }}\` 119 |
Show Plan 120 | \`\`\`${process.env.PLAN}\`\`\` 121 |
122 | *Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ env.TF_WORKING_DIR }}\`, Workflow: \`${{ github.workflow }}\`*`; 123 | github.issues.createComment({ 124 | issue_number: context.issue.number, 125 | owner: context.repo.owner, 126 | repo: context.repo.repo, 127 | body: output 128 | }) 129 | -------------------------------------------------------------------------------- /0013-terraform-test/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Terramino 5 | 24 | 25 | 26 | 27 | 28 | 290 | 291 | 292 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/lambdas/cat-api/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cat-api", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@sindresorhus/is": { 8 | "version": "4.0.1", 9 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", 10 | "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", 11 | "dev": true 12 | }, 13 | "@szmarczak/http-timer": { 14 | "version": "4.0.5", 15 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", 16 | "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", 17 | "dev": true, 18 | "requires": { 19 | "defer-to-connect": "^2.0.0" 20 | } 21 | }, 22 | "@types/cacheable-request": { 23 | "version": "6.0.1", 24 | "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", 25 | "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", 26 | "dev": true, 27 | "requires": { 28 | "@types/http-cache-semantics": "*", 29 | "@types/keyv": "*", 30 | "@types/node": "*", 31 | "@types/responselike": "*" 32 | } 33 | }, 34 | "@types/http-cache-semantics": { 35 | "version": "4.0.0", 36 | "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", 37 | "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", 38 | "dev": true 39 | }, 40 | "@types/keyv": { 41 | "version": "3.1.1", 42 | "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", 43 | "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", 44 | "dev": true, 45 | "requires": { 46 | "@types/node": "*" 47 | } 48 | }, 49 | "@types/node": { 50 | "version": "14.14.41", 51 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", 52 | "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==", 53 | "dev": true 54 | }, 55 | "@types/responselike": { 56 | "version": "1.0.0", 57 | "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", 58 | "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", 59 | "dev": true, 60 | "requires": { 61 | "@types/node": "*" 62 | } 63 | }, 64 | "cacheable-lookup": { 65 | "version": "5.0.4", 66 | "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", 67 | "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", 68 | "dev": true 69 | }, 70 | "cacheable-request": { 71 | "version": "7.0.1", 72 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", 73 | "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", 74 | "dev": true, 75 | "requires": { 76 | "clone-response": "^1.0.2", 77 | "get-stream": "^5.1.0", 78 | "http-cache-semantics": "^4.0.0", 79 | "keyv": "^4.0.0", 80 | "lowercase-keys": "^2.0.0", 81 | "normalize-url": "^4.1.0", 82 | "responselike": "^2.0.0" 83 | } 84 | }, 85 | "clone-response": { 86 | "version": "1.0.2", 87 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 88 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 89 | "dev": true, 90 | "requires": { 91 | "mimic-response": "^1.0.0" 92 | } 93 | }, 94 | "decompress-response": { 95 | "version": "6.0.0", 96 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 97 | "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 98 | "dev": true, 99 | "requires": { 100 | "mimic-response": "^3.1.0" 101 | }, 102 | "dependencies": { 103 | "mimic-response": { 104 | "version": "3.1.0", 105 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 106 | "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 107 | "dev": true 108 | } 109 | } 110 | }, 111 | "defer-to-connect": { 112 | "version": "2.0.1", 113 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", 114 | "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", 115 | "dev": true 116 | }, 117 | "end-of-stream": { 118 | "version": "1.4.4", 119 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 120 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 121 | "dev": true, 122 | "requires": { 123 | "once": "^1.4.0" 124 | } 125 | }, 126 | "get-stream": { 127 | "version": "5.2.0", 128 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 129 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 130 | "dev": true, 131 | "requires": { 132 | "pump": "^3.0.0" 133 | } 134 | }, 135 | "got": { 136 | "version": "11.8.2", 137 | "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", 138 | "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", 139 | "dev": true, 140 | "requires": { 141 | "@sindresorhus/is": "^4.0.0", 142 | "@szmarczak/http-timer": "^4.0.5", 143 | "@types/cacheable-request": "^6.0.1", 144 | "@types/responselike": "^1.0.0", 145 | "cacheable-lookup": "^5.0.3", 146 | "cacheable-request": "^7.0.1", 147 | "decompress-response": "^6.0.0", 148 | "http2-wrapper": "^1.0.0-beta.5.2", 149 | "lowercase-keys": "^2.0.0", 150 | "p-cancelable": "^2.0.0", 151 | "responselike": "^2.0.0" 152 | } 153 | }, 154 | "http-cache-semantics": { 155 | "version": "4.1.0", 156 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 157 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", 158 | "dev": true 159 | }, 160 | "http2-wrapper": { 161 | "version": "1.0.3", 162 | "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", 163 | "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", 164 | "dev": true, 165 | "requires": { 166 | "quick-lru": "^5.1.1", 167 | "resolve-alpn": "^1.0.0" 168 | } 169 | }, 170 | "json-buffer": { 171 | "version": "3.0.1", 172 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 173 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 174 | "dev": true 175 | }, 176 | "keyv": { 177 | "version": "4.0.3", 178 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", 179 | "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", 180 | "dev": true, 181 | "requires": { 182 | "json-buffer": "3.0.1" 183 | } 184 | }, 185 | "lowercase-keys": { 186 | "version": "2.0.0", 187 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 188 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", 189 | "dev": true 190 | }, 191 | "mimic-response": { 192 | "version": "1.0.1", 193 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 194 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", 195 | "dev": true 196 | }, 197 | "normalize-url": { 198 | "version": "4.5.0", 199 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", 200 | "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", 201 | "dev": true 202 | }, 203 | "once": { 204 | "version": "1.4.0", 205 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 206 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 207 | "dev": true, 208 | "requires": { 209 | "wrappy": "1" 210 | } 211 | }, 212 | "p-cancelable": { 213 | "version": "2.1.0", 214 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", 215 | "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", 216 | "dev": true 217 | }, 218 | "pump": { 219 | "version": "3.0.0", 220 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 221 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 222 | "dev": true, 223 | "requires": { 224 | "end-of-stream": "^1.1.0", 225 | "once": "^1.3.1" 226 | } 227 | }, 228 | "quick-lru": { 229 | "version": "5.1.1", 230 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 231 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 232 | "dev": true 233 | }, 234 | "resolve-alpn": { 235 | "version": "1.1.2", 236 | "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz", 237 | "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==", 238 | "dev": true 239 | }, 240 | "responselike": { 241 | "version": "2.0.0", 242 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", 243 | "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", 244 | "dev": true, 245 | "requires": { 246 | "lowercase-keys": "^2.0.0" 247 | } 248 | }, 249 | "wrappy": { 250 | "version": "1.0.2", 251 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 252 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 253 | "dev": true 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /0009-terraform-optional-vars/blog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Blog Template · Bootstrap v5.2 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
82 |
83 |
84 |
85 | Subscribe 86 |
87 |
88 | 89 |
90 | 96 |
97 |
98 | 99 | 115 |
116 | 117 |
118 |
119 |
120 |

Title of a longer featured blog post

121 |

Multiple lines of text that form the lede, informing new readers quickly and efficiently about what’s most interesting in this post’s contents.

122 |

Continue reading...

123 |
124 |
125 | 126 |
127 |
128 |
129 |
130 | World 131 |

Featured post

132 |
Nov 12
133 |

This is a wider card with supporting text below as a natural lead-in to additional content.

134 | Continue reading 135 |
136 |
137 | PlaceholderThumbnail 138 | 139 |
140 |
141 |
142 |
143 |
144 |
145 | Design 146 |

Post title

147 |
Nov 11
148 |

This is a wider card with supporting text below as a natural lead-in to additional content.

149 | Continue reading 150 |
151 |
152 | PlaceholderThumbnail 153 | 154 |
155 |
156 |
157 |
158 | 159 |
160 |
161 |

162 | From the Firehose 163 |

164 | 165 |
166 |

Sample blog post

167 | 168 | 169 |

This blog post shows a few different types of content that’s supported and styled with Bootstrap. Basic typography, lists, tables, images, code, and more are all supported as expected.

170 |
171 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

172 |

Blockquotes

173 |

This is an example blockquote in action:

174 |
175 |

Quoted text goes here.

176 |
177 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

178 |

Example lists

179 |

This is some additional paragraph placeholder content. It's a slightly shorter version of the other highly repetitive body text used throughout. This is an example unordered list:

180 |
    181 |
  • First list item
  • 182 |
  • Second list item with a longer description
  • 183 |
  • Third list item to close it out
  • 184 |
185 |

And this is an ordered list:

186 |
    187 |
  1. First list item
  2. 188 |
  3. Second list item with a longer description
  4. 189 |
  5. Third list item to close it out
  6. 190 |
191 |

And this is a definition list:

192 |
193 |
HyperText Markup Language (HTML)
194 |
The language used to describe and define the content of a Web page
195 |
Cascading Style Sheets (CSS)
196 |
Used to describe the appearance of Web content
197 |
JavaScript (JS)
198 |
The programming language used to build advanced Web sites and applications
199 |
200 |

Inline HTML elements

201 |

HTML defines a long list of available inline tags, a complete list of which can be found on the Mozilla Developer Network.

202 |
    203 |
  • To bold text, use <strong>.
  • 204 |
  • To italicize text, use <em>.
  • 205 |
  • Abbreviations, like HTML should use <abbr>, with an optional title attribute for the full phrase.
  • 206 |
  • Citations, like — Mark Otto, should use <cite>.
  • 207 |
  • Deleted text should use <del> and inserted text should use <ins>.
  • 208 |
  • Superscript text uses <sup> and subscript text uses <sub>.
  • 209 |
210 |

Most of these elements are styled by browsers with few modifications on our part.

211 |

Heading

212 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

213 |

Sub-heading

214 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

215 |
Example code block
216 |

This is some additional paragraph placeholder content. It's a slightly shorter version of the other highly repetitive body text used throughout.

217 |
218 | 219 |
220 |

Another blog post

221 | 222 | 223 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

224 |
225 |

Longer quote goes here, maybe with some emphasized text in the middle of it.

226 |
227 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

228 |

Example table

229 |

And don't forget about tables in these posts:

230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 |
NameUpvotesDownvotes
Alice1011
Bob43
Charlie79
Totals2123
263 | 264 |

This is some additional paragraph placeholder content. It's a slightly shorter version of the other highly repetitive body text used throughout.

265 |
266 | 267 |
268 |

New feature

269 | 270 | 271 |

This is some additional paragraph placeholder content. It has been written to fill the available space and show how a longer snippet of text affects the surrounding content. We'll repeat it often to keep the demonstration flowing, so be on the lookout for this exact same string of text.

272 |
    273 |
  • First list item
  • 274 |
  • Second list item with a longer description
  • 275 |
  • Third list item to close it out
  • 276 |
277 |

This is some additional paragraph placeholder content. It's a slightly shorter version of the other highly repetitive body text used throughout.

278 |
279 | 280 | 284 | 285 |
286 | 287 |
288 |
289 |
290 |

About

291 |

Customize this section to tell your visitors a little bit about your publication, writers, content, or something else entirely. Totally up to you.

292 |
293 | 294 |
295 |

Archives

296 |
    297 |
  1. March 2021
  2. 298 |
  3. February 2021
  4. 299 |
  5. January 2021
  6. 300 |
  7. December 2020
  8. 301 |
  9. November 2020
  10. 302 |
  11. October 2020
  12. 303 |
  13. September 2020
  14. 304 |
  15. August 2020
  16. 305 |
  17. July 2020
  18. 306 |
  19. June 2020
  20. 307 |
  21. May 2020
  22. 308 |
  23. April 2020
  24. 309 |
310 |
311 | 312 |
313 |

Elsewhere

314 |
    315 |
  1. GitHub
  2. 316 |
  3. Twitter
  4. 317 |
  5. Facebook
  6. 318 |
319 |
320 |
321 |
322 |
323 | 324 |
325 | 326 | 332 | 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /0002-terraform-lambda-layer/layers/got/nodejs/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.0", 9 | "license": "ISC", 10 | "dependencies": { 11 | "got": "^11.8.2" 12 | }, 13 | "devDependencies": {} 14 | }, 15 | "node_modules/@sindresorhus/is": { 16 | "version": "4.0.1", 17 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", 18 | "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==", 19 | "engines": { 20 | "node": ">=10" 21 | } 22 | }, 23 | "node_modules/@szmarczak/http-timer": { 24 | "version": "4.0.5", 25 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", 26 | "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", 27 | "dependencies": { 28 | "defer-to-connect": "^2.0.0" 29 | }, 30 | "engines": { 31 | "node": ">=10" 32 | } 33 | }, 34 | "node_modules/@types/cacheable-request": { 35 | "version": "6.0.1", 36 | "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", 37 | "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", 38 | "dependencies": { 39 | "@types/http-cache-semantics": "*", 40 | "@types/keyv": "*", 41 | "@types/node": "*", 42 | "@types/responselike": "*" 43 | } 44 | }, 45 | "node_modules/@types/http-cache-semantics": { 46 | "version": "4.0.0", 47 | "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", 48 | "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" 49 | }, 50 | "node_modules/@types/keyv": { 51 | "version": "3.1.1", 52 | "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", 53 | "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", 54 | "dependencies": { 55 | "@types/node": "*" 56 | } 57 | }, 58 | "node_modules/@types/node": { 59 | "version": "14.14.41", 60 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", 61 | "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" 62 | }, 63 | "node_modules/@types/responselike": { 64 | "version": "1.0.0", 65 | "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", 66 | "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", 67 | "dependencies": { 68 | "@types/node": "*" 69 | } 70 | }, 71 | "node_modules/cacheable-lookup": { 72 | "version": "5.0.4", 73 | "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", 74 | "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", 75 | "engines": { 76 | "node": ">=10.6.0" 77 | } 78 | }, 79 | "node_modules/cacheable-request": { 80 | "version": "7.0.1", 81 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", 82 | "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", 83 | "dependencies": { 84 | "clone-response": "^1.0.2", 85 | "get-stream": "^5.1.0", 86 | "http-cache-semantics": "^4.0.0", 87 | "keyv": "^4.0.0", 88 | "lowercase-keys": "^2.0.0", 89 | "normalize-url": "^4.1.0", 90 | "responselike": "^2.0.0" 91 | }, 92 | "engines": { 93 | "node": ">=8" 94 | } 95 | }, 96 | "node_modules/clone-response": { 97 | "version": "1.0.2", 98 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 99 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 100 | "dependencies": { 101 | "mimic-response": "^1.0.0" 102 | } 103 | }, 104 | "node_modules/decompress-response": { 105 | "version": "6.0.0", 106 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 107 | "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 108 | "dependencies": { 109 | "mimic-response": "^3.1.0" 110 | }, 111 | "engines": { 112 | "node": ">=10" 113 | } 114 | }, 115 | "node_modules/decompress-response/node_modules/mimic-response": { 116 | "version": "3.1.0", 117 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 118 | "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", 119 | "engines": { 120 | "node": ">=10" 121 | } 122 | }, 123 | "node_modules/defer-to-connect": { 124 | "version": "2.0.1", 125 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", 126 | "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", 127 | "engines": { 128 | "node": ">=10" 129 | } 130 | }, 131 | "node_modules/end-of-stream": { 132 | "version": "1.4.4", 133 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 134 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 135 | "dependencies": { 136 | "once": "^1.4.0" 137 | } 138 | }, 139 | "node_modules/get-stream": { 140 | "version": "5.2.0", 141 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 142 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 143 | "dependencies": { 144 | "pump": "^3.0.0" 145 | }, 146 | "engines": { 147 | "node": ">=8" 148 | } 149 | }, 150 | "node_modules/got": { 151 | "version": "11.8.2", 152 | "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", 153 | "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", 154 | "dependencies": { 155 | "@sindresorhus/is": "^4.0.0", 156 | "@szmarczak/http-timer": "^4.0.5", 157 | "@types/cacheable-request": "^6.0.1", 158 | "@types/responselike": "^1.0.0", 159 | "cacheable-lookup": "^5.0.3", 160 | "cacheable-request": "^7.0.1", 161 | "decompress-response": "^6.0.0", 162 | "http2-wrapper": "^1.0.0-beta.5.2", 163 | "lowercase-keys": "^2.0.0", 164 | "p-cancelable": "^2.0.0", 165 | "responselike": "^2.0.0" 166 | }, 167 | "engines": { 168 | "node": ">=10.19.0" 169 | } 170 | }, 171 | "node_modules/http-cache-semantics": { 172 | "version": "4.1.0", 173 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 174 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" 175 | }, 176 | "node_modules/http2-wrapper": { 177 | "version": "1.0.3", 178 | "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", 179 | "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", 180 | "dependencies": { 181 | "quick-lru": "^5.1.1", 182 | "resolve-alpn": "^1.0.0" 183 | }, 184 | "engines": { 185 | "node": ">=10.19.0" 186 | } 187 | }, 188 | "node_modules/json-buffer": { 189 | "version": "3.0.1", 190 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 191 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" 192 | }, 193 | "node_modules/keyv": { 194 | "version": "4.0.3", 195 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", 196 | "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", 197 | "dependencies": { 198 | "json-buffer": "3.0.1" 199 | } 200 | }, 201 | "node_modules/lowercase-keys": { 202 | "version": "2.0.0", 203 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 204 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", 205 | "engines": { 206 | "node": ">=8" 207 | } 208 | }, 209 | "node_modules/mimic-response": { 210 | "version": "1.0.1", 211 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 212 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", 213 | "engines": { 214 | "node": ">=4" 215 | } 216 | }, 217 | "node_modules/normalize-url": { 218 | "version": "4.5.0", 219 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", 220 | "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", 221 | "engines": { 222 | "node": ">=8" 223 | } 224 | }, 225 | "node_modules/once": { 226 | "version": "1.4.0", 227 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 228 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 229 | "dependencies": { 230 | "wrappy": "1" 231 | } 232 | }, 233 | "node_modules/p-cancelable": { 234 | "version": "2.1.0", 235 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", 236 | "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==", 237 | "engines": { 238 | "node": ">=8" 239 | } 240 | }, 241 | "node_modules/pump": { 242 | "version": "3.0.0", 243 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 244 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 245 | "dependencies": { 246 | "end-of-stream": "^1.1.0", 247 | "once": "^1.3.1" 248 | } 249 | }, 250 | "node_modules/quick-lru": { 251 | "version": "5.1.1", 252 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 253 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", 254 | "engines": { 255 | "node": ">=10" 256 | } 257 | }, 258 | "node_modules/resolve-alpn": { 259 | "version": "1.1.2", 260 | "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz", 261 | "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==" 262 | }, 263 | "node_modules/responselike": { 264 | "version": "2.0.0", 265 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", 266 | "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", 267 | "dependencies": { 268 | "lowercase-keys": "^2.0.0" 269 | } 270 | }, 271 | "node_modules/wrappy": { 272 | "version": "1.0.2", 273 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 274 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 275 | } 276 | }, 277 | "dependencies": { 278 | "@sindresorhus/is": { 279 | "version": "4.0.1", 280 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.1.tgz", 281 | "integrity": "sha512-Qm9hBEBu18wt1PO2flE7LPb30BHMQt1eQgbV76YntdNk73XZGpn3izvGTYxbGgzXKgbCjiia0uxTd3aTNQrY/g==" 282 | }, 283 | "@szmarczak/http-timer": { 284 | "version": "4.0.5", 285 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", 286 | "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", 287 | "requires": { 288 | "defer-to-connect": "^2.0.0" 289 | } 290 | }, 291 | "@types/cacheable-request": { 292 | "version": "6.0.1", 293 | "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", 294 | "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", 295 | "requires": { 296 | "@types/http-cache-semantics": "*", 297 | "@types/keyv": "*", 298 | "@types/node": "*", 299 | "@types/responselike": "*" 300 | } 301 | }, 302 | "@types/http-cache-semantics": { 303 | "version": "4.0.0", 304 | "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", 305 | "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==" 306 | }, 307 | "@types/keyv": { 308 | "version": "3.1.1", 309 | "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", 310 | "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", 311 | "requires": { 312 | "@types/node": "*" 313 | } 314 | }, 315 | "@types/node": { 316 | "version": "14.14.41", 317 | "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.41.tgz", 318 | "integrity": "sha512-dueRKfaJL4RTtSa7bWeTK1M+VH+Gns73oCgzvYfHZywRCoPSd8EkXBL0mZ9unPTveBn+D9phZBaxuzpwjWkW0g==" 319 | }, 320 | "@types/responselike": { 321 | "version": "1.0.0", 322 | "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", 323 | "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", 324 | "requires": { 325 | "@types/node": "*" 326 | } 327 | }, 328 | "cacheable-lookup": { 329 | "version": "5.0.4", 330 | "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", 331 | "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==" 332 | }, 333 | "cacheable-request": { 334 | "version": "7.0.1", 335 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", 336 | "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", 337 | "requires": { 338 | "clone-response": "^1.0.2", 339 | "get-stream": "^5.1.0", 340 | "http-cache-semantics": "^4.0.0", 341 | "keyv": "^4.0.0", 342 | "lowercase-keys": "^2.0.0", 343 | "normalize-url": "^4.1.0", 344 | "responselike": "^2.0.0" 345 | } 346 | }, 347 | "clone-response": { 348 | "version": "1.0.2", 349 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 350 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 351 | "requires": { 352 | "mimic-response": "^1.0.0" 353 | } 354 | }, 355 | "decompress-response": { 356 | "version": "6.0.0", 357 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", 358 | "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", 359 | "requires": { 360 | "mimic-response": "^3.1.0" 361 | }, 362 | "dependencies": { 363 | "mimic-response": { 364 | "version": "3.1.0", 365 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", 366 | "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" 367 | } 368 | } 369 | }, 370 | "defer-to-connect": { 371 | "version": "2.0.1", 372 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", 373 | "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==" 374 | }, 375 | "end-of-stream": { 376 | "version": "1.4.4", 377 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 378 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 379 | "requires": { 380 | "once": "^1.4.0" 381 | } 382 | }, 383 | "get-stream": { 384 | "version": "5.2.0", 385 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", 386 | "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", 387 | "requires": { 388 | "pump": "^3.0.0" 389 | } 390 | }, 391 | "got": { 392 | "version": "11.8.2", 393 | "resolved": "https://registry.npmjs.org/got/-/got-11.8.2.tgz", 394 | "integrity": "sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ==", 395 | "requires": { 396 | "@sindresorhus/is": "^4.0.0", 397 | "@szmarczak/http-timer": "^4.0.5", 398 | "@types/cacheable-request": "^6.0.1", 399 | "@types/responselike": "^1.0.0", 400 | "cacheable-lookup": "^5.0.3", 401 | "cacheable-request": "^7.0.1", 402 | "decompress-response": "^6.0.0", 403 | "http2-wrapper": "^1.0.0-beta.5.2", 404 | "lowercase-keys": "^2.0.0", 405 | "p-cancelable": "^2.0.0", 406 | "responselike": "^2.0.0" 407 | } 408 | }, 409 | "http-cache-semantics": { 410 | "version": "4.1.0", 411 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 412 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" 413 | }, 414 | "http2-wrapper": { 415 | "version": "1.0.3", 416 | "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", 417 | "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", 418 | "requires": { 419 | "quick-lru": "^5.1.1", 420 | "resolve-alpn": "^1.0.0" 421 | } 422 | }, 423 | "json-buffer": { 424 | "version": "3.0.1", 425 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 426 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" 427 | }, 428 | "keyv": { 429 | "version": "4.0.3", 430 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz", 431 | "integrity": "sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA==", 432 | "requires": { 433 | "json-buffer": "3.0.1" 434 | } 435 | }, 436 | "lowercase-keys": { 437 | "version": "2.0.0", 438 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 439 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" 440 | }, 441 | "mimic-response": { 442 | "version": "1.0.1", 443 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 444 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" 445 | }, 446 | "normalize-url": { 447 | "version": "4.5.0", 448 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", 449 | "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" 450 | }, 451 | "once": { 452 | "version": "1.4.0", 453 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 454 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 455 | "requires": { 456 | "wrappy": "1" 457 | } 458 | }, 459 | "p-cancelable": { 460 | "version": "2.1.0", 461 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.0.tgz", 462 | "integrity": "sha512-HAZyB3ZodPo+BDpb4/Iu7Jv4P6cSazBz9ZM0ChhEXp70scx834aWCEjQRwgt41UzzejUAPdbqqONfRWTPYrPAQ==" 463 | }, 464 | "pump": { 465 | "version": "3.0.0", 466 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 467 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 468 | "requires": { 469 | "end-of-stream": "^1.1.0", 470 | "once": "^1.3.1" 471 | } 472 | }, 473 | "quick-lru": { 474 | "version": "5.1.1", 475 | "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", 476 | "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==" 477 | }, 478 | "resolve-alpn": { 479 | "version": "1.1.2", 480 | "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.1.2.tgz", 481 | "integrity": "sha512-8OyfzhAtA32LVUsJSke3auIyINcwdh5l3cvYKdKO0nvsYSKuiLfTM5i78PJswFPT8y6cPW+L1v6/hE95chcpDA==" 482 | }, 483 | "responselike": { 484 | "version": "2.0.0", 485 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", 486 | "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", 487 | "requires": { 488 | "lowercase-keys": "^2.0.0" 489 | } 490 | }, 491 | "wrappy": { 492 | "version": "1.0.2", 493 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 494 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 495 | } 496 | } 497 | } 498 | --------------------------------------------------------------------------------