Add lab 1

This commit is contained in:
Ric Harvey 2024-04-16 15:23:10 +01:00
parent bcf2d8d308
commit bdd349bdd3
Signed by: ric
GPG key ID: 7FDEB4C0C7D5CB61
55 changed files with 219 additions and 10442 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

BIN
1-getting-started/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -1,18 +1,51 @@
Log into AWS ## Introduction
Open Cloud Shell In this first lab we are going to do the basics. We'll:
Install Terraform - Log into AWS
- Use cloudshell (as this has all your credentials set up already)
- Get your environment ready to run terraform/tofu
- Run a simple example
``` ### Log into AWS
First of all head to [https://aws.amazon.com](https://aws.amazon.com) and sign into your console. The next thing to do is check that you are working inthe correct region. For this lab everything is coded for eu-west-1 (Ireland) so make sure you select the correct region at the top right hand of the page.
![eu-west-1 Ireland Region](./img/eu-west-1.png)
Once in this region we are setup for the following labs.
> [!NOTE]
> We'll be creating infrastructure throughout and AWS does charge for your actual usage
### Open CloudShell
For these labs we are going to use the AWS supplied CloudShell, now you can do this from your own machine, however, you'll need to setup the [AWS CLI tool](https://aws.amazon.com/cli/) and AWS credentials which is beyond the scope of these labs. The nice thing about CloudShell is that it's already configured with your credentials for accessing AWS resources. You also launch this by heading up to the main bar in the AWS console and clicking the icon highlighted below:
![Launch CloudShell](./img/cloudshell.png)
Once launched you can hit the arrow to break the shell out into it's own browser tab. I personally find it easier to work this way. It's also good to know there is no charge to run CloudShell. It'll look a little like this when opened:
![CloudShell in it's own browser window](./img/cloudshell2.png)
Right lets get you setup to run terraform or openTofu! You only need to do one of these so choose your tool of choice. I lean toward using tofu as it's fully open source, but if you want some of the newer features in terraform 1.8.0 and higher the guide is here for you also.
> [!Note]
> Periodically you CloudShell is rebuilt and anything thats not stored in your home directory will be deleted, for this reason the method's below store the tooling in your home directory.
#### Install Terraform
First we are going to install a tool that make's it easy to install the binaries for Terraform. Copy and Paste the below into your CloudShell:
```bash
git clone https://github.com/tfutils/tfenv.git ~/.tfenv git clone https://github.com/tfutils/tfenv.git ~/.tfenv
mkdir ~/bin mkdir ~/bin
ln -s ~/.tfenv/bin/* ~/bin/ ln -s ~/.tfenv/bin/* ~/bin/
``` ```
Find the latest version and install it: Find the latest version and install it (at the time of writing it was **1.8.0**):
``` ```bash
tfenv list-remote tfenv list-remote
tfenv install 1.8.0 tfenv install 1.8.0
tfenv use 1.8.0 tfenv use 1.8.0
@ -20,13 +53,19 @@ tfenv use 1.8.0
Now test it's all working: Now test it's all working:
``` ```bash
terraform --version terraform --version
``` ```
Install Tofu The output should look likt e following screenshot:
``` ![Terraform output of the version flag](./img/terraform-working.png)
#### Install Tofu
If like me you prefer to use open source tools this is how to make tofu persist in CloudShell, Copy and Paste the following into your terminal:
```bash
# Download the installer script: # Download the installer script:
curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh -o install-opentofu.sh
# Alternatively: wget --secure-protocol=TLSv1_2 --https-only https://get.opentofu.org/install-opentofu.sh -O install-opentofu.sh # Alternatively: wget --secure-protocol=TLSv1_2 --https-only https://get.opentofu.org/install-opentofu.sh -O install-opentofu.sh
@ -41,4 +80,44 @@ chmod +x install-opentofu.sh
# Remove the installer: # Remove the installer:
rm install-opentofu.sh rm install-opentofu.sh
# Make Tofu remain in your path after reboots
mkdir ~/bin
sudo mv /usr/bin/tofu ~/bin
``` ```
Now test it's all working:
```
tofu --version
```
The output should look likt e following screenshot:
![Tofu output of the version flag](./img/tofu-working.png)
Now your tooling is installed and should survice you reconnecting to CloudShell.
### Test Deployment
Before we deploy to AWS we need to pull the code down from [GitLab](https://gitlab.com) onto your CloudShell. To do this run the follwoing commands:
```bash
git clone https://gitlab.com/ric_harvey/terraform-tofu-labs.git
cd terraform-tofu-labs
```
The output will look like the following:
![Git Clone the examples](./img/git-clone.png)
Now lets run some code.
> [!Note]
> You can replace the commands for ```terraform``` from ```tofu``` if you are running that version
First lets initialise our terraform/tofu environment:
```bash
tofu init
```

View file

@ -0,0 +1,9 @@
locals {
default_tags = merge(
var.additional_tags,
{
Owner = var.name
Environment = var.environment
ManagedBy = "tofu/terraform"
})
}

View file

@ -0,0 +1,48 @@
provider "aws" {
region = "eu-west-1"
}
resource "aws_security_group" "web_server_sg_tf" {
name = "web-server-sg-tf"
description = "Allow HTTP to web server"
vpc_id = module.vpc.vpc_id
ingress {
description = "SSH ingress"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTP ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
description = "HTTPS ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "test_ami" {
ami = "ami-029b91ed285a24a90"
instance_type = "t4g.nano"
associate_public_ip_address = true
subnet_id = module.vpc.public_subnets[0]
vpc_security_group_ids = [aws_security_group.web_server_sg_tf.id]
}

View file

@ -0,0 +1,27 @@
# VPC
output "vpc_id" {
description = "The ID of the VPC"
value = module.vpc.vpc_id
}
# Subnets
output "private_subnets" {
description = "List of IDs of private subnets"
value = module.vpc.private_subnets
}
output "public_subnets" {
description = "List of IDs of public subnets"
value = module.vpc.public_subnets
}
output "database_subnets" {
description = "List of IDs of database subnets"
value = module.vpc.database_subnets
}
# NAT gateways
output "nat_public_ips" {
description = "List of public Elastic IPs created for AWS NAT Gateway"
value = module.vpc.nat_public_ips
}

View file

@ -0,0 +1,11 @@
variable "name" {
description = "Name of our Application"
type = string
default = "lab-1-app"
}
variable "environment" {
description = "The deployment environment"
type = string
default = "dev"
}

View file

@ -0,0 +1,36 @@
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 3"
name = "${var.name}-${var.environment}"
cidr = "20.10.0.0/16" # 10.0.0.0/8 is reserved for EC2-Classic
azs = data.aws_availability_zones.available.names
private_subnets = ["20.10.1.0/24", "20.10.2.0/24", "20.10.3.0/24"]
public_subnets = ["20.10.11.0/24", "20.10.12.0/24", "20.10.13.0/24"]
database_subnets = ["20.10.21.0/24", "20.10.22.0/24", "20.10.23.0/24"]
private_subnet_tags = { "name": "${var.private_subnet_suffix}-${var.name}-${var.environment}" }
public_subnet_tags = { "name": "${var.public_subnet_suffix}-${var.name}-${var.environment}" }
database_subnet_tags = { "name": "${var.database_subnet_suffix}-${var.name}-${var.environment}" }
create_database_subnet_group = true
enable_nat_gateway = true
single_nat_gateway = true
enable_dhcp_options = false
# Default security group - ingress/egress rules cleared to deny all
manage_default_security_group = true
default_security_group_ingress = []
default_security_group_egress = []
# VPC Flow Logs (Cloudwatch log group and IAM role will be created)
enable_flow_log = true
create_flow_log_cloudwatch_log_group = true
create_flow_log_cloudwatch_iam_role = true
flow_log_max_aggregation_interval = 60
tags = local.default_tags
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

View file

@ -1 +0,0 @@
node_modules/

1
chat-app/.gitignore vendored
View file

@ -1 +0,0 @@
node_modules/

View file

@ -1,10 +0,0 @@
FROM node:21-alpine AS build
WORKDIR /srv
ADD package.json .
RUN npm install
ADD . .
FROM node:21-alpine
COPY --from=build /srv .
EXPOSE 3000
CMD ["node", "index.js"]

View file

@ -1,14 +0,0 @@
# Socket.IO Chat
A simple chat demo for socket.io with a redis/valkey backend
## How to build
```
docker build -t <username>/fargate-chat-demo:latest .
docker push
```
## Deploy on AWS
See the main README and change the image path to your own repo.

View file

@ -1,103 +0,0 @@
// Setup basic express server
var express = require('express');
var app = express();
var path = require('path');
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var redis = require('socket.io-redis');
io.adapter(redis({ host: process.env.REDIS_ENDPOINT , port: 6379 }));
var Presence = require('./lib/presence');
// Lower the heartbeat timeout
io.set('heartbeat timeout', 8000);
io.set('heartbeat interval', 4000);
var port = process.env.PORT || 3000;
server.listen(port, function() {
console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(path.join(__dirname, 'public')));
io.on('connection', function(socket) {
var addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function(data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});
socket.conn.on('heartbeat', function() {
if (!addedUser) {
// Don't start upserting until the user has added themselves.
return;
}
Presence.upsert(socket.id, {
username: socket.username
});
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function(username) {
if (addedUser) {
return;
}
// we store the username in the socket session for this client
socket.username = username;
Presence.upsert(socket.id, {
username: socket.username
});
addedUser = true;
Presence.list(function(users) {
socket.emit('login', {
numUsers: users.length
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: users.length
});
});
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function() {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function() {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function() {
if (addedUser) {
Presence.remove(socket.id);
Presence.list(function(users) {
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: users.length
});
});
}
});
});

View file

@ -1,96 +0,0 @@
var redis = require('redis');
function Presence() {
this.client = redis.createClient({
host: process.env.REDIS_ENDPOINT
});
}
module.exports = new Presence();
/**
* Remember a present user with their connection ID
*
* @param {string} connectionId - The ID of the connection
* @param {object} meta - Any metadata about the connection
**/
Presence.prototype.upsert = function(connectionId, meta) {
this.client.hset(
'presence',
connectionId,
JSON.stringify({
meta: meta,
when: Date.now()
}),
function(err) {
if (err) {
console.error('Failed to store presence in redis: ' + err);
}
}
);
};
/**
* Remove a presence. Used when someone disconnects
*
* @param {string} connectionId - The ID of the connection
* @param {object} meta - Any metadata about the connection
**/
Presence.prototype.remove = function(connectionId) {
this.client.hdel(
'presence',
connectionId,
function(err) {
if (err) {
console.error('Failed to remove presence in redis: ' + err);
}
}
);
};
/**
* Returns a list of present users, minus any expired
*
* @param {function} returnPresent - callback to return the present users
**/
Presence.prototype.list = function(returnPresent) {
var active = [];
var dead = [];
var now = Date.now();
var self = this;
this.client.hgetall('presence', function(err, presence) {
if (err) {
console.error('Failed to get presence from Redis: ' + err);
return returnPresent([]);
}
for (var connection in presence) {
var details = JSON.parse(presence[connection]);
details.connection = connection;
if (now - details.when > 8000) {
dead.push(details);
} else {
active.push(details);
}
}
if (dead.length) {
self._clean(dead);
}
return returnPresent(active);
});
};
/**
* Cleans a list of connections by removing expired ones
*
* @param
**/
Presence.prototype._clean = function(toDelete) {
console.log(`Cleaning ${toDelete.length} expired presences`);
for (var presence of toDelete) {
this.remove(presence.connection);
}
};

View file

@ -1,749 +0,0 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"after": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
"integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
},
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"backo2": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
"integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
},
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"base64id": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
},
"better-assert": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
"integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
"requires": {
"callsite": "1.0.0"
}
},
"blob": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
"integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
},
"body-parser": {
"version": "1.18.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
"integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
"requires": {
"bytes": "3.0.0",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "~1.1.2",
"http-errors": "~1.6.3",
"iconv-lite": "0.4.23",
"on-finished": "~2.3.0",
"qs": "6.5.2",
"raw-body": "2.3.3",
"type-is": "~1.6.16"
}
},
"bytes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
},
"callsite": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
},
"component-bind": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
"integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
},
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
"integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
},
"component-inherit": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
"integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"content-disposition": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
},
"content-type": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
"integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
},
"destroy": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
"integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
"engine.io": {
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz",
"integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==",
"requires": {
"accepts": "~1.3.4",
"base64id": "1.0.0",
"cookie": "0.3.1",
"debug": "~3.1.0",
"engine.io-parser": "~2.1.0",
"uws": "~9.14.0",
"ws": "~3.3.1"
},
"dependencies": {
"accepts": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
"integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
"requires": {
"mime-types": "~2.1.18",
"negotiator": "0.6.1"
}
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
}
}
},
"engine.io-client": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz",
"integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==",
"requires": {
"component-emitter": "1.2.1",
"component-inherit": "0.0.3",
"debug": "~3.1.0",
"engine.io-parser": "~2.1.1",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"ws": "~3.3.1",
"xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"engine.io-parser": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
"integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
"requires": {
"after": "0.8.2",
"arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5",
"blob": "0.0.5",
"has-binary2": "~1.0.2"
}
},
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
},
"etag": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"express": {
"version": "4.16.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
"integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
"requires": {
"accepts": "~1.3.5",
"array-flatten": "1.1.1",
"body-parser": "1.18.3",
"content-disposition": "0.5.2",
"content-type": "~1.0.4",
"cookie": "0.3.1",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.1.1",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.4",
"qs": "6.5.2",
"range-parser": "~1.2.0",
"safe-buffer": "5.1.2",
"send": "0.16.2",
"serve-static": "1.13.2",
"setprototypeof": "1.1.0",
"statuses": "~1.4.0",
"type-is": "~1.6.16",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
}
},
"finalhandler": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
"integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
"requires": {
"debug": "2.6.9",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"on-finished": "~2.3.0",
"parseurl": "~1.3.2",
"statuses": "~1.4.0",
"unpipe": "~1.0.0"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
},
"fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"has-binary2": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
"requires": {
"isarray": "2.0.1"
}
},
"has-cors": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
"integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
},
"http-errors": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
"integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
"requires": {
"depd": "~1.1.2",
"inherits": "2.0.3",
"setprototypeof": "1.1.0",
"statuses": ">= 1.4.0 < 2"
}
},
"iconv-lite": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
"requires": {
"safer-buffer": ">= 2.1.2 < 3"
}
},
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
"integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"ipaddr.js": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
"integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4="
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
},
"merge-descriptors": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
},
"methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
},
"mime": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
},
"mime-db": {
"version": "1.38.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
"integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg=="
},
"mime-types": {
"version": "2.1.22",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
"integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
"requires": {
"mime-db": "~1.38.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
},
"notepack.io": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.1.3.tgz",
"integrity": "sha512-AgSt+cP5XMooho1Ppn8NB3FFaVWefV+qZoZncYTUSch2GAEwlYLcIIbT5YVkMlFeNHnfwOvc4HDlbvrB5BRxXA=="
},
"object-component": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
"requires": {
"ee-first": "1.1.1"
}
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
"integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseuri": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
"integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
"requires": {
"better-assert": "~1.0.0"
}
},
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
},
"path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"proxy-addr": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
"integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
"requires": {
"forwarded": "~0.1.2",
"ipaddr.js": "1.8.0"
}
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"range-parser": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raw-body": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
"integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
"requires": {
"bytes": "3.0.0",
"http-errors": "1.6.3",
"iconv-lite": "0.4.23",
"unpipe": "1.0.0"
}
},
"redis": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
"integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
"requires": {
"double-ended-queue": "^2.1.0-0",
"redis-commands": "^1.2.0",
"redis-parser": "^2.6.0"
}
},
"redis-commands": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz",
"integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw=="
},
"redis-parser": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
"integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"send": {
"version": "0.16.2",
"resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
"integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
"requires": {
"debug": "2.6.9",
"depd": "~1.1.2",
"destroy": "~1.0.4",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"fresh": "0.5.2",
"http-errors": "~1.6.2",
"mime": "1.4.1",
"ms": "2.0.0",
"on-finished": "~2.3.0",
"range-parser": "~1.2.0",
"statuses": "~1.4.0"
}
},
"serve-static": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
"integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
"requires": {
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"parseurl": "~1.3.2",
"send": "0.16.2"
}
},
"setprototypeof": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
},
"socket.io": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz",
"integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=",
"requires": {
"debug": "~2.6.6",
"engine.io": "~3.1.0",
"socket.io-adapter": "~1.1.0",
"socket.io-client": "2.0.4",
"socket.io-parser": "~3.1.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"socket.io-adapter": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
"integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
},
"socket.io-client": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz",
"integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~2.6.4",
"engine.io-client": "~3.1.0",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.1.1",
"to-array": "0.1.4"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"socket.io-parser": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz",
"integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==",
"requires": {
"component-emitter": "1.2.1",
"debug": "~3.1.0",
"has-binary2": "~1.0.2",
"isarray": "2.0.1"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"socket.io-redis": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/socket.io-redis/-/socket.io-redis-5.2.0.tgz",
"integrity": "sha1-j+KtlEX8UIhvtwq8dZ1nQD1Ymd8=",
"requires": {
"debug": "~2.6.8",
"notepack.io": "~2.1.2",
"redis": "~2.8.0",
"socket.io-adapter": "~1.1.0",
"uid2": "0.0.3"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"statuses": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
"integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
},
"to-array": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
"integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
},
"type-is": {
"version": "1.6.16",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
"requires": {
"media-typer": "0.3.0",
"mime-types": "~2.1.18"
}
},
"uid2": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
"integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I="
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uws": {
"version": "9.14.0",
"resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz",
"integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==",
"optional": true
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "~1.0.0",
"safe-buffer": "~5.1.0",
"ultron": "~1.1.0"
}
},
"xmlhttprequest-ssl": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
"integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
},
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}

View file

@ -1,18 +0,0 @@
{
"name": "socket.io-chat",
"version": "0.0.0",
"description": "A simple chat client using socket.io",
"main": "index.js",
"author": "Grant Timmerman",
"private": true,
"license": "BSD",
"dependencies": {
"express": "^4.16.4",
"redis": "2.8.0",
"socket.io": "2.0.4",
"socket.io-redis": "5.2.0"
},
"scripts": {
"start": "node index.js"
}
}

View file

@ -1,28 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fargate.chat</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<ul class="pages">
<li class="chat page">
<div class="chatArea">
<ul class="messages"></ul>
</div>
<input class="inputMessage" placeholder="Type here..."/>
</li>
<li class="login page">
<div class="form">
<h3 class="title">What's your nickname?</h3>
<input class="usernameInput" type="text" maxlength="14" />
</div>
</li>
</ul>
<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="/main.js"></script>
</body>
</html>

View file

