9 |
10 |
11 |
26 |
--------------------------------------------------------------------------------
/Code/client/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | publicPath: '/',
3 | chainWebpack: config => {
4 | config.module.rules.delete('eslint');
5 | },
6 | devServer: {
7 | port: 3000,
8 | disableHostCheck: true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Code/server/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | FROM public.ecr.aws/bitnami/node:latest
5 |
6 | RUN mkdir -p /home/node/app/node_modules
7 |
8 | WORKDIR /home/node/app
9 |
10 | COPY package*.json ./
11 |
12 | RUN npm install
13 |
14 | COPY . .
15 |
16 | EXPOSE 3001
17 |
18 | CMD [ "npm", "start" ]
--------------------------------------------------------------------------------
/Code/server/README.md:
--------------------------------------------------------------------------------
1 | # Backend of the demo app
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 | ### Compiles and hot-reloads for development
8 | ```
9 | npm start
10 | ```
--------------------------------------------------------------------------------
/Code/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "aws-ecs-demo-backend",
3 | "version": "1.0.0",
4 | "description": "AWS Demo",
5 | "author": "Marina Burkhardt",
6 | "private": true,
7 | "license": "MIT",
8 | "main": "app.js",
9 | "scripts": {
10 | "start": "node ./src/app.js --exec \"npm run lint && node\""
11 | },
12 | "keywords": [
13 | "nodejs",
14 | "express"
15 | ],
16 | "dependencies": {
17 | "artillery": "^1.6.2",
18 | "aws-sdk": "^2.876.0",
19 | "cors": "^2.8.5",
20 | "express": "^4.16.4",
21 | "swagger-jsdoc": "6.0.0",
22 | "swagger-model-validator": "^3.0.20",
23 | "swagger-ui-express": "^4.1.6"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Code/server/src/app.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | const express = require('express');
5 | const app = express();
6 | var cors = require('cors');
7 | const AWS = require('aws-sdk')
8 | const port = 3001;
9 |
10 | app.use(cors());
11 |
12 | var swagger = require('./swagger/swagger')
13 | app.use('/api/docs', swagger.router)
14 |
15 | /**
16 | * @swagger
17 | * /status:
18 | * get:
19 | * description: Dummy endpoint used as an API health check
20 | * tags:
21 | * - Status
22 | * produces:
23 | * - application/json
24 | * responses:
25 | * 200:
26 | * description: Retrieves a string with a health status
27 | */
28 | app.get('/status', (req, res) => {
29 | res.send({
30 | message: 'AWS Demo server is up and running!'
31 | })
32 | })
33 |
34 | /**
35 | * @swagger
36 | * /api/getAllProducts:
37 | * get:
38 | * description: Retrieves all products available in the Dynamodb table
39 | * tags:
40 | * - Products
41 | * produces:
42 | * - application/json
43 | * responses:
44 | * 200:
45 | * description: Retrieves a list of products
46 | */
47 | app.get('/api/getAllProducts', (req, res) => {
48 | AB3_TABLE = "DYNAMODB_TABLE" //DYNAMODB_TABLE value is retrieved from the generated resources created by the terraform code
49 | const docClient = new AWS.DynamoDB.DocumentClient();
50 | const params = {
51 | TableName: AB3_TABLE
52 | }
53 |
54 | docClient.scan(params, function(err, data) {
55 | if (err) {
56 | res.send({
57 | code: err.status,
58 | description: err.message
59 | });
60 | } else {
61 | var products = data.Items
62 | res.send({
63 | products
64 | });
65 | }
66 | });
67 |
68 | })
69 |
70 | // catch 404 and forward to error handler
71 | app.use(function (req, res, next) {
72 | var err = new Error('Not Found')
73 | err.status = 404
74 | next(err)
75 | })
76 |
77 | // error handler
78 | app.use(function (err, req, res, next) {
79 | console.error(`Error catched! ${err}`)
80 |
81 | let error = {
82 | code: err.status,
83 | description: err.message
84 | }
85 | status: err.status || 500
86 |
87 | res.status(error.code).send(error)
88 | })
89 |
90 | app.listen(port)
91 | console.log('Server started on port ' + port)
--------------------------------------------------------------------------------
/Code/server/src/swagger/swagger.js:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | const express = require('express')
5 | const router = express.Router()
6 |
7 | const options = {
8 | swaggerDefinition: {
9 | info: {
10 | title: 'AWS Demo',
11 | version: '1.0.0',
12 | description: 'An AWS Demo for a full stack application with Amazon ECS, applying DevOps practices',
13 | contact: {
14 | email: 'burkhmar@amazon.de'
15 | }
16 | },
17 | tags: [
18 | {
19 | name: 'AWS Demo Endpoints',
20 | description: 'Enpoints descriptions'
21 | }
22 | ],
23 | schemes: ['http'],
24 | host: '',
25 | basePath: '/'
26 | },
27 | apis: [
28 | './src/app.js',
29 | ],
30 | }
31 |
32 | const swaggerJSDoc = require('swagger-jsdoc')
33 | const swaggerUi = require('swagger-ui-express')
34 | const swaggerSpec = swaggerJSDoc(options)
35 | require('swagger-model-validator')(swaggerSpec)
36 |
37 | router.get('/json', function (req, res) {
38 | res.setHeader('Content-Type', 'application/json')
39 | res.send(swaggerSpec)
40 | })
41 |
42 | router.use('/', swaggerUi.serve, swaggerUi.setup(swaggerSpec))
43 |
44 | function validateModel(name, model) {
45 | const responseValidation = swaggerSpec.validateModel(name, model, false, true)
46 | if (!responseValidation.valid) {
47 | console.error(responseValidation.errors)
48 | throw new Error(`Model doesn't match Swagger contract`)
49 | }
50 | }
51 |
52 | module.exports = {
53 | router,
54 | validateModel
55 | }
56 |
--------------------------------------------------------------------------------
/Code/server/src/tests/stresstests/stress_server.yml:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | config:
5 | target: "" # Change me to the right env target
6 | phases:
7 | - duration: 100
8 | arrivalRate: 20
9 | http:
10 | timeout: 5 # Wait 5 sec before aborting the request
11 | pool: 50 # Fixed number of pool connection, to be reused
12 | scenarios:
13 | - name: "Generating load on the server fleet of tasks"
14 | flow:
15 | - loop:
16 | - get:
17 | url: "/api/getAllProducts" # Change me to "/" if necessary
18 | count: 10
--------------------------------------------------------------------------------
/Documentation_assets/CICD_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NinjaDevOps0831/amazon-ecs-fullstack-app-terraform/f25b5de6cf26a771429049628fdcd0c06fdcf22b/Documentation_assets/CICD_architecture.png
--------------------------------------------------------------------------------
/Documentation_assets/Infrastructure_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NinjaDevOps0831/amazon-ecs-fullstack-app-terraform/f25b5de6cf26a771429049628fdcd0c06fdcf22b/Documentation_assets/Infrastructure_architecture.png
--------------------------------------------------------------------------------
/Infrastructure/Modules/ALB/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*==============================================================
5 | AWS Application Load Balancer + Target groups
6 | ===============================================================*/
7 |
8 | resource "aws_alb" "alb" {
9 | count = var.create_alb == true ? 1 : 0
10 | name = "alb-${var.name}"
11 | subnets = [var.subnets[0], var.subnets[1]]
12 | security_groups = [var.security_group]
13 | load_balancer_type = "application"
14 | internal = false
15 | enable_http2 = true
16 | idle_timeout = 30
17 | }
18 |
19 | # ------- ALB Listenet for HTTPS -------
20 | resource "aws_alb_listener" "https_listener" {
21 | count = var.create_alb == true ? (var.enable_https == true ? 1 : 0) : 0
22 | load_balancer_arn = aws_alb.alb[0].id
23 | port = "443"
24 | protocol = "HTTPS"
25 |
26 | default_action {
27 | target_group_arn = var.target_group
28 | type = "forward"
29 | }
30 |
31 | lifecycle {
32 | // to avoid changes generated by CodeDeploy changes
33 | ignore_changes = [default_action]
34 | }
35 | }
36 |
37 | # ------- ALB Listener for HTTP -------
38 | resource "aws_alb_listener" "http_listener" {
39 | count = var.create_alb == true ? 1 : 0
40 | load_balancer_arn = aws_alb.alb[0].id
41 | port = "80"
42 | protocol = "HTTP"
43 |
44 | default_action {
45 | target_group_arn = var.target_group
46 | type = "forward"
47 | }
48 |
49 | lifecycle {
50 | // to avoid changes generated by CodeDeploy changes
51 | ignore_changes = [default_action]
52 | }
53 | }
54 |
55 | # ------- Target Groups for ALB -------
56 | resource "aws_alb_target_group" "target_group" {
57 | count = var.create_target_group == true ? 1 : 0
58 | name = var.name
59 | port = var.port
60 | protocol = var.protocol
61 | vpc_id = var.vpc
62 | target_type = var.tg_type
63 | deregistration_delay = 5
64 |
65 | health_check {
66 | enabled = true
67 | interval = 15
68 | path = var.health_check_path
69 | port = var.health_check_port
70 | protocol = var.protocol
71 | timeout = 10
72 | healthy_threshold = 2
73 | unhealthy_threshold = 3
74 | matcher = "200"
75 | }
76 |
77 | lifecycle {
78 | create_before_destroy = true
79 | }
80 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ALB/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "arn_alb" {
5 | value = (var.create_alb == true
6 | ? (length(aws_alb.alb) > 0 ? aws_alb.alb[0].arn : "") : "")
7 | }
8 | output "arn_tg" {
9 | value = (var.create_target_group == true
10 | ? (length(aws_alb_target_group.target_group) > 0 ? aws_alb_target_group.target_group[0].arn : "") : "")
11 | }
12 |
13 | output "tg_name" {
14 | value = (var.create_target_group == true
15 | ? (length(aws_alb_target_group.target_group) > 0 ? aws_alb_target_group.target_group[0].name : "") : "")
16 | }
17 |
18 | output "arn_listener" {
19 | value = (var.create_alb == true
20 | ? (length(aws_alb_listener.http_listener) > 0 ? aws_alb_listener.http_listener[0].arn : "") : "")
21 | }
22 |
23 | output "dns_alb" {
24 | value = (var.create_alb == true
25 | ? (length(aws_alb.alb) > 0 ? aws_alb.alb[0].dns_name : "") : "")
26 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ALB/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "A name for the target group or ALB"
6 | type = string
7 | }
8 |
9 | variable "target_group" {
10 | description = "The ARN of the created target group"
11 | type = string
12 | default = ""
13 | }
14 |
15 | variable "target_group_green" {
16 | description = "The ANR of the created target group"
17 | type = string
18 | default = ""
19 | }
20 |
21 | variable "create_alb" {
22 | description = "Set to true to create an ALB"
23 | type = bool
24 | default = false
25 | }
26 |
27 | variable "enable_https" {
28 | description = "Set to true to create a HTTPS listener"
29 | type = bool
30 | default = false
31 | }
32 |
33 | variable "create_target_group" {
34 | description = "Set to true to create a Target Group"
35 | type = bool
36 | default = false
37 | }
38 |
39 | variable "subnets" {
40 | description = "Subnets IDs for ALB"
41 | type = list(any)
42 | default = []
43 | }
44 |
45 | variable "security_group" {
46 | description = "Security group ID for the ALB"
47 | type = string
48 | default = ""
49 | }
50 |
51 | variable "port" {
52 | description = "The port that the targer group will use"
53 | type = number
54 | default = 80
55 | }
56 |
57 | variable "protocol" {
58 | description = "The protocol that the target group will use"
59 | type = string
60 | default = ""
61 | }
62 |
63 | variable "vpc" {
64 | description = "VPC ID for the Target Group"
65 | type = string
66 | default = ""
67 | }
68 |
69 | variable "tg_type" {
70 | description = "Target Group Type (instance, IP, lambda)"
71 | type = string
72 | default = ""
73 | }
74 |
75 | variable "health_check_path" {
76 | description = "The path in which the ALB will send health checks"
77 | type = string
78 | default = ""
79 | }
80 |
81 | variable "health_check_port" {
82 | description = "The port to which the ALB will send health checks"
83 | type = number
84 | default = 80
85 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeBuild/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*==================================
5 | AWS CodeBuild Project
6 | ===================================*/
7 |
8 | resource "aws_codebuild_project" "aws_codebuild" {
9 | name = var.name
10 | description = "Terraform codebuild project"
11 | build_timeout = "10"
12 | service_role = var.iam_role
13 |
14 | artifacts {
15 | type = "CODEPIPELINE"
16 | }
17 |
18 | environment {
19 | compute_type = "BUILD_GENERAL1_SMALL"
20 | image = "aws/codebuild/standard:4.0"
21 | type = "LINUX_CONTAINER"
22 | privileged_mode = true
23 |
24 | environment_variable {
25 | name = "AWS_REGION"
26 | value = var.region
27 | }
28 |
29 | environment_variable {
30 | name = "AWS_ACCOUNT_ID"
31 | value = var.account_id
32 | }
33 |
34 | environment_variable {
35 | name = "REPO_URL"
36 | value = var.ecr_repo_url
37 | }
38 |
39 | environment_variable {
40 | name = "IMAGE_TAG"
41 | value = "latest"
42 | }
43 |
44 | environment_variable {
45 | name = "DYNAMODB_TABLE"
46 | value = var.dynamodb_table_name
47 | }
48 |
49 | environment_variable {
50 | name = "TASK_DEFINITION_FAMILY"
51 | value = var.task_definition_family
52 | }
53 |
54 | environment_variable {
55 | name = "CONTAINER_NAME"
56 | value = var.container_name
57 | }
58 |
59 | environment_variable {
60 | name = "SERVICE_PORT"
61 | value = var.service_port
62 | }
63 |
64 | environment_variable {
65 | name = "FOLDER_PATH"
66 | value = var.folder_path
67 | }
68 |
69 | environment_variable {
70 | name = "ECS_ROLE"
71 | value = var.ecs_role
72 | }
73 |
74 | environment_variable {
75 | name = "ECS_TASK_ROLE"
76 | value = var.ecs_task_role
77 | }
78 |
79 | environment_variable {
80 | name = "SERVER_ALB_URL"
81 | value = var.server_alb_url
82 | }
83 | }
84 |
85 | logs_config {
86 | cloudwatch_logs {
87 | group_name = "log-group"
88 | stream_name = "log-stream"
89 | }
90 | }
91 |
92 | source {
93 | type = "CODEPIPELINE"
94 | buildspec = var.buildspec_path
95 | }
96 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeBuild/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "project_id" {
5 | value = aws_codebuild_project.aws_codebuild.id
6 | }
7 |
8 | output "project_arn" {
9 | value = aws_codebuild_project.aws_codebuild.arn
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeBuild/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | type = string
6 | description = "CodeBuild Project name"
7 | }
8 |
9 | variable "iam_role" {
10 | type = string
11 | description = "IAM role to attach to CodeBuild"
12 | }
13 | variable "region" {
14 | type = string
15 | description = "AWS Region used"
16 | }
17 | variable "account_id" {
18 | description = "AWS Account ID where the solution is being deployed"
19 | type = string
20 | }
21 | variable "ecr_repo_url" {
22 | description = "AWS ECR repository URL where docker images are being stored"
23 | type = string
24 | }
25 |
26 | variable "folder_path" {
27 | description = "Folder path to use to build the docker images/containers"
28 | type = string
29 | }
30 |
31 | variable "buildspec_path" {
32 | description = "Path to for the Buildspec file"
33 | type = string
34 | }
35 |
36 | variable "task_definition_family" {
37 | description = "The family name of the Task definition"
38 | type = string
39 | }
40 |
41 | variable "container_name" {
42 | description = "The name of the Container specified in the Task definition"
43 | type = string
44 | }
45 |
46 | variable "service_port" {
47 | description = "The number of the port used by the ECS Service"
48 | type = number
49 | }
50 |
51 | variable "ecs_role" {
52 | description = "The name of the ECS Task Excecution role to specify in the Task Definition"
53 | type = string
54 | }
55 |
56 | variable "server_alb_url" {
57 | description = "The server ALB DNS. Used to build the code for the frontend layer"
58 | type = string
59 | default = ""
60 | }
61 |
62 | variable "ecs_task_role" {
63 | description = "The name of the ECS Task role to specify in the Task Definition"
64 | type = string
65 | default = "null"
66 | }
67 |
68 | variable "dynamodb_table_name" {
69 | description = "The name of Dynamodb table used by the server application"
70 | type = string
71 | default = ""
72 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeDeploy/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*====================================================================
5 | AWS CodeDeploy integration for Blue/Green Deployments.
6 | ====================================================================*/
7 |
8 | # ------- AWS CodeDeploy App defintion for each module -------
9 | resource "aws_codedeploy_app" "main" {
10 | compute_platform = "ECS"
11 | name = var.name
12 | }
13 |
14 | # ------- AWS CodeDeploy Group for each CodeDeploy App created -------
15 | resource "aws_codedeploy_deployment_group" "main" {
16 | app_name = aws_codedeploy_app.main.name
17 | deployment_config_name = "CodeDeployDefault.ECSAllAtOnce"
18 | deployment_group_name = "deployment-group-${var.name}"
19 | service_role_arn = var.codedeploy_role
20 |
21 | auto_rollback_configuration {
22 | enabled = true
23 | events = ["DEPLOYMENT_FAILURE"]
24 | }
25 |
26 | blue_green_deployment_config {
27 | deployment_ready_option {
28 | action_on_timeout = "CONTINUE_DEPLOYMENT"
29 | }
30 |
31 | terminate_blue_instances_on_deployment_success {
32 | action = "TERMINATE"
33 | termination_wait_time_in_minutes = 5
34 | }
35 | }
36 |
37 | deployment_style {
38 | deployment_option = "WITH_TRAFFIC_CONTROL"
39 | deployment_type = "BLUE_GREEN"
40 | }
41 |
42 | ecs_service {
43 | cluster_name = var.ecs_cluster
44 | service_name = var.ecs_service
45 | }
46 |
47 | load_balancer_info {
48 | target_group_pair_info {
49 | prod_traffic_route {
50 | listener_arns = [
51 | var.alb_listener
52 | ]
53 | }
54 |
55 | target_group {
56 | name = var.tg_blue
57 | }
58 |
59 | target_group {
60 | name = var.tg_green
61 | }
62 | }
63 | }
64 |
65 | trigger_configuration {
66 | trigger_events = [
67 | "DeploymentSuccess",
68 | "DeploymentFailure",
69 | ]
70 |
71 | trigger_name = var.trigger_name
72 | trigger_target_arn = var.sns_topic_arn
73 | }
74 |
75 | lifecycle {
76 | ignore_changes = [blue_green_deployment_config]
77 | }
78 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeDeploy/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "deployment_group_name" {
5 | value = aws_codedeploy_deployment_group.main.deployment_group_name
6 | }
7 |
8 | output "deployment_group_arn" {
9 | value = aws_codedeploy_deployment_group.main.arn
10 | }
11 |
12 | output "application_name" {
13 | value = aws_codedeploy_deployment_group.main.app_name
14 | }
15 |
16 | output "application_arn" {
17 | value = aws_codedeploy_app.main.arn
18 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodeDeploy/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name of the CodeDeploy application"
6 | type = string
7 | }
8 |
9 | variable "ecs_cluster" {
10 | description = "The name of the ECS cluster where to deploy"
11 | type = string
12 | }
13 |
14 | variable "ecs_service" {
15 | description = "The name of the ECS service to deploy"
16 | type = string
17 | }
18 |
19 | variable "alb_listener" {
20 | description = "The ARN of the ALB listener for production"
21 | type = string
22 | }
23 |
24 | variable "tg_blue" {
25 | description = "The Target group name for the Blue part"
26 | type = string
27 | }
28 |
29 | variable "tg_green" {
30 | description = "The Target group name for the Green part"
31 | type = string
32 | }
33 |
34 | variable "sns_topic_arn" {
35 | description = "The ARN of the SNS topic where to deliver notifications"
36 | type = string
37 | }
38 |
39 | variable "trigger_name" {
40 | description = "The name of the notification trigger"
41 | type = string
42 | default = "CodeDeploy_notification"
43 | }
44 |
45 | variable "codedeploy_role" {
46 | description = "The role to be assumed by CodeDeploy"
47 | type = string
48 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodePipeline/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*=======================================================
5 | AWS CodePipeline for build and deployment
6 | ========================================================*/
7 |
8 | resource "aws_codepipeline" "aws_codepipeline" {
9 | name = var.name
10 | role_arn = var.pipe_role
11 |
12 | artifact_store {
13 | location = var.s3_bucket
14 | type = "S3"
15 | }
16 |
17 | stage {
18 | name = "Source"
19 |
20 | action {
21 | name = "Source"
22 | category = "Source"
23 | owner = "ThirdParty"
24 | provider = "GitHub"
25 | version = "1"
26 | output_artifacts = ["SourceArtifact"]
27 |
28 | configuration = {
29 | OAuthToken = var.github_token
30 | Owner = var.repo_owner
31 | Repo = var.repo_name
32 | Branch = var.branch
33 | PollForSourceChanges = true
34 | }
35 | }
36 | }
37 |
38 | stage {
39 | name = "Build"
40 |
41 | action {
42 | name = "Build_server"
43 | category = "Build"
44 | owner = "AWS"
45 | provider = "CodeBuild"
46 | version = "1"
47 | input_artifacts = ["SourceArtifact"]
48 | output_artifacts = ["BuildArtifact_server"]
49 |
50 | configuration = {
51 | ProjectName = var.codebuild_project_server
52 | }
53 | }
54 |
55 | action {
56 | name = "Build_client"
57 | category = "Build"
58 | owner = "AWS"
59 | provider = "CodeBuild"
60 | version = "1"
61 | input_artifacts = ["SourceArtifact"]
62 | output_artifacts = ["BuildArtifact_client"]
63 | configuration = {
64 | ProjectName = var.codebuild_project_client
65 | }
66 | }
67 | }
68 |
69 | stage {
70 | name = "Deploy"
71 |
72 | action {
73 | name = "Deploy_server"
74 | category = "Deploy"
75 | owner = "AWS"
76 | provider = "CodeDeployToECS"
77 | input_artifacts = ["BuildArtifact_server"]
78 | version = "1"
79 |
80 | configuration = {
81 | ApplicationName = var.app_name_server
82 | DeploymentGroupName = var.deployment_group_server
83 | TaskDefinitionTemplateArtifact = "BuildArtifact_server"
84 | TaskDefinitionTemplatePath = "taskdef.json"
85 | AppSpecTemplateArtifact = "BuildArtifact_server"
86 | AppSpecTemplatePath = "appspec.yaml"
87 | }
88 | }
89 |
90 | action {
91 | name = "Deploy_client"
92 | category = "Deploy"
93 | owner = "AWS"
94 | provider = "CodeDeployToECS"
95 | input_artifacts = ["BuildArtifact_client"]
96 | version = "1"
97 |
98 | configuration = {
99 | ApplicationName = var.app_name_client
100 | DeploymentGroupName = var.deployment_group_client
101 | TaskDefinitionTemplateArtifact = "BuildArtifact_client"
102 | TaskDefinitionTemplatePath = "taskdef.json"
103 | AppSpecTemplateArtifact = "BuildArtifact_client"
104 | AppSpecTemplatePath = "appspec.yaml"
105 | }
106 | }
107 | }
108 |
109 | lifecycle {
110 | # prevents github OAuthToken from causing updates, since it's removed from state file
111 | ignore_changes = [stage[0].action[0].configuration]
112 | }
113 |
114 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodePipeline/outputs.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NinjaDevOps0831/amazon-ecs-fullstack-app-terraform/f25b5de6cf26a771429049628fdcd0c06fdcf22b/Infrastructure/Modules/CodePipeline/outputs.tf
--------------------------------------------------------------------------------
/Infrastructure/Modules/CodePipeline/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The CodePipeline pipeline name"
6 | type = string
7 | }
8 |
9 | variable "pipe_role" {
10 | description = "The role assumed by CodePipeline"
11 | type = string
12 | }
13 |
14 | variable "s3_bucket" {
15 | description = "S3 bucket used for the artifact store"
16 | type = string
17 | }
18 |
19 | variable "github_token" {
20 | description = "Personal access token from Github"
21 | type = string
22 | sensitive = true
23 | }
24 |
25 | variable "repo_owner" {
26 | description = "The username of the Github repository owner"
27 | type = string
28 | }
29 |
30 | variable "repo_name" {
31 | description = "Github repository's name"
32 | type = string
33 | }
34 |
35 | variable "branch" {
36 | description = "Github branch used to trigger the CodePipeline"
37 | type = string
38 | }
39 |
40 | variable "codebuild_project_server" {
41 | description = "Server's CodeBuild project name"
42 | type = string
43 | }
44 |
45 | variable "codebuild_project_client" {
46 | description = "Client's CodeBuild project name"
47 | type = string
48 | }
49 |
50 | variable "app_name_server" {
51 | description = "CodeDeploy Application name for the server"
52 | type = string
53 | }
54 |
55 | variable "app_name_client" {
56 | description = "CodeDeploy Application name for the client"
57 | type = string
58 | }
59 |
60 | variable "deployment_group_server" {
61 | description = "CodeDeploy deployment group name for the server"
62 | type = string
63 | }
64 |
65 | variable "deployment_group_client" {
66 | description = "CodeDeploy deployment group name for the client"
67 | type = string
68 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Dynamodb/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*=======================================
5 | Amazon Dynamodb resources
6 | ========================================*/
7 |
8 | resource "aws_dynamodb_table" "dynamodb_table" {
9 | name = var.name
10 | billing_mode = "PAY_PER_REQUEST"
11 | hash_key = var.hash_key
12 | range_key = var.range_key
13 |
14 | dynamic "attribute" {
15 | for_each = var.attributes
16 | content {
17 | name = attribute.value.name
18 | type = attribute.value.type
19 | }
20 | }
21 |
22 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Dynamodb/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "dynamodb_table_arn" {
5 | value = aws_dynamodb_table.dynamodb_table.arn
6 | }
7 |
8 | output "dynamodb_table_name" {
9 | value = aws_dynamodb_table.dynamodb_table.name
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Dynamodb/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name of your Dynamodb table"
6 | type = string
7 | }
8 |
9 | variable "hash_key" {
10 | description = "The identifier of your hash key"
11 | type = string
12 | default = "id"
13 | }
14 |
15 | variable "range_key" {
16 | description = "The identifier of your range key"
17 | type = string
18 | default = null
19 | }
20 |
21 | variable "attributes" {
22 | description = "A set of atributes names and types that compone the table"
23 | type = list(object({ name = string, type = string }))
24 | default = [
25 | {
26 | name = "id",
27 | type = "N",
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECR/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*=========================================
5 | AWS Elastic Container Repository
6 | ==========================================*/
7 |
8 | resource "aws_ecr_repository" "ecr_repository" {
9 | name = var.name
10 | image_tag_mutability = "MUTABLE"
11 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECR/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "ecr_repository_url" {
5 | value = aws_ecr_repository.ecr_repository.repository_url
6 | }
7 |
8 | output "ecr_repository_arn" {
9 | value = aws_ecr_repository.ecr_repository.arn
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECR/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name of your ECR repository"
6 | type = string
7 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Autoscaling/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*====================================
5 | AWS ECS Autoscaling
6 | =====================================*/
7 |
8 | # ------- AWS Autoscaling target to linke the ECS cluster and service -------
9 | resource "aws_appautoscaling_target" "ecs_target" {
10 | min_capacity = var.min_capacity
11 | max_capacity = var.max_capacity
12 | resource_id = "service/${var.cluster_name}/Service-${var.name}"
13 | scalable_dimension = "ecs:service:DesiredCount"
14 | service_namespace = "ecs"
15 |
16 | lifecycle {
17 | ignore_changes = [
18 | role_arn,
19 | ]
20 | }
21 | }
22 |
23 | # ------- AWS Autoscaling policy using CPU allocation -------
24 | resource "aws_appautoscaling_policy" "cpu" {
25 | name = "ecs_scale_cpu_service_${var.name}"
26 | resource_id = aws_appautoscaling_target.ecs_target.resource_id
27 | scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
28 | service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
29 | policy_type = "TargetTrackingScaling"
30 |
31 | target_tracking_scaling_policy_configuration {
32 | target_value = 50
33 | scale_in_cooldown = 60
34 | scale_out_cooldown = 60
35 |
36 | predefined_metric_specification {
37 | predefined_metric_type = "ECSServiceAverageCPUUtilization"
38 | }
39 | }
40 | }
41 |
42 | # ------- AWS Autoscaling policy using memory allocation -------
43 | resource "aws_appautoscaling_policy" "memory" {
44 | name = "ecs_scale_memory_service_${var.name}"
45 | resource_id = aws_appautoscaling_target.ecs_target.resource_id
46 | scalable_dimension = aws_appautoscaling_target.ecs_target.scalable_dimension
47 | service_namespace = aws_appautoscaling_target.ecs_target.service_namespace
48 | policy_type = "TargetTrackingScaling"
49 |
50 | target_tracking_scaling_policy_configuration {
51 | target_value = 50
52 | scale_in_cooldown = 60
53 | scale_out_cooldown = 60
54 |
55 | predefined_metric_specification {
56 | predefined_metric_type = "ECSServiceAverageMemoryUtilization"
57 | }
58 | }
59 | }
60 |
61 | /*==============================================
62 | AWS Cloudwatch for ECS Autoscaling
63 | ===============================================*/
64 |
65 | # ------- High memory alarm -------
66 | resource "aws_cloudwatch_metric_alarm" "high-memory-policy-alarm" {
67 | alarm_name = "high-memory-ecs-service-${var.name}"
68 | alarm_description = "High Memory for ecs service-${var.name}"
69 | comparison_operator = "GreaterThanOrEqualToThreshold"
70 | evaluation_periods = "2"
71 | metric_name = "MemoryUtilization"
72 | namespace = "AWS/ECS"
73 | period = "60"
74 | statistic = "Maximum"
75 | threshold = 50
76 |
77 | dimensions = {
78 | "ServiceName" = "Service-${var.name}",
79 | "ClusterName" = var.cluster_name
80 | }
81 |
82 | }
83 |
84 | # ------- High CPU alarm -------
85 | resource "aws_cloudwatch_metric_alarm" "high-cpu-policy-alarm" {
86 | alarm_name = "high-cpu-ecs-service-${var.name}"
87 | alarm_description = "High CPUPolicy Landing Page for ecs service-${var.name}"
88 | comparison_operator = "GreaterThanOrEqualToThreshold"
89 | evaluation_periods = "2"
90 | metric_name = "CPUUtilization"
91 | namespace = "AWS/ECS"
92 | period = "60"
93 | statistic = "Maximum"
94 | threshold = 50
95 |
96 | dimensions = {
97 | "ServiceName" = "Service-${var.name}",
98 | "ClusterName" = var.cluster_name
99 | }
100 |
101 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Autoscaling/outputs.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NinjaDevOps0831/amazon-ecs-fullstack-app-terraform/f25b5de6cf26a771429049628fdcd0c06fdcf22b/Infrastructure/Modules/ECS/Autoscaling/outputs.tf
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Autoscaling/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "min_capacity" {
5 | description = "The minimal number of ECS tasks to run"
6 | type = number
7 | }
8 |
9 | variable "max_capacity" {
10 | description = "The maximal number of ECS tasks to run"
11 | type = number
12 | }
13 |
14 | variable "cluster_name" {
15 | description = "The name of the ECS cluster"
16 | type = string
17 | }
18 |
19 | variable "name" {
20 | description = "The name for the ECS service"
21 | type = string
22 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Cluster/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*=============================
5 | AWS ECS Cluster
6 | ===============================*/
7 |
8 | resource "aws_ecs_cluster" "ecs_cluster" {
9 | name = "Cluster-${var.name}"
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Cluster/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "ecs_cluster_name" {
5 | value = aws_ecs_cluster.ecs_cluster.name
6 | }
7 |
8 | output "ecs_cluster_id" {
9 | value = aws_ecs_cluster.ecs_cluster.id
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Cluster/variable.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name of the deployed environment"
6 | type = string
7 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Service/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*==========================
5 | AWS ECS Service
6 | ===========================*/
7 |
8 | resource "aws_ecs_service" "ecs_service" {
9 | name = "Service-${var.name}"
10 | cluster = var.ecs_cluster_id
11 | task_definition = var.arn_task_definition
12 | desired_count = var.desired_tasks
13 | health_check_grace_period_seconds = 10
14 | launch_type = "FARGATE"
15 |
16 | network_configuration {
17 | security_groups = [var.arn_security_group]
18 | subnets = [var.subnets_id[0], var.subnets_id[1]]
19 | }
20 |
21 | load_balancer {
22 | target_group_arn = var.arn_target_group
23 | container_name = var.container_name
24 | container_port = var.container_port
25 | }
26 |
27 | deployment_controller {
28 | type = "CODE_DEPLOY"
29 | }
30 |
31 | lifecycle {
32 | // to avoid changes generated by autoscaling or new CodeDeploy changes
33 | ignore_changes = [desired_count, task_definition, load_balancer]
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Service/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "ecs_service_name" {
5 | value = aws_ecs_service.ecs_service.name
6 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/Service/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name for the ecs service"
6 | type = string
7 | }
8 |
9 | variable "desired_tasks" {
10 | description = "The minumum number of tasks to run in the service"
11 | type = string
12 | }
13 |
14 | variable "arn_security_group" {
15 | description = "ARN of the security group for the tasks"
16 | type = string
17 | }
18 |
19 | variable "ecs_cluster_id" {
20 | description = "The ECS cluster ID in which the resources will be created"
21 | type = string
22 | }
23 |
24 | variable "arn_target_group" {
25 | description = "The ARN of the AWS Target Group to put the ECS task"
26 | type = string
27 | }
28 |
29 | variable "arn_task_definition" {
30 | description = "The ARN of the Task Definition to use to deploy the tasks"
31 | type = string
32 | }
33 |
34 | variable "subnets_id" {
35 | description = "Subnet ID in which ecs will deploy the tasks"
36 | type = list(string)
37 | }
38 |
39 | variable "container_port" {
40 | description = "The port that the container will listen request"
41 | type = string
42 | }
43 |
44 | variable "container_name" {
45 | description = "The name of the container"
46 | type = string
47 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/ECS/TaskDefinition/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*====================================
5 | AWS ECS Task definition
6 | =====================================*/
7 |
8 | resource "aws_ecs_task_definition" "ecs_task_definition" {
9 | family = "task-definition-${var.name}"
10 | network_mode = "awsvpc"
11 | requires_compatibilities = ["FARGATE"]
12 | cpu = var.cpu
13 | memory = var.memory
14 | execution_role_arn = var.execution_role_arn
15 | task_role_arn = var.task_role_arn
16 |
17 | container_definitions = < 0 ? 1 : 0
150 | policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
151 | role = aws_iam_role.ecs_task_excecution_role[0].name
152 |
153 | lifecycle {
154 | create_before_destroy = true
155 | }
156 | }
157 |
158 | resource "aws_iam_role_policy_attachment" "attachment2" {
159 | count = var.create_devops_policy == true ? 1 : 0
160 | policy_arn = aws_iam_policy.policy_for_role[0].arn
161 | role = var.attach_to
162 |
163 | lifecycle {
164 | create_before_destroy = true
165 | }
166 | }
167 |
168 | resource "aws_iam_role_policy_attachment" "codedeploy_attachment" {
169 | count = var.create_codedeploy_role == true ? 1 : 0
170 | policy_arn = "arn:aws:iam::aws:policy/AWSCodeDeployRoleForECS"
171 | role = aws_iam_role.codedeploy_role[0].name
172 | }
173 |
174 | # ------- IAM Policy Documents -------
175 | data "aws_iam_policy_document" "role_policy_devops_role" {
176 | statement {
177 | sid = "AllowS3Actions"
178 | effect = "Allow"
179 | actions = [
180 | "s3:PutObject",
181 | "s3:GetObject",
182 | "s3:GetObjectVersion",
183 | "s3:GetBucketAcl",
184 | "s3:List*"
185 | ]
186 | resources = ["*"]
187 | }
188 | statement {
189 | sid = "AllowCodebuildActions"
190 | effect = "Allow"
191 | actions = [
192 | "codebuild:BatchGetBuilds",
193 | "codebuild:StartBuild",
194 | "codebuild:BatchGetBuildBatches",
195 | "codebuild:StartBuildBatch",
196 | "codebuild:StopBuild"
197 | ]
198 | resources = var.code_build_projects
199 | }
200 | statement {
201 | sid = "AllowCodebuildList"
202 | effect = "Allow"
203 | actions = [
204 | "codebuild:ListBuilds"
205 | ]
206 | resources = ["*"]
207 | }
208 | statement {
209 | sid = "AllowCodeDeployActions"
210 | effect = "Allow"
211 | actions = [
212 | "codedeploy:CreateDeployment",
213 | "codedeploy:GetApplication",
214 | "codedeploy:GetApplicationRevision",
215 | "codedeploy:GetDeployment",
216 | "codedeploy:GetDeploymentGroup",
217 | "codedeploy:RegisterApplicationRevision"
218 | ]
219 | resources = var.code_deploy_resources
220 | }
221 | statement {
222 | sid = "AllowCodeDeployConfigs"
223 | effect = "Allow"
224 | actions = [
225 | "codedeploy:GetDeploymentConfig",
226 | "codedeploy:CreateDeploymentConfig",
227 | "codedeploy:CreateDeploymentGroup",
228 | "codedeploy:GetDeploymentTarget",
229 | "codedeploy:StopDeployment",
230 | "codedeploy:ListApplications",
231 | "codedeploy:ListDeploymentConfigs",
232 | "codedeploy:ListDeploymentGroups",
233 | "codedeploy:ListDeployments"
234 |
235 | ]
236 | resources = ["*"]
237 | }
238 | statement {
239 | sid = "AllowECRActions"
240 | effect = "Allow"
241 | actions = [
242 | "ecr:BatchCheckLayerAvailability",
243 | "ecr:CompleteLayerUpload",
244 | "ecr:BatchGetImage",
245 | "ecr:GetDownloadUrlForLayer",
246 | "ecr:InitiateLayerUpload",
247 | "ecr:PutImage",
248 | "ecr:UploadLayerPart"
249 | ]
250 | resources = var.ecr_repositories
251 | }
252 | statement {
253 | sid = "AllowECRAuthorization"
254 | effect = "Allow"
255 | actions = [
256 | "ecr:GetAuthorizationToken",
257 | ]
258 | resources = ["*"]
259 | }
260 | statement {
261 | sid = "AllowCECSServiceActions"
262 | effect = "Allow"
263 | actions = [
264 | "ecs:ListServices",
265 | "ecs:ListTasks",
266 | "ecs:DescribeServices",
267 | "ecs:DescribeTasks",
268 | "ecs:DescribeTaskDefinition",
269 | "ecs:DescribeTaskSets",
270 | "ecs:DeleteTaskSet",
271 | "ecs:DeregisterContainerInstance",
272 | "ecs:CreateTaskSet",
273 | "ecs:UpdateCapacityProvider",
274 | "ecs:PutClusterCapacityProviders",
275 | "ecs:UpdateServicePrimaryTaskSet",
276 | "ecs:RegisterTaskDefinition",
277 | "ecs:RunTask",
278 | "ecs:StartTask",
279 | "ecs:StopTask",
280 | "ecs:UpdateService",
281 | "ecs:UpdateCluster",
282 | "ecs:UpdateTaskSet"
283 | ]
284 | resources = ["*"]
285 | }
286 | statement {
287 | sid = "AllowIAMPassRole"
288 | effect = "Allow"
289 | actions = [
290 | "iam:PassRole"
291 | ]
292 | resources = ["*"]
293 | }
294 | statement {
295 | sid = "AllowCloudWatchActions"
296 | effect = "Allow"
297 | actions = [
298 | "logs:CreateLogGroup",
299 | "logs:CreateLogStream",
300 | "logs:PutLogEvents"
301 | ]
302 | resources = ["*"]
303 | }
304 | }
305 |
306 | data "aws_iam_policy_document" "role_policy_ecs_task_role" {
307 | statement {
308 | sid = "AllowS3Actions"
309 | effect = "Allow"
310 | actions = [
311 | "s3:GetObject",
312 | "s3:ListBucket"
313 | ]
314 | resources = var.s3_bucket_assets
315 | }
316 | statement {
317 | sid = "AllowIAMPassRole"
318 | effect = "Allow"
319 | actions = [
320 | "iam:PassRole"
321 | ]
322 | resources = ["*"]
323 | }
324 | statement {
325 | sid = "AllowDynamodbActions"
326 | effect = "Allow"
327 | actions = [
328 | "dynamodb:BatchGetItem",
329 | "dynamodb:Describe*",
330 | "dynamodb:List*",
331 | "dynamodb:GetItem",
332 | "dynamodb:Query",
333 | "dynamodb:Scan",
334 | ]
335 | resources = var.dynamodb_table
336 | }
337 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/IAM/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "arn_role" {
5 | value = (var.create_ecs_role == true
6 | ? (length(aws_iam_role.ecs_task_excecution_role) > 0 ? aws_iam_role.ecs_task_excecution_role[0].arn : "")
7 | : (length(aws_iam_role.devops_role) > 0 ? aws_iam_role.devops_role[0].arn : ""))
8 | }
9 |
10 | output "name_role" {
11 | value = (var.create_ecs_role == true
12 | ? (length(aws_iam_role.ecs_task_excecution_role) > 0 ? aws_iam_role.ecs_task_excecution_role[0].name : "")
13 | : (length(aws_iam_role.devops_role) > 0 ? aws_iam_role.devops_role[0].name : ""))
14 | }
15 |
16 | output "arn_role_codedeploy" {
17 | value = (var.create_codedeploy_role == true
18 | ? (length(aws_iam_role.codedeploy_role) > 0 ? aws_iam_role.codedeploy_role[0].arn : "")
19 | : "")
20 | }
21 |
22 | output "arn_role_ecs_task_role" {
23 | value = (var.create_ecs_role == true
24 | ? (length(aws_iam_role.ecs_task_role) > 0 ? aws_iam_role.ecs_task_role[0].arn : "")
25 | : "")
26 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/IAM/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name for the Role"
6 | type = string
7 | }
8 |
9 | variable "name_ecs_task_role" {
10 | description = "The name for the Ecs Task Role"
11 | type = string
12 | default = null
13 | }
14 |
15 | variable "create_ecs_role" {
16 | description = "Set this variable to true if you want to create a role for ECS"
17 | type = bool
18 | default = false
19 | }
20 |
21 |
22 | variable "create_devops_role" {
23 | description = "Set this variable to true if you want to create a role for AWS DevOps Tools"
24 | type = bool
25 | default = false
26 | }
27 |
28 | variable "create_codedeploy_role" {
29 | description = "Set this variable to true if you want to create a role for AWS CodeDeploy"
30 | type = bool
31 | default = false
32 | }
33 |
34 | variable "create_devops_policy" {
35 | description = "Set this variable to true if you want to create a policy for AWS DevOps Tools"
36 | type = bool
37 | default = false
38 | }
39 |
40 | variable "create_policy" {
41 | description = "Set this variable to true if you want to create an IAM Policy"
42 | type = bool
43 | default = false
44 | }
45 |
46 | variable "attach_to" {
47 | description = "The ARN or role name to attach the policy created"
48 | type = string
49 | default = ""
50 | }
51 |
52 | variable "ecr_repositories" {
53 | description = "The ECR repositories to which grant IAM access"
54 | type = list(string)
55 | default = ["*"]
56 | }
57 |
58 | variable "code_build_projects" {
59 | description = "The Code Build projects to which grant IAM access"
60 | type = list(string)
61 | default = ["*"]
62 | }
63 |
64 | variable "code_deploy_resources" {
65 | description = "The Code Deploy applications and deployment groups to which grant IAM access"
66 | type = list(string)
67 | default = ["*"]
68 | }
69 |
70 | variable "dynamodb_table" {
71 | description = "The name of the Dynamodb table to which grant IAM access"
72 | type = list(string)
73 | default = ["*"]
74 | }
75 |
76 | variable "s3_bucket_assets" {
77 | description = "The name of the S3 bucket to which grant IAM access"
78 | type = list(string)
79 | default = ["*"]
80 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Networking/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*==================================================
5 | AWS Networking for the whole solution
6 | ===================================================*/
7 |
8 | # ------- VPC Creation -------
9 | resource "aws_vpc" "aws_vpc" {
10 | cidr_block = var.cidr[0]
11 | instance_tenancy = "default"
12 | enable_dns_hostnames = true
13 | enable_dns_support = true
14 | tags = {
15 | Name = "vpc_${var.name}"
16 | }
17 | }
18 |
19 | # ------- Get Region Available Zones -------
20 | data "aws_availability_zones" "az_availables" {
21 | state = "available"
22 | }
23 |
24 | # ------- Subnets Creation -------
25 |
26 | # ------- Public Subnets -------
27 | resource "aws_subnet" "public_subnets" {
28 | count = 2
29 | availability_zone = data.aws_availability_zones.az_availables.names[count.index]
30 | vpc_id = aws_vpc.aws_vpc.id
31 | cidr_block = cidrsubnet(aws_vpc.aws_vpc.cidr_block, 7, count.index + 1)
32 | map_public_ip_on_launch = true
33 | tags = {
34 | Name = "public_subnet_${count.index}_${var.name}"
35 | }
36 | }
37 |
38 | # ------- Private Subnets -------
39 | resource "aws_subnet" "private_subnets_client" {
40 | count = 2
41 | availability_zone = data.aws_availability_zones.az_availables.names[count.index]
42 | vpc_id = aws_vpc.aws_vpc.id
43 | cidr_block = cidrsubnet(aws_vpc.aws_vpc.cidr_block, 7, count.index + 3)
44 | tags = {
45 | Name = "private_subnet_client_${count.index}_${var.name}"
46 | }
47 | }
48 |
49 | resource "aws_subnet" "private_subnets_server" {
50 | count = 2
51 | availability_zone = data.aws_availability_zones.az_availables.names[count.index]
52 | vpc_id = aws_vpc.aws_vpc.id
53 | cidr_block = cidrsubnet(aws_vpc.aws_vpc.cidr_block, 7, count.index + 5)
54 | tags = {
55 | Name = "private_subnet_server_${count.index}_${var.name}"
56 | }
57 | }
58 |
59 | # ------- Internet Gateway -------
60 | resource "aws_internet_gateway" "igw" {
61 | vpc_id = aws_vpc.aws_vpc.id
62 | tags = {
63 | Name = "igw_${var.name}"
64 | }
65 | }
66 |
67 | # ------- Create Default Route Public Table -------
68 | resource "aws_default_route_table" "rt_public" {
69 | default_route_table_id = aws_vpc.aws_vpc.default_route_table_id
70 |
71 | # ------- Internet Route -------
72 | route {
73 | cidr_block = "0.0.0.0/0"
74 | gateway_id = aws_internet_gateway.igw.id
75 | }
76 |
77 | tags = {
78 | Name = "public_rt_${var.name}"
79 | }
80 | }
81 |
82 | # ------- Create EIP -------
83 | resource "aws_eip" "eip" {
84 | vpc = true
85 | tags = {
86 | Name = "eip-${var.name}"
87 | }
88 | }
89 |
90 | # ------- Attach EIP to Nat Gateway -------
91 | resource "aws_nat_gateway" "natgw" {
92 | allocation_id = aws_eip.eip.id
93 | subnet_id = aws_subnet.public_subnets[0].id
94 | tags = {
95 | Name = "nat_${var.name}"
96 | }
97 | }
98 |
99 | # ------- Create Private Route Private Table -------
100 | resource "aws_route_table" "rt_private" {
101 | vpc_id = aws_vpc.aws_vpc.id
102 |
103 | # ------- Internet Route -------
104 | route {
105 | cidr_block = "0.0.0.0/0"
106 | gateway_id = aws_nat_gateway.natgw.id
107 | }
108 |
109 | tags = {
110 | Name = "private_rt_${var.name}"
111 | }
112 | }
113 |
114 | # ------- Private Subnets Association -------
115 | resource "aws_route_table_association" "rt_assoc_priv_subnets_client" {
116 | count = 2
117 | subnet_id = aws_subnet.private_subnets_client[count.index].id
118 | route_table_id = aws_route_table.rt_private.id
119 | }
120 |
121 | resource "aws_route_table_association" "rt_assoc_priv_subnets_server" {
122 | count = 2
123 | subnet_id = aws_subnet.private_subnets_server[count.index].id
124 | route_table_id = aws_route_table.rt_private.id
125 | }
126 |
127 | # ------- Public Subnets Association -------
128 | resource "aws_route_table_association" "rt_assoc_pub_subnets" {
129 | count = 2
130 | subnet_id = aws_subnet.public_subnets[count.index].id
131 | route_table_id = aws_vpc.aws_vpc.main_route_table_id
132 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Networking/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "aws_vpc" {
5 | value = aws_vpc.aws_vpc.id
6 | }
7 |
8 | output "public_subnets" {
9 | value = [aws_subnet.public_subnets[0].id, aws_subnet.public_subnets[1].id]
10 |
11 | }
12 | output "private_subnets_client" {
13 | value = [aws_subnet.private_subnets_client[0].id, aws_subnet.private_subnets_client[1].id]
14 | }
15 |
16 | output "private_subnets_server" {
17 | value = [aws_subnet.private_subnets_server[0].id, aws_subnet.private_subnets_server[1].id]
18 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/Networking/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "Provided name used for name concatenation of resources"
6 | type = string
7 | }
8 |
9 | variable "cidr" {
10 | description = "CIDR block"
11 | type = list(any)
12 | }
13 |
14 | variable "subnets_number" {
15 | description = "Number of subnets to create (independent from type)"
16 | default = 2
17 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/S3/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*===========================
5 | AWS S3 resources
6 | ============================*/
7 |
8 | resource "aws_s3_bucket" "s3_bucket" {
9 | bucket = var.bucket_name
10 | acl = "private"
11 | force_destroy = true
12 | tags = {
13 | Name = var.bucket_name
14 | }
15 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/S3/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "s3_bucket_id" {
5 | value = aws_s3_bucket.s3_bucket.id
6 | }
7 |
8 | output "s3_bucket_arn" {
9 | value = aws_s3_bucket.s3_bucket.arn
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/S3/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "bucket_name" {
5 | description = "The name of your S3 bucket"
6 | type = string
7 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SNS/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*====================================================
5 | AWS SNS topic for deployment notifications
6 | =====================================================*/
7 |
8 | resource "aws_sns_topic" "sns_notifications" {
9 | name = var.sns_name
10 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SNS/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "sns_arn" {
5 | value = aws_sns_topic.sns_notifications.arn
6 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SNS/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "sns_name" {
5 | description = "The name of the SNS topic"
6 | type = string
7 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SecurityGroup/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*================================
5 | AWS Security group
6 | =================================*/
7 |
8 | resource "aws_security_group" "sg" {
9 | name = var.name
10 | description = var.description
11 | vpc_id = var.vpc_id
12 |
13 | ingress {
14 | protocol = "tcp"
15 | from_port = var.ingress_port
16 | to_port = var.ingress_port
17 | cidr_blocks = var.cidr_blocks_ingress
18 | security_groups = var.security_groups
19 | }
20 | egress {
21 | from_port = var.egress_port
22 | to_port = var.egress_port
23 | protocol = "-1"
24 | cidr_blocks = var.cidr_blocks_egress
25 | }
26 |
27 | tags = {
28 | Name = var.name
29 | }
30 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SecurityGroup/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "sg_id" {
5 | value = aws_security_group.sg.id
6 | }
--------------------------------------------------------------------------------
/Infrastructure/Modules/SecurityGroup/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "name" {
5 | description = "The name of your security group"
6 | type = string
7 | }
8 |
9 | variable "description" {
10 | description = "A description of the purpose"
11 | type = string
12 | }
13 |
14 | variable "vpc_id" {
15 | description = "The ID of the VPC where the security group will take place"
16 | type = string
17 | }
18 |
19 | variable "ingress_port" {
20 | description = "Number of the port to open in the ingress rules"
21 | type = number
22 | default = 0
23 | }
24 |
25 | variable "egress_port" {
26 | description = "Number of the port to open in the egress rules"
27 | type = number
28 | default = 0
29 | }
30 |
31 | variable "security_groups" {
32 | description = "List of security group Group Names if using EC2-Classic, or Group IDs if using a VPC"
33 | type = list(any)
34 | default = null
35 | }
36 |
37 | variable "cidr_blocks_ingress" {
38 | description = "An ingress block of CIDR to grant access to"
39 | type = list(any)
40 | default = null
41 | }
42 |
43 | variable "cidr_blocks_egress" {
44 | description = "An ingress block of CIDR to grant access to"
45 | type = list(any)
46 | default = ["0.0.0.0/0"]
47 | }
--------------------------------------------------------------------------------
/Infrastructure/Templates/appspec.yaml:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | version: 0.0
5 | Resources:
6 | - TargetService:
7 | Type: AWS::ECS::Service
8 | Properties:
9 | TaskDefinition: # Do not change this, the value is updated when your pipeline runs
10 | LoadBalancerInfo:
11 | ContainerName: ""
12 | ContainerPort:
13 | PlatformVersion: "LATEST"
--------------------------------------------------------------------------------
/Infrastructure/Templates/buildspec.yml:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | version: 0.2
5 |
6 | phases:
7 | pre_build:
8 | commands:
9 | - echo Logging in to Amazon ECR...
10 | - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com
11 | - echo Checking for build type
12 | - |
13 | if expr "${FOLDER_PATH}" : ".*client*" ; then
14 | echo "Client build, embedding frontend layer file with ALB backend DNS"
15 | sed -i "s||$SERVER_ALB_URL|g" ./Code/client/src/services/RestServices.js
16 | else
17 | echo "Server build, adding ECS Task Role to the task definition file"
18 | sed -i "3i\"taskRoleArn\": \"arn:aws:iam:::role/\"," ./Infrastructure/Templates/taskdef.json
19 | echo "Replacing table name in the server function"
20 | sed -i "s|DYNAMODB_TABLE|$DYNAMODB_TABLE|g" ./Code/server/src/app.js
21 | echo "Adding Swagger host"
22 | sed -i "s||$SERVER_ALB_URL|g" ./Code/server/src/swagger/swagger.js
23 | fi
24 | build:
25 | commands:
26 | - echo Build started on `date`
27 | - echo Building the Docker image...
28 | - docker build -t $REPO_URL $FOLDER_PATH
29 | post_build:
30 | commands:
31 | - echo Build completed on `date`
32 | - echo Pushing the Docker image...
33 | - docker push $REPO_URL:$IMAGE_TAG
34 | - echo Changing directory to Templates directory
35 | - cd ./Infrastructure/Templates
36 | - echo Preparing spec files in new folder
37 | - mkdir Artifacts
38 | - cp appspec.yaml Artifacts/appspec.yaml && cp taskdef.json Artifacts/taskdef.json
39 | - echo Changing directory to the Artifacts directory
40 | - cd Artifacts
41 | - echo Preparating artifacts
42 | - sed -i "s||$TASK_DEFINITION_FAMILY|g" taskdef.json
43 | - sed -i "s||$CONTAINER_NAME|g" appspec.yaml taskdef.json
44 | - sed -i "s||$SERVICE_PORT|g" appspec.yaml taskdef.json
45 | - sed -i "s||$ECS_ROLE|g" taskdef.json
46 | - sed -i "s||$ECS_TASK_ROLE|g" taskdef.json
47 | - sed -i "s||$REPO_URL|g" taskdef.json
48 | - sed -i "s||$AWS_ACCOUNT_ID|g" taskdef.json
49 | - sed -i "s||$AWS_REGION|g" taskdef.json
50 |
51 | artifacts:
52 | files:
53 | - '**/*'
54 | base-directory: 'Infrastructure/Templates/Artifacts'
55 | discard-paths: yes
--------------------------------------------------------------------------------
/Infrastructure/Templates/taskdef.json:
--------------------------------------------------------------------------------
1 | {
2 | "executionRoleArn": "arn:aws:iam:::role/",
3 | "containerDefinitions": [
4 | {
5 | "name": "",
6 | "image": "",
7 | "essential": true,
8 | "logConfiguration": {
9 | "logDriver": "awslogs",
10 | "secretOptions": null,
11 | "options": {
12 | "awslogs-group": "/ecs/",
13 | "awslogs-region": "",
14 | "awslogs-stream-prefix": "ecs"
15 | }
16 | },
17 | "portMappings": [
18 | {
19 | "hostPort": ,
20 | "protocol": "tcp",
21 | "containerPort":
22 | }
23 | ]
24 | }
25 | ],
26 | "requiresCompatibilities": [
27 | "FARGATE"
28 | ],
29 | "networkMode": "awsvpc",
30 | "memory": "512",
31 | "cpu": "256",
32 | "family": ""
33 | }
--------------------------------------------------------------------------------
/Infrastructure/main.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | /*===========================
5 | Root file
6 | ============================*/
7 |
8 | # ------- Providers -------
9 | provider "aws" {
10 | profile = var.aws_profile
11 | region = var.aws_region
12 |
13 | # provider level tags - yet inconsistent when executing
14 | # default_tags {
15 | # tags = {
16 | # Created_by = "Terraform"
17 | # Project = "AWS_demo_fullstack_devops"
18 | # }
19 | # }
20 | }
21 |
22 | # ------- Random numbers intended to be used as unique identifiers for resources -------
23 | resource "random_id" "RANDOM_ID" {
24 | byte_length = "2"
25 | }
26 |
27 | # ------- Account ID -------
28 | data "aws_caller_identity" "id_current_account" {}
29 |
30 | # ------- Networking -------
31 | module "networking" {
32 | source = "./Modules/Networking"
33 | cidr = ["10.120.0.0/16"]
34 | name = var.environment_name
35 | }
36 |
37 | # ------- Creating Target Group for the server ALB blue environment -------
38 | module "target_group_server_blue" {
39 | source = "./Modules/ALB"
40 | create_target_group = true
41 | name = "tg-${var.environment_name}-s-b"
42 | port = 80
43 | protocol = "HTTP"
44 | vpc = module.networking.aws_vpc
45 | tg_type = "ip"
46 | health_check_path = "/status"
47 | health_check_port = var.port_app_server
48 | }
49 |
50 | # ------- Creating Target Group for the server ALB green environment -------
51 | module "target_group_server_green" {
52 | source = "./Modules/ALB"
53 | create_target_group = true
54 | name = "tg-${var.environment_name}-s-g"
55 | port = 80
56 | protocol = "HTTP"
57 | vpc = module.networking.aws_vpc
58 | tg_type = "ip"
59 | health_check_path = "/status"
60 | health_check_port = var.port_app_server
61 | }
62 |
63 | # ------- Creating Target Group for the client ALB blue environment -------
64 | module "target_group_client_blue" {
65 | source = "./Modules/ALB"
66 | create_target_group = true
67 | name = "tg-${var.environment_name}-c-b"
68 | port = 80
69 | protocol = "HTTP"
70 | vpc = module.networking.aws_vpc
71 | tg_type = "ip"
72 | health_check_path = "/"
73 | health_check_port = var.port_app_client
74 | }
75 |
76 | # ------- Creating Target Group for the client ALB green environment -------
77 | module "target_group_client_green" {
78 | source = "./Modules/ALB"
79 | create_target_group = true
80 | name = "tg-${var.environment_name}-c-g"
81 | port = 80
82 | protocol = "HTTP"
83 | vpc = module.networking.aws_vpc
84 | tg_type = "ip"
85 | health_check_path = "/"
86 | health_check_port = var.port_app_client
87 | }
88 |
89 | # ------- Creating Security Group for the server ALB -------
90 | module "security_group_alb_server" {
91 | source = "./Modules/SecurityGroup"
92 | name = "alb-${var.environment_name}-server"
93 | description = "Controls access to the server ALB"
94 | vpc_id = module.networking.aws_vpc
95 | cidr_blocks_ingress = ["0.0.0.0/0"]
96 | ingress_port = 80
97 | }
98 |
99 | # ------- Creating Security Group for the client ALB -------
100 | module "security_group_alb_client" {
101 | source = "./Modules/SecurityGroup"
102 | name = "alb-${var.environment_name}-client"
103 | description = "Controls access to the client ALB"
104 | vpc_id = module.networking.aws_vpc
105 | cidr_blocks_ingress = ["0.0.0.0/0"]
106 | ingress_port = 80
107 | }
108 |
109 | # ------- Creating Server Application ALB -------
110 | module "alb_server" {
111 | source = "./Modules/ALB"
112 | create_alb = true
113 | name = "${var.environment_name}-ser"
114 | subnets = [module.networking.public_subnets[0], module.networking.public_subnets[1]]
115 | security_group = module.security_group_alb_server.sg_id
116 | target_group = module.target_group_server_blue.arn_tg
117 | }
118 |
119 | # ------- Creating Client Application ALB -------
120 | module "alb_client" {
121 | source = "./Modules/ALB"
122 | create_alb = true
123 | name = "${var.environment_name}-cli"
124 | subnets = [module.networking.public_subnets[0], module.networking.public_subnets[1]]
125 | security_group = module.security_group_alb_client.sg_id
126 | target_group = module.target_group_client_blue.arn_tg
127 | }
128 |
129 | # ------- ECS Role -------
130 | module "ecs_role" {
131 | source = "./Modules/IAM"
132 | create_ecs_role = true
133 | name = var.iam_role_name["ecs"]
134 | name_ecs_task_role = var.iam_role_name["ecs_task_role"]
135 | dynamodb_table = [module.dynamodb_table.dynamodb_table_arn]
136 | }
137 |
138 | # ------- Creating a IAM Policy for role -------
139 | module "ecs_role_policy" {
140 | source = "./Modules/IAM"
141 | name = "ecs-ecr-${var.environment_name}"
142 | create_policy = true
143 | attach_to = module.ecs_role.name_role
144 | }
145 |
146 | # ------- Creating server ECR Repository to store Docker Images -------
147 | module "ecr_server" {
148 | source = "./Modules/ECR"
149 | name = "repo-server"
150 | }
151 |
152 | # ------- Creating client ECR Repository to store Docker Images -------
153 | module "ecr_client" {
154 | source = "./Modules/ECR"
155 | name = "repo-client"
156 | }
157 |
158 | # ------- Creating ECS Task Definition for the server -------
159 | module "ecs_taks_definition_server" {
160 | source = "./Modules/ECS/TaskDefinition"
161 | name = "${var.environment_name}-server"
162 | container_name = var.container_name["server"]
163 | execution_role_arn = module.ecs_role.arn_role
164 | task_role_arn = module.ecs_role.arn_role_ecs_task_role
165 | cpu = 256
166 | memory = "512"
167 | docker_repo = module.ecr_server.ecr_repository_url
168 | region = var.aws_region
169 | container_port = var.port_app_server
170 | }
171 |
172 | # ------- Creating ECS Task Definition for the client -------
173 | module "ecs_taks_definition_client" {
174 | source = "./Modules/ECS/TaskDefinition"
175 | name = "${var.environment_name}-client"
176 | container_name = var.container_name["client"]
177 | execution_role_arn = module.ecs_role.arn_role
178 | task_role_arn = module.ecs_role.arn_role_ecs_task_role
179 | cpu = 256
180 | memory = "512"
181 | docker_repo = module.ecr_client.ecr_repository_url
182 | region = var.aws_region
183 | container_port = var.port_app_client
184 | }
185 |
186 | # ------- Creating a server Security Group for ECS TASKS -------
187 | module "security_group_ecs_task_server" {
188 | source = "./Modules/SecurityGroup"
189 | name = "ecs-task-${var.environment_name}-server"
190 | description = "Controls access to the server ECS task"
191 | vpc_id = module.networking.aws_vpc
192 | ingress_port = var.port_app_server
193 | security_groups = [module.security_group_alb_server.sg_id]
194 | }
195 | # ------- Creating a client Security Group for ECS TASKS -------
196 | module "security_group_ecs_task_client" {
197 | source = "./Modules/SecurityGroup"
198 | name = "ecs-task-${var.environment_name}-client"
199 | description = "Controls access to the client ECS task"
200 | vpc_id = module.networking.aws_vpc
201 | ingress_port = var.port_app_client
202 | security_groups = [module.security_group_alb_client.sg_id]
203 | }
204 |
205 | # ------- Creating ECS Cluster -------
206 | module "ecs_cluster" {
207 | source = "./Modules/ECS/Cluster"
208 | name = var.environment_name
209 | }
210 |
211 | # ------- Creating ECS Service server -------
212 | module "ecs_service_server" {
213 | depends_on = [module.alb_server]
214 | source = "./Modules/ECS/Service"
215 | name = "${var.environment_name}-server"
216 | desired_tasks = 1
217 | arn_security_group = module.security_group_ecs_task_server.sg_id
218 | ecs_cluster_id = module.ecs_cluster.ecs_cluster_id
219 | arn_target_group = module.target_group_server_blue.arn_tg
220 | arn_task_definition = module.ecs_taks_definition_server.arn_task_definition
221 | subnets_id = [module.networking.private_subnets_server[0], module.networking.private_subnets_server[1]]
222 | container_port = var.port_app_server
223 | container_name = var.container_name["server"]
224 | }
225 |
226 | # ------- Creating ECS Service client -------
227 | module "ecs_service_client" {
228 | depends_on = [module.alb_client]
229 | source = "./Modules/ECS/Service"
230 | name = "${var.environment_name}-client"
231 | desired_tasks = 1
232 | arn_security_group = module.security_group_ecs_task_client.sg_id
233 | ecs_cluster_id = module.ecs_cluster.ecs_cluster_id
234 | arn_target_group = module.target_group_client_blue.arn_tg
235 | arn_task_definition = module.ecs_taks_definition_client.arn_task_definition
236 | subnets_id = [module.networking.private_subnets_client[0], module.networking.private_subnets_client[1]]
237 | container_port = var.port_app_client
238 | container_name = var.container_name["client"]
239 | }
240 |
241 | # ------- Creating ECS Autoscaling policies for the server application -------
242 | module "ecs_autoscaling_server" {
243 | depends_on = [module.ecs_service_server]
244 | source = "./Modules/ECS/Autoscaling"
245 | name = "${var.environment_name}-server"
246 | cluster_name = module.ecs_cluster.ecs_cluster_name
247 | min_capacity = 1
248 | max_capacity = 4
249 | }
250 |
251 | # ------- Creating ECS Autoscaling policies for the client application -------
252 | module "ecs_autoscaling_client" {
253 | depends_on = [module.ecs_service_client]
254 | source = "./Modules/ECS/Autoscaling"
255 | name = "${var.environment_name}-client"
256 | cluster_name = module.ecs_cluster.ecs_cluster_name
257 | min_capacity = 1
258 | max_capacity = 4
259 | }
260 |
261 | # ------- CodePipeline -------
262 |
263 | # ------- Creating Bucket to store CodePipeline artifacts -------
264 | module "s3_codepipeline" {
265 | source = "./Modules/S3"
266 | bucket_name = "codepipeline-${var.aws_region}-${random_id.RANDOM_ID.hex}"
267 | }
268 |
269 | # ------- Creating IAM roles used during the pipeline excecution -------
270 | module "devops_role" {
271 | source = "./Modules/IAM"
272 | create_devops_role = true
273 | name = var.iam_role_name["devops"]
274 | }
275 |
276 | module "codedeploy_role" {
277 | source = "./Modules/IAM"
278 | create_codedeploy_role = true
279 | name = var.iam_role_name["codedeploy"]
280 | }
281 |
282 | # ------- Creating an IAM Policy for role -------
283 | module "policy_devops_role" {
284 | source = "./Modules/IAM"
285 | name = "devops-${var.environment_name}"
286 | create_policy = true
287 | attach_to = module.devops_role.name_role
288 | create_devops_policy = true
289 | ecr_repositories = [module.ecr_server.ecr_repository_arn, module.ecr_client.ecr_repository_arn]
290 | code_build_projects = [module.codebuild_client.project_arn, module.codebuild_server.project_arn]
291 | code_deploy_resources = [module.codedeploy_server.application_arn, module.codedeploy_server.deployment_group_arn, module.codedeploy_client.application_arn, module.codedeploy_client.deployment_group_arn]
292 | }
293 |
294 | # ------- Creating a SNS topic -------
295 | module "sns" {
296 | source = "./Modules/SNS"
297 | sns_name = "sns-${var.environment_name}"
298 | }
299 |
300 | # ------- Creating the server CodeBuild project -------
301 | module "codebuild_server" {
302 | source = "./Modules/CodeBuild"
303 | name = "codebuild-${var.environment_name}-server"
304 | iam_role = module.devops_role.arn_role
305 | region = var.aws_region
306 | account_id = data.aws_caller_identity.id_current_account.account_id
307 | ecr_repo_url = module.ecr_server.ecr_repository_url
308 | folder_path = var.folder_path_server
309 | buildspec_path = var.buildspec_path
310 | task_definition_family = module.ecs_taks_definition_server.task_definition_family
311 | container_name = var.container_name["server"]
312 | service_port = var.port_app_server
313 | ecs_role = var.iam_role_name["ecs"]
314 | ecs_task_role = var.iam_role_name["ecs_task_role"]
315 | dynamodb_table_name = module.dynamodb_table.dynamodb_table_name
316 | }
317 |
318 | # ------- Creating the client CodeBuild project -------
319 | module "codebuild_client" {
320 | source = "./Modules/CodeBuild"
321 | name = "codebuild-${var.environment_name}-client"
322 | iam_role = module.devops_role.arn_role
323 | region = var.aws_region
324 | account_id = data.aws_caller_identity.id_current_account.account_id
325 | ecr_repo_url = module.ecr_client.ecr_repository_url
326 | folder_path = var.folder_path_client
327 | buildspec_path = var.buildspec_path
328 | task_definition_family = module.ecs_taks_definition_client.task_definition_family
329 | container_name = var.container_name["client"]
330 | service_port = var.port_app_client
331 | ecs_role = var.iam_role_name["ecs"]
332 | server_alb_url = module.alb_server.dns_alb
333 | }
334 |
335 | # ------- Creating the server CodeDeploy project -------
336 | module "codedeploy_server" {
337 | source = "./Modules/CodeDeploy"
338 | name = "Deploy-${var.environment_name}-server"
339 | ecs_cluster = module.ecs_cluster.ecs_cluster_name
340 | ecs_service = module.ecs_service_server.ecs_service_name
341 | alb_listener = module.alb_server.arn_listener
342 | tg_blue = module.target_group_server_blue.tg_name
343 | tg_green = module.target_group_server_green.tg_name
344 | sns_topic_arn = module.sns.sns_arn
345 | codedeploy_role = module.codedeploy_role.arn_role_codedeploy
346 | }
347 |
348 | # ------- Creating the client CodeDeploy project -------
349 | module "codedeploy_client" {
350 | source = "./Modules/CodeDeploy"
351 | name = "Deploy-${var.environment_name}-client"
352 | ecs_cluster = module.ecs_cluster.ecs_cluster_name
353 | ecs_service = module.ecs_service_client.ecs_service_name
354 | alb_listener = module.alb_client.arn_listener
355 | tg_blue = module.target_group_client_blue.tg_name
356 | tg_green = module.target_group_client_green.tg_name
357 | sns_topic_arn = module.sns.sns_arn
358 | codedeploy_role = module.codedeploy_role.arn_role_codedeploy
359 | }
360 |
361 | # ------- Creating CodePipeline -------
362 | module "codepipeline" {
363 | source = "./Modules/CodePipeline"
364 | name = "pipeline-${var.environment_name}"
365 | pipe_role = module.devops_role.arn_role
366 | s3_bucket = module.s3_codepipeline.s3_bucket_id
367 | github_token = var.github_token
368 | repo_owner = var.repository_owner
369 | repo_name = var.repository_name
370 | branch = var.repository_branch
371 | codebuild_project_server = module.codebuild_server.project_id
372 | codebuild_project_client = module.codebuild_client.project_id
373 | app_name_server = module.codedeploy_server.application_name
374 | app_name_client = module.codedeploy_client.application_name
375 | deployment_group_server = module.codedeploy_server.deployment_group_name
376 | deployment_group_client = module.codedeploy_client.deployment_group_name
377 |
378 | depends_on = [module.policy_devops_role]
379 | }
380 |
381 | # ------- Creating Bucket to store assets accessed by the Back-end -------
382 | module "s3_assets" {
383 | source = "./Modules/S3"
384 | bucket_name = "assets-${var.aws_region}-${random_id.RANDOM_ID.hex}"
385 | }
386 |
387 | # ------- Creating Dynamodb table by the Back-end -------
388 | module "dynamodb_table" {
389 | source = "./Modules/Dynamodb"
390 | name = "assets-table-${var.environment_name}"
391 | }
--------------------------------------------------------------------------------
/Infrastructure/outputs.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | output "application_url" {
5 | value = module.alb_client.dns_alb
6 | description = "Copy this value in your browser in order to access the deployed app"
7 | }
8 |
9 | output "swagger_endpoint" {
10 | value = "${module.alb_server.dns_alb}/api/docs"
11 | description = "Copy this value in your browser in order to access the swagger documentation"
12 | }
--------------------------------------------------------------------------------
/Infrastructure/variables.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | variable "aws_profile" {
5 | description = "The profile name that you have configured in the file .aws/credentials"
6 | type = string
7 | }
8 |
9 | variable "aws_region" {
10 | description = "The AWS Region in which you want to deploy the resources"
11 | type = string
12 | }
13 |
14 | variable "environment_name" {
15 | description = "The name of your environment"
16 | type = string
17 |
18 | validation {
19 | condition = length(var.environment_name) < 23
20 | error_message = "Due the this variable is used for concatenation of names of other resources, the value must have less than 23 characters."
21 | }
22 | }
23 |
24 | variable "github_token" {
25 | description = "Personal access token from Github"
26 | type = string
27 | sensitive = true
28 | }
29 |
30 | variable "port_app_server" {
31 | description = "The port used by your server application"
32 | type = number
33 | default = 3001
34 | }
35 |
36 | variable "port_app_client" {
37 | description = "The port used by your client application"
38 | type = number
39 | default = 80
40 | }
41 |
42 | variable "buildspec_path" {
43 | description = "The location of the buildspec file"
44 | type = string
45 | default = "./Infrastructure/Templates/buildspec.yml"
46 | }
47 |
48 | variable "folder_path_server" {
49 | description = "The location of the server files"
50 | type = string
51 | default = "./Code/server/."
52 | }
53 |
54 | variable "folder_path_client" {
55 | description = "The location of the client files"
56 | type = string
57 | default = "./Code/client/."
58 | }
59 |
60 | variable "container_name" {
61 | description = "The name of the container of each ECS service"
62 | type = map(string)
63 | default = {
64 | server = "Container-server"
65 | client = "Container-client"
66 | }
67 | }
68 |
69 | variable "iam_role_name" {
70 | description = "The name of the IAM Role for each service"
71 | type = map(string)
72 | default = {
73 | devops = "DevOps-Role"
74 | ecs = "ECS-task-excecution-Role"
75 | ecs_task_role = "ECS-task-Role"
76 | codedeploy = "CodeDeploy-Role"
77 | }
78 | }
79 |
80 | variable "repository_owner" {
81 | description = "The name of the owner of the Github repository"
82 | type = string
83 | }
84 |
85 | variable "repository_name" {
86 | description = "The name of the Github repository"
87 | type = string
88 | }
89 |
90 | variable "repository_branch" {
91 | description = "The name of branch the Github repository, which is going to trigger a new CodePipeline excecution"
92 | type = string
93 | default = "main"
94 | }
--------------------------------------------------------------------------------
/Infrastructure/versions.tf:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | terraform {
5 | required_version = ">= 0.13"
6 | required_providers {
7 | aws = {
8 | source = "hashicorp/aws"
9 | version = "~> 3.38"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Amazon ECS Demo with fullstack app / DevOps practices / Terraform sample
2 |
3 | ## Table of content
4 |
5 | * [Solution overview](#solution-overview)
6 | * [General information](#general-information)
7 | * [Infrastructure](#infrastructure)
8 | * [Infrastructure Architecture](#infrastructure-architecture)
9 | * [Infrastructure considerations due to demo proposals](#infrastructure-considerations-due-to-demo-proposals)
10 | * [CI/CD Architecture](#ci/cd-architecture)
11 | * [Prerequisites](#prerequisites)
12 | * [Usage](#usage)
13 | * [Autoscaling test](#autoscaling-test)
14 | * [Application Code](#application-code)
15 | * [Client app](#client-app)
16 | * [Client considerations due to demo proposal](#client-considerations-due-to-demo-proposals)
17 | * [Server app](#server-app)
18 | * [Cleanup](#cleanup)
19 | * [Security](#security)
20 | * [License](#license)
21 |
22 |
23 | ## Solution overview
24 |
25 | This repository contains Terraform code to deploy a solution that is intended to be used to run a demo. It shows how AWS resources can be used to build an architecture that reduces defects while deploying, eases remediation, mitigates deployment risks and improves the flow into production environments while gaining the advantages of a managed underlying infrastructure for containers.
26 |
27 | ## General information
28 |
29 | The project has been divided into two parts:
30 | - Code: the code for the running application
31 | - client: Vue.js code for the frontend application
32 | - server: Node.js code for the backend application
33 | - Infrastructure: contains the Terraform code to deploy the needed AWS resources for the solution
34 |
35 | ## Infrastructure
36 |
37 | The Infrastructure folder contains the terraform code to deploy the AWS resources. The *Modules* folder has been created to store the Terraform modules used in this project. The *Templates* folder contains the different configuration files needed within the modules. The Terraform state is stored locally in the machine where you execute the terraform commands, but feel free to set a Terraform backend configuration like an AWS S3 Bucket or Terraform Cloud to store the state remotely. The AWS resources created by the script are detailed bellow:
38 |
39 | - AWS Networking resources, following best practices for HA
40 | - 2 ECR Repositories
41 | - 1 ECS Cluster
42 | - 2 ECS Services
43 | - 2 Task definitions
44 | - 4 Autoscaling Policies + Cloudwatch Alarms
45 | - 2 Application Load Balancer (Public facing)
46 | - IAM Roles and policies for ECS Tasks, CodeBuild, CodeDeploy and CodePipeline
47 | - Security Groups for ALBs and ECS tasks
48 | - 2 CodeBuild Projects
49 | - 2 CodeDeploy Applications
50 | - 1 CodePipeline pipeline
51 | - 2 S3 Buckets (1 used by CodePipeline to store the artifacts and another one used to store assets accessible from within the application)
52 | - 1 DynamoDB table (used by the application)
53 | - 1 SNS topic for notifications
54 |
55 | ## Infrastructure Architecture
56 |
57 | The following diagram represents the Infrastructure architecture being deployed with this project:
58 |
59 |
60 |
61 |
62 |
63 | ### Infrastructure considerations due to demo proposals
64 | The task definition template (Infrastructure/Templates/taskdef.json) that enables the CodePipeline to execute a Blue/Green deployment in ECS has hardcoded values for the memory and CPU values for the server and client application.
65 |
66 | Feel free to change it, by adding for example a set of "sed" commands in CodeBuild (following the ones already provided as example) to replace the values dynamically.
67 |
68 | Feel free to create a subscriptor for the SNS topic created by this code, in order to get informed of the status of each finished CodeDeploy deployment.
69 |
70 | ## CI/CD Architecture
71 |
72 | The following diagram represents the CI/CD architecture being deployed with this project:
73 |
74 |
75 |
76 |
77 |
78 | ## Prerequisites
79 | There are general steps that you must follow in order to launch the infrastructure resources.
80 |
81 | Before launching the solution please follow the next steps:
82 |
83 | 1) Install Terraform, use Terraform v0.13 or above. You can visit [this](https://releases.hashicorp.com/terraform/) Terraform official webpage to download it.
84 | 2) Configure the AWS credentials into your machine (~/.aws/credentials). You need to use the following format:
85 |
86 | ```shell
87 | [AWS_PROFILE_NAME]
88 | aws_access_key_id = Replace_with_the_correct_access_Key
89 | aws_secret_access_key = Replace_with_the_correct_secret_Key
90 | ```
91 |
92 | 3) Generate a GitHub token. You can follow [this](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) steps to generate it.
93 |
94 | ## Usage
95 |
96 | **1.** Fork this repository and create the GitHub token granting access to this new repository in your account.
97 |
98 | **2.** Clone that recently forked repository from your account (not the one from the aws-sample organization) and change the directory to the appropriate one as shown below:
99 |
100 | ```bash
101 | cd Infrastructure/
102 | ```
103 |
104 | **3.** Run Terraform init to download the providers and install the modules
105 |
106 | ```shell
107 | terraform init
108 | ```
109 | **4.** Run the terraform plan command, feel free to use a tfvars file to specify the variables.
110 | You need to set at least the following variables:
111 | + **aws_profile** = according to the profiles name in ~/.aws/credentials
112 | + **aws_region** = the AWS region in which you want to create the resources
113 | + **environment_name** = a unique name used for concatenation to give place to the resources names
114 | + **github_token** = your GitHub token, the one generated a few steps above
115 | + **repository_name** = your GitHub repository name
116 | + **repository_owner** = the owner of the GitHub repository used
117 |
118 | ```shell
119 | terraform plan -var aws_profile="your-profile" -var aws_region="your-region" -var environment_name="your-env" -var github_token="your-personal-token" -var repository_name="your-github-repository" -var repository_owner="the-github-repository-owner"
120 | ```
121 |
122 | Example of the previous command with replaced dummy values:
123 |
124 | ```shell
125 | terraform plan -var aws_profile="development" -var aws_region="eu-central-1" -var environment_name="developmentenv" -var github_token="your-personal-token" -var repository_name="your-github-repository" -var repository_owner="the-github-repository-owner"
126 | ```
127 |
128 | **5.** Review the terraform plan, take a look at the changes that terraform will execute:
129 |
130 | ```shell
131 | terraform apply -var aws_profile="your-profile" -var aws_region="your-region" -var environment_name="your-env" -var github_token="your-personal-token" -var repository_name="your-github-repository" -var repository_owner="the-github-repository-owner"
132 | ```
133 |
134 | **6.** Once Terraform finishes the deployment, open the AWS Management Console and go to the AWS CodePipeline service. You will see that the pipeline, which was created by this Terraform code, is in progress. Add some files and DynamoDB items as mentioned [here](#client-considerations-due-to-demo-proposals). Once the pipeline finished successfully and the before assets were added, go back to the console where Terraform was executed, copy the *application_url* value from the output and open it in a browser.
135 |
136 | **7.** In order to access the also implemented Swagger endpoint, copy the *swagger_endpoint* value from the Terraform output and open it in a browser.
137 |
138 | ## Autoscaling test
139 |
140 | To test how your application will perform under a peak of traffic, a stress test configuration file is provided.
141 |
142 | For this stress test [Artillery](https://artillery.io/) is being used. Please be sure to install it following [these](https://artillery.io/docs/guides/getting-started/installing-artillery.html) steps.
143 |
144 | Once installed, please change the ALB DNS to the desired layer to test (front/backend) in the **target** attribute, which you can copy from the generated Terraform output, or you can also search it in the AWS Management Console.
145 |
146 | To execute it, run the following commands:
147 |
148 | *Frontend layer:*
149 | ```bash
150 | artillery run Code/client/src/tests/stresstests/stress_client.yml
151 | ```
152 |
153 | *Backend layer:*
154 | ```bash
155 | artillery run Code/server/src/tests/stresstests/stress_server.yml
156 | ```
157 |
158 | To learn more about Amazon ECS Autoscaling, please take a look to [this](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/service-auto-scaling.html) documentation.
159 | ## Application Code
160 |
161 | ### Client app
162 |
163 | The Client folder contains the code to run the frontend. This code is written in Vue.js and uses the port 80 in the deployed version, but when run localy it uses port 3000.
164 |
165 | The application folder structure is separeted in components, views and services, despite the router and the assets.
166 |
167 | ### Client considerations due to demo proposals
168 | 1) The assets used by the client application are going to be requested from the S3 bucket created with this code. Please add 3 images to the created S3 bucket.
169 |
170 | 2) The DynamoDB structure used by the client application is the following one:
171 |
172 | ```shell
173 | - id: N (HASH)
174 | - path: S
175 | - title: S
176 | ```
177 | Feel free to change the structure as needed. But in order to have full demo experience, please add 3 DynamoDB Items with the specified structure from above. Below is an example.
178 |
179 | *Note: The path attribute correspondes to the S3 Object URL of each added asset from the previous step.*
180 |
181 | Example of a DynamoDB Item:
182 |
183 | ```json
184 | {
185 | "id": {
186 | "N": "1"
187 | },
188 | "path": {
189 | "S": "https://mybucket.s3.eu-central-1.amazonaws.com/MyImage.jpeg"
190 | },
191 | "title": {
192 | "S": "My title"
193 | }
194 | }
195 | ```
196 |
197 | ### Server app
198 |
199 | The Server folder contains the code to run the backend. This code is written in Node.js and uses the port 80 in the deployed version, but when run localy it uses port 3001.
200 |
201 | Swagger was also implemented in order to document the APIs. The Swagger endpoint is provided as part of the Terraform output, you can grab the output link and access it through a browser.
202 |
203 | The server exposes 3 endpoints:
204 | - /status: serves as a dummy endpoint to know if the server is up and running. This one is used as the health check endpoint by the AWS ECS resources
205 | - /api/getAllProducts: main endpoint, which returns all the Items from an AWS DynamoDB table
206 | - /api/docs: the Swagger endpoint for the API documentation
207 |
208 | ## Cleanup
209 |
210 | Run the following command if you want to delete all the resources created before:
211 |
212 | ```shell
213 | terraform destroy -var aws_profile="your-profile" -var AWS_REGION="your-region" -var environment_name="your-env" -var github_token="your-personal-token" -var repository_name="your-github-repository" - var repository_owner="the-github-repository-owner"
214 | ```
215 |
216 | ## Security
217 |
218 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
219 |
220 | ## License
221 | This library is licensed under the MIT-0 License. See the [LICENSE](LICENSE) file.
--------------------------------------------------------------------------------