Reputation: 40784
Here is my terraform script
variable "aws_region" { }
variable "flavor" { } # test or prod
variable "task_worker_service_name" { }
variable "task_cpu" {}
variable "task_memory" {}
variable "az_count" {}
terraform {
required_version = "= 0.12.6"
}
provider "aws" {
version = "~> 2.21.1"
region = "${var.aws_region}"
}
data "aws_availability_zones" "available" {}
data "aws_iam_policy_document" "ecs_service_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [ "ecs.amazonaws.com" ]
}
}
}
data "aws_iam_policy_document" "task_worker_iam_role_policy" {
statement {
actions = [ "sts:AssumeRole" ]
principals {
type = "Service"
identifiers = [
"ecs-tasks.amazonaws.com"
]
}
}
}
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [ "ecs-tasks.amazonaws.com" ]
}
}
}
resource "aws_iam_role" "ecs_service_role" {
name = "${var.flavor}-task-ecs-service-role"
path = "/"
assume_role_policy = "${data.aws_iam_policy_document.ecs_service_policy.json}"
}
resource "aws_iam_role_policy_attachment" "ecs_service_role_attachment" {
role = "${aws_iam_role.ecs_service_role.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
}
resource "aws_vpc" "ecs" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
enable_dns_support = true
instance_tenancy = "default"
tags = {
Name = "ecs"
}
}
resource "aws_security_group" "vpc_ecs_task_worker" {
name = "${var.flavor}-vpc_ecs_task_worker"
description = "ECS Allowed Ports"
ingress {
from_port = 32768
to_port = 65535
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_iam_role" "ecs_task_execution_role" {
name = "${var.flavor}-ecs-task-worker-task-execution-role"
assume_role_policy = data.aws_iam_policy_document.assume_role_policy.json
}
resource "aws_iam_role" "task_worker_iam_role" {
name = "${var.flavor}-task-worker-role"
path = "/"
assume_role_policy = data.aws_iam_policy_document.task_worker_iam_role_policy.json
}
# Create var.az_count private subnets, each in a different AZ
resource "aws_subnet" "private" {
count = "${var.az_count}"
cidr_block = "${cidrsubnet(aws_vpc.ecs.cidr_block, 8, count.index)}"
availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
vpc_id = "${aws_vpc.ecs.id}"
}
resource "aws_ecs_task_definition" "task_worker" {
family = "${var.flavor}-${var.task_worker_service_name}"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = var.task_cpu
memory = var.task_memory
execution_role_arn = aws_iam_role.ecs_task_execution_role.arn
task_role_arn = aws_iam_role.task_worker_iam_role.arn
container_definitions = <<JSON
[
{
"dnsSearchDomains": null,
"logConfiguration": null,
"entryPoint": null,
"portMappings": [],
"command": null,
"linuxParameters": null,
"cpu": ${var.task_cpu},
"environment": [],
"resourceRequirements": null,
"ulimits": null,
"dnsServers": null,
"mountPoints": [],
"workingDirectory": null,
"secrets": null,
"dockerSecurityOptions": null,
"memory": null,
"memoryReservation": ${var.task_memory},
"volumesFrom": [],
"stopTimeout": null,
"image": "us-west-2.amazonaws.com/task:4383669",
"startTimeout": null,
"dependsOn": null,
"disableNetworking": null,
"interactive": null,
"healthCheck": null,
"essential": true,
"links": null,
"hostname": null,
"extraHosts": null,
"pseudoTerminal": null,
"user": null,
"readonlyRootFilesystem": null,
"dockerLabels": null,
"systemControls": null,
"privileged": null,
"name": "task-worker"
}
]
JSON
}
resource "aws_ecs_cluster" "task_pool" {
name = "${var.flavor}-task-pool"
}
resource "aws_ecs_service" "task_service" {
name = "${var.flavor}-task-worker-service"
cluster = "${aws_ecs_cluster.task_pool.id}"
task_definition = "${aws_ecs_task_definition.task_worker.arn}"
launch_type = "FARGATE"
desired_count = 2
network_configuration {
subnets = "${aws_subnet.private[*].id}"
security_groups = ["${aws_security_group.vpc_ecs_task_worker.id}" ]
assign_public_ip = "true"
}
}
In this script I have added two 'aws_iam_policy_document':
data "aws_iam_policy_document" "pdf_conversion_iam_role_policy"
, and
data "aws_iam_policy_document" "assume_role_policy"
respectively
They are basically identical i.e.
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [ "ecs-tasks.amazonaws.com" ]
}
}
}
I used assume_role_policy
in aws_iam_role.ecs_task_execution_role
and pdf_conversion_iam_role_policy
in aws_iam_role.pdf_conversion_iam_role
.
My question is: Is it necessary to have two aws_iam_policy_document
in this case? Also do you have a good suggestion for a naming convention for this kind of terraform resource names?
Upvotes: 2
Views: 1011
Reputation: 74544
If you are using a recent version of Terraform then you can potentially write this using the resource for_each
feature to produce multiple instances of aws_iam_policy_document
from a single data
block:
data "aws_iam_policy_document" "assume_role" {
for_each = toset([
"ecs-tasks.amazonaws.com",
"ecs.amazonaws.com",
])
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = [each.value]
}
}
}
resource "aws_iam_role" "service" {
for_each = data.aws_iam_policy_document.assume_role
name = "${var.flavor}-${each.key}-service-role"
assume_role_policy = each.value.json
}
resource "aws_iam_role_policy_attachment" "ecs_service" {
role = aws_iam_role.service["ecs.amazonaws.com"].name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"
}
for_each
inside a data
or resource
block makes references to the resource produce a map of resource objects. In this case, our map keys are the service identifiers. That gives us some resources with the following addresses:
data.aws_iam_policy_document.assume_role["ecs.amazonaws.com"]
data.aws_iam_policy_document.assume_role["ecs-tasks.amazonaws.com"]
aws_iam_role.service["ecs.amazonaws.com"]
aws_iam_role.service["ecs-tasks.amazonaws.com"]
The aws_iam_role_policy_attachment.ecs_service
resource then refers directly to the specific role object for ecs.amazonaws.com
, allowing it to represent the policy that is uniquely attached to that role even though we generalized the creation of the roles themselves.
We can add more service identifiers to the for_each
in data "aws_iam_policy_document" "assume_role"
, or possibly even factor this pattern out into a module that takes the set of service hostnames as an input variable and exports the role names via an output value, if you find yourself creating lots of these.
Upvotes: 3