@ -1,282 +0,0 @@
$(function() {
var FADE_TIME = 150; // ms
var TYPING_TIMER_LENGTH = 400; // ms
var COLORS = [
'#e21400', '#91580f', '#f8a700', '#f78b00',
'#58dc00', '#287b00', '#a8f07a', '#4ae8c4',
'#3b88eb', '#3824aa', '#a700ff', '#d300e7'
];
// Initialize variables
var $window = $(window);
var $usernameInput = $('.usernameInput'); // Input for username
var $messages = $('.messages'); // Messages area
var $inputMessage = $('.inputMessage'); // Input message input box
var $loginPage = $('.login.page'); // The login page
var $chatPage = $('.chat.page'); // The chatroom page
// Prompt for setting a username
var username;
var connected = false;
var typing = false;
var lastTypingTime;
var $currentInput = $usernameInput.focus();
var socket = io();
function addParticipantsMessage (data) {
var message = '';
if (data.numUsers === 1) {
message += "there's 1 participant";
} else {
message += "there are " + data.numUsers + " participants";
}
log(message);
}
// Sets the client's username
function setUsername () {
username = cleanInput($usernameInput.val().trim());
// If the username is valid
if (username) {
$loginPage.fadeOut();
$chatPage.show();
$loginPage.off('click');
$currentInput = $inputMessage.focus();
// Tell the server your username
socket.emit('add user', username);
}
}
// Sends a chat message
function sendMessage () {
var message = $inputMessage.val();
// Prevent markup from being injected into the message
message = cleanInput(message);
// if there is a non-empty message and a socket connection
if (message && connected) {
$inputMessage.val('');
addChatMessage({
username: username,
message: message
});
// tell server to execute 'new message' and send along one parameter
socket.emit('new message', message);
}
}
// Log a message
function log (message, options) {
var $el = $('<li>').addClass('log').text(message);
addMessageElement($el, options);
}
// Adds the visual chat message to the message list
function addChatMessage (data, options) {
// Don't fade the message in if there is an 'X was typing'
var $typingMessages = getTypingMessages(data);
options = options || {};
if ($typingMessages.length !== 0) {
options.fade = false;
$typingMessages.remove();
}
var $usernameDiv = $('<span class="username"/>')
.text(data.username)
.css('color', getUsernameColor(data.username));
var $messageBodyDiv = $('<span class="messageBody">')
.text(data.message);
var typingClass = data.typing ? 'typing' : '';
var $messageDiv = $('<li class="message"/>')
.data('username', data.username)
.addClass(typingClass)
.append($usernameDiv, $messageBodyDiv);
addMessageElement($messageDiv, options);
}
// Adds the visual chat typing message
function addChatTyping (data) {
data.typing = true;
data.message = 'is typing';
addChatMessage(data);
}
// Removes the visual chat typing message
function removeChatTyping (data) {
getTypingMessages(data).fadeOut(function () {
$(this).remove();
});
}
// Adds a message element to the messages and scrolls to the bottom
// el - The element to add as a message
// options.fade - If the element should fade-in (default = true)
// options.prepend - If the element should prepend
// all other messages (default = false)
function addMessageElement (el, options) {
var $el = $(el);
// Setup default options
if (!options) {
options = {};
}
if (typeof options.fade === 'undefined') {
options.fade = true;
}
if (typeof options.prepend === 'undefined') {
options.prepend = false;
}
// Apply options
if (options.fade) {
$el.hide().fadeIn(FADE_TIME);
}
if (options.prepend) {
$messages.prepend($el);
} else {
$messages.append($el);
}
$messages[0].scrollTop = $messages[0].scrollHeight;
}
// Prevents input from having injected markup
function cleanInput (input) {
return $('<div/>').text(input).html();
}
// Updates the typing event
function updateTyping () {
if (connected) {
if (!typing) {
typing = true;
socket.emit('typing');
}
lastTypingTime = (new Date()).getTime();
setTimeout(function () {
var typingTimer = (new Date()).getTime();
var timeDiff = typingTimer - lastTypingTime;
if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
socket.emit('stop typing');
typing = false;
}
}, TYPING_TIMER_LENGTH);
}
}
// Gets the 'X is typing' messages of a user
function getTypingMessages (data) {
return $('.typing.message').filter(function (i) {
return $(this).data('username') === data.username;
});
}
// Gets the color of a username through our hash function
function getUsernameColor (username) {
// Compute hash code
var hash = 7;
for (var i = 0; i < username.length; i++) {
hash = username.charCodeAt(i) + (hash << 5) - hash;
}
// Calculate color
var index = Math.abs(hash % COLORS.length);
return COLORS[index];
}
// Keyboard events
$window.keydown(function (event) {
// Auto-focus the current input when a key is typed
if (!(event.ctrlKey || event.metaKey || event.altKey)) {
$currentInput.focus();
}
// When the client hits ENTER on their keyboard
if (event.which === 13) {
if (username) {
sendMessage();
socket.emit('stop typing');
typing = false;
} else {
setUsername();
}
}
});
$inputMessage.on('input', function() {
updateTyping();
});
// Click events
// Focus input when clicking anywhere on login page
$loginPage.click(function () {
$currentInput.focus();
});
// Focus input when clicking on the message input's border
$inputMessage.click(function () {
$inputMessage.focus();
});
// Socket events
// Whenever the server emits 'login', log the login message
socket.on('login', function (data) {
connected = true;
// Display the welcome message
var message = "Welcome to Socket.IO Chat ";
log(message, {
prepend: true
});
addParticipantsMessage(data);
});
// Whenever the server emits 'new message', update the chat body
socket.on('new message', function (data) {
addChatMessage(data);
});
// Whenever the server emits 'user joined', log it in the chat body
socket.on('user joined', function (data) {
log(data.username + ' joined');
addParticipantsMessage(data);
});
// Whenever the server emits 'user left', log it in the chat body
socket.on('user left', function (data) {
log(data.username + ' left');
addParticipantsMessage(data);
removeChatTyping(data);
});
// Whenever the server emits 'typing', show the typing message
socket.on('typing', function (data) {
addChatTyping(data);
});
// Whenever the server emits 'stop typing', kill the typing message
socket.on('stop typing', function (data) {
removeChatTyping(data);
});
socket.on('disconnect', function () {
log('you have been disconnected');
});
socket.on('reconnect', function () {
log('you have been reconnected');
if (username) {
socket.emit('add user', username);
}
});
socket.on('reconnect_error', function () {
log('attempt to reconnect has failed');
});
});

View file

@ -1,149 +0,0 @@
/* Fix user-agent */
* {
box-sizing: border-box;
}
html {
font-weight: 300;
-webkit-font-smoothing: antialiased;
}
html, input {
font-family:
"HelveticaNeue-Light",
"Helvetica Neue Light",
"Helvetica Neue",
Helvetica,
Arial,
"Lucida Grande",
sans-serif;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
ul {
list-style: none;
word-wrap: break-word;
}
/* Pages */
.pages {
height: 100%;
margin: 0;
padding: 0;
width: 100%;
}
.page {
height: 100%;
position: absolute;
width: 100%;
}
/* Login Page */
.login.page {
background-color: #401664;
}
.login.page .form {
height: 100px;
margin-top: -100px;
position: absolute;
text-align: center;
top: 50%;
width: 100%;
}
.login.page .form .usernameInput {
background-color: transparent;
border: none;
border-bottom: 2px solid #fff;
outline: none;
padding-bottom: 15px;
text-align: center;
width: 400px;
}
.login.page .title {
font-size: 200%;
}
.login.page .usernameInput {
font-size: 200%;
letter-spacing: 3px;
}
.login.page .title, .login.page .usernameInput {
color: #fff;
font-weight: 100;
}
/* Chat page */
.chat.page {
display: none;
}
/* Font */
.messages {
font-size: 150%;
}
.inputMessage {
font-size: 100%;
}
.log {
color: gray;
font-size: 70%;
margin: 5px;
text-align: center;
}
/* Messages */
.chatArea {
height: 100%;
padding-bottom: 60px;
}
.messages {
height: 100%;
margin: 0;
overflow-y: scroll;
padding: 10px 20px 10px 20px;
}
.message.typing .messageBody {
color: gray;
}
.username {
font-weight: 700;
overflow: hidden;
padding-right: 15px;
text-align: right;
}
/* Input */
.inputMessage {
border: 10px solid #000;
bottom: 0;
height: 60px;
left: 0;
outline: none;
padding-left: 10px;
position: absolute;
right: 0;
width: 100%;
}

View file

@ -1,320 +0,0 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: Socket.io chat service
Parameters:
EnvironmentName:
Type: String
Default: production
Description: A name for the environment that this cloudformation will be part of.
Used to locate other resources in the same environment.
ServiceName:
Type: String
Default: chat
Description: A name for the service
ImageUrl:
Type: String
Default: nginx
Description: The url of a docker image that contains the application process that
will handle the traffic for this service
ContainerPort:
Type: Number
Default: 3000
Description: What port number the application inside the docker container is binding to
ContainerCpu:
Type: Number
Default: 1024
Description: How much CPU to give the container. 1024 is 1 CPU
ContainerMemory:
Type: Number
Default: 2048
Description: How much memory in megabytes to give the container
Path:
Type: String
Default: "*"
Description: A path on the public load balancer that this service
should be connected to. Use * to send all load balancer
traffic to this service.
Priority:
Type: Number
Default: 1
Description: The priority for the routing rule added to the load balancer.
This only applies if your have multiple services which have been
assigned to different paths on the load balancer.
DesiredCount:
Type: Number
Default: 2
Description: How many copies of the service task to run
Role:
Type: String
Default: ""
Description: (Optional) An IAM role to give the service's containers if the code within needs to
access other AWS resources like S3 buckets, DynamoDB tables, etc
Conditions:
HasCustomRole: !Not [ !Equals [!Ref 'Role', ''] ]
Resources:
# A log group for storing the container logs for this service
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ['-', [!Ref 'EnvironmentName', 'service', !Ref 'ServiceName']]
# The task definition. This is a simple metadata description of what
# container to run, and what resource requirements it has.
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref 'ServiceName'
Cpu: !Ref 'ContainerCpu'
Memory: !Ref 'ContainerMemory'
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
ExecutionRoleArn:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'ECSTaskExecutionRole']]
TaskRoleArn:
Fn::If:
- 'HasCustomRole'
- !Ref 'Role'
- !Ref "AWS::NoValue"
ContainerDefinitions:
- Name: !Ref 'ServiceName'
Cpu: !Ref 'ContainerCpu'
Memory: !Ref 'ContainerMemory'
Image: !Ref 'ImageUrl'
Environment:
- Name: REDIS_ENDPOINT
Value:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'RedisEndpoint']]
PortMappings:
- ContainerPort: !Ref 'ContainerPort'
LogConfiguration:
LogDriver: 'awslogs'
Options:
awslogs-group: !Join ['-', [!Ref 'EnvironmentName', 'service', !Ref 'ServiceName']]
awslogs-region: !Ref 'AWS::Region'
awslogs-stream-prefix: !Ref 'ServiceName'
# The service. The service is a resource which allows you to run multiple
# copies of a type of task, and gather up their logs and metrics, as well
# as monitor the number of running tasks and replace any that have crashed
Service:
Type: AWS::ECS::Service
DependsOn: LoadBalancerRule
Properties:
ServiceName: !Ref 'ServiceName'
Cluster:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
LaunchType: FARGATE
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 75
DesiredCount: !Ref 'DesiredCount'
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
SecurityGroups:
- Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'FargateContainerSecurityGroup']]
Subnets:
- Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'PublicSubnetOne']]
- Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'PublicSubnetTwo']]
TaskDefinition: !Ref 'TaskDefinition'
LoadBalancers:
- ContainerName: !Ref 'ServiceName'
ContainerPort: !Ref 'ContainerPort'
TargetGroupArn: !Ref 'TargetGroup'
# A target group. This is used for keeping track of all the tasks, and
# what IP addresses / port numbers they have. You can query it yourself,
# to use the addresses yourself, but most often this target group is just
# connected to an application load balancer, or network load balancer, so
# it can automatically distribute traffic across all the targets.
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
TargetType: ip
Name: !Ref 'ServiceName'
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
TargetGroupAttributes:
- Key: stickiness.enabled
Value: true
- Key: deregistration_delay.timeout_seconds
Value: 30
VpcId:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'VPCId']]
# Create a rule on the load balancer for routing traffic to the target group
LoadBalancerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
Actions:
- TargetGroupArn: !Ref 'TargetGroup'
Type: 'forward'
Conditions:
- Field: path-pattern
Values: [!Ref 'Path']
ListenerArn:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'PublicListener']]
Priority: !Ref 'Priority'
# Enable autoscaling for this service
ScalableTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
DependsOn: Service
Properties:
ServiceNamespace: 'ecs'
ScalableDimension: 'ecs:service:DesiredCount'
ResourceId:
Fn::Join:
- '/'
- - service
- Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
- !Ref 'ServiceName'
MinCapacity: 2
MaxCapacity: 10
RoleARN:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'AutoscalingRole']]
# Create scaling policies for the service
ScaleDownPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
DependsOn: ScalableTarget
Properties:
PolicyName:
Fn::Join:
- '/'
- - scale
- !Ref 'EnvironmentName'
- !Ref 'ServiceName'
- down
PolicyType: StepScaling
ResourceId:
Fn::Join:
- '/'
- - service
- Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
- !Ref 'ServiceName'
ScalableDimension: 'ecs:service:DesiredCount'
ServiceNamespace: 'ecs'
StepScalingPolicyConfiguration:
AdjustmentType: 'ChangeInCapacity'
StepAdjustments:
- MetricIntervalUpperBound: 0
ScalingAdjustment: -1
MetricAggregationType: 'Average'
Cooldown: 60
ScaleUpPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
DependsOn: ScalableTarget
Properties:
PolicyName:
Fn::Join:
- '/'
- - scale
- !Ref 'EnvironmentName'
- !Ref 'ServiceName'
- up
PolicyType: StepScaling
ResourceId:
Fn::Join:
- '/'
- - service
- Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
- !Ref 'ServiceName'
ScalableDimension: 'ecs:service:DesiredCount'
ServiceNamespace: 'ecs'
StepScalingPolicyConfiguration:
AdjustmentType: 'ChangeInCapacity'
StepAdjustments:
- MetricIntervalLowerBound: 0
MetricIntervalUpperBound: 15
ScalingAdjustment: 1
- MetricIntervalLowerBound: 15
MetricIntervalUpperBound: 25
ScalingAdjustment: 2
- MetricIntervalLowerBound: 25
ScalingAdjustment: 3
MetricAggregationType: 'Average'
Cooldown: 60
# Create alarms to trigger these policies
LowCpuUsageAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName:
Fn::Join:
- '-'
- - low-cpu
- !Ref 'EnvironmentName'
- !Ref 'ServiceName'
AlarmDescription:
Fn::Join:
- ' '
- - "Low CPU utilization for service"
- !Ref 'ServiceName'
- "in stack"
- !Ref 'EnvironmentName'
MetricName: CPUUtilization
Namespace: AWS/ECS
Dimensions:
- Name: ServiceName
Value: !Ref 'ServiceName'
- Name: ClusterName
Value:
Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
Statistic: Average
Period: 60
EvaluationPeriods: 1
Threshold: 20
ComparisonOperator: LessThanOrEqualToThreshold
AlarmActions:
- !Ref ScaleDownPolicy
HighCpuUsageAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName:
Fn::Join:
- '-'
- - high-cpu
- !Ref 'EnvironmentName'
- !Ref 'ServiceName'
AlarmDescription:
Fn::Join:
- ' '
- - "High CPU utilization for service"
- !Ref 'ServiceName'
- "in stack"
- !Ref 'EnvironmentName'
MetricName: CPUUtilization
Namespace: AWS/ECS
Dimensions:
- Name: ServiceName
Value: !Ref 'ServiceName'
- Name: ClusterName
Value:
Fn::ImportValue: !Join [':', [!Ref 'EnvironmentName', 'ClusterName']]
Statistic: Average
Period: 60
EvaluationPeriods: 1
Threshold: 70
ComparisonOperator: GreaterThanOrEqualToThreshold
AlarmActions:
- !Ref ScaleUpPolicy

View file

