CodePipeline with Terraform and Beanstalk - amazon-web-services

I'm trying to create a pipeline to deploy on Beanstalk but I constantly get an error in the deploy section of the pipeline:
Insufficient permissions
The provided role does not have sufficient permissions to access
Elastic Beanstalk: Access Denied
What am I missing?
/************************************************
* Code Build
***********************************************/
resource "aws_codebuild_project" "project-name-codebuild" {
name = "${var.project}-codebuild"
build_timeout = "15"
service_role = "${aws_iam_role.project-name-codebuild-role.arn}"
artifacts {
type = "CODEPIPELINE"
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
type = "LINUX_CONTAINER"
image = "aws/codebuild/java:openjdk-8"
}
source {
type = "CODEPIPELINE"
}
tags {
Name = "${var.project}"
Environment = "${var.environment}"
}
}
resource "aws_ecr_repository" "project-name-ecr-repository" {
name = "${var.project}-ecr-repository"
}
resource "aws_iam_role" "project-name-codebuild-role" {
name = "${var.project}-codebuild-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "project-name-codebuild-role-policy" {
role = "${aws_iam_role.project-name-codebuild-role.id}"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
}
]
}
POLICY
}
resource "aws_iam_role_policy_attachment" "project-name-codebuild-role-policy-bucket" {
policy_arn = "${aws_iam_policy.project-name-code-pipeline-bucket-access.arn}"
role = "${aws_iam_role.project-name-codebuild-role.name}"
}
/************************************************
* Code Pipeline
***********************************************/
resource "aws_codepipeline" "project-name-code-pipeline" {
name = "${var.project}-code-pipeline"
role_arn = "${aws_iam_role.project-name-code-pipeline-role.arn}"
artifact_store {
location = "${aws_s3_bucket.project-name-code-pipeline-bucket.bucket}"
type = "S3"
}
stage {
name = "Source"
action {
name = "Source"
category = "Source"
owner = "ThirdParty"
provider = "GitHub"
version = "1"
output_artifacts = [
"source"]
configuration {
Owner = "Owner"
Repo = "project-name"
Branch = "master"
OAuthToken = "${var.github-token}"
}
}
}
stage {
name = "Build-Everything"
action {
name = "Build"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
input_artifacts = [
"source"]
output_artifacts = [
"build"]
version = "1"
configuration {
ProjectName = "${aws_codebuild_project.project-name-codebuild.name}"
}
}
}
stage {
name = "Deploy"
action {
name = "Deploy"
category = "Deploy"
owner = "AWS"
provider = "ElasticBeanstalk"
input_artifacts = [
"build"]
version = "1"
configuration {
ApplicationName = "${aws_elastic_beanstalk_application.project-name.name}"
EnvironmentName = "${aws_elastic_beanstalk_environment.project-name-environment.name}"
}
}
}
}
resource "aws_s3_bucket" "project-name-code-pipeline-bucket" {
bucket = "${var.project}-code-pipeline-bucket"
acl = "private"
}
resource "aws_iam_policy" "project-name-code-pipeline-bucket-access" {
name = "${var.project}-code-pipeline-bucket-access"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect":"Allow",
"Resource": [
"${aws_s3_bucket.project-name-code-pipeline-bucket.arn}",
"${aws_s3_bucket.project-name-code-pipeline-bucket.arn}/*"
],
"Action": [
"s3:CreateBucket",
"s3:GetAccelerateConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketPolicy",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetLifecycleConfiguration",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:GetObjectTagging",
"s3:GetObjectTorrent",
"s3:GetObjectVersion",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionTagging",
"s3:GetObjectVersionTorrent",
"s3:GetReplicationConfiguration",
"s3:ListAllMyBuckets",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:ListBucketVersions",
"s3:ListMultipartUploadParts",
"s3:PutObject"
]
}
]
}
POLICY
}
resource "aws_iam_role" "project-name-code-pipeline-role" {
name = "${var.project}-code-pipeline-role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codepipeline.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "project-name-code-pipeline-role-policy" {
name = "${var.project}-code-pipeline-role-policy"
role = "${aws_iam_role.project-name-code-pipeline-role.id}"
policy = <<EOF
{
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketVersioning"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::codepipeline*",
"arn:aws:s3:::elasticbeanstalk*"
],
"Effect": "Allow"
},
{
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetApplicationRevision",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:RegisterApplicationRevision"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"elasticbeanstalk:CreateApplicationVersion",
"elasticbeanstalk:DescribeApplicationVersions",
"elasticbeanstalk:DescribeEnvironments",
"elasticbeanstalk:DescribeEvents",
"elasticbeanstalk:UpdateEnvironment",
"autoscaling:DescribeAutoScalingGroups",
"autoscaling:DescribeLaunchConfigurations",
"autoscaling:DescribeScalingActivities",
"autoscaling:ResumeProcesses",
"autoscaling:SuspendProcesses",
"cloudformation:GetTemplate",
"cloudformation:DescribeStackResource",
"cloudformation:DescribeStackResources",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStacks",
"cloudformation:UpdateStack",
"ec2:DescribeInstances",
"ec2:DescribeImages",
"ec2:DescribeAddresses",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:DescribeSecurityGroups",
"ec2:DescribeKeyPairs",
"elasticloadbalancing:DescribeLoadBalancers",
"rds:DescribeDBInstances",
"rds:DescribeOrderableDBInstanceOptions",
"sns:ListSubscriptionsByTopic"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"lambda:invokefunction",
"lambda:listfunctions"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"s3:ListBucket",
"s3:GetBucketPolicy",
"s3:GetObjectAcl",
"s3:PutObjectAcl",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::elasticbeanstalk*",
"Effect": "Allow"
}
],
"Version": "2012-10-17"
}
EOF
}
resource "aws_iam_role_policy_attachment" "project-name-code-pipeline-role-policy-attachment" {
policy_arn = "${aws_iam_policy.project-name-code-pipeline-bucket-access.arn}"
role = "${aws_iam_role.project-name-code-pipeline-role.name}"
}

