如何将 IAM 角色附加到 EC2 实例,以便它们可以从 Terraform 中的 ECR 中提取特定图像

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

我正在尝试将 IAM 角色附加到 EC2 实例(而非 ECS),以便它们可以从 ECR 中提取图像。

做这样的事情。请注意,您可能希望限制可访问的 ECR 存储库。

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
}

已知这在 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

@Eric M. Johnson 给出的答案是正确的,只是为了完整性,

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
}