@ -1,329 +0,0 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: The baseline resources used to create a Fargate environment
to launch containerized applications in.
Parameters:
EnvironmentName:
Type: String
Default: production
Description: A name for the environment that this cloudformation will be part of.
Mappings:
# Hard values for the subnet masks. These masks define
# the range of internal IP addresses that can be assigned.
# The VPC can have all IP's from 10.0.0.0 to 10.0.255.255
# There are two subnets which cover the ranges:
#
# 10.0.0.0 - 10.0.0.255
# 10.0.1.0 - 10.0.1.255
#
# If you need more IP addresses (perhaps you have so many
# instances that you run out) then you can customize these
# ranges to add more
SubnetConfig:
VPC:
CIDR: '10.0.0.0/16'
PublicOne:
CIDR: '10.0.0.0/24'
PublicTwo:
CIDR: '10.0.1.0/24'
Resources:
# VPC in which containers will be networked.
# It has two public subnets
# We distribute the subnets across the first two available subnets
# for the region, for high availability.
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ['SubnetConfig', 'VPC', 'CIDR']
# Two public subnets, where containers can have public IP addresses
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicOne', 'CIDR']
MapPublicIpOnLaunch: true
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId: !Ref 'VPC'
CidrBlock: !FindInMap ['SubnetConfig', 'PublicTwo', 'CIDR']
MapPublicIpOnLaunch: true
# Setup networking resources for the public subnets. Containers
# in the public subnets have public IP addresses and the routing table
# sends network traffic via the internet gateway.
InternetGateway:
Type: AWS::EC2::InternetGateway
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'VPC'
InternetGatewayId: !Ref 'InternetGateway'
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref 'VPC'
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
Properties:
RouteTableId: !Ref 'PublicRouteTable'
DestinationCidrBlock: '0.0.0.0/0'
GatewayId: !Ref 'InternetGateway'
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
# ECS Resources
ECSCluster:
Type: AWS::ECS::Cluster
# A security group for the containers we will run in Fargate.
# Two rules, allowing network traffic from a public facing load
# balancer and from other members of the security group.
#
# Remove any of the following ingress rules that are not needed.
# If you want to make direct requests to a container using its
# public IP address you'll need to add a security group rule
# to allow traffic from all IP addresses.
FargateContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the Fargate containers
VpcId: !Ref 'VPC'
EcsSecurityGroupIngressFromPublicALB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from the public ALB
GroupId: !Ref 'FargateContainerSecurityGroup'
IpProtocol: -1
SourceSecurityGroupId: !Ref 'PublicLoadBalancerSG'
EcsSecurityGroupIngressFromSelf:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from other containers in the same security group
GroupId: !Ref 'FargateContainerSecurityGroup'
IpProtocol: -1
SourceSecurityGroupId: !Ref 'FargateContainerSecurityGroup'
# Load balancers for getting traffic to containers.
# This sample template creates one load balancer:
#
# - One public load balancer, hosted in public subnets that is accessible
# to the public, and is intended to route traffic to one or more public
# facing services.
# A public facing load balancer, this is used for accepting traffic from the public
# internet and directing it to public facing microservices
PublicLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access to the public facing load balancer
VpcId: !Ref 'VPC'
SecurityGroupIngress:
# Allow access to ALB from anywhere on the internet
- CidrIp: 0.0.0.0/0
IpProtocol: -1
PublicLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: '30'
Subnets:
# The load balancer is placed into the public subnets, so that traffic
# from the internet can reach the load balancer directly via the internet gateway
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups: [!Ref 'PublicLoadBalancerSG']
# A dummy target group is used to setup the ALB to just drop traffic
# initially, before any real service target groups have been added.
DummyTargetGroupPublic:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Name: !Join ['-', [!Ref 'EnvironmentName', 'drop-1']]
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref 'VPC'
PublicLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- PublicLoadBalancer
Properties:
DefaultActions:
- TargetGroupArn: !Ref 'DummyTargetGroupPublic'
Type: 'forward'
LoadBalancerArn: !Ref 'PublicLoadBalancer'
Port: 80
Protocol: HTTP
# This is an IAM role which authorizes ECS to manage resources on your
# account on your behalf, such as updating your load balancer with the
# details of where your containers are, so that traffic can reach your
# containers.
ECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
# Rules which allow ECS to attach network interfaces to instances
# on your behalf in order for awsvpc networking mode to work right
- 'ec2:AttachNetworkInterface'
- 'ec2:CreateNetworkInterface'
- 'ec2:CreateNetworkInterfacePermission'
- 'ec2:DeleteNetworkInterface'
- 'ec2:DeleteNetworkInterfacePermission'
- 'ec2:Describe*'
- 'ec2:DetachNetworkInterface'
# Rules which allow ECS to update load balancers on your behalf
# with the information sabout how to send traffic to your containers
- 'elasticloadbalancing:DeregisterInstancesFromLoadBalancer'
- 'elasticloadbalancing:DeregisterTargets'
- 'elasticloadbalancing:Describe*'
- 'elasticloadbalancing:RegisterInstancesWithLoadBalancer'
- 'elasticloadbalancing:RegisterTargets'
Resource: '*'
# This is a role which is used by the ECS tasks themselves.
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# Allow the ECS Tasks to download images from ECR
- 'ecr:GetAuthorizationToken'
- 'ecr:BatchCheckLayerAvailability'
- 'ecr:GetDownloadUrlForLayer'
- 'ecr:BatchGetImage'
# Allow the ECS tasks to upload logs to CloudWatch
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: '*'
# A role used by AWS Autoscaling to get the stats for a Fargate
# service, and update it to increase or decrease the number of containers
AutoscalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [application-autoscaling.amazonaws.com]
Action: ['sts:AssumeRole']
Path: /
Policies:
- PolicyName: service-autoscaling
PolicyDocument:
Statement:
- Effect: Allow
Action:
- 'application-autoscaling:*'
- 'cloudwatch:DescribeAlarms'
- 'cloudwatch:PutMetricAlarm'
- 'ecs:DescribeServices'
- 'ecs:UpdateService'
Resource: '*'
# These are the values output by the CloudFormation template. Be careful
# about changing any of them, because of them are exported with specific
# names so that the other task related CF templates can use them.
Outputs:
ClusterName:
Description: The name of the ECS cluster
Value: !Ref 'ECSCluster'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'ClusterName' ] ]
ExternalUrl:
Description: The url of the external load balancer
Value: !Join ['', ['http://', !GetAtt 'PublicLoadBalancer.DNSName']]
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'ExternalUrl' ] ]
ECSRole:
Description: The ARN of the ECS role
Value: !GetAtt 'ECSRole.Arn'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'ECSRole' ] ]
ECSTaskExecutionRole:
Description: The ARN of the ECS role
Value: !GetAtt 'ECSTaskExecutionRole.Arn'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'ECSTaskExecutionRole' ] ]
AutoscalingRole:
Description: The ARN of the ECS role
Value: !GetAtt 'ECSTaskExecutionRole.Arn'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'AutoscalingRole' ] ]
PublicListener:
Description: The ARN of the public load balancer's Listener
Value: !Ref PublicLoadBalancerListener
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'PublicListener' ] ]
VPCId:
Description: The ID of the VPC that this stack is deployed in
Value: !Ref 'VPC'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'VPCId' ] ]
PublicSubnetOne:
Description: Public subnet one
Value: !Ref 'PublicSubnetOne'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'PublicSubnetOne' ] ]
PublicSubnetTwo:
Description: Public subnet two
Value: !Ref 'PublicSubnetTwo'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'PublicSubnetTwo' ] ]
FargateContainerSecurityGroup:
Description: A security group used to allow Fargate containers to receive traffic
Value: !Ref 'FargateContainerSecurityGroup'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'FargateContainerSecurityGroup' ] ]

View file

@ -1,59 +0,0 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: Redis, and any other resources that the chat app needs.
Parameters:
EnvironmentName:
Type: String
Default: production
Description: The environment name, used for locating outputs from the
prerequisite stacks
Resources:
# Subnet group to control where the Redis gets placed
RedisSubnetGroup:
Type: AWS::ElastiCache::SubnetGroup
Properties:
Description: Group of subnets to place Redis into
SubnetIds:
- Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'PublicSubnetOne']]
- Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'PublicSubnetTwo']]
# Security group to add the Redis cluster to the VPC,
# and to allow the Fargate containers to talk to Redis on port 6379
RedisSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: "Redis Security Group"
VpcId:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'VPCId']]
RedisIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
Description: Ingress from Fargate containers
GroupId: !Ref 'RedisSecurityGroup'
IpProtocol: tcp
FromPort: 6379
ToPort: 6379
SourceSecurityGroupId:
Fn::ImportValue:
!Join [':', [!Ref 'EnvironmentName', 'FargateContainerSecurityGroup']]
# The cluster itself.
Redis:
Type: AWS::ElastiCache::CacheCluster
Properties:
Engine: redis
CacheNodeType: cache.m4.large
NumCacheNodes: 1
CacheSubnetGroupName: !Ref 'RedisSubnetGroup'
VpcSecurityGroupIds:
- !GetAtt 'RedisSecurityGroup.GroupId'
Outputs:
RedisEndpoint:
Description: The endpoint of the redis cluster
Value: !GetAtt 'Redis.RedisEndpoint.Address'
Export:
Name: !Join [ ':', [ !Ref 'EnvironmentName', 'RedisEndpoint' ] ]

View file

@ -1,38 +0,0 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/aws" {
version = "5.45.0"
constraints = ">= 4.66.1, >= 5.30.0, >= 5.33.0"
hashes = [
"h1:3zU3yp1SY+8vHAQvhfhYdPnFYQpFwXXXar+hOrnofzQ=",
"h1:A8MJa+VwONA4BNO5xzeleguJbrblNLnXBImHTK/qgFg=",
"zh:1d71c406aeaf4ba762eb62e4595ab9c9f8da1a2c9b74bb4277c0acfd9678ae65",
"zh:3b00b13154eadedb37bca99bf7cbd556fa9472e6900c970effa17a270ee9f721",
"zh:6f264e8b70153925ac8abfa83ebffe2c2d5a27ab5557a6b16124269b08ac2441",
"zh:80f7d552faf5c43d7dc22c6c1f7e70557b9f01c67db07abbb0330d5d3fc0e464",
"zh:863a2a2e6ae5b42fc46b209d8f2761c882d46aca481a8c49ef221d290b4fd88e",
"zh:8e3bddeb2da7e6bcfd0b0221a083778d2f7fc5cd64f55de7d8d79bd1f7378bae",
"zh:c726104e46cd743bbf240101d7975f44091d893b6e97b46070df0041779b04d2",
"zh:db73a89b462fdd6eb6f32e6ed464430a895fc2e54fb629e8b99773fc32a6a7a8",
"zh:e35179b89eba358f521ffd4546345b4d0683ca3364a9deb8f3b7b4bf60be6f02",
"zh:e7b54a0faecd34a9c73729d1d1f0cfc1b8f56bae789f95987002616f1265ce72",
]
}
provider "registry.opentofu.org/hashicorp/null" {
version = "3.2.2"
hashes = [
"h1:xN1tSeF/rUBfaddk/AVqk4i65z/MMM9uVZWd2cWCCH0=",
"zh:00e5877d19fb1c1d8c4b3536334a46a5c86f57146fd115c7b7b4b5d2bf2de86d",
"zh:1755c2999e73e4d73f9de670c145c9a0dc5a373802799dff06a0e9c161354163",
"zh:2b29d706353bc9c4edda6a2946af3322abe94372ffb421d81fa176f1e57e33be",
"zh:34f65259c6d2bd51582b6da536e782b181b23725782b181193b965f519fbbacd",
"zh:370f6eb744475926a1fa7464d82d46ad83c2e1148b4b21681b4cec4d75b97969",
"zh:5950bdb23b4fcc6431562d7eba3dea37844aa4220c4da2eb898ae3e4d1b64ec4",
"zh:8f3d5c8d4b9d497fec36953a227f80c76d37fc8431b683a23fb1c42b9cccbf8a",
"zh:8f6eb5e65c047bf490ad3891efecefc488503b65898d4ee106f474697ba257d7",
"zh:a7040eed688316fe00379574c72bb8c47dbe2638b038bb705647cbf224de8f72",
"zh:e561f28df04d9e51b75f33004b7767a53c45ad96e3375d86181ba1363bffbc77",
]
}

View file

@ -1 +0,0 @@
data "aws_availability_zones" "available" {}

View file

@ -1,301 +0,0 @@
################################################################################
# Cluster
################################################################################
module "ecs_cluster" {
source = "./modules/cluster"
cluster_name = local.name
# Capacity provider
fargate_capacity_providers = {
FARGATE = {
default_capacity_provider_strategy = {
weight = 50
base = 20
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 50
}
}
}
tags = local.tags
}
################################################################################
# Service
################################################################################
module "ecs_service" {
source = "./modules/service"
name = local.name
cluster_arn = module.ecs_cluster.arn
desired_count = 1
cpu = 1024
memory = 4096
# Enables ECS Exec
enable_execute_command = true
# Container definition(s)
container_definitions = {
(local.container_name) = {
cpu = 512
memory = 1024
image = "richarvey/chat-app:latest"
port_mappings = [
{
name = local.container_name
containerPort = local.container_port
hostPort = local.container_port
protocol = "tcp"
}
]
environment = [
{
name = "REDIS_ENDPOINT"
value = "valkey"
},
]
memory_reservation = 100
}
}
service_connect_configuration = {
namespace = aws_service_discovery_http_namespace.this.arn
service = {
client_alias = {
port = local.container_port
dns_name = local.container_name
}
port_name = local.container_name
discovery_name = local.container_name
}
}
load_balancer = {
service = {
target_group_arn = module.alb.target_groups["ex_ecs"].arn
container_name = local.container_name
container_port = local.container_port
}
}
subnet_ids = module.vpc.private_subnets
security_group_rules = {
alb_ingress_3000 = {
type = "ingress"
from_port = local.container_port
to_port = local.container_port
protocol = "tcp"
description = "Service port"
source_security_group_id = module.alb.security_group_id
}
egress_all = {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
service_tags = {
"ServiceTag" = "Tag on service level"
}
tags = local.tags
}
resource "aws_ecs_task_definition" "task" {
family = "service"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE", "EC2"]
cpu = 512
memory = 1024
container_definitions = jsonencode([
{
name = "valkey"
image = "valkey/valkey:7.2.4-rc1-alpine"
cpu = 512
memory = 1024
essential = true # if true and if fails, all other containers fail. Must have at least one essential
portMappings = [
{
name = "valkey"
containerPort = 6379
hostPort = 6379
}
]
}
])
}
resource "aws_ecs_service" "service" {
name = "valkey"
cluster = module.ecs_cluster.id
task_definition = aws_ecs_task_definition.task.id
desired_count = 1
launch_type = "FARGATE"
platform_version = "LATEST"
network_configuration {
assign_public_ip = false
security_groups = [aws_security_group.sg.id]
subnets = module.vpc.private_subnets
}
lifecycle {
ignore_changes = [task_definition]
}
service_connect_configuration {
enabled = true
namespace = "chat-app-demo"
service {
discovery_name = "valkey"
port_name = "valkey"
client_alias {
dns_name = "valkey"
port = 6379
}
}
}
}
resource "aws_security_group" "sg" {
name = "ecs"
vpc_id = module.vpc.vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
protocol = -1
self = true
from_port = 0
to_port = 0
description = ""
}
ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
self = "false"
cidr_blocks = ["0.0.0.0/0"]
description = "Port 80"
}
}
resource "null_resource" "update_desired_count" {
triggers = {
# Changes to this value will trigger the API call execution below
desired_count = 3
}
provisioner "local-exec" {
interpreter = ["/bin/bash", "-c"]
# Note: this requires the awscli to be installed locally where Terraform is executed
command = <<-EOT
aws ecs update-service \
--cluster ${module.ecs_cluster.name} \
--service ${module.ecs_service.name} \
--desired-count ${null_resource.update_desired_count.triggers.desired_count}
EOT
}
}
################################################################################
# Supporting Resources
################################################################################
resource "aws_service_discovery_http_namespace" "this" {
name = local.name
description = "CloudMap namespace for ${local.name}"
tags = local.tags
}
module "alb" {
source = "terraform-aws-modules/alb/aws"
version = "~> 9.0"
name = local.name
load_balancer_type = "application"
vpc_id = module.vpc.vpc_id
subnets = module.vpc.public_subnets
# For example only
enable_deletion_protection = false
# Security Group
security_group_ingress_rules = {
all_http = {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
}
}
security_group_egress_rules = {
all = {
ip_protocol = "-1"
cidr_ipv4 = module.vpc.vpc_cidr_block
}
}
listeners = {
ex_http = {
port = 80
protocol = "HTTP"
forward = {
target_group_key = "ex_ecs"
}
}
}
target_groups = {
ex_ecs = {
backend_protocol = "HTTP"
backend_port = local.container_port
target_type = "ip"
deregistration_delay = 5
load_balancing_cross_zone_enabled = true
health_check = {
enabled = true
healthy_threshold = 5
interval = 30
matcher = "200"
path = "/"
port = "traffic-port"
protocol = "HTTP"
timeout = 5
unhealthy_threshold = 2
}
# There's nothing to attach here in this definition. Instead,
# ECS will attach the IPs of the tasks to this target group
create_attachment = false
}
}
tags = local.tags
}

View file

@ -1,16 +0,0 @@
locals {
region = "eu-west-1"
name = "chat-app-demo"
vpc_cidr = "10.0.0.0/16"
azs = slice(data.aws_availability_zones.available.names, 0, 3)
container_name = "chat-app"
container_port = 3000
tags = {
Name = local.name
Example = local.name
Repository = "https://github.com/terraform-aws-modules/terraform-aws-ecs"
}
}

View file

@ -1,4 +0,0 @@
provider "aws" {
region = var.region
}

View file

@ -1,214 +0,0 @@
# Amazon ECS Cluster Terraform Module
Terraform module which creates Amazon ECS (Elastic Container Service) cluster resources on AWS.
## Available Features
- ECS cluster
- Fargate capacity providers
- EC2 AutoScaling Group capacity providers
- ECS Service w/ task definition, task set, and container definition support
For more details see the [design doc](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/README.md)
## Usage
### Fargate Capacity Providers
```hcl
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws//modules/cluster"
cluster_name = "ecs-fargate"
cluster_configuration = {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
}
fargate_capacity_providers = {
FARGATE = {
default_capacity_provider_strategy = {
weight = 50
}
}
FARGATE_SPOT = {
default_capacity_provider_strategy = {
weight = 50
}
}
}
tags = {
Environment = "Development"
Project = "EcsEc2"
}
}
```
### EC2 Autoscaling Capacity Providers
```hcl
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws//modules/cluster"
cluster_name = "ecs-ec2"
cluster_configuration = {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = "/aws/ecs/aws-ec2"
}
}
}
autoscaling_capacity_providers = {
one = {
auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-one-20220603194933774300000011"
managed_termination_protection = "ENABLED"
managed_scaling = {
maximum_scaling_step_size = 5
minimum_scaling_step_size = 1
status = "ENABLED"
target_capacity = 60
}
default_capacity_provider_strategy = {
weight = 60
base = 20
}
}
two = {
auto_scaling_group_arn = "arn:aws:autoscaling:eu-west-1:012345678901:autoScalingGroup:08419a61:autoScalingGroupName/ecs-ec2-two-20220603194933774300000022"
managed_termination_protection = "ENABLED"
managed_scaling = {
maximum_scaling_step_size = 15
minimum_scaling_step_size = 5
status = "ENABLED"
target_capacity = 90
}
default_capacity_provider_strategy = {
weight = 40
}
}
}
tags = {
Environment = "Development"
Project = "EcsEc2"
}
}
```
## Conditional Creation
The following values are provided to toggle on/off creation of the associated resources as desired:
```hcl
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws//modules/cluster"
# Disable creation of cluster and all resources
create = false
# ... omitted
}
```
## Examples
- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.66.1 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.66.1 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_ecs_capacity_provider.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_capacity_provider) | resource |
| [aws_ecs_cluster.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster) | resource |
| [aws_ecs_cluster_capacity_providers.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_cluster_capacity_providers) | resource |
| [aws_iam_policy.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.task_exec_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_policy_document.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.task_exec_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_autoscaling_capacity_providers"></a> [autoscaling\_capacity\_providers](#input\_autoscaling\_capacity\_providers) | Map of autoscaling capacity provider definitions to create for the cluster | `any` | `{}` | no |
| <a name="input_cloudwatch_log_group_kms_key_id"></a> [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no |
| <a name="input_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Custom name of CloudWatch Log Group for ECS cluster | `string` | `null` | no |
| <a name="input_cloudwatch_log_group_retention_in_days"></a> [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events | `number` | `90` | no |
| <a name="input_cloudwatch_log_group_tags"></a> [cloudwatch\_log\_group\_tags](#input\_cloudwatch\_log\_group\_tags) | A map of additional tags to add to the log group created | `map(string)` | `{}` | no |
| <a name="input_cluster_configuration"></a> [cluster\_configuration](#input\_cluster\_configuration) | The execute command configuration for the cluster | `any` | `{}` | no |
| <a name="input_cluster_name"></a> [cluster\_name](#input\_cluster\_name) | Name of the cluster (up to 255 letters, numbers, hyphens, and underscores) | `string` | `""` | no |
| <a name="input_cluster_service_connect_defaults"></a> [cluster\_service\_connect\_defaults](#input\_cluster\_service\_connect\_defaults) | Configures a default Service Connect namespace | `map(string)` | `{}` | no |
| <a name="input_cluster_settings"></a> [cluster\_settings](#input\_cluster\_settings) | List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster | `any` | <pre>[<br> {<br> "name": "containerInsights",<br> "value": "enabled"<br> }<br>]</pre> | no |
| <a name="input_create"></a> [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
| <a name="input_create_cloudwatch_log_group"></a> [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no |
| <a name="input_create_task_exec_iam_role"></a> [create\_task\_exec\_iam\_role](#input\_create\_task\_exec\_iam\_role) | Determines whether the ECS task definition IAM role should be created | `bool` | `false` | no |
| <a name="input_create_task_exec_policy"></a> [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no |
| <a name="input_default_capacity_provider_use_fargate"></a> [default\_capacity\_provider\_use\_fargate](#input\_default\_capacity\_provider\_use\_fargate) | Determines whether to use Fargate or autoscaling for default capacity provider strategy | `bool` | `true` | no |
| <a name="input_fargate_capacity_providers"></a> [fargate\_capacity\_providers](#input\_fargate\_capacity\_providers) | Map of Fargate capacity provider definitions to use for the cluster | `any` | `{}` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
| <a name="input_task_exec_iam_role_description"></a> [task\_exec\_iam\_role\_description](#input\_task\_exec\_iam\_role\_description) | Description of the role | `string` | `null` | no |
| <a name="input_task_exec_iam_role_name"></a> [task\_exec\_iam\_role\_name](#input\_task\_exec\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
| <a name="input_task_exec_iam_role_path"></a> [task\_exec\_iam\_role\_path](#input\_task\_exec\_iam\_role\_path) | IAM role path | `string` | `null` | no |
| <a name="input_task_exec_iam_role_permissions_boundary"></a> [task\_exec\_iam\_role\_permissions\_boundary](#input\_task\_exec\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
| <a name="input_task_exec_iam_role_policies"></a> [task\_exec\_iam\_role\_policies](#input\_task\_exec\_iam\_role\_policies) | Map of IAM role policy ARNs to attach to the IAM role | `map(string)` | `{}` | no |
| <a name="input_task_exec_iam_role_tags"></a> [task\_exec\_iam\_role\_tags](#input\_task\_exec\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
| <a name="input_task_exec_iam_role_use_name_prefix"></a> [task\_exec\_iam\_role\_use\_name\_prefix](#input\_task\_exec\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix | `bool` | `true` | no |
| <a name="input_task_exec_iam_statements"></a> [task\_exec\_iam\_statements](#input\_task\_exec\_iam\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no |
| <a name="input_task_exec_secret_arns"></a> [task\_exec\_secret\_arns](#input\_task\_exec\_secret\_arns) | List of SecretsManager secret ARNs the task execution role will be permitted to get/read | `list(string)` | <pre>[<br> "arn:aws:secretsmanager:*:*:secret:*"<br>]</pre> | no |
| <a name="input_task_exec_ssm_param_arns"></a> [task\_exec\_ssm\_param\_arns](#input\_task\_exec\_ssm\_param\_arns) | List of SSM parameter ARNs the task execution role will be permitted to get/read | `list(string)` | <pre>[<br> "arn:aws:ssm:*:*:parameter/*"<br>]</pre> | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_arn"></a> [arn](#output\_arn) | ARN that identifies the cluster |
| <a name="output_autoscaling_capacity_providers"></a> [autoscaling\_capacity\_providers](#output\_autoscaling\_capacity\_providers) | Map of autoscaling capacity providers created and their attributes |
| <a name="output_cloudwatch_log_group_arn"></a> [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | ARN of CloudWatch log group created |
| <a name="output_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of CloudWatch log group created |
| <a name="output_cluster_capacity_providers"></a> [cluster\_capacity\_providers](#output\_cluster\_capacity\_providers) | Map of cluster capacity providers attributes |
| <a name="output_id"></a> [id](#output\_id) | ID that identifies the cluster |
| <a name="output_name"></a> [name](#output\_name) | Name that identifies the cluster |
| <a name="output_task_exec_iam_role_arn"></a> [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN |
| <a name="output_task_exec_iam_role_name"></a> [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | Task execution IAM role name |
| <a name="output_task_exec_iam_role_unique_id"></a> [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## License
Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE).

View file

@ -1,324 +0,0 @@
################################################################################
# Cluster
################################################################################
locals {
execute_command_configuration = {
logging = "OVERRIDE"
log_configuration = {
cloud_watch_log_group_name = try(aws_cloudwatch_log_group.this[0].name, null)
}
}
}
resource "aws_ecs_cluster" "this" {
count = var.create ? 1 : 0
name = var.cluster_name
dynamic "configuration" {
for_each = var.create_cloudwatch_log_group ? [var.cluster_configuration] : []
content {
dynamic "execute_command_configuration" {
for_each = try([merge(local.execute_command_configuration, configuration.value.execute_command_configuration)], [{}])
content {
kms_key_id = try(execute_command_configuration.value.kms_key_id, null)
logging = try(execute_command_configuration.value.logging, "DEFAULT")
dynamic "log_configuration" {
for_each = try([execute_command_configuration.value.log_configuration], [])
content {
cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null)
cloud_watch_log_group_name = try(log_configuration.value.cloud_watch_log_group_name, null)
s3_bucket_name = try(log_configuration.value.s3_bucket_name, null)
s3_bucket_encryption_enabled = try(log_configuration.value.s3_bucket_encryption_enabled, null)
s3_key_prefix = try(log_configuration.value.s3_key_prefix, null)
}
}
}
}
}
}
dynamic "configuration" {
for_each = !var.create_cloudwatch_log_group && length(var.cluster_configuration) > 0 ? [var.cluster_configuration] : []
content {
dynamic "execute_command_configuration" {
for_each = try([configuration.value.execute_command_configuration], [{}])
content {
kms_key_id = try(execute_command_configuration.value.kms_key_id, null)
logging = try(execute_command_configuration.value.logging, "DEFAULT")
dynamic "log_configuration" {
for_each = try([execute_command_configuration.value.log_configuration], [])
content {
cloud_watch_encryption_enabled = try(log_configuration.value.cloud_watch_encryption_enabled, null)
cloud_watch_log_group_name = try(log_configuration.value.cloud_watch_log_group_name, null)
s3_bucket_name = try(log_configuration.value.s3_bucket_name, null)
s3_bucket_encryption_enabled = try(log_configuration.value.s3_bucket_encryption_enabled, null)
s3_key_prefix = try(log_configuration.value.s3_key_prefix, null)
}
}
}
}
}
}
dynamic "service_connect_defaults" {
for_each = length(var.cluster_service_connect_defaults) > 0 ? [var.cluster_service_connect_defaults] : []
content {
namespace = service_connect_defaults.value.namespace
}
}
dynamic "setting" {
for_each = flatten([var.cluster_settings])
content {
name = setting.value.name
value = setting.value.value
}
}
tags = var.tags
}
################################################################################
# CloudWatch Log Group
################################################################################
resource "aws_cloudwatch_log_group" "this" {
count = var.create && var.create_cloudwatch_log_group ? 1 : 0
name = try(coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.cluster_name}"), "")
retention_in_days = var.cloudwatch_log_group_retention_in_days
kms_key_id = var.cloudwatch_log_group_kms_key_id
tags = merge(var.tags, var.cloudwatch_log_group_tags)
}
################################################################################
# Cluster Capacity Providers
################################################################################
locals {
default_capacity_providers = merge(
{ for k, v in var.fargate_capacity_providers : k => v if var.default_capacity_provider_use_fargate },
{ for k, v in var.autoscaling_capacity_providers : k => v if !var.default_capacity_provider_use_fargate }
)
}
resource "aws_ecs_cluster_capacity_providers" "this" {
count = var.create && length(merge(var.fargate_capacity_providers, var.autoscaling_capacity_providers)) > 0 ? 1 : 0
cluster_name = aws_ecs_cluster.this[0].name
capacity_providers = distinct(concat(
[for k, v in var.fargate_capacity_providers : try(v.name, k)],
[for k, v in var.autoscaling_capacity_providers : try(v.name, k)]
))
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/cluster-capacity-providers.html#capacity-providers-considerations
dynamic "default_capacity_provider_strategy" {
for_each = local.default_capacity_providers
iterator = strategy
content {
capacity_provider = try(strategy.value.name, strategy.key)
base = try(strategy.value.default_capacity_provider_strategy.base, null)
weight = try(strategy.value.default_capacity_provider_strategy.weight, null)
}
}
depends_on = [
aws_ecs_capacity_provider.this
]
}
################################################################################
# Capacity Provider - Autoscaling Group(s)
################################################################################
resource "aws_ecs_capacity_provider" "this" {
for_each = { for k, v in var.autoscaling_capacity_providers : k => v if var.create }
name = try(each.value.name, each.key)
auto_scaling_group_provider {
auto_scaling_group_arn = each.value.auto_scaling_group_arn
# When you use managed termination protection, you must also use managed scaling otherwise managed termination protection won't work
managed_termination_protection = length(try([each.value.managed_scaling], [])) == 0 ? "DISABLED" : try(each.value.managed_termination_protection, null)
dynamic "managed_scaling" {
for_each = try([each.value.managed_scaling], [])
content {
instance_warmup_period = try(managed_scaling.value.instance_warmup_period, null)
maximum_scaling_step_size = try(managed_scaling.value.maximum_scaling_step_size, null)
minimum_scaling_step_size = try(managed_scaling.value.minimum_scaling_step_size, null)
status = try(managed_scaling.value.status, null)
target_capacity = try(managed_scaling.value.target_capacity, null)
}
}
}
tags = merge(var.tags, try(each.value.tags, {}))
}
################################################################################
# Task Execution - IAM Role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
locals {
task_exec_iam_role_name = try(coalesce(var.task_exec_iam_role_name, var.cluster_name), "")
create_task_exec_iam_role = var.create && var.create_task_exec_iam_role
create_task_exec_policy = local.create_task_exec_iam_role && var.create_task_exec_policy
}
data "aws_iam_policy_document" "task_exec_assume" {
count = local.create_task_exec_iam_role ? 1 : 0
statement {
sid = "ECSTaskExecutionAssumeRole"
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role" "task_exec" {
count = local.create_task_exec_iam_role ? 1 : 0
name = var.task_exec_iam_role_use_name_prefix ? null : local.task_exec_iam_role_name
name_prefix = var.task_exec_iam_role_use_name_prefix ? "${local.task_exec_iam_role_name}-" : null
path = var.task_exec_iam_role_path
description = coalesce(var.task_exec_iam_role_description, "Task execution role for ${var.cluster_name}")
assume_role_policy = data.aws_iam_policy_document.task_exec_assume[0].json
permissions_boundary = var.task_exec_iam_role_permissions_boundary
force_detach_policies = true
tags = merge(var.tags, var.task_exec_iam_role_tags)
}
resource "aws_iam_role_policy_attachment" "task_exec_additional" {
for_each = { for k, v in var.task_exec_iam_role_policies : k => v if local.create_task_exec_iam_role }
role = aws_iam_role.task_exec[0].name
policy_arn = each.value
}
data "aws_iam_policy_document" "task_exec" {
count = local.create_task_exec_policy ? 1 : 0
# Pulled from AmazonECSTaskExecutionRolePolicy
statement {
sid = "Logs"
actions = [
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = ["*"]
}
# Pulled from AmazonECSTaskExecutionRolePolicy
statement {
sid = "ECR"
actions = [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
]
resources = ["*"]
}
dynamic "statement" {
for_each = length(var.task_exec_ssm_param_arns) > 0 ? [1] : []
content {
sid = "GetSSMParams"
actions = ["ssm:GetParameters"]
resources = var.task_exec_ssm_param_arns
}
}
dynamic "statement" {
for_each = length(var.task_exec_secret_arns) > 0 ? [1] : []
content {
sid = "GetSecrets"
actions = ["secretsmanager:GetSecretValue"]
resources = var.task_exec_secret_arns
}
}
dynamic "statement" {
for_each = var.task_exec_iam_statements
content {
sid = try(statement.value.sid, null)
actions = try(statement.value.actions, null)
not_actions = try(statement.value.not_actions, null)
effect = try(statement.value.effect, null)
resources = try(statement.value.resources, null)
not_resources = try(statement.value.not_resources, null)
dynamic "principals" {
for_each = try(statement.value.principals, [])
content {
type = principals.value.type
identifiers = principals.value.identifiers
}
}
dynamic "not_principals" {
for_each = try(statement.value.not_principals, [])
content {
type = not_principals.value.type
identifiers = not_principals.value.identifiers
}
}
dynamic "condition" {
for_each = try(statement.value.conditions, [])
content {
test = condition.value.test
values = condition.value.values
variable = condition.value.variable
}
}
}
}
}
resource "aws_iam_policy" "task_exec" {
count = local.create_task_exec_policy ? 1 : 0
name = var.task_exec_iam_role_use_name_prefix ? null : local.task_exec_iam_role_name
name_prefix = var.task_exec_iam_role_use_name_prefix ? "${local.task_exec_iam_role_name}-" : null
description = coalesce(var.task_exec_iam_role_description, "Task execution role IAM policy")
policy = data.aws_iam_policy_document.task_exec[0].json
tags = merge(var.tags, var.task_exec_iam_role_tags)
}
resource "aws_iam_role_policy_attachment" "task_exec" {
count = local.create_task_exec_policy ? 1 : 0
role = aws_iam_role.task_exec[0].name
policy_arn = aws_iam_policy.task_exec[0].arn
}

View file

@ -1,70 +0,0 @@
################################################################################
# Cluster
################################################################################
output "arn" {
description = "ARN that identifies the cluster"
value = try(aws_ecs_cluster.this[0].arn, null)
}
output "id" {
description = "ID that identifies the cluster"
value = try(aws_ecs_cluster.this[0].id, null)
}
output "name" {
description = "Name that identifies the cluster"
value = try(aws_ecs_cluster.this[0].name, null)
}
################################################################################
# CloudWatch Log Group
################################################################################
output "cloudwatch_log_group_name" {
description = "Name of CloudWatch log group created"
value = try(aws_cloudwatch_log_group.this[0].name, null)
}
output "cloudwatch_log_group_arn" {
description = "ARN of CloudWatch log group created"
value = try(aws_cloudwatch_log_group.this[0].arn, null)
}
################################################################################
# Cluster Capacity Providers
################################################################################
output "cluster_capacity_providers" {
description = "Map of cluster capacity providers attributes"
value = { for k, v in aws_ecs_cluster_capacity_providers.this : v.id => v }
}
################################################################################
# Capacity Provider - Autoscaling Group(s)
################################################################################
output "autoscaling_capacity_providers" {
description = "Map of autoscaling capacity providers created and their attributes"
value = aws_ecs_capacity_provider.this
}
################################################################################
# Task Execution - IAM Role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
output "task_exec_iam_role_name" {
description = "Task execution IAM role name"
value = try(aws_iam_role.task_exec[0].name, null)
}
output "task_exec_iam_role_arn" {
description = "Task execution IAM role ARN"
value = try(aws_iam_role.task_exec[0].arn, null)
}
output "task_exec_iam_role_unique_id" {
description = "Stable and unique string identifying the task execution IAM role"
value = try(aws_iam_role.task_exec[0].unique_id, null)
}

View file

@ -1,177 +0,0 @@
variable "create" {
description = "Determines whether resources will be created (affects all resources)"
type = bool
default = true
}
variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
default = {}
}
################################################################################
# Cluster
################################################################################
variable "cluster_name" {
description = "Name of the cluster (up to 255 letters, numbers, hyphens, and underscores)"
type = string
default = ""
}
variable "cluster_configuration" {
description = "The execute command configuration for the cluster"
type = any
default = {}
}
variable "cluster_settings" {
description = "List of configuration block(s) with cluster settings. For example, this can be used to enable CloudWatch Container Insights for a cluster"
type = any
default = [
{
name = "containerInsights"
value = "enabled"
}
]
}
variable "cluster_service_connect_defaults" {
description = "Configures a default Service Connect namespace"
type = map(string)
default = {}
}
################################################################################
# CloudWatch Log Group
################################################################################
variable "create_cloudwatch_log_group" {
description = "Determines whether a log group is created by this module for the cluster logs. If not, AWS will automatically create one if logging is enabled"
type = bool
default = true
}
variable "cloudwatch_log_group_name" {
description = "Custom name of CloudWatch Log Group for ECS cluster"
type = string
default = null
}
variable "cloudwatch_log_group_retention_in_days" {
description = "Number of days to retain log events"
type = number
default = 90
}
variable "cloudwatch_log_group_kms_key_id" {
description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)"
type = string
default = null
}
variable "cloudwatch_log_group_tags" {
description = "A map of additional tags to add to the log group created"
type = map(string)
default = {}
}
################################################################################
# Capacity Providers
################################################################################
variable "default_capacity_provider_use_fargate" {
description = "Determines whether to use Fargate or autoscaling for default capacity provider strategy"
type = bool
default = true
}
variable "fargate_capacity_providers" {
description = "Map of Fargate capacity provider definitions to use for the cluster"
type = any
default = {}
}
variable "autoscaling_capacity_providers" {
description = "Map of autoscaling capacity provider definitions to create for the cluster"
type = any
default = {}
}
################################################################################
# Task Execution - IAM Role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
variable "create_task_exec_iam_role" {
description = "Determines whether the ECS task definition IAM role should be created"
type = bool
default = false
}
variable "task_exec_iam_role_name" {
description = "Name to use on IAM role created"
type = string
default = null
}
variable "task_exec_iam_role_use_name_prefix" {
description = "Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix"
type = bool
default = true
}
variable "task_exec_iam_role_path" {
description = "IAM role path"
type = string
default = null
}
variable "task_exec_iam_role_description" {
description = "Description of the role"
type = string
default = null
}
variable "task_exec_iam_role_permissions_boundary" {
description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
type = string
default = null
}
variable "task_exec_iam_role_tags" {
description = "A map of additional tags to add to the IAM role created"
type = map(string)
default = {}
}
variable "task_exec_iam_role_policies" {
description = "Map of IAM role policy ARNs to attach to the IAM role"
type = map(string)
default = {}
}
variable "create_task_exec_policy" {
description = "Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters"
type = bool
default = true
}
variable "task_exec_ssm_param_arns" {
description = "List of SSM parameter ARNs the task execution role will be permitted to get/read"
type = list(string)
default = ["arn:aws:ssm:*:*:parameter/*"]
}
variable "task_exec_secret_arns" {
description = "List of SecretsManager secret ARNs the task execution role will be permitted to get/read"
type = list(string)
default = ["arn:aws:secretsmanager:*:*:secret:*"]
}
variable "task_exec_iam_statements" {
description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
type = any
default = {}
}

View file

@ -1,203 +0,0 @@
# Amazon ECS Container Definition Module
Configuration in this directory creates an Amazon ECS container definition.
The module defaults to creating and utilizing a CloudWatch log group. You can disable this behavior by setting `enable_cloudwatch_logging` = `false` - useful for scenarios where Firelens is used for log forwarding.
For more details see the [design doc](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/design.md)
## Usage
### Standard
```hcl
module "ecs_container_definition" {
source = "terraform-aws-modules/ecs/aws//modules/container-definition"
name = "example"
cpu = 512
memory = 1024
essential = true
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
port_mappings = [
{
name = "ecs-sample"
containerPort = 80
protocol = "tcp"
}
]
# Example image used requires access to write to root filesystem
readonly_root_filesystem = false
memory_reservation = 100
tags = {
Environment = "dev"
Terraform = "true"
}
}
```
### W/ Firelens
```hcl
module "fluentbit_ecs_container_definition" {
source = "terraform-aws-modules/ecs/aws//modules/container-definition"
name = "fluent-bit"
cpu = 512
memory = 1024
essential = true
image = "906394416424.dkr.ecr.us-west-2.amazonaws.com/aws-for-fluent-bit:stable"
firelens_configuration = {
type = "fluentbit"
}
memory_reservation = 50
tags = {
Environment = "dev"
Terraform = "true"
}
}
module "example_ecs_container_definition" {
source = "terraform-aws-modules/ecs/aws//modules/container-definition"
name = "example"
cpu = 512
memory = 1024
essential = true
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
port_mappings = [
{
name = "ecs-sample"
containerPort = 80
protocol = "tcp"
}
]
# Example image used requires access to write to root filesystem
readonly_root_filesystem = false
dependencies = [{
containerName = "fluent-bit"
condition = "START"
}]
enable_cloudwatch_logging = false
log_configuration = {
logDriver = "awsfirelens"
options = {
Name = "firehose"
region = "eu-west-1"
delivery_stream = "my-stream"
log-driver-buffer-limit = "2097152"
}
}
memory_reservation = 100
tags = {
Environment = "dev"
Terraform = "true"
}
}
```
## Examples
- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.66.1 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.66.1 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_cloudwatch_log_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_group) | resource |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_cloudwatch_log_group_kms_key_id"></a> [cloudwatch\_log\_group\_kms\_key\_id](#input\_cloudwatch\_log\_group\_kms\_key\_id) | If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html) | `string` | `null` | no |
| <a name="input_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#input\_cloudwatch\_log\_group\_name) | Custom name of CloudWatch log group for a service associated with the container definition | `string` | `null` | no |
| <a name="input_cloudwatch_log_group_retention_in_days"></a> [cloudwatch\_log\_group\_retention\_in\_days](#input\_cloudwatch\_log\_group\_retention\_in\_days) | Number of days to retain log events. Default is 30 days | `number` | `30` | no |
| <a name="input_cloudwatch_log_group_use_name_prefix"></a> [cloudwatch\_log\_group\_use\_name\_prefix](#input\_cloudwatch\_log\_group\_use\_name\_prefix) | Determines whether the log group name should be used as a prefix | `bool` | `false` | no |
| <a name="input_command"></a> [command](#input\_command) | The command that's passed to the container | `list(string)` | `[]` | no |
| <a name="input_cpu"></a> [cpu](#input\_cpu) | The number of cpu units to reserve for the container. This is optional for tasks using Fargate launch type and the total amount of `cpu` of all containers in a task will need to be lower than the task-level cpu value | `number` | `null` | no |
| <a name="input_create_cloudwatch_log_group"></a> [create\_cloudwatch\_log\_group](#input\_create\_cloudwatch\_log\_group) | Determines whether a log group is created by this module. If not, AWS will automatically create one if logging is enabled | `bool` | `true` | no |
| <a name="input_dependencies"></a> [dependencies](#input\_dependencies) | The dependencies defined for container startup and shutdown. A container can contain multiple dependencies. When a dependency is defined for container startup, for container shutdown it is reversed. The condition can be one of START, COMPLETE, SUCCESS or HEALTHY | <pre>list(object({<br> condition = string<br> containerName = string<br> }))</pre> | `[]` | no |
| <a name="input_disable_networking"></a> [disable\_networking](#input\_disable\_networking) | When this parameter is true, networking is disabled within the container | `bool` | `null` | no |
| <a name="input_dns_search_domains"></a> [dns\_search\_domains](#input\_dns\_search\_domains) | Container DNS search domains. A list of DNS search domains that are presented to the container | `list(string)` | `[]` | no |
| <a name="input_dns_servers"></a> [dns\_servers](#input\_dns\_servers) | Container DNS servers. This is a list of strings specifying the IP addresses of the DNS servers | `list(string)` | `[]` | no |
| <a name="input_docker_labels"></a> [docker\_labels](#input\_docker\_labels) | A key/value map of labels to add to the container | `map(string)` | `{}` | no |
| <a name="input_docker_security_options"></a> [docker\_security\_options](#input\_docker\_security\_options) | A list of strings to provide custom labels for SELinux and AppArmor multi-level security systems. This field isn't valid for containers in tasks using the Fargate launch type | `list(string)` | `[]` | no |
| <a name="input_enable_cloudwatch_logging"></a> [enable\_cloudwatch\_logging](#input\_enable\_cloudwatch\_logging) | Determines whether CloudWatch logging is configured for this container definition. Set to `false` to use other logging drivers | `bool` | `true` | no |
| <a name="input_enable_execute_command"></a> [enable\_execute\_command](#input\_enable\_execute\_command) | Specifies whether to enable Amazon ECS Exec for the tasks within the service | `bool` | `false` | no |
| <a name="input_entrypoint"></a> [entrypoint](#input\_entrypoint) | The entry point that is passed to the container | `list(string)` | `[]` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | The environment variables to pass to the container | <pre>list(object({<br> name = string<br> value = string<br> }))</pre> | `[]` | no |
| <a name="input_environment_files"></a> [environment\_files](#input\_environment\_files) | A list of files containing the environment variables to pass to a container | <pre>list(object({<br> value = string<br> type = string<br> }))</pre> | `[]` | no |
| <a name="input_essential"></a> [essential](#input\_essential) | If the `essential` parameter of a container is marked as `true`, and that container fails or stops for any reason, all other containers that are part of the task are stopped | `bool` | `null` | no |
| <a name="input_extra_hosts"></a> [extra\_hosts](#input\_extra\_hosts) | A list of hostnames and IP address mappings to append to the `/etc/hosts` file on the container | <pre>list(object({<br> hostname = string<br> ipAddress = string<br> }))</pre> | `[]` | no |
| <a name="input_firelens_configuration"></a> [firelens\_configuration](#input\_firelens\_configuration) | The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more information, see [Custom Log Routing](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html) in the Amazon Elastic Container Service Developer Guide | `any` | `{}` | no |
| <a name="input_health_check"></a> [health\_check](#input\_health\_check) | The container health check command and associated configuration parameters for the container. See [HealthCheck](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_HealthCheck.html) | `any` | `{}` | no |
| <a name="input_hostname"></a> [hostname](#input\_hostname) | The hostname to use for your container | `string` | `null` | no |
| <a name="input_image"></a> [image](#input\_image) | The image used to start a container. This string is passed directly to the Docker daemon. By default, images in the Docker Hub registry are available. Other repositories are specified with either `repository-url/image:tag` or `repository-url/image@digest` | `string` | `null` | no |
| <a name="input_interactive"></a> [interactive](#input\_interactive) | When this parameter is `true`, you can deploy containerized applications that require `stdin` or a `tty` to be allocated | `bool` | `false` | no |
| <a name="input_links"></a> [links](#input\_links) | The links parameter allows containers to communicate with each other without the need for port mappings. This parameter is only supported if the network mode of a task definition is `bridge` | `list(string)` | `[]` | no |
| <a name="input_linux_parameters"></a> [linux\_parameters](#input\_linux\_parameters) | Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more information see [KernelCapabilities](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_KernelCapabilities.html) | `any` | `{}` | no |
| <a name="input_log_configuration"></a> [log\_configuration](#input\_log\_configuration) | The log configuration for the container. For more information see [LogConfiguration](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html) | `any` | `{}` | no |
| <a name="input_memory"></a> [memory](#input\_memory) | The amount (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed. The total amount of memory reserved for all containers within a task must be lower than the task `memory` value, if one is specified | `number` | `null` | no |
| <a name="input_memory_reservation"></a> [memory\_reservation](#input\_memory\_reservation) | The soft limit (in MiB) of memory to reserve for the container. When system memory is under heavy contention, Docker attempts to keep the container memory to this soft limit. However, your container can consume more memory when it needs to, up to either the hard limit specified with the `memory` parameter (if applicable), or all of the available memory on the container instance | `number` | `null` | no |
| <a name="input_mount_points"></a> [mount\_points](#input\_mount\_points) | The mount points for data volumes in your container | `list(any)` | `[]` | no |
| <a name="input_name"></a> [name](#input\_name) | The name of a container. If you're linking multiple containers together in a task definition, the name of one container can be entered in the links of another container to connect the containers. Up to 255 letters (uppercase and lowercase), numbers, underscores, and hyphens are allowed | `string` | `null` | no |
| <a name="input_operating_system_family"></a> [operating\_system\_family](#input\_operating\_system\_family) | The OS family for task | `string` | `"LINUX"` | no |
| <a name="input_port_mappings"></a> [port\_mappings](#input\_port\_mappings) | The list of port mappings for the container. Port mappings allow containers to access ports on the host container instance to send or receive traffic. For task definitions that use the awsvpc network mode, only specify the containerPort. The hostPort can be left blank or it must be the same value as the containerPort | `list(any)` | `[]` | no |
| <a name="input_privileged"></a> [privileged](#input\_privileged) | When this parameter is true, the container is given elevated privileges on the host container instance (similar to the root user) | `bool` | `false` | no |
| <a name="input_pseudo_terminal"></a> [pseudo\_terminal](#input\_pseudo\_terminal) | When this parameter is true, a `TTY` is allocated | `bool` | `false` | no |
| <a name="input_readonly_root_filesystem"></a> [readonly\_root\_filesystem](#input\_readonly\_root\_filesystem) | When this parameter is true, the container is given read-only access to its root file system | `bool` | `true` | no |
| <a name="input_repository_credentials"></a> [repository\_credentials](#input\_repository\_credentials) | Container repository credentials; required when using a private repo. This map currently supports a single key; "credentialsParameter", which should be the ARN of a Secrets Manager's secret holding the credentials | `map(string)` | `{}` | no |
| <a name="input_resource_requirements"></a> [resource\_requirements](#input\_resource\_requirements) | The type and amount of a resource to assign to a container. The only supported resource is a GPU | <pre>list(object({<br> type = string<br> value = string<br> }))</pre> | `[]` | no |
| <a name="input_secrets"></a> [secrets](#input\_secrets) | The secrets to pass to the container. For more information, see [Specifying Sensitive Data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the Amazon Elastic Container Service Developer Guide | <pre>list(object({<br> name = string<br> valueFrom = string<br> }))</pre> | `[]` | no |
| <a name="input_service"></a> [service](#input\_service) | The name of the service that the container definition is associated with | `string` | `""` | no |
| <a name="input_start_timeout"></a> [start\_timeout](#input\_start\_timeout) | Time duration (in seconds) to wait before giving up on resolving dependencies for a container | `number` | `30` | no |
| <a name="input_stop_timeout"></a> [stop\_timeout](#input\_stop\_timeout) | Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own | `number` | `120` | no |
| <a name="input_system_controls"></a> [system\_controls](#input\_system\_controls) | A list of namespaced kernel parameters to set in the container | `list(map(string))` | `[]` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
| <a name="input_ulimits"></a> [ulimits](#input\_ulimits) | A list of ulimits to set in the container. If a ulimit value is specified in a task definition, it overrides the default values set by Docker | <pre>list(object({<br> hardLimit = number<br> name = string<br> softLimit = number<br> }))</pre> | `[]` | no |
| <a name="input_user"></a> [user](#input\_user) | The user to run as inside the container. Can be any of these formats: user, user:group, uid, uid:gid, user:gid, uid:group. The default (null) will use the container's configured `USER` directive or root if not set | `string` | `null` | no |
| <a name="input_volumes_from"></a> [volumes\_from](#input\_volumes\_from) | Data volumes to mount from another container | `list(any)` | `[]` | no |
| <a name="input_working_directory"></a> [working\_directory](#input\_working\_directory) | The working directory to run commands inside the container | `string` | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_cloudwatch_log_group_arn"></a> [cloudwatch\_log\_group\_arn](#output\_cloudwatch\_log\_group\_arn) | ARN of CloudWatch log group created |
| <a name="output_cloudwatch_log_group_name"></a> [cloudwatch\_log\_group\_name](#output\_cloudwatch\_log\_group\_name) | Name of CloudWatch log group created |
| <a name="output_container_definition"></a> [container\_definition](#output\_container\_definition) | Container definition |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## License
Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE).

View file

@ -1,83 +0,0 @@
data "aws_region" "current" {}
locals {
is_not_windows = contains(["LINUX"], var.operating_system_family)
log_group_name = try(coalesce(var.cloudwatch_log_group_name, "/aws/ecs/${var.service}/${var.name}"), "")
log_configuration = merge(
{ for k, v in {
logDriver = "awslogs",
options = {
awslogs-region = data.aws_region.current.name,
awslogs-group = try(aws_cloudwatch_log_group.this[0].name, ""),
awslogs-stream-prefix = "ecs"
},
} : k => v if var.enable_cloudwatch_logging },
var.log_configuration
)
linux_parameters = var.enable_execute_command ? merge({ "initProcessEnabled" : true }, var.linux_parameters) : merge({ "initProcessEnabled" : false }, var.linux_parameters)
health_check = length(var.health_check) > 0 ? merge({
interval = 30,
retries = 3,
timeout = 5
}, var.health_check) : null
definition = {
command = length(var.command) > 0 ? var.command : null
cpu = var.cpu
dependsOn = length(var.dependencies) > 0 ? var.dependencies : null # depends_on is a reserved word
disableNetworking = local.is_not_windows ? var.disable_networking : null
dnsSearchDomains = local.is_not_windows && length(var.dns_search_domains) > 0 ? var.dns_search_domains : null
dnsServers = local.is_not_windows && length(var.dns_servers) > 0 ? var.dns_servers : null
dockerLabels = length(var.docker_labels) > 0 ? var.docker_labels : null
dockerSecurityOptions = length(var.docker_security_options) > 0 ? var.docker_security_options : null
entrypoint = length(var.entrypoint) > 0 ? var.entrypoint : null
environment = var.environment
environmentFiles = length(var.environment_files) > 0 ? var.environment_files : null
essential = var.essential
extraHosts = local.is_not_windows && length(var.extra_hosts) > 0 ? var.extra_hosts : null
firelensConfiguration = length(var.firelens_configuration) > 0 ? var.firelens_configuration : null
healthCheck = local.health_check
hostname = var.hostname
image = var.image
interactive = var.interactive
links = local.is_not_windows && length(var.links) > 0 ? var.links : null
linuxParameters = local.is_not_windows && length(local.linux_parameters) > 0 ? local.linux_parameters : null
logConfiguration = length(local.log_configuration) > 0 ? local.log_configuration : null
memory = var.memory
memoryReservation = var.memory_reservation
mountPoints = var.mount_points
name = var.name
portMappings = var.port_mappings
privileged = local.is_not_windows ? var.privileged : null
pseudoTerminal = var.pseudo_terminal
readonlyRootFilesystem = local.is_not_windows ? var.readonly_root_filesystem : null
repositoryCredentials = length(var.repository_credentials) > 0 ? var.repository_credentials : null
resourceRequirements = length(var.resource_requirements) > 0 ? var.resource_requirements : null
secrets = length(var.secrets) > 0 ? var.secrets : null
startTimeout = var.start_timeout
stopTimeout = var.stop_timeout
systemControls = length(var.system_controls) > 0 ? var.system_controls : []
ulimits = local.is_not_windows && length(var.ulimits) > 0 ? var.ulimits : null
user = local.is_not_windows ? var.user : null
volumesFrom = var.volumes_from
workingDirectory = var.working_directory
}
# Strip out all null values, ECS API will provide defaults in place of null/empty values
container_definition = { for k, v in local.definition : k => v if v != null }
}
resource "aws_cloudwatch_log_group" "this" {
count = var.create_cloudwatch_log_group && var.enable_cloudwatch_logging ? 1 : 0
name = var.cloudwatch_log_group_use_name_prefix ? null : local.log_group_name
name_prefix = var.cloudwatch_log_group_use_name_prefix ? "${local.log_group_name}-" : null
retention_in_days = var.cloudwatch_log_group_retention_in_days
kms_key_id = var.cloudwatch_log_group_kms_key_id
tags = var.tags
}

View file

@ -1,22 +0,0 @@
################################################################################
# Container Definition
################################################################################
output "container_definition" {
description = "Container definition"
value = local.container_definition
}
################################################################################
# CloudWatch Log Group
################################################################################
output "cloudwatch_log_group_name" {
description = "Name of CloudWatch log group created"
value = try(aws_cloudwatch_log_group.this[0].name, null)
}
output "cloudwatch_log_group_arn" {
description = "ARN of CloudWatch log group created"
value = try(aws_cloudwatch_log_group.this[0].arn, null)
}

View file

@ -1,323 +0,0 @@
variable "operating_system_family" {
description = "The OS family for task"
type = string
default = "LINUX"
}
################################################################################
# Container Definition
################################################################################
variable "command" {
description = "The command that's passed to the container"
type = list(string)
default = []
}
variable "cpu" {
description = "The number of cpu units to reserve for the container. This is optional for tasks using Fargate launch type and the total amount of `cpu` of all containers in a task will need to be lower than the task-level cpu value"
type = number
default = null
}
variable "dependencies" {
description = "The dependencies defined for container startup and shutdown. A container can contain multiple dependencies. When a dependency is defined for container startup, for container shutdown it is reversed. The condition can be one of START, COMPLETE, SUCCESS or HEALTHY"
type = list(object({
condition = string
containerName = string
}))
default = []
}
variable "disable_networking" {
description = "When this parameter is true, networking is disabled within the container"
type = bool
default = null
}
variable "dns_search_domains" {
description = "Container DNS search domains. A list of DNS search domains that are presented to the container"
type = list(string)
default = []
}
variable "dns_servers" {
description = "Container DNS servers. This is a list of strings specifying the IP addresses of the DNS servers"
type = list(string)
default = []
}
variable "docker_labels" {
description = "A key/value map of labels to add to the container"
type = map(string)
default = {}
}
variable "docker_security_options" {
description = "A list of strings to provide custom labels for SELinux and AppArmor multi-level security systems. This field isn't valid for containers in tasks using the Fargate launch type"
type = list(string)
default = []
}
variable "enable_execute_command" {
description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service"
type = bool
default = false
}
variable "entrypoint" {
description = "The entry point that is passed to the container"
type = list(string)
default = []
}
variable "environment" {
description = "The environment variables to pass to the container"
type = list(object({
name = string
value = string
}))
default = []
}
variable "environment_files" {
description = "A list of files containing the environment variables to pass to a container"
type = list(object({
value = string
type = string
}))
default = []
}
variable "essential" {
description = "If the `essential` parameter of a container is marked as `true`, and that container fails or stops for any reason, all other containers that are part of the task are stopped"
type = bool
default = null
}
variable "extra_hosts" {
description = "A list of hostnames and IP address mappings to append to the `/etc/hosts` file on the container"
type = list(object({
hostname = string
ipAddress = string
}))
default = []
}
variable "firelens_configuration" {
description = "The FireLens configuration for the container. This is used to specify and configure a log router for container logs. For more information, see [Custom Log Routing](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/using_firelens.html) in the Amazon Elastic Container Service Developer Guide"
type = any
default = {}
}
variable "health_check" {
description = "The container health check command and associated configuration parameters for the container. See [HealthCheck](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_HealthCheck.html)"
type = any
default = {}
}
variable "hostname" {
description = "The hostname to use for your container"
type = string
default = null
}
variable "image" {
description = "The image used to start a container. This string is passed directly to the Docker daemon. By default, images in the Docker Hub registry are available. Other repositories are specified with either `repository-url/image:tag` or `repository-url/image@digest`"
type = string
default = null
}
variable "interactive" {
description = "When this parameter is `true`, you can deploy containerized applications that require `stdin` or a `tty` to be allocated"
type = bool
default = false
}
variable "links" {
description = "The links parameter allows containers to communicate with each other without the need for port mappings. This parameter is only supported if the network mode of a task definition is `bridge`"
type = list(string)
default = []
}
variable "linux_parameters" {
description = "Linux-specific modifications that are applied to the container, such as Linux kernel capabilities. For more information see [KernelCapabilities](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_KernelCapabilities.html)"
type = any
default = {}
}
variable "log_configuration" {
description = "The log configuration for the container. For more information see [LogConfiguration](https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html)"
type = any
default = {}
}
variable "memory" {
description = "The amount (in MiB) of memory to present to the container. If your container attempts to exceed the memory specified here, the container is killed. The total amount of memory reserved for all containers within a task must be lower than the task `memory` value, if one is specified"
type = number
default = null
}
variable "memory_reservation" {
description = "The soft limit (in MiB) of memory to reserve for the container. When system memory is under heavy contention, Docker attempts to keep the container memory to this soft limit. However, your container can consume more memory when it needs to, up to either the hard limit specified with the `memory` parameter (if applicable), or all of the available memory on the container instance"
type = number
default = null
}
variable "mount_points" {
description = "The mount points for data volumes in your container"
type = list(any)
default = []
}
variable "name" {
description = "The name of a container. If you're linking multiple containers together in a task definition, the name of one container can be entered in the links of another container to connect the containers. Up to 255 letters (uppercase and lowercase), numbers, underscores, and hyphens are allowed"
type = string
default = null
}
variable "port_mappings" {
description = "The list of port mappings for the container. Port mappings allow containers to access ports on the host container instance to send or receive traffic. For task definitions that use the awsvpc network mode, only specify the containerPort. The hostPort can be left blank or it must be the same value as the containerPort"
type = list(any)
default = []
}
variable "privileged" {
description = "When this parameter is true, the container is given elevated privileges on the host container instance (similar to the root user)"
type = bool
default = false
}
variable "pseudo_terminal" {
description = "When this parameter is true, a `TTY` is allocated"
type = bool
default = false
}
variable "readonly_root_filesystem" {
description = "When this parameter is true, the container is given read-only access to its root file system"
type = bool
default = true
}
variable "repository_credentials" {
description = "Container repository credentials; required when using a private repo. This map currently supports a single key; \"credentialsParameter\", which should be the ARN of a Secrets Manager's secret holding the credentials"
type = map(string)
default = {}
}
variable "resource_requirements" {
description = "The type and amount of a resource to assign to a container. The only supported resource is a GPU"
type = list(object({
type = string
value = string
}))
default = []
}
variable "secrets" {
description = "The secrets to pass to the container. For more information, see [Specifying Sensitive Data](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/specifying-sensitive-data.html) in the Amazon Elastic Container Service Developer Guide"
type = list(object({
name = string
valueFrom = string
}))
default = []
}
variable "start_timeout" {
description = "Time duration (in seconds) to wait before giving up on resolving dependencies for a container"
type = number
default = 30
}
variable "stop_timeout" {
description = "Time duration (in seconds) to wait before the container is forcefully killed if it doesn't exit normally on its own"
type = number
default = 120
}
variable "system_controls" {
description = "A list of namespaced kernel parameters to set in the container"
type = list(map(string))
default = []
}
variable "ulimits" {
description = "A list of ulimits to set in the container. If a ulimit value is specified in a task definition, it overrides the default values set by Docker"
type = list(object({
hardLimit = number
name = string
softLimit = number
}))
default = []
}
variable "user" {
description = "The user to run as inside the container. Can be any of these formats: user, user:group, uid, uid:gid, user:gid, uid:group. The default (null) will use the container's configured `USER` directive or root if not set"
type = string
default = null
}
variable "volumes_from" {
description = "Data volumes to mount from another container"
type = list(any)
default = []
}
variable "working_directory" {
description = "The working directory to run commands inside the container"
type = string
default = null
}
################################################################################
# CloudWatch Log Group
################################################################################
variable "service" {
description = "The name of the service that the container definition is associated with"
type = string
default = ""
}
variable "enable_cloudwatch_logging" {
description = "Determines whether CloudWatch logging is configured for this container definition. Set to `false` to use other logging drivers"
type = bool
default = true
}
variable "create_cloudwatch_log_group" {
description = "Determines whether a log group is created by this module. If not, AWS will automatically create one if logging is enabled"
type = bool
default = true
}
variable "cloudwatch_log_group_name" {
description = "Custom name of CloudWatch log group for a service associated with the container definition"
type = string
default = null
}
variable "cloudwatch_log_group_use_name_prefix" {
description = "Determines whether the log group name should be used as a prefix"
type = bool
default = false
}
variable "cloudwatch_log_group_retention_in_days" {
description = "Number of days to retain log events. Default is 30 days"
type = number
default = 30
}
variable "cloudwatch_log_group_kms_key_id" {
description = "If a KMS Key ARN is set, this key will be used to encrypt the corresponding log group. Please be sure that the KMS Key has an appropriate key policy (https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html)"
type = string
default = null
}
variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
default = {}
}

View file

@ -1,10 +0,0 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.66.1"
}
}
}

View file

@ -1,358 +0,0 @@
# Amazon ECS Service Module
Configuration in this directory creates an Amazon ECS Service and associated resources.
Some notable configurations to be aware of when using this module:
1. `desired_count`/`scale` is always ignored; the module is designed to utilize autoscaling by default (though it can be disabled)
2. The default configuration is intended for `FARGATE` use
For more details see the [design doc](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/docs/README.md)
### Logging
Please refer to [FireLens examples repository](https://github.com/aws-samples/amazon-ecs-firelens-examples) for logging configuration examples for FireLens on Amazon ECS and AWS Fargate.
## Usage
```hcl
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
name = "example"
cluster_arn = "arn:aws:ecs:us-west-2:123456789012:cluster/default"
cpu = 1024
memory = 4096
# Container definition(s)
container_definitions = {
fluent-bit = {
cpu = 512
memory = 1024
essential = true
image = "906394416424.dkr.ecr.us-west-2.amazonaws.com/aws-for-fluent-bit:stable"
firelens_configuration = {
type = "fluentbit"
}
memory_reservation = 50
}
ecs-sample = {
cpu = 512
memory = 1024
essential = true
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
port_mappings = [
{
name = "ecs-sample"
containerPort = 80
protocol = "tcp"
}
]
# Example image used requires access to write to root filesystem
readonly_root_filesystem = false
dependencies = [{
containerName = "fluent-bit"
condition = "START"
}]
enable_cloudwatch_logging = false
log_configuration = {
logDriver = "awsfirelens"
options = {
Name = "firehose"
region = "eu-west-1"
delivery_stream = "my-stream"
log-driver-buffer-limit = "2097152"
}
}
memory_reservation = 100
}
}
service_connect_configuration = {
namespace = "example"
service = {
client_alias = {
port = 80
dns_name = "ecs-sample"
}
port_name = "ecs-sample"
discovery_name = "ecs-sample"
}
}
load_balancer = {
service = {
target_group_arn = "arn:aws:elasticloadbalancing:eu-west-1:1234567890:targetgroup/bluegreentarget1/209a844cd01825a4"
container_name = "ecs-sample"
container_port = 80
}
}
subnet_ids = ["subnet-abcde012", "subnet-bcde012a", "subnet-fghi345a"]
security_group_rules = {
alb_ingress_3000 = {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
description = "Service port"
source_security_group_id = "sg-12345678"
}
egress_all = {
type = "egress"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
tags = {
Environment = "dev"
Terraform = "true"
}
}
```
## Conditional Creation
The following values are provided to toggle on/off creation of the associated resources as desired:
```hcl
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
# Disable creation of service and all resources
create = false
# Enable ECS Exec
enable_execute_command = true
# Disable creation of the service IAM role; `iam_role_arn` should be provided
create_iam_role = false
# Disable creation of the task definition; `task_definition_arn` should be provided
create_task_definition = false
# Disable creation of the task execution IAM role; `task_exec_iam_role_arn` should be provided
create_task_exec_iam_role = false
# Disable creation of the task execution IAM role policy
create_task_exec_policy = false
# Disable creation of the tasks IAM role; `tasks_iam_role_arn` should be provided
create_tasks_iam_role = false
# Disable creation of the service security group
create_security_group = false
# ... omitted
}
```
## Examples
- [ECS Cluster Complete](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/complete)
- [ECS Cluster w/ EC2 Autoscaling Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/ec2-autoscaling)
- [ECS Cluster w/ Fargate Capacity Provider](https://github.com/terraform-aws-modules/terraform-aws-ecs/tree/master/examples/fargate)
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 4.66.1 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.66.1 |
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_container_definition"></a> [container\_definition](#module\_container\_definition) | ../container-definition | n/a |
## Resources
| Name | Type |
|------|------|
| [aws_appautoscaling_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_policy) | resource |
| [aws_appautoscaling_scheduled_action.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_scheduled_action) | resource |
| [aws_appautoscaling_target.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/appautoscaling_target) | resource |
| [aws_ecs_service.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource |
| [aws_ecs_service.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_service) | resource |
| [aws_ecs_task_definition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
| [aws_ecs_task_set.ignore_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_set) | resource |
| [aws_ecs_task_set.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_set) | resource |
| [aws_iam_policy.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role.tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy.tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws_iam_role_policy_attachment.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.task_exec_additional](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_iam_role_policy_attachment.tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_security_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws_ecs_task_definition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ecs_task_definition) | data source |
| [aws_iam_policy_document.service](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.service_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.task_exec](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.task_exec_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.tasks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.tasks_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source |
| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
| [aws_subnet.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_alarms"></a> [alarms](#input\_alarms) | Information about the CloudWatch alarms | `any` | `{}` | no |
| <a name="input_assign_public_ip"></a> [assign\_public\_ip](#input\_assign\_public\_ip) | Assign a public IP address to the ENI (Fargate launch type only) | `bool` | `false` | no |
| <a name="input_autoscaling_max_capacity"></a> [autoscaling\_max\_capacity](#input\_autoscaling\_max\_capacity) | Maximum number of tasks to run in your service | `number` | `10` | no |
| <a name="input_autoscaling_min_capacity"></a> [autoscaling\_min\_capacity](#input\_autoscaling\_min\_capacity) | Minimum number of tasks to run in your service | `number` | `1` | no |
| <a name="input_autoscaling_policies"></a> [autoscaling\_policies](#input\_autoscaling\_policies) | Map of autoscaling policies to create for the service | `any` | <pre>{<br> "cpu": {<br> "policy_type": "TargetTrackingScaling",<br> "target_tracking_scaling_policy_configuration": {<br> "predefined_metric_specification": {<br> "predefined_metric_type": "ECSServiceAverageCPUUtilization"<br> }<br> }<br> },<br> "memory": {<br> "policy_type": "TargetTrackingScaling",<br> "target_tracking_scaling_policy_configuration": {<br> "predefined_metric_specification": {<br> "predefined_metric_type": "ECSServiceAverageMemoryUtilization"<br> }<br> }<br> }<br>}</pre> | no |
| <a name="input_autoscaling_scheduled_actions"></a> [autoscaling\_scheduled\_actions](#input\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions to create for the service | `any` | `{}` | no |
| <a name="input_capacity_provider_strategy"></a> [capacity\_provider\_strategy](#input\_capacity\_provider\_strategy) | Capacity provider strategies to use for the service. Can be one or more | `any` | `{}` | no |
| <a name="input_cluster_arn"></a> [cluster\_arn](#input\_cluster\_arn) | ARN of the ECS cluster where the resources will be provisioned | `string` | `""` | no |
| <a name="input_container_definition_defaults"></a> [container\_definition\_defaults](#input\_container\_definition\_defaults) | A map of default values for [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html) created by `container_definitions` | `any` | `{}` | no |
| <a name="input_container_definitions"></a> [container\_definitions](#input\_container\_definitions) | A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document | `any` | `{}` | no |
| <a name="input_cpu"></a> [cpu](#input\_cpu) | Number of cpu units used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `1024` | no |
| <a name="input_create"></a> [create](#input\_create) | Determines whether resources will be created (affects all resources) | `bool` | `true` | no |
| <a name="input_create_iam_role"></a> [create\_iam\_role](#input\_create\_iam\_role) | Determines whether the ECS service IAM role should be created | `bool` | `true` | no |
| <a name="input_create_security_group"></a> [create\_security\_group](#input\_create\_security\_group) | Determines if a security group is created | `bool` | `true` | no |
| <a name="input_create_service"></a> [create\_service](#input\_create\_service) | Determines whether service resource will be created (set to `false` in case you want to create task definition only) | `bool` | `true` | no |
| <a name="input_create_task_definition"></a> [create\_task\_definition](#input\_create\_task\_definition) | Determines whether to create a task definition or use existing/provided | `bool` | `true` | no |
| <a name="input_create_task_exec_iam_role"></a> [create\_task\_exec\_iam\_role](#input\_create\_task\_exec\_iam\_role) | Determines whether the ECS task definition IAM role should be created | `bool` | `true` | no |
| <a name="input_create_task_exec_policy"></a> [create\_task\_exec\_policy](#input\_create\_task\_exec\_policy) | Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters | `bool` | `true` | no |
| <a name="input_create_tasks_iam_role"></a> [create\_tasks\_iam\_role](#input\_create\_tasks\_iam\_role) | Determines whether the ECS tasks IAM role should be created | `bool` | `true` | no |
| <a name="input_deployment_circuit_breaker"></a> [deployment\_circuit\_breaker](#input\_deployment\_circuit\_breaker) | Configuration block for deployment circuit breaker | `any` | `{}` | no |
| <a name="input_deployment_controller"></a> [deployment\_controller](#input\_deployment\_controller) | Configuration block for deployment controller configuration | `any` | `{}` | no |
| <a name="input_deployment_maximum_percent"></a> [deployment\_maximum\_percent](#input\_deployment\_maximum\_percent) | Upper limit (as a percentage of the service's `desired_count`) of the number of running tasks that can be running in a service during a deployment | `number` | `200` | no |
| <a name="input_deployment_minimum_healthy_percent"></a> [deployment\_minimum\_healthy\_percent](#input\_deployment\_minimum\_healthy\_percent) | Lower limit (as a percentage of the service's `desired_count`) of the number of running tasks that must remain running and healthy in a service during a deployment | `number` | `66` | no |
| <a name="input_desired_count"></a> [desired\_count](#input\_desired\_count) | Number of instances of the task definition to place and keep running | `number` | `1` | no |
| <a name="input_enable_autoscaling"></a> [enable\_autoscaling](#input\_enable\_autoscaling) | Determines whether to enable autoscaling for the service | `bool` | `true` | no |
| <a name="input_enable_ecs_managed_tags"></a> [enable\_ecs\_managed\_tags](#input\_enable\_ecs\_managed\_tags) | Specifies whether to enable Amazon ECS managed tags for the tasks within the service | `bool` | `true` | no |
| <a name="input_enable_execute_command"></a> [enable\_execute\_command](#input\_enable\_execute\_command) | Specifies whether to enable Amazon ECS Exec for the tasks within the service | `bool` | `false` | no |
| <a name="input_ephemeral_storage"></a> [ephemeral\_storage](#input\_ephemeral\_storage) | The amount of ephemeral storage to allocate for the task. This parameter is used to expand the total amount of ephemeral storage available, beyond the default amount, for tasks hosted on AWS Fargate | `any` | `{}` | no |
| <a name="input_external_id"></a> [external\_id](#input\_external\_id) | The external ID associated with the task set | `string` | `null` | no |
| <a name="input_family"></a> [family](#input\_family) | A unique name for your task definition | `string` | `null` | no |
| <a name="input_force_delete"></a> [force\_delete](#input\_force\_delete) | Whether to allow deleting the task set without waiting for scaling down to 0 | `bool` | `null` | no |
| <a name="input_force_new_deployment"></a> [force\_new\_deployment](#input\_force\_new\_deployment) | Enable to force a new task deployment of the service. This can be used to update tasks to use a newer Docker image with same image/tag combination, roll Fargate tasks onto a newer platform version, or immediately deploy `ordered_placement_strategy` and `placement_constraints` updates | `bool` | `true` | no |
| <a name="input_health_check_grace_period_seconds"></a> [health\_check\_grace\_period\_seconds](#input\_health\_check\_grace\_period\_seconds) | Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 2147483647. Only valid for services configured to use load balancers | `number` | `null` | no |
| <a name="input_iam_role_arn"></a> [iam\_role\_arn](#input\_iam\_role\_arn) | Existing IAM role ARN | `string` | `null` | no |
| <a name="input_iam_role_description"></a> [iam\_role\_description](#input\_iam\_role\_description) | Description of the role | `string` | `null` | no |
| <a name="input_iam_role_name"></a> [iam\_role\_name](#input\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
| <a name="input_iam_role_path"></a> [iam\_role\_path](#input\_iam\_role\_path) | IAM role path | `string` | `null` | no |
| <a name="input_iam_role_permissions_boundary"></a> [iam\_role\_permissions\_boundary](#input\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
| <a name="input_iam_role_statements"></a> [iam\_role\_statements](#input\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no |
| <a name="input_iam_role_tags"></a> [iam\_role\_tags](#input\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
| <a name="input_iam_role_use_name_prefix"></a> [iam\_role\_use\_name\_prefix](#input\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`iam_role_name`) is used as a prefix | `bool` | `true` | no |
| <a name="input_ignore_task_definition_changes"></a> [ignore\_task\_definition\_changes](#input\_ignore\_task\_definition\_changes) | Whether changes to service `task_definition` changes should be ignored | `bool` | `false` | no |
| <a name="input_inference_accelerator"></a> [inference\_accelerator](#input\_inference\_accelerator) | Configuration block(s) with Inference Accelerators settings | `any` | `{}` | no |
| <a name="input_ipc_mode"></a> [ipc\_mode](#input\_ipc\_mode) | IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none` | `string` | `null` | no |
| <a name="input_launch_type"></a> [launch\_type](#input\_launch\_type) | Launch type on which to run your service. The valid values are `EC2`, `FARGATE`, and `EXTERNAL`. Defaults to `FARGATE` | `string` | `"FARGATE"` | no |
| <a name="input_load_balancer"></a> [load\_balancer](#input\_load\_balancer) | Configuration block for load balancers | `any` | `{}` | no |
| <a name="input_memory"></a> [memory](#input\_memory) | Amount (in MiB) of memory used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `2048` | no |
| <a name="input_name"></a> [name](#input\_name) | Name of the service (up to 255 letters, numbers, hyphens, and underscores) | `string` | `null` | no |
| <a name="input_network_mode"></a> [network\_mode](#input\_network\_mode) | Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host` | `string` | `"awsvpc"` | no |
| <a name="input_ordered_placement_strategy"></a> [ordered\_placement\_strategy](#input\_ordered\_placement\_strategy) | Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence | `any` | `{}` | no |
| <a name="input_pid_mode"></a> [pid\_mode](#input\_pid\_mode) | Process namespace to use for the containers in the task. The valid values are `host` and `task` | `string` | `null` | no |
| <a name="input_placement_constraints"></a> [placement\_constraints](#input\_placement\_constraints) | Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the service, see `task_definition_placement_constraints` for setting at the task definition | `any` | `{}` | no |
| <a name="input_platform_version"></a> [platform\_version](#input\_platform\_version) | Platform version on which to run your service. Only applicable for `launch_type` set to `FARGATE`. Defaults to `LATEST` | `string` | `null` | no |
| <a name="input_propagate_tags"></a> [propagate\_tags](#input\_propagate\_tags) | Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are `SERVICE` and `TASK_DEFINITION` | `string` | `null` | no |
| <a name="input_proxy_configuration"></a> [proxy\_configuration](#input\_proxy\_configuration) | Configuration block for the App Mesh proxy | `any` | `{}` | no |
| <a name="input_requires_compatibilities"></a> [requires\_compatibilities](#input\_requires\_compatibilities) | Set of launch types required by the task. The valid values are `EC2` and `FARGATE` | `list(string)` | <pre>[<br> "FARGATE"<br>]</pre> | no |
| <a name="input_runtime_platform"></a> [runtime\_platform](#input\_runtime\_platform) | Configuration block for `runtime_platform` that containers in your task may use | `any` | <pre>{<br> "cpu_architecture": "X86_64",<br> "operating_system_family": "LINUX"<br>}</pre> | no |
| <a name="input_scale"></a> [scale](#input\_scale) | A floating-point percentage of the desired number of tasks to place and keep running in the task set | `any` | `{}` | no |
| <a name="input_scheduling_strategy"></a> [scheduling\_strategy](#input\_scheduling\_strategy) | Scheduling strategy to use for the service. The valid values are `REPLICA` and `DAEMON`. Defaults to `REPLICA` | `string` | `null` | no |
| <a name="input_security_group_description"></a> [security\_group\_description](#input\_security\_group\_description) | Description of the security group created | `string` | `null` | no |
| <a name="input_security_group_ids"></a> [security\_group\_ids](#input\_security\_group\_ids) | List of security groups to associate with the task or service | `list(string)` | `[]` | no |
| <a name="input_security_group_name"></a> [security\_group\_name](#input\_security\_group\_name) | Name to use on security group created | `string` | `null` | no |
| <a name="input_security_group_rules"></a> [security\_group\_rules](#input\_security\_group\_rules) | Security group rules to add to the security group created | `any` | `{}` | no |
| <a name="input_security_group_tags"></a> [security\_group\_tags](#input\_security\_group\_tags) | A map of additional tags to add to the security group created | `map(string)` | `{}` | no |
| <a name="input_security_group_use_name_prefix"></a> [security\_group\_use\_name\_prefix](#input\_security\_group\_use\_name\_prefix) | Determines whether the security group name (`security_group_name`) is used as a prefix | `bool` | `true` | no |
| <a name="input_service_connect_configuration"></a> [service\_connect\_configuration](#input\_service\_connect\_configuration) | The ECS Service Connect configuration for this service to discover and connect to services, and be discovered by, and connected from, other services within a namespace | `any` | `{}` | no |
| <a name="input_service_registries"></a> [service\_registries](#input\_service\_registries) | Service discovery registries for the service | `any` | `{}` | no |
| <a name="input_service_tags"></a> [service\_tags](#input\_service\_tags) | A map of additional tags to add to the service | `map(string)` | `{}` | no |
| <a name="input_skip_destroy"></a> [skip\_destroy](#input\_skip\_destroy) | If true, the task is not deleted when the service is deleted | `bool` | `null` | no |
| <a name="input_subnet_ids"></a> [subnet\_ids](#input\_subnet\_ids) | List of subnets to associate with the task or service | `list(string)` | `[]` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
| <a name="input_task_definition_arn"></a> [task\_definition\_arn](#input\_task\_definition\_arn) | Existing task definition ARN. Required when `create_task_definition` is `false` | `string` | `null` | no |
| <a name="input_task_definition_placement_constraints"></a> [task\_definition\_placement\_constraints](#input\_task\_definition\_placement\_constraints) | Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the task definition, see `placement_constraints` for setting at the service | `any` | `{}` | no |
| <a name="input_task_exec_iam_role_arn"></a> [task\_exec\_iam\_role\_arn](#input\_task\_exec\_iam\_role\_arn) | Existing IAM role ARN | `string` | `null` | no |
| <a name="input_task_exec_iam_role_description"></a> [task\_exec\_iam\_role\_description](#input\_task\_exec\_iam\_role\_description) | Description of the role | `string` | `null` | no |
| <a name="input_task_exec_iam_role_max_session_duration"></a> [task\_exec\_iam\_role\_max\_session\_duration](#input\_task\_exec\_iam\_role\_max\_session\_duration) | Maximum session duration (in seconds) for ECS task execution role. Default is 3600. | `number` | `null` | no |
| <a name="input_task_exec_iam_role_name"></a> [task\_exec\_iam\_role\_name](#input\_task\_exec\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
| <a name="input_task_exec_iam_role_path"></a> [task\_exec\_iam\_role\_path](#input\_task\_exec\_iam\_role\_path) | IAM role path | `string` | `null` | no |
| <a name="input_task_exec_iam_role_permissions_boundary"></a> [task\_exec\_iam\_role\_permissions\_boundary](#input\_task\_exec\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
| <a name="input_task_exec_iam_role_policies"></a> [task\_exec\_iam\_role\_policies](#input\_task\_exec\_iam\_role\_policies) | Map of IAM role policy ARNs to attach to the IAM role | `map(string)` | `{}` | no |
| <a name="input_task_exec_iam_role_tags"></a> [task\_exec\_iam\_role\_tags](#input\_task\_exec\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
| <a name="input_task_exec_iam_role_use_name_prefix"></a> [task\_exec\_iam\_role\_use\_name\_prefix](#input\_task\_exec\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix | `bool` | `true` | no |
| <a name="input_task_exec_iam_statements"></a> [task\_exec\_iam\_statements](#input\_task\_exec\_iam\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no |
| <a name="input_task_exec_secret_arns"></a> [task\_exec\_secret\_arns](#input\_task\_exec\_secret\_arns) | List of SecretsManager secret ARNs the task execution role will be permitted to get/read | `list(string)` | <pre>[<br> "arn:aws:secretsmanager:*:*:secret:*"<br>]</pre> | no |
| <a name="input_task_exec_ssm_param_arns"></a> [task\_exec\_ssm\_param\_arns](#input\_task\_exec\_ssm\_param\_arns) | List of SSM parameter ARNs the task execution role will be permitted to get/read | `list(string)` | <pre>[<br> "arn:aws:ssm:*:*:parameter/*"<br>]</pre> | no |
| <a name="input_task_tags"></a> [task\_tags](#input\_task\_tags) | A map of additional tags to add to the task definition/set created | `map(string)` | `{}` | no |
| <a name="input_tasks_iam_role_arn"></a> [tasks\_iam\_role\_arn](#input\_tasks\_iam\_role\_arn) | Existing IAM role ARN | `string` | `null` | no |
| <a name="input_tasks_iam_role_description"></a> [tasks\_iam\_role\_description](#input\_tasks\_iam\_role\_description) | Description of the role | `string` | `null` | no |
| <a name="input_tasks_iam_role_name"></a> [tasks\_iam\_role\_name](#input\_tasks\_iam\_role\_name) | Name to use on IAM role created | `string` | `null` | no |
| <a name="input_tasks_iam_role_path"></a> [tasks\_iam\_role\_path](#input\_tasks\_iam\_role\_path) | IAM role path | `string` | `null` | no |
| <a name="input_tasks_iam_role_permissions_boundary"></a> [tasks\_iam\_role\_permissions\_boundary](#input\_tasks\_iam\_role\_permissions\_boundary) | ARN of the policy that is used to set the permissions boundary for the IAM role | `string` | `null` | no |
| <a name="input_tasks_iam_role_policies"></a> [tasks\_iam\_role\_policies](#input\_tasks\_iam\_role\_policies) | Map of IAM role policy ARNs to attach to the IAM role | `map(string)` | `{}` | no |
| <a name="input_tasks_iam_role_statements"></a> [tasks\_iam\_role\_statements](#input\_tasks\_iam\_role\_statements) | A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage | `any` | `{}` | no |
| <a name="input_tasks_iam_role_tags"></a> [tasks\_iam\_role\_tags](#input\_tasks\_iam\_role\_tags) | A map of additional tags to add to the IAM role created | `map(string)` | `{}` | no |
| <a name="input_tasks_iam_role_use_name_prefix"></a> [tasks\_iam\_role\_use\_name\_prefix](#input\_tasks\_iam\_role\_use\_name\_prefix) | Determines whether the IAM role name (`tasks_iam_role_name`) is used as a prefix | `bool` | `true` | no |
| <a name="input_timeouts"></a> [timeouts](#input\_timeouts) | Create, update, and delete timeout configurations for the service | `map(string)` | `{}` | no |
| <a name="input_triggers"></a> [triggers](#input\_triggers) | Map of arbitrary keys and values that, when changed, will trigger an in-place update (redeployment). Useful with `timestamp()` | `any` | `{}` | no |
| <a name="input_volume"></a> [volume](#input\_volume) | Configuration block for volumes that containers in your task may use | `any` | `{}` | no |
| <a name="input_wait_for_steady_state"></a> [wait\_for\_steady\_state](#input\_wait\_for\_steady\_state) | If true, Terraform will wait for the service to reach a steady state before continuing. Default is `false` | `bool` | `null` | no |
| <a name="input_wait_until_stable"></a> [wait\_until\_stable](#input\_wait\_until\_stable) | Whether terraform should wait until the task set has reached `STEADY_STATE` | `bool` | `null` | no |
| <a name="input_wait_until_stable_timeout"></a> [wait\_until\_stable\_timeout](#input\_wait\_until\_stable\_timeout) | Wait timeout for task set to reach `STEADY_STATE`. Valid time units include `ns`, `us` (or µs), `ms`, `s`, `m`, and `h`. Default `10m` | `string` | `null` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_autoscaling_policies"></a> [autoscaling\_policies](#output\_autoscaling\_policies) | Map of autoscaling policies and their attributes |
| <a name="output_autoscaling_scheduled_actions"></a> [autoscaling\_scheduled\_actions](#output\_autoscaling\_scheduled\_actions) | Map of autoscaling scheduled actions and their attributes |
| <a name="output_container_definitions"></a> [container\_definitions](#output\_container\_definitions) | Container definitions |
| <a name="output_iam_role_arn"></a> [iam\_role\_arn](#output\_iam\_role\_arn) | Service IAM role ARN |
| <a name="output_iam_role_name"></a> [iam\_role\_name](#output\_iam\_role\_name) | Service IAM role name |
| <a name="output_iam_role_unique_id"></a> [iam\_role\_unique\_id](#output\_iam\_role\_unique\_id) | Stable and unique string identifying the service IAM role |
| <a name="output_id"></a> [id](#output\_id) | ARN that identifies the service |
| <a name="output_name"></a> [name](#output\_name) | Name of the service |
| <a name="output_security_group_arn"></a> [security\_group\_arn](#output\_security\_group\_arn) | Amazon Resource Name (ARN) of the security group |
| <a name="output_security_group_id"></a> [security\_group\_id](#output\_security\_group\_id) | ID of the security group |
| <a name="output_task_definition_arn"></a> [task\_definition\_arn](#output\_task\_definition\_arn) | Full ARN of the Task Definition (including both `family` and `revision`) |
| <a name="output_task_definition_family"></a> [task\_definition\_family](#output\_task\_definition\_family) | The unique name of the task definition |
| <a name="output_task_definition_family_revision"></a> [task\_definition\_family\_revision](#output\_task\_definition\_family\_revision) | The family and revision (family:revision) of the task definition |
| <a name="output_task_definition_revision"></a> [task\_definition\_revision](#output\_task\_definition\_revision) | Revision of the task in a particular family |
| <a name="output_task_exec_iam_role_arn"></a> [task\_exec\_iam\_role\_arn](#output\_task\_exec\_iam\_role\_arn) | Task execution IAM role ARN |
| <a name="output_task_exec_iam_role_name"></a> [task\_exec\_iam\_role\_name](#output\_task\_exec\_iam\_role\_name) | Task execution IAM role name |
| <a name="output_task_exec_iam_role_unique_id"></a> [task\_exec\_iam\_role\_unique\_id](#output\_task\_exec\_iam\_role\_unique\_id) | Stable and unique string identifying the task execution IAM role |
| <a name="output_task_set_arn"></a> [task\_set\_arn](#output\_task\_set\_arn) | The Amazon Resource Name (ARN) that identifies the task set |
| <a name="output_task_set_id"></a> [task\_set\_id](#output\_task\_set\_id) | The ID of the task set |
| <a name="output_task_set_stability_status"></a> [task\_set\_stability\_status](#output\_task\_set\_stability\_status) | The stability status. This indicates whether the task set has reached a steady state |
| <a name="output_task_set_status"></a> [task\_set\_status](#output\_task\_set\_status) | The status of the task set |
| <a name="output_tasks_iam_role_arn"></a> [tasks\_iam\_role\_arn](#output\_tasks\_iam\_role\_arn) | Tasks IAM role ARN |
| <a name="output_tasks_iam_role_name"></a> [tasks\_iam\_role\_name](#output\_tasks\_iam\_role\_name) | Tasks IAM role name |
| <a name="output_tasks_iam_role_unique_id"></a> [tasks\_iam\_role\_unique\_id](#output\_tasks\_iam\_role\_unique\_id) | Stable and unique string identifying the tasks IAM role |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## License
Apache-2.0 Licensed. See [LICENSE](https://github.com/terraform-aws-modules/terraform-aws-ecs/blob/master/LICENSE).

File diff suppressed because it is too large Load diff

View file

@ -1,157 +0,0 @@
################################################################################
# Service
################################################################################
output "id" {
description = "ARN that identifies the service"
value = try(aws_ecs_service.this[0].id, aws_ecs_service.ignore_task_definition[0].id, null)
}
output "name" {
description = "Name of the service"
value = try(aws_ecs_service.this[0].name, aws_ecs_service.ignore_task_definition[0].name, null)
}
################################################################################
# IAM Role
################################################################################
output "iam_role_name" {
description = "Service IAM role name"
value = try(aws_iam_role.service[0].name, null)
}
output "iam_role_arn" {
description = "Service IAM role ARN"
value = try(aws_iam_role.service[0].arn, var.iam_role_arn)
}
output "iam_role_unique_id" {
description = "Stable and unique string identifying the service IAM role"
value = try(aws_iam_role.service[0].unique_id, null)
}
################################################################################
# Container Definition
################################################################################
output "container_definitions" {
description = "Container definitions"
value = module.container_definition
}
################################################################################
# Task Definition
################################################################################
output "task_definition_arn" {
description = "Full ARN of the Task Definition (including both `family` and `revision`)"
value = try(aws_ecs_task_definition.this[0].arn, var.task_definition_arn)
}
output "task_definition_revision" {
description = "Revision of the task in a particular family"
value = try(aws_ecs_task_definition.this[0].revision, null)
}
output "task_definition_family" {
description = "The unique name of the task definition"
value = try(aws_ecs_task_definition.this[0].family, null)
}
output "task_definition_family_revision" {
description = "The family and revision (family:revision) of the task definition"
value = "${try(aws_ecs_task_definition.this[0].family, "")}:${local.max_task_def_revision}"
}
################################################################################
# Task Execution - IAM Role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
output "task_exec_iam_role_name" {
description = "Task execution IAM role name"
value = try(aws_iam_role.task_exec[0].name, null)
}
output "task_exec_iam_role_arn" {
description = "Task execution IAM role ARN"
value = try(aws_iam_role.task_exec[0].arn, var.task_exec_iam_role_arn)
}
output "task_exec_iam_role_unique_id" {
description = "Stable and unique string identifying the task execution IAM role"
value = try(aws_iam_role.task_exec[0].unique_id, null)
}
################################################################################
# Tasks - IAM role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
################################################################################
output "tasks_iam_role_name" {
description = "Tasks IAM role name"
value = try(aws_iam_role.tasks[0].name, null)
}
output "tasks_iam_role_arn" {
description = "Tasks IAM role ARN"
value = try(aws_iam_role.tasks[0].arn, var.tasks_iam_role_arn)
}
output "tasks_iam_role_unique_id" {
description = "Stable and unique string identifying the tasks IAM role"
value = try(aws_iam_role.tasks[0].unique_id, null)
}
################################################################################
# Task Set
################################################################################
output "task_set_id" {
description = "The ID of the task set"
value = try(aws_ecs_task_set.this[0].task_set_id, aws_ecs_task_set.ignore_task_definition[0].task_set_id, null)
}
output "task_set_arn" {
description = "The Amazon Resource Name (ARN) that identifies the task set"
value = try(aws_ecs_task_set.this[0].arn, aws_ecs_task_set.ignore_task_definition[0].arn, null)
}
output "task_set_stability_status" {
description = "The stability status. This indicates whether the task set has reached a steady state"
value = try(aws_ecs_task_set.this[0].stability_status, aws_ecs_task_set.ignore_task_definition[0].stability_status, null)
}
output "task_set_status" {
description = "The status of the task set"
value = try(aws_ecs_task_set.this[0].status, aws_ecs_task_set.ignore_task_definition[0].status, null)
}
################################################################################
# Autoscaling
################################################################################
output "autoscaling_policies" {
description = "Map of autoscaling policies and their attributes"
value = aws_appautoscaling_policy.this
}
output "autoscaling_scheduled_actions" {
description = "Map of autoscaling scheduled actions and their attributes"
value = aws_appautoscaling_scheduled_action.this
}
################################################################################
# Security Group
################################################################################
output "security_group_arn" {
description = "Amazon Resource Name (ARN) of the security group"
value = try(aws_security_group.this[0].arn, null)
}
output "security_group_id" {
description = "ID of the security group"
value = try(aws_security_group.this[0].id, null)
}

View file

@ -1,661 +0,0 @@
variable "create" {
description = "Determines whether resources will be created (affects all resources)"
type = bool
default = true
}
variable "create_service" {
description = "Determines whether service resource will be created (set to `false` in case you want to create task definition only)"
type = bool
default = true
}
variable "tags" {
description = "A map of tags to add to all resources"
type = map(string)
default = {}
}
################################################################################
# Service
################################################################################
variable "ignore_task_definition_changes" {
description = "Whether changes to service `task_definition` changes should be ignored"
type = bool
default = false
}
variable "alarms" {
description = "Information about the CloudWatch alarms"
type = any
default = {}
}
variable "capacity_provider_strategy" {
description = "Capacity provider strategies to use for the service. Can be one or more"
type = any
default = {}
}
variable "cluster_arn" {
description = "ARN of the ECS cluster where the resources will be provisioned"
type = string
default = ""
}
variable "deployment_circuit_breaker" {
description = "Configuration block for deployment circuit breaker"
type = any
default = {}
}
variable "deployment_controller" {
description = "Configuration block for deployment controller configuration"
type = any
default = {}
}
variable "deployment_maximum_percent" {
description = "Upper limit (as a percentage of the service's `desired_count`) of the number of running tasks that can be running in a service during a deployment"
type = number
default = 200
}
variable "deployment_minimum_healthy_percent" {
description = "Lower limit (as a percentage of the service's `desired_count`) of the number of running tasks that must remain running and healthy in a service during a deployment"
type = number
default = 66
}
variable "desired_count" {
description = "Number of instances of the task definition to place and keep running"
type = number
default = 1
}
variable "enable_ecs_managed_tags" {
description = "Specifies whether to enable Amazon ECS managed tags for the tasks within the service"
type = bool
default = true
}
variable "enable_execute_command" {
description = "Specifies whether to enable Amazon ECS Exec for the tasks within the service"
type = bool
default = false
}
variable "force_new_deployment" {
description = "Enable to force a new task deployment of the service. This can be used to update tasks to use a newer Docker image with same image/tag combination, roll Fargate tasks onto a newer platform version, or immediately deploy `ordered_placement_strategy` and `placement_constraints` updates"
type = bool
default = true
}
variable "health_check_grace_period_seconds" {
description = "Seconds to ignore failing load balancer health checks on newly instantiated tasks to prevent premature shutdown, up to 2147483647. Only valid for services configured to use load balancers"
type = number
default = null
}
variable "launch_type" {
description = "Launch type on which to run your service. The valid values are `EC2`, `FARGATE`, and `EXTERNAL`. Defaults to `FARGATE`"
type = string
default = "FARGATE"
}
variable "load_balancer" {
description = "Configuration block for load balancers"
type = any
default = {}
}
variable "name" {
description = "Name of the service (up to 255 letters, numbers, hyphens, and underscores)"
type = string
default = null
}
variable "assign_public_ip" {
description = "Assign a public IP address to the ENI (Fargate launch type only)"
type = bool
default = false
}
variable "security_group_ids" {
description = "List of security groups to associate with the task or service"
type = list(string)
default = []
}
variable "subnet_ids" {
description = "List of subnets to associate with the task or service"
type = list(string)
default = []
}
variable "ordered_placement_strategy" {
description = "Service level strategy rules that are taken into consideration during task placement. List from top to bottom in order of precedence"
type = any
default = {}
}
variable "placement_constraints" {
description = "Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the service, see `task_definition_placement_constraints` for setting at the task definition"
type = any
default = {}
}
variable "platform_version" {
description = "Platform version on which to run your service. Only applicable for `launch_type` set to `FARGATE`. Defaults to `LATEST`"
type = string
default = null
}
variable "propagate_tags" {
description = "Specifies whether to propagate the tags from the task definition or the service to the tasks. The valid values are `SERVICE` and `TASK_DEFINITION`"
type = string
default = null
}
variable "scheduling_strategy" {
description = "Scheduling strategy to use for the service. The valid values are `REPLICA` and `DAEMON`. Defaults to `REPLICA`"
type = string
default = null
}
variable "service_connect_configuration" {
description = "The ECS Service Connect configuration for this service to discover and connect to services, and be discovered by, and connected from, other services within a namespace"
type = any
default = {}
}
variable "service_registries" {
description = "Service discovery registries for the service"
type = any
default = {}
}
variable "timeouts" {
description = "Create, update, and delete timeout configurations for the service"
type = map(string)
default = {}
}
variable "triggers" {
description = "Map of arbitrary keys and values that, when changed, will trigger an in-place update (redeployment). Useful with `timestamp()`"
type = any
default = {}
}
variable "wait_for_steady_state" {
description = "If true, Terraform will wait for the service to reach a steady state before continuing. Default is `false`"
type = bool
default = null
}
variable "service_tags" {
description = "A map of additional tags to add to the service"
type = map(string)
default = {}
}
################################################################################
# Service - IAM Role
################################################################################
variable "create_iam_role" {
description = "Determines whether the ECS service IAM role should be created"
type = bool
default = true
}
variable "iam_role_arn" {
description = "Existing IAM role ARN"
type = string
default = null
}
variable "iam_role_name" {
description = "Name to use on IAM role created"
type = string
default = null
}
variable "iam_role_use_name_prefix" {
description = "Determines whether the IAM role name (`iam_role_name`) is used as a prefix"
type = bool
default = true
}
variable "iam_role_path" {
description = "IAM role path"
type = string
default = null
}
variable "iam_role_description" {
description = "Description of the role"
type = string
default = null
}
variable "iam_role_permissions_boundary" {
description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
type = string
default = null
}
variable "iam_role_tags" {
description = "A map of additional tags to add to the IAM role created"
type = map(string)
default = {}
}
variable "iam_role_statements" {
description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
type = any
default = {}
}
################################################################################
# Task Definition
################################################################################
variable "create_task_definition" {
description = "Determines whether to create a task definition or use existing/provided"
type = bool
default = true
}
variable "task_definition_arn" {
description = "Existing task definition ARN. Required when `create_task_definition` is `false`"
type = string
default = null
}
variable "container_definitions" {
description = "A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document"
type = any
default = {}
}
variable "container_definition_defaults" {
description = "A map of default values for [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html) created by `container_definitions`"
type = any
default = {}
}
variable "cpu" {
description = "Number of cpu units used by the task. If the `requires_compatibilities` is `FARGATE` this field is required"
type = number
default = 1024
}
variable "ephemeral_storage" {
description = "The amount of ephemeral storage to allocate for the task. This parameter is used to expand the total amount of ephemeral storage available, beyond the default amount, for tasks hosted on AWS Fargate"
type = any
default = {}
}
variable "family" {
description = "A unique name for your task definition"
type = string
default = null
}
variable "inference_accelerator" {
description = "Configuration block(s) with Inference Accelerators settings"
type = any
default = {}
}
variable "ipc_mode" {
description = "IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none`"
type = string
default = null
}
variable "memory" {
description = "Amount (in MiB) of memory used by the task. If the `requires_compatibilities` is `FARGATE` this field is required"
type = number
default = 2048
}
variable "network_mode" {
description = "Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host`"
type = string
default = "awsvpc"
}
variable "pid_mode" {
description = "Process namespace to use for the containers in the task. The valid values are `host` and `task`"
type = string
default = null
}
variable "task_definition_placement_constraints" {
description = "Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the task definition, see `placement_constraints` for setting at the service"
type = any
default = {}
}
variable "proxy_configuration" {
description = "Configuration block for the App Mesh proxy"
type = any
default = {}
}
variable "requires_compatibilities" {
description = "Set of launch types required by the task. The valid values are `EC2` and `FARGATE`"
type = list(string)
default = ["FARGATE"]
}
variable "runtime_platform" {
description = "Configuration block for `runtime_platform` that containers in your task may use"
type = any
default = {
operating_system_family = "LINUX"
cpu_architecture = "X86_64"
}
}
variable "skip_destroy" {
description = "If true, the task is not deleted when the service is deleted"
type = bool
default = null
}
variable "volume" {
description = "Configuration block for volumes that containers in your task may use"
type = any
default = {}
}
variable "task_tags" {
description = "A map of additional tags to add to the task definition/set created"
type = map(string)
default = {}
}
################################################################################
# Task Execution - IAM Role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_execution_IAM_role.html
################################################################################
variable "create_task_exec_iam_role" {
description = "Determines whether the ECS task definition IAM role should be created"
type = bool
default = true
}
variable "task_exec_iam_role_arn" {
description = "Existing IAM role ARN"
type = string
default = null
}
variable "task_exec_iam_role_name" {
description = "Name to use on IAM role created"
type = string
default = null
}
variable "task_exec_iam_role_use_name_prefix" {
description = "Determines whether the IAM role name (`task_exec_iam_role_name`) is used as a prefix"
type = bool
default = true
}
variable "task_exec_iam_role_path" {
description = "IAM role path"
type = string
default = null
}
variable "task_exec_iam_role_description" {
description = "Description of the role"
type = string
default = null
}
variable "task_exec_iam_role_permissions_boundary" {
description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
type = string
default = null
}
variable "task_exec_iam_role_tags" {
description = "A map of additional tags to add to the IAM role created"
type = map(string)
default = {}
}
variable "task_exec_iam_role_policies" {
description = "Map of IAM role policy ARNs to attach to the IAM role"
type = map(string)
default = {}
}
variable "task_exec_iam_role_max_session_duration" {
description = "Maximum session duration (in seconds) for ECS task execution role. Default is 3600."
type = number
default = null
}
variable "create_task_exec_policy" {
description = "Determines whether the ECS task definition IAM policy should be created. This includes permissions included in AmazonECSTaskExecutionRolePolicy as well as access to secrets and SSM parameters"
type = bool
default = true
}
variable "task_exec_ssm_param_arns" {
description = "List of SSM parameter ARNs the task execution role will be permitted to get/read"
type = list(string)
default = ["arn:aws:ssm:*:*:parameter/*"]
}
variable "task_exec_secret_arns" {
description = "List of SecretsManager secret ARNs the task execution role will be permitted to get/read"
type = list(string)
default = ["arn:aws:secretsmanager:*:*:secret:*"]
}
variable "task_exec_iam_statements" {
description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
type = any
default = {}
}
################################################################################
# Tasks - IAM role
# https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
################################################################################
variable "create_tasks_iam_role" {
description = "Determines whether the ECS tasks IAM role should be created"
type = bool
default = true
}
variable "tasks_iam_role_arn" {
description = "Existing IAM role ARN"
type = string
default = null
}
variable "tasks_iam_role_name" {
description = "Name to use on IAM role created"
type = string
default = null
}
variable "tasks_iam_role_use_name_prefix" {
description = "Determines whether the IAM role name (`tasks_iam_role_name`) is used as a prefix"
type = bool
default = true
}
variable "tasks_iam_role_path" {
description = "IAM role path"
type = string
default = null
}
variable "tasks_iam_role_description" {
description = "Description of the role"
type = string
default = null
}
variable "tasks_iam_role_permissions_boundary" {
description = "ARN of the policy that is used to set the permissions boundary for the IAM role"
type = string
default = null
}
variable "tasks_iam_role_tags" {
description = "A map of additional tags to add to the IAM role created"
type = map(string)
default = {}
}
variable "tasks_iam_role_policies" {
description = "Map of IAM role policy ARNs to attach to the IAM role"
type = map(string)
default = {}
}
variable "tasks_iam_role_statements" {
description = "A map of IAM policy [statements](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document#statement) for custom permission usage"
type = any
default = {}
}
################################################################################
# Task Set
################################################################################
variable "external_id" {
description = "The external ID associated with the task set"
type = string
default = null
}
variable "scale" {
description = "A floating-point percentage of the desired number of tasks to place and keep running in the task set"
type = any
default = {}
}
variable "force_delete" {
description = "Whether to allow deleting the task set without waiting for scaling down to 0"
type = bool
default = null
}
variable "wait_until_stable" {
description = "Whether terraform should wait until the task set has reached `STEADY_STATE`"
type = bool
default = null
}
variable "wait_until_stable_timeout" {
description = "Wait timeout for task set to reach `STEADY_STATE`. Valid time units include `ns`, `us` (or µs), `ms`, `s`, `m`, and `h`. Default `10m`"
type = string
default = null
}
################################################################################
# Autoscaling
################################################################################
variable "enable_autoscaling" {
description = "Determines whether to enable autoscaling for the service"
type = bool
default = true
}
variable "autoscaling_min_capacity" {
description = "Minimum number of tasks to run in your service"
type = number
default = 1
}
variable "autoscaling_max_capacity" {
description = "Maximum number of tasks to run in your service"
type = number
default = 10
}
variable "autoscaling_policies" {
description = "Map of autoscaling policies to create for the service"
type = any
default = {
cpu = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageCPUUtilization"
}
}
}
memory = {
policy_type = "TargetTrackingScaling"
target_tracking_scaling_policy_configuration = {
predefined_metric_specification = {
predefined_metric_type = "ECSServiceAverageMemoryUtilization"
}
}
}
}
}
variable "autoscaling_scheduled_actions" {
description = "Map of autoscaling scheduled actions to create for the service"
type = any
default = {}
}
################################################################################
# Security Group
################################################################################
variable "create_security_group" {
description = "Determines if a security group is created"
type = bool
default = true
}
variable "security_group_name" {
description = "Name to use on security group created"
type = string
default = null
}
variable "security_group_use_name_prefix" {
description = "Determines whether the security group name (`security_group_name`) is used as a prefix"
type = bool
default = true
}
variable "security_group_description" {
description = "Description of the security group created"
type = string
default = null
}
variable "security_group_rules" {
description = "Security group rules to add to the security group created"
type = any
default = {}
}
variable "security_group_tags" {
description = "A map of additional tags to add to the security group created"
type = map(string)
default = {}
}

View file

@ -1,10 +0,0 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.66.1"
}
}
}

View file

@ -1,153 +0,0 @@
################################################################################
# Cluster
################################################################################
output "cluster_arn" {
description = "ARN that identifies the cluster"
value = module.ecs_cluster.arn
}
output "cluster_id" {
description = "ID that identifies the cluster"
value = module.ecs_cluster.id
}
output "cluster_name" {
description = "Name that identifies the cluster"
value = module.ecs_cluster.name
}
output "cluster_capacity_providers" {
description = "Map of cluster capacity providers attributes"
value = module.ecs_cluster.cluster_capacity_providers
}
output "cluster_autoscaling_capacity_providers" {
description = "Map of capacity providers created and their attributes"
value = module.ecs_cluster.autoscaling_capacity_providers
}
################################################################################
# Service
################################################################################
output "service_id" {
description = "ARN that identifies the service"
value = module.ecs_service.id
}
output "service_name" {
description = "Name of the service"
value = module.ecs_service.name
}
output "service_iam_role_name" {
description = "Service IAM role name"
value = module.ecs_service.iam_role_name
}
output "service_iam_role_arn" {
description = "Service IAM role ARN"
value = module.ecs_service.iam_role_arn
}
output "service_iam_role_unique_id" {
description = "Stable and unique string identifying the service IAM role"
value = module.ecs_service.iam_role_unique_id
}
output "service_container_definitions" {
description = "Container definitions"
value = module.ecs_service.container_definitions
}
output "service_task_definition_arn" {
description = "Full ARN of the Task Definition (including both `family` and `revision`)"
value = module.ecs_service.task_definition_arn
}
output "service_task_definition_revision" {
description = "Revision of the task in a particular family"
value = module.ecs_service.task_definition_revision
}
output "service_task_definition_family" {
description = "The unique name of the task definition"
value = module.ecs_service.task_definition_family
}
output "service_task_definition_family_revision" {
description = "The family and revision (family:revision) of the task definition"
value = module.ecs_service.task_definition_family_revision
}
output "service_task_exec_iam_role_name" {
description = "Task execution IAM role name"
value = module.ecs_service.task_exec_iam_role_name
}
output "service_task_exec_iam_role_arn" {
description = "Task execution IAM role ARN"
value = module.ecs_service.task_exec_iam_role_arn
}
output "service_task_exec_iam_role_unique_id" {
description = "Stable and unique string identifying the task execution IAM role"
value = module.ecs_service.task_exec_iam_role_unique_id
}
output "service_tasks_iam_role_name" {
description = "Tasks IAM role name"
value = module.ecs_service.tasks_iam_role_name
}
output "service_tasks_iam_role_arn" {
description = "Tasks IAM role ARN"
value = module.ecs_service.tasks_iam_role_arn
}
output "service_tasks_iam_role_unique_id" {
description = "Stable and unique string identifying the tasks IAM role"
value = module.ecs_service.tasks_iam_role_unique_id
}
output "service_task_set_id" {
description = "The ID of the task set"
value = module.ecs_service.task_set_id
}
output "service_task_set_arn" {
description = "The Amazon Resource Name (ARN) that identifies the task set"
value = module.ecs_service.task_set_arn
}
output "service_task_set_stability_status" {
description = "The stability status. This indicates whether the task set has reached a steady state"
value = module.ecs_service.task_set_stability_status
}
output "service_task_set_status" {
description = "The status of the task set"
value = module.ecs_service.task_set_status
}
output "service_autoscaling_policies" {
description = "Map of autoscaling policies and their attributes"
value = module.ecs_service.autoscaling_policies
}
output "service_autoscaling_scheduled_actions" {
description = "Map of autoscaling scheduled actions and their attributes"
value = module.ecs_service.autoscaling_scheduled_actions
}
output "service_security_group_arn" {
description = "Amazon Resource Name (ARN) of the security group"
value = module.ecs_service.security_group_arn
}
output "service_security_group_id" {
description = "ID of the security group"
value = module.ecs_service.security_group_id
}

View file

@ -1,11 +0,0 @@
resource "aws_route53_record" "chat" {
zone_id = "Z2TWGHEC8YQMWW"
name = "chat.ngd.io"
type = "A"
alias {
name = module.alb.dns_name
zone_id = module.alb.zone_id
evaluate_target_health = true
}
}

View file

@ -1,9 +0,0 @@
{
"version": 4,
"terraform_version": "1.6.2",
"serial": 306,
"lineage": "eee76b09-c66c-0cf5-be4a-cdd9e6b807e9",
"outputs": {},
"resources": [],
"check_results": null
}

File diff suppressed because it is too large Load diff

View file

@ -1,51 +0,0 @@
variable "name" {
description = "The name of this template (e.g., my-app-prod)"
type = string
default = "chat-app-demo"
}
variable "env" {
description = "Environment"
type = string
default = "demo"
}
variable "region" {
description = "The AWS region to deploy to (e.g., us-east-1)"
type = string
default = "eu-west-1"
}
variable "container_name" {
description = "The name of the container"
type = string
default = "chat-app"
}
variable "health_check" {
description = "A map containing configuration for the health check"
type = string
default = "/health"
}
# The port the container will listen on, used for load balancer health check
# Best practice is that this value is higher than 1024 so the container processes
# isn't running at root.
variable "container_port" {
description = "The port the container will listen on, used for load balancer health check. Best practice is that this value is higher than 1024 so the container processes isn't running at root."
type = number
default = 3000
}
variable "tags" {
description = "A map of tags to apply to all resources"
type = map(string)
default = {}
}
variable "image" {
description = "container image to initially bootstrap. future images can be deployed using a separate mechanism"
type = string
default = "richarvey/chat-app"
}

View file

@ -1,10 +0,0 @@
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.66.1"
}
}
}

View file

@ -1,16 +0,0 @@
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
name = local.name
cidr = local.vpc_cidr
azs = local.azs
private_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 4, k)]
public_subnets = [for k, v in local.azs : cidrsubnet(local.vpc_cidr, 8, k + 48)]
enable_nat_gateway = true
single_nat_gateway = true
tags = local.tags
}