Came across the same problem,
the issue is that you need to enable s3 access to "arn:aws:s3:::elasticbeanstalk*"
Agree that error message is kind of obscure

I would suggest checking these things to debug:
Did the template create the resources you were expecting?
Does the pipeline role have the right policy attached to it? You can run aws codepipeline get-pipeline to get the pipeline's ARN, and use the IAM console to check that the policy is what you expect.
Are you missing some elastic beanstalk permissions in the policy? I'm not sure you are, but try to change the policy to "elasticbeanstalk:*".
Try to assume the pipeline's role in the console, and try to deploy the elastic beanstalk instance, see if you get any more detailed information from the elastic beanstalk console.

Related

AWS User not authorized to perform PassRole

I'm trying to create a job in AWS Glue using the Windows AWS Client and I'm receiving that I'm not authorized to perform: iam:PassRole as you can see:
Console>aws glue create-job --name "aws_glue_test" --role "My_Role" --command "Name=glueetlpythonshell,ScriptLocation=s3://mys3bucket/jobs/aws_glue_test.py,PythonVersion=3"
An error occurred (AccessDeniedException) when calling the CreateJob operation: User: arn:aws:iam::1111:user/My_User is not authorized to perform: iam:PassRole on resource: arn:aws:iam::1111:role/My_Role because no identity-based policy allows the iam:PassRole action
The configuration in AWS is set by using Terraform, something like this:
resource "aws_s3_bucket" "mys3bucket" {
bucket = "mys3bucket"
tags = {
Name = "mys3bucket"
ITOwnerEmail = "my#email.com"
}
}
resource "aws_s3_bucket_acl" "mys3bucket_acl" {
bucket = aws_s3_bucket.mys3bucket.id
acl = "private"
}
#=========IAM user======#
resource "aws_iam_user" "My_User" {
name = "My_User "
path = "/"
}
resource "aws_iam_user_policy" "My_User-p" {
name = "My_User-p"
user = "My_User"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::mys3bucket"
},
{
"Action": "glue:*",
"Effect": "Allow",
"Resource": "*"
},
#-- THIS IS THE SOLUTION -- #
{
"Action":[
"iam:GetRole",
"iam:PassRole"
],
"Effect":"Allow",
"Resource": "*"
}
]
}
EOF
}
#===========S3-Bucket-policy=======#
resource "aws_s3_bucket_policy" "mys3bucket-p" {
bucket = aws_s3_bucket.mys3bucket.id
policy = <<POLICY
{
"Version": "2008-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1111:user/My_User"
},
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::mys3bucket/*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1111:user/My_User"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::mys3bucket"
}
]
}
POLICY
}
#===========Glue-policy=======#
resource "aws_iam_role" "My_Role" {
name = "My_Role"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"ec2.amazonaws.com",
"glue.amazonaws.com"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
### Attach policy to above Role ###
resource "aws_iam_role_policy_attachment" "My_Role_GlueService_attach" {
role = aws_iam_role.My_Role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole"
}
#===========IAM-Pass-Role=======#
resource "aws_iam_policy" "My_IAMPass_policy" {
name = "My_IAMPass_policy"
description = "IAM Pass Role Policy"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:PassRole"
],
"Resource": "arn:aws:iam::1111:role/My_Role"
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "My_IAMPass_attach" {
role = aws_iam_role.My_Role.name
policy_arn = aws_iam_policy.My_IAMPass_policy.arn
}
I tried to attach IAM Pass Role but it still failing and I don't know why.
Any help is welcomed. Thank you in advance
SOLUTION: Added in the Code.
You need to add iam:PassRole action to the policy of the IAM user that is being used to create-job. Something like:
{
"Action": [
"iam:PassRole"
],
"Effect": "Allow",
"Resource": [
"arn:aws:iam::1111:role/My_Role"
],
"Condition": {
"StringLike": {
"iam:PassedToService": [
"glue.amazonaws.com"
]
}
}
}

I am unable to add condition in resource based policy in lambda function using terraform

I want to add source_account in lambda resource-based policy condition. So I am executing below terraform code.
data "aws_caller_identity" "current" {
# Retrieves information about the AWS account corresponding to the
# access key being used to run Terraform, which we need to populate
# the "source_account" on the permission resource.
}
resource "aws_lambda_permission" "example" {
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
function_name = "${aws_lambda_function.example.arn}"
principal = "s3.amazonaws.com"
source_account = "${data.aws_caller_identity.current.account_id}"
source_arn = "${aws_s3_bucket.example.arn}"
}
after applying terraform changes and doing plan I am unable to get (this is desired but not getting for S3)
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "lambda-8433be2d-00f7-48dc-9296-7c432662f91e",
"Effect": "Allow",
"Principal": {
"Service": "logs.us-east-1.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:yyyyyyyyyyyy",
"Condition": {
"StringEquals": {
"AWS:SourceAccount": "xxxxxxxxxxxx"
},
"ArnLike": {
"AWS:SourceArn": "arn:aws:logs:us-east-1:xxxxxxxxxxxx:log-group:/aws/lambda/lambda_handler:*"
}
}
}
]
}
I have tried many ways I am not getting any clue.
afer doing terraform plan i am getting below output :
module.environment.aws_lambda_permission.xxxxxxxxxxxx: Creating...
action: "" => "lambda:InvokeFunction"
function_name: "" => "arn:aws:lambda:us-east-1:yyyyyyyyyyyyyy:function:xxxxxxxxxxxx"
principal: "" => "s3.amazonaws.com"
source_arn: "" => "arn:aws:s3:::xxxxxxxxxxxx"
statement_id: "" => "AllowExecutionFromS3Bucket"
I am getting like this :
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "AllowExecutionFromS3Bucket",
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:yyyyyyyyyy:function:xxxxxxxxxx",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:s3:::xxxxxxxxxx"
}
}
}
]
}
I am able to add conditions using AWS CLI .
I am not using the root account. Someone please help me.
You just need to add source_account
resource "aws_lambda_permission" "allow_bucket" {
statement_id = "AllowExecutionFromS3Bucket"
action = "lambda:InvokeFunction"
function_name = "arn:aws:lambda:us-east-1:123456789123:function:mylambda"
principal = "s3.amazonaws.com"
source_account = "123456789123"
source_arn = "arn:aws:s3:::my-bucket"
}
will create
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "AllowExecutionFromS3Bucket",
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:123456789123:function:mylambda",
"Condition": {
"StringEquals": {
"AWS:SourceAccount": "123456789123"
},
"ArnLike": {
"AWS:SourceArn": "arn:aws:s3:::my-bucket"
}
}
}
]
}

AWS SSM: permissions required for aws:domainJoin?

I am trying to setup an EC2 role to allow an instance to join a domain using the New-SSMAssociation powershell cmdlet. Does anyone know what the minimum permissions required to accomplish this are?
I've read the article here https://aws.amazon.com/premiumsupport/knowledge-center/ec2-systems-manager-dx-domain/ but the AmazonEC2RoleforSSM is being deprecated in favor of the AmazonSSMManagedInstanceCore policy however when using that policy in combination with the AmazonSSMDirectoryServiceAccess policy I get an error:
New-SSMAssociation : User: arn:aws:sts:::assumed-role/MyEC2Role/ is not
authorized to perform: ssm:CreateAssociation on resource:
arn:aws:ec2:us-east-1::instance/
The only way I have been able to get it to work is with ssm:*, however I would prefer not to do that if possible. The combined policy I am using is(without the ssm:*):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstanceStatus"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:CreateAssociation"
],
"Resource": "arn:aws:ssm:<region>:<account-id>:document/JoinDomain"
},
{
"Effect": "Allow",
"Action": [
"ds:CreateComputer",
"ds:DescribeDirectories"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssm:DescribeAssociation",
"ssm:GetDeployablePatchSnapshotForInstance",
"ssm:GetDocument",
"ssm:DescribeDocument",
"ssm:GetManifest",
"ssm:GetParameters",
"ssm:ListAssociations",
"ssm:ListInstanceAssociations",
"ssm:PutInventory",
"ssm:PutComplianceItems",
"ssm:PutConfigurePackageResult",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceAssociationStatus",
"ssm:UpdateInstanceInformation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2messages:AcknowledgeMessage",
"ec2messages:DeleteMessage",
"ec2messages:FailMessage",
"ec2messages:GetEndpoint",
"ec2messages:GetMessages",
"ec2messages:SendReply"
],
"Resource": "*"
}
]
}
The appoach that worked in our environment.
Create a IAM Role that has
"Statement": [
{
"Sid": "SSMDocument",
"Effect": "Allow",
"Action": [
"ssm:CreateAssociation"
],
"Resource": [
"arn:aws:ec2:${AWS_REGION}:${AWS_ACCOUNT}:instance/*",
"arn:aws:ssm:${AWS_REGION}:${AWS_ACCOUNT}:document/${SSM_DOCUMENT_NAME}"
]
}
]
plus the pre-defined policies AmazonSSMDirectoryServiceAccess and AmazonSSMManagedInstanceCore
userdata as follows:
<powershell>
[Environment]::SetEnvironmentVariable("ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE", "true", "Machine")
[Environment]::SetEnvironmentVariable("ECS_ENABLE_CONTAINER_METADATA", "true", "Machine")
Import-Module ECSTools
Initialize-ECSAgent -Cluster '${ECS_CLUSTER_NAME}' -EnableTaskIAMRole
Set-DefaultAWSRegion -Region ${AWS_REGION}
Set-Variable -name instance_id -value (Invoke-Restmethod -uri http://169.254.169.254/latest/meta-data/instance-id)
New-SSMAssociation -Name "${SSM_DOCUMENT_NAME}" -Target #{Key="instanceids";Values=#($instance_id)}
</powershell>
terraform code fragment for SSM Document
data "aws_directory_service_directory" "domain_controller" {
directory_id = var.directory_id
}
data "template_file" "userdata" {
template = file("${path.module}/files/userdata.ps1")
vars = {
SSM_DOCUMENT_NAME = aws_ssm_document.ad_join_domain.name
AWS_REGION = var.region
ECS_CLUSTER_NAME = local.cluster_name
}
}
resource "aws_ssm_document" "ad_join_domain" {
name = "${var.environment}-ad-join-domain"
document_type = "Command"
content = jsonencode(
{
"schemaVersion" = "2.2"
"description" = "join aws directory services domain"
"mainSteps" = [
{
"action" = "aws:domainJoin",
"name" = "domainJoin",
"inputs" = {
"directoryId" : data.aws_directory_service_directory.domain_controller.id,
"directoryName" : data.aws_directory_service_directory.domain_controller.name
"dnsIpAddresses" : sort(data.aws_directory_service_directory.domain_controller.dns_ip_addresses)
}
}
]
}
)
tags = {
environment = var.environment
}
}

AWS invalid policy - terraform

I am creating a policy and a role for API gateway to access dynamodb api endpoints with below terraform config. What am I missing? I am getting invalid policy error on terraform plan
resource "aws_iam_role_policy" "api_dbaccess_policy" {
name = "api_dbaccess_policy"
role = "${aws_iam_role.apiGatewayDynamoDbAccessRole.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:BatchGet*",
"dynamodb:DescribeStream",
"dynamodb:DescribeTable",
"dynamodb:Get*",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchWrite*",
"dynamodb:CreateTable",
"dynamodb:Delete*",
"dynamodb:Update*",
"dynamodb:PutItem"
],
"Resource": "*"
}
]
}
EOF
# depends_on = [
# "aws_dynamodb_table.us-east-1"
# ]
}
resource "aws_iam_role" "apiGatewayDynamoDbAccessRole" {
name = "apiGatewayDynamoDbAccessRole"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
What am i doing wrong? I am getting invalid policy error.
As already mentioned, just remove the indention in the EOF block...
Another option is to use the aws_iam_policy_document data source. For me it is a cleaner approach and is better maintainable, e.g. when you are using an IDE with terraform support.
Your config would look like this ("Effect": "Allow" is not needed here, because it's default behavior):
resource "aws_iam_role_policy" "api_dbaccess_policy" {
name = "api_dbaccess_policy"
role = "${aws_iam_role.apiGatewayDynamoDbAccessRole.id}"
policy = "${data.aws_iam_policy_document.dynamodb.json}"
}
resource "aws_iam_role" "apiGatewayDynamoDbAccessRole" {
name = "apiGatewayDynamoDbAccessRole"
assume_role_policy = "${data.aws_iam_policy_document.apigateway.json}"
}
data "aws_iam_policy_document" "dynamodb" {
statement {
actions = [
"dynamodb:BatchGet*",
"dynamodb:DescribeStream",
"dynamodb:DescribeTable",
"dynamodb:Get*",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchWrite*",
"dynamodb:CreateTable",
"dynamodb:Delete*",
"dynamodb:Update*",
"dynamodb:PutItem"
]
resources = ["*"]
}
}
data "aws_iam_policy_document" "apigateway" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["apigateway.amazonaws.com"]
}
}
}

How to attach IAM roles to EC2 instances so they can pull an specific image from ECR in Terraform

I'm trying to attach an IAM roles to EC2 instances (not ECS) so they can pull images from ECR.
Do something like this. Note you may want to limit which ECR repos are accessible.
resource "aws_instance" "test" {
...
}
resource "aws_launch_configuration" "ecs_cluster" {
...
iam_instance_profile = "${aws_iam_instance_profile.test.id}"
}
resource "aws_iam_role" "test" {
name = "test_role"
assume_role_policy = "..."
}
resource "aws_iam_instance_profile" "test" {
name = "ec2-instance-profile"
role = "${aws_iam_role.test.name}"
}
resource "aws_iam_role_policy_attachment" "test" {
role = "${aws_iam_role.test.name}"
policy_arn = "${aws_iam_policy.test.arn}"
}
resource "aws_iam_policy" "test" {
name = "ec2-instance-pulls-from-ecr"
description = "EC2 instance can pull from ECR"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "*"
}
]
}
EOF
}
This is known to work in Terraform v0.11.13
cluster.tf
locals {
cluster_name = "cluster-${terraform.workspace}"
}
resource "aws_iam_role_policy" "cluster_member" {
name = "${local.cluster_name}"
role = "${aws_iam_role.cluster_member.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:UpdateContainerInstancesState",
"ecs:DeregisterContainerInstance",
"ecs:DiscoverPollEndpoint",
"ecs:Poll",
"ecs:RegisterContainerInstance",
"ecs:StartTelemetrySession",
"ecs:Submit*",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role" "cluster_member" {
name = "${local.cluster_name}"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_instance_profile" "cluster_member" {
name = "${local.cluster_name}"
role = "${aws_iam_role.cluster_member.name}"
}
data "template_file" "cloud_config" {
template = "${file("${path.module}/templates/user_data.sh")}"
vars {
ecs_cluster = "${local.cluster_name}"
}
}
resource "aws_instance" "cluster_member" {
# http://docs.aws.amazon.com/AmazonECS/latest/developerguide/instance_IAM_role.html
iam_instance_profile = "${aws_iam_instance_profile.cluster_member.name}"
user_data = "${data.template_file.cloud_config.rendered}"
}
templates/user_data.sh
#!/bin/bash
# See https://docs.aws.amazon.com/AmazonECS/latest/developerguide/launch_container_instance.html
cat <<'EOF' >> /etc/ecs/ecs.config
ECS_CLUSTER=${ecs_cluster}
EOF
Answer Given by #Eric M. Johnson is correct, Just for the completeness,
resource "aws_launch_configuration" "ecs_launch_configuration" {
name = "${var.application_name}-${var.stack}"
image_id = var.image_id
instance_type = var.instance_type
iam_instance_profile = aws_iam_instance_profile.esc_launch_configuration_iam_instance_profile.arn
security_groups = split(",",aws_security_group.ecs_launch_configuration_security_group.id)
associate_public_ip_address = "true"
// key_name = "${var.ecs-key-pair-name}"
user_data = data.template_file.user_data.rendered
}
data "template_file" "user_data" {
template = file("${path.module}/ec2/user-data.sh")
vars = {
ecs_cluster_name = "${var.application_name}-${var.stack}"
}
}
resource "aws_iam_instance_profile" "esc_launch_configuration_iam_instance_profile" {
name = "${var.application_name}-${var.stack}"
role = aws_iam_role.iam_role.name
}
resource "aws_iam_role" "iam_role" {
name = "${var.application_name}-${var.stack}"
force_detach_policies = true
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": [
"ec2.amazonaws.com",
"ecs.amazonaws.com"
]
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy_attachment" "iam_role_policy_attachment" {
role = aws_iam_role.iam_role.name
policy_arn = aws_iam_policy.ecs_iam_policy.arn
}
resource "aws_iam_policy" "ecs_iam_policy" {
name = "${var.application_name}-${var.stack}"
description = "EC2 instance can pull from ECR"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:DescribeTags",
"ecs:CreateCluster",
"ecs:DeregisterContainerInstance",
"ecs:DiscoverPollEndpoint",
"ecs:Poll",
"ecs:RegisterContainerInstance",
"ecs:StartTelemetrySession",
"ecs:UpdateContainerInstancesState",
"ecs:Submit*",
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
EOF
}