I was wondering if anyone could help with this issue? I'm trying to call an SSM document using terraform to stop an ec2 instance. But, it doesn't seems to work. I keep having the error:
Automation Step Execution fails when it is changing the state of each instance. Get Exception from StopInstances API of ec2 Service. Exception Message from StopInstances API: [You are not authorized to perform this operation.
Any suggestion here?
As you could see, there are the right roles. I pass it in parameter.
provider "aws" {
profile = "profile"
region = "eu-west-1"
}
data "aws_ssm_document" "stop_ec2_doc" {
name = "AWS-StopEC2Instance"
document_format = "JSON"
}
data "aws_iam_policy_document" "assume_role" {
version = "2012-10-17"
statement {
sid = "EC2AssumeRole"
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
identifiers = ["ec2.amazonaws.com"]
type = "Service"
}
principals {
identifiers = ["ssm.amazonaws.com"]
type = "Service"
}
}
}
data "aws_ami" "latest_amazon_2" {
most_recent = true
owners = ["amazon"]
name_regex = "^amzn2-ami-hvm-.*x86_64-gp2"
}
#
resource "aws_iam_role" "iam_assume_role" {
name = "iam_assume_role"
assume_role_policy = data.aws_iam_policy_document.assume_role.json
}
#
resource "aws_iam_role_policy_attachment" "role_1" {
role = aws_iam_role.iam_assume_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
# the instance profile
resource "aws_iam_instance_profile" "iam_instance_profile" {
name = "iam_instance_profile"
role = aws_iam_role.iam_assume_role.name
}
# amazon ec2 instances
resource "aws_instance" "ec2_instances" {
count = 2
ami = data.aws_ami.latest_amazon_2.id
instance_type = "t2.micro"
subnet_id = "subnet-12345678901"
iam_instance_profile = aws_iam_instance_profile.iam_instance_profile.name
root_block_device {
volume_size = 8
volume_type = "gp2"
delete_on_termination = true
}
}
resource "aws_ssm_association" "example" {
name = data.aws_ssm_document.stop_ec2_doc.name
parameters = {
AutomationAssumeRole = "arn:aws:iam::12345678901:role/aws-service-role/ssm.amazonaws.com/AWSServiceRoleForAmazonSSM"
InstanceId = aws_instance.ec2_instances[0].id
}
}
Any suggestion is welcome. I tried to create an easy Terraform code to illustrate what I'm trying to do. And to me it should be straight forward.
I create the role. I create the instance profile. I create the association passing the proper role and the instance id.
AWSServiceRoleForAmazonSSM role does not have permissions to stop instances. Instead you should create new role for SSM with such permissions. The simplest way is as follows:
resource "aws_iam_role" "ssm_role" {
name = "ssm_role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Sid = ""
Principal = {
Service = "ssm.amazonaws.com"
}
},
]
})
}
resource "aws_iam_role_policy_attachment" "ec2-attach" {
role = aws_iam_role.ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2FullAccess"
}
resource "aws_ssm_association" "example" {
name = data.aws_ssm_document.stop_ec2_doc.name
parameters = {
AutomationAssumeRole = aws_iam_role.ssm_role.arn
InstanceId = aws_instance.ec2_instances[0].id
}
}
The AmazonEC2FullAccess is too permissive just for stopping instances, but I use it as a working example.
Related
So I have multiple IAM groups which I am looping through as follows :
resource "aws_iam_group" "all_iam_groups" {
for_each = var.iam_user_groups
name = "${local.csi}-${each.key}"
path = "/"
}
This will create multiple IAM groups. Now for each IAM groups I will have an IAM Policy which I will attatch. Rather than having to do it manually and create multiple resources what is the best approach to take here. So far I have been doing it as follows :
resource "aws_iam_policy" "finance_read_only" {
name = "${local.csi}-finance-read-only"
path = "/"
policy = data.aws_iam_policy_document.finance_read_only.json
}
resource "aws_iam_policy" "security_read_only" {
name = "${local.csi}-security-read-only"
path = "/"
policy = data.aws_iam_policy_document.security_read_only.json
}
resource "aws_iam_group_policy_attachment" "security_read_only" {
group = aws_iam_group.security_team.name
policy_arn = aws_iam_policy.security_read_only.arn
}
resource "aws_iam_group_policy_attachment" "finance_read_only" {
group = aws_iam_group.finance_team.name
policy_arn = aws_iam_policy.finance_read_only.arn
}
For example purposes I have added in the group names but I know i can use a for-each loop to go through the names but am not sure how I would be able to look through the IAM Policies and policy attatchments.
I do understand that I will have multiple aws_iam_policy_document which is fine.
There's a direct relationship between the following resources: aws_iam_policy , the data source for aws_iam_policy_document, the aws_iam_group_policy_attachment and the aws_iam_group resource.
My suggestion is to create a variable of a list of objects type, where you define different attributes that are needed for the resources required.
Example:
variable "policies" {
type = list(object({
name = string
statement = object
}))
default = [
{
name = "finance-read-only"
statement = {
sid = "XXX"
actions = ["ec2:XXX"]
resources = ["XXX"]
}
},
{
name = "security-read-only"
statement = {
sid = "XXX"
actions = ["ec2:XXX"]
resources = ["XXX"]
}
}
]
}
data "aws_iam_policy_document" "this" {
for_each = { for policy in var.policies : policy.name => policy }
statement = each.value.statement
}
resource "aws_iam_policy" "this" {
for_each = { for policy in var.policies : policy.name => policy }
name = format("${local.csi}-%s", each.key)
path = "/"
policy = data.aws_iam_policy_document.this[each.key].json
}
resource "aws_iam_group" "this" {
for_each = { for policy in var.policies : policy.name => policy }
name = each.key
}
resource "aws_iam_group_policy_attachment" "security_read_only" {
for_each = { for policy in var.policies : policy.name => policy }
group = aws_iam_group.this[each.key].name
policy_arn = aws_iam_policy.this[each.key].arn
}
I want to create two Amazon SNS topics with the same aws_iam_policy_document, aws_sns_topic_policy & time_sleep configs.
This is my terraform, my_sns_topic.tf:
resource "aws_sns_topic" "topic_a" {
name = "topic-a"
}
resource "aws_sns_topic" "topic_b" {
name = "topic-b"
}
data "aws_iam_policy_document" "topic_notification" {
version = "2008-10-17"
statement {
sid = "__default_statement_ID"
actions = [
"SNS:Publish"
]
# Cut off some lines for simplification.
## NEW LINE ADDED
statement {
sid = "allow_snowflake_subscription"
principals {
type = "AWS"
identifiers = [var.storage_aws_iam_user_arn]
}
actions = ["SNS:Subscribe"]
resources = [aws_sns_topic.topic_a.arn] # Troubles with this line
}
}
resource "aws_sns_topic_policy" "topic_policy_notification" {
arn = aws_sns_topic.topic_a.arn
policy = data.aws_iam_policy_document.topic_policy_notification.json
}
resource "time_sleep" "topic_wait_10s" {
depends_on = [aws_sns_topic.topic_a]
create_duration = "10s"
}
As you can see here, I set up the configuration only for topic-a. I want to loop this over to apply for topic-b as well.
It would be better to use map and for_each, instead of separately creating "a" and "b" topics:
variable "topics" {
default = ["a", "b"]
}
resource "aws_sns_topic" "topic" {
for_each = toset(var.topics)
name = "topic-${each.key}"
}
data "aws_iam_policy_document" "topic_notification" {
version = "2008-10-17"
statement {
sid = "__default_statement_ID"
actions = [
"SNS:Publish"
]
# Cut off some lines for simplification.
}
resource "aws_sns_topic_policy" "topic_policy_notification" {
for_each = toset(var.topics)
arn = aws_sns_topic.topic[each.key].arn
policy = data.aws_iam_policy_document.topic_policy_notification.json
}
resource "time_sleep" "topic_wait_10s" {
for_each = toset(var.topics)
depends_on = [aws_sns_topic.topic[each.key]]
create_duration = "10s"
}
How do get a list/array of ARNs for a resource created with for_each ?
# DynamoDB
resource "aws_dynamodb_table" "terraform_state" {
for_each = var.aws_shared_accounts
name = "${each.key}-terraform-state"
read_capacity = 5
write_capacity = 5
hash_key = "LockID"
attribute {
resource "aws_iam_role" "infra_github_role" {
name = "TerraformBackendRole"
inline_policy {
name = "TerrafomBackendPolicy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "*"
Effect = "Allow"
Resource = [
concat(
[aws_s3_bucket.terraform_bucket.arn, "${aws_s3_bucket.terraform_bucket.arn}/*"],
aws_dynamodb_table.terraform_state[*].arn
)
]
},
]
})
}
}
Without the aws_iam_role I can run terraform plan and can see the resources
# aws_dynamodb_table.terraform-state["main"] will be created
+ resource "aws_dynamodb_table" "terraform-state" {
# aws_dynamodb_table.terraform-state["main-dev"] will be created
+ resource "aws_dynamodb_table" "terraform-state" {
The error I am receiving after adding the aws_iam_role
Error: Unsupported attribute
on 03-iam.tf line 16, in resource "aws_iam_role" "infra_github_role":
16: aws_dynamodb_table.terraform_state[*].arn
Since you used for_each, to get arn values you should do:
values(aws_dynamodb_table.terraform_state)[*].arn
I'm deploying an app through ECS (with FARGATE being the capacity provider). My app needs to access a Kinesis stream (already existing and running). I can't figure out the exact IAM assume policy I need to provide. I have the below configuration in Terraform (removed tags, log configuration and proprietary names). Every time I deploy the task I receive an error that the task couldn't assume the role.
What am I missing?
resource "aws_ecs_cluster" "cluster" {
name = var.cluster_name
}
resource "aws_ecs_service" "service" {
name = var.service_name
cluster = aws_ecs_cluster.cluster.id
task_definition = aws_ecs_task_definition.task.arn
desired_count = var.task_count
launch_type = var.task_launch_type
load_balancer {
target_group_arn = var.alb_target
container_name = "container"
container_port = 3000
}
network_configuration {
subnets = [for subnet in var.subnets : "${subnet}"]
assign_public_ip = true
security_groups = [var.sg_id]
}
}
resource "aws_ecs_task_definition" "task" {
family = "task_family"
container_definitions = file( var.container_definitions_json )
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
memory = 1024
cpu = 512
execution_role_arn = "${aws_iam_role.ecsTaskExecutionRole.arn}"
task_role_arn = "${aws_iam_role.ecsTaskRole.arn}"
}
resource "aws_iam_role" "ecsTaskRole" {
name = "ecsTaskRole"
assume_role_policy = "${data.aws_iam_policy_document.ecsTaskRole.json}"
}
data "aws_caller_identity" "current" {}
data "aws_partition" "current" {}
data "aws_region" "current" {}
data "aws_iam_policy_document" "ecsTaskRole" {
statement {
effect = "Allow"
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = [
format("arn:%s:iam::%s:root", data.aws_partition.current.partition, data.aws_caller_identity.current.account_id)
]
}
}
}
resource "aws_iam_role" "ecsTaskExecutionRole" {
name = "ecsTaskExecutionRole"
assume_role_policy = "${data.aws_iam_policy_document.assume_role_policy.json}"
}
data "aws_iam_policy_document" "assume_role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" {
role = "${aws_iam_role.ecsTaskExecutionRole.name}"
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}
Both roles must have a trust policy that allows ecs-tasks.amazonaws.com.
See this document for the task role, and this document for the execution role.
I am very new to terraform, but I am trying to grant this resource
resource aws_instance "myinstance" {
ami = "${data.aws_ami.awsami.id}"
instance_type = "t2.small"
key_name = "${aws_key_pair.my_key.key_name}"
vpc_security_group_ids = ["${module.security.my_sg_id}", "${module.security.my_security_group_id}"]
subnet_id = "${element(module.network.public_subnets,1)}"
tags {
Name = "My instance"
}
}
Access to secrets manager. The instance needs to be able to read secrets via ansible script. I've found a blog on using instance profiles. How do I use an instance profile role to grant the instance access to secrets manager?
I was able to accomplish my goal by using the code below. You'll need to add ASSUME_ROLE_POLICY_HERE and POLICY_GOES_HERE. The important piece is specifying iam_instance_profile ="{aws_iam_instance_profile.myinstance_instance_profile.id}"
locals {
env_account = "${terraform.workspace}"
deploy_env_name = "${lookup(var.workspace_deploy_env, local.env_account)}"
}
resource "aws_eip" "myinstanceip" {
instance = "${aws_instance.myinstance.id}"
vpc = true
}
resource aws_instance "myinstance" {
ami = "${data.aws_ami.awsami.id}"
instance_type = "t2.small"
key_name = "${aws_key_pair.my_key.key_name}"
vpc_security_group_ids = ["${module.security.my_sg_id}", "${module.security.my_security_group_id}"]
subnet_id = "${element(module.network.public_subnets,1)}"
iam_instance_profile ="{aws_iam_instance_profile.myinstance_instance_profile.id}"
tags {
Name = "My instance"
}
}
resource aws_route53_record "myinstance_domain_name" {
zone_id = "${module.tf_aws_route53_zone.zone_id}"
name = "myinstance.${module.tf_aws_route53_zone.domain_name}"
type = "A"
ttl = "300"
records = ["${aws_eip.myinstanceip.public_ip}"]
}
output myinstance_ip {
value = "${aws_eip.myinstanceip.public_ip}"
}
resource "aws_iam_instance_profile" "myinstance_instance_profile" {
name = "myinstance-instance-profile"
role = "myinstance-role"
}
resource "aws_iam_role" "myinstance_role" {
name = "myinstance-role"
assume_role_policy = <<EOF
{
ASSUME_ROLE_POLICY_HERE
}
EOF
}
resource "aws_iam_policy" "secrets_manager" {
name = "secrets-manager-myinstance"
description = "Read secrets"
policy = <<POLICY
{
POLICY_GOES_HERE
}
POLICY
}