Выполнить Terraform apply с AWS взять на себя роль

Мне нужно выполнить шаблон Terraform для предоставления инфраструктуры для учетной записи AWS, к которой я могу получить доступ, взяв на себя роль.

Проблема, с которой я столкнулся сейчас, заключается в том, что у меня нет пользователя IAM в этой учетной записи AWS, поэтому у меня нет aws_access_key_id или aws_secret_access_key для создания другого именованного профиля в моем ~/.aws/credentials. Когда я запускаю команду terraform apply, шаблон создает инфраструктуру для моей учетной записи, а не для другой учетной записи.

Как запустить шаблон Terraform, используя свою учетную запись, которая имеет роль для доступа к сервисам другой учетной записи AWS?

Вот мой файл Terraform:

# Input variables
variable "aws_region" {
    type = "string"
    default = "us-east-1"
}

variable "pipeline_name" {
    type = "string"
    default = "static-website-terraform"
}

variable "github_username" {
    type = "string"
    default = "COMPANY"
}

variable "github_token" {
    type = "string"
}

variable "github_repo" {
    type = "string"
}

provider "aws" {
    region = "${var.aws_region}"
    assume_role {
        role_arn = "arn:aws:iam::<AWS-ACCOUNT-ID>:role/admin"
        profile = "default"
    }
}

# CodePipeline resources
resource "aws_s3_bucket" "build_artifact_bucket" {
    bucket = "${var.pipeline_name}-artifact-bucket"
    acl = "private"
}

data "aws_iam_policy_document" "codepipeline_assume_policy" {
    statement {
        effect = "Allow"
        actions = ["sts:AssumeRole"]

        principals {
            type = "Service"
            identifiers = ["codepipeline.amazonaws.com"]
        }
    }
}

resource "aws_iam_role" "codepipeline_role" {
    name = "${var.pipeline_name}-codepipeline-role"
    assume_role_policy = "${data.aws_iam_policy_document.codepipeline_assume_policy.json}"
}

# CodePipeline policy needed to use CodeCommit and CodeBuild
resource "aws_iam_role_policy" "attach_codepipeline_policy" {
    name = "${var.pipeline_name}-codepipeline-policy"
    role = "${aws_iam_role.codepipeline_role.id}"

    policy = <<EOF
{
    "Statement": [
        {
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketVersioning",
                "s3:PutObject"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "cloudwatch:*",
                "sns:*",
                "sqs:*",
                "iam:PassRole"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "codebuild:BatchGetBuilds",
                "codebuild:StartBuild"
            ],
            "Resource": "*",
            "Effect": "Allow"
        }
    ],
    "Version": "2012-10-17"
}
EOF
}

# CodeBuild IAM Permissions
resource "aws_iam_role" "codebuild_assume_role" {
    name = "${var.pipeline_name}-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" "codebuild_policy" {
    name = "${var.pipeline_name}-codebuild-policy"
    role = "${aws_iam_role.codebuild_assume_role.id}"

    policy = <<POLICY
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:GetBucketVersioning"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Effect": "Allow",
            "Resource": [
                "${aws_codebuild_project.build_project.id}"
            ],
            "Action": [
                "codebuild:*"
            ]
        },
        {
            "Effect": "Allow",
            "Resource": [
                "*"
            ],
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ]
        }
    ]
}
POLICY
}

# CodeBuild Section for the Package stage
resource "aws_codebuild_project" "build_project" {
    name = "${var.pipeline_name}-build"
    description = "The CodeBuild project for ${var.pipeline_name}"
    service_role = "${aws_iam_role.codebuild_assume_role.arn}"
    build_timeout = "60"

    artifacts {
        type = "CODEPIPELINE"
    }

    environment {
        compute_type = "BUILD_GENERAL1_SMALL"
        image = "aws/codebuild/nodejs:6.3.1"
        type = "LINUX_CONTAINER"
    }

    source {
        type = "CODEPIPELINE"
        buildspec = "buildspec.yml"
    }
}

# Full CodePipeline
resource "aws_codepipeline" "codepipeline" {
    name = "${var.pipeline_name}-codepipeline"
    role_arn = "${aws_iam_role.codepipeline_role.arn}"

    artifact_store = {
        location = "${aws_s3_bucket.build_artifact_bucket.bucket}"
        type     = "S3"
    }

    stage {
        name = "Source"

        action {
            name = "Source"
            category = "Source"
            owner = "ThirdParty"
            provider = "GitHub"
            version = "1"
            output_artifacts = ["SourceArtifact"]

            configuration {
                Owner = "${var.github_username}"
                OAuthToken = "${var.github_token}"
                Repo = "${var.github_repo}"
                Branch = "master"
                PollForSourceChanges = "true"
            }
        }
    }

    stage {
        name = "Deploy"

        action {
            name = "DeployToS3"
            category = "Test"
            owner = "AWS"
            provider = "CodeBuild"
            input_artifacts = ["SourceArtifact"]
            output_artifacts = ["OutputArtifact"]
            version = "1"

            configuration {
                ProjectName = "${aws_codebuild_project.build_project.name}"
            }
        }
    }
}

Обновлять:

Следуя приведенному ниже ответу Даррена (это имеет большой смысл), я добавил:

provider "aws" {
  region                  = "us-east-1"
  shared_credentials_file = "${pathexpand("~/.aws/credentials")}"
  profile                 = "default"

  assume_role {
    role_arn = "arn:aws:iam::<OTHER-ACCOUNT>:role/<ROLE-NAME>"
  }
}

Однако я столкнулся с этой ошибкой:

  • provider.aws: роль "arn: aws: iam ::: role /" недопустима.

    Этому есть ряд возможных причин, наиболее частыми из которых являются:

    • The credentials used in order to assume the role are invalid
    • У учетных данных нет соответствующего разрешения для принятия роли
    • Роль ARN недействительна

Я проверил роль в другой учетной записи и могу переключиться на эту роль с помощью консоли AWS из своей учетной записи. Я также проверил руководство AWS здесь

Итак: эта роль ARN действительна, у меня есть учетные данные для принятия этой роли и все разрешения, которые мне нужны для запуска стека.

Обновлять

Я также пробовал использовать новую роль, которая имеет полный доступ к службам. Однако я столкнулся с этой ошибкой:

Ошибка: Ошибка обновления состояния: 2 ошибки:

    * aws_codebuild_project.build_project: 1 error(s) occurred:

    * aws_codebuild_project.build_project: aws_codebuild_project.build_project: Error retreiving Projects:

"InvalidInputException: недопустимый ARN проекта: идентификатор учетной записи не соответствует учетной записи вызывающего абонента \ n \ tstatus code: 400, идентификатор запроса: ..." * aws_s3_bucket.build_artifact_bucket: произошла 1 ошибка (и):

    * aws_s3_bucket.build_artifact_bucket: aws_s3_bucket.build_artifact_bucket: error getting S3 Bucket CORS

конфигурация: AccessDenied: Доступ запрещен, код состояния: 403, идентификатор запроса: ..., идентификатор хоста: ...

=====

ОБНОВЛЕНИЕ 29 апреля 2019 г .:

Следуя предложению @Rolando, я добавил эту политику к пользователю ОСНОВНОЙ УЧЕТНОЙ ЗАПИСИ, которую я пытаюсь использовать, чтобы взять на себя роль ДРУГОЙ УЧЕТНОЙ ЗАПИСИ, где я планирую выполнить terraform apply.

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::<OTHER-ACCOUNT-ID>:role/admin"
    }
}

Это Trust Relationship роли admin, принадлежащей ДРУГОМУ АККАУНТУ:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<MAIN_ACCOUNT_ID>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }
    }
  ]
}

Однако когда я запустил эту команду:

aws sts assume-role --role-arn arn:aws:iam::<OTHER-ACCOUNT-ID>:role/admin --role-session-name "RoleSession1" --profile default > assume-role-output.txt

У меня такая ошибка:

An error occurred (AccessDenied) when calling the AssumeRole operation: Access denied

person Viet    schedule 12.03.2019    source источник


Ответы (4)


У меня есть надежное решение в любое время, когда вы хотите запускать команды с определенной ролью (включая другие учетные записи). Я предполагаю, что у вас установлены инструменты AWS CLI. Вам также придется установить jq (простой инструмент для анализа и извлечения данных из json), хотя вы можете анализировать данные любым удобным для вас способом.

aws_credentials=$(aws sts assume-role --role-arn arn:aws:iam::1234567890:role/nameOfMyrole --role-session-name "RoleSession1")

export AWS_ACCESS_KEY_ID=$(echo $aws_credentials|jq '.Credentials.AccessKeyId'|tr -d '"')
export AWS_SECRET_ACCESS_KEY=$(echo $aws_credentials|jq '.Credentials.SecretAccessKey'|tr -d '"')
export AWS_SESSION_TOKEN=$(echo $aws_credentials|jq '.Credentials.SessionToken'|tr -d '"')

Первая строка назначает ответ от команды aws sts и помещает его в переменную. Последние 3 строки выберут значения из первой команды и назначат их переменным, которые использует aws cli.

Соображения:

Если вы создаете сценарий bash, добавьте туда и свои команды terraform. Вы также можете просто создать bash с приведенными выше строками и запустить его с помощью символа '.' впереди (например: . ./get-creds.sh). Это создаст переменные в вашей текущей оболочке bash.

Срок действия роли истекает, имейте в виду, что срок действия ролей обычно составляет час.

Теперь ваша оболочка будет иметь три переменные AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN. Это означает, что он заменит ваш ~/.aws/credentials. Самый простой способ очистить это - просто начать новый сеанс bash.

Чтобы понять это, я использовал эту статью: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html

person Rolando Cintron    schedule 25.04.2019
comment
Я перешел по опубликованной вами ссылке и предоставил доступ к роли (в другой учетной записи) следующим образом: docs.aws.amazon.com/IAM/latest/UserGuide/. Однако, когда я запустил команду: aws sts assume-role --role-arn arn:aws:iam::123456789012:role/role-name --role-session-name "RoleSession1" --profile IAM-user-name > assume-role-output.txt, у меня была эта ошибка: An error occurred (AccessDenied) when calling the AssumeRole operation: Access denied - person Viet; 29.04.2019
comment
Это означает, что пользователь, принимающий роль, не имеет доступа для принятия роли. Проверьте отношения доверия для роли, которую пытается принять пользователь, должно выглядеть примерно так: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::1234567890:root" }, "Action": "sts:AssumeRole" } ] } Где 1234567890 - это учетная запись, к которой принадлежит пользователь ... - person Rolando Cintron; 29.04.2019
comment
Также убедитесь, что пользователь может брать на себя роли. Если вы администратор, у вас должна быть возможность, но если нет, вам придется добавить политику, которая позволяет вам брать на себя роли. - person Rolando Cintron; 29.04.2019
comment
У меня есть. Я только что обновил вопрос выше. Я добавил политику, чтобы мой пользователь в ОСНОВНОЙ УЧЕТНОЙ ЗАПИСИ мог взять на себя роль ДРУГОЙ УЧЕТНОЙ ЗАПИСИ. Кроме того, я подтверждаю, что ДРУГОЙ АККАУНТ имеет доверенные лица ГЛАВНОГО АККАУНТА. - person Viet; 29.04.2019
comment
К вашему сведению, вы можете использовать jq -r, чтобы удалить канал до tr, например. export AWS_ACCESS_KEY_ID=$(echo $aws_credentials | jq -r '.Credentials.AccessKeyId') - person mm689; 19.05.2021

У вас должно получиться так: В Terraform настройте провайдер aws для использования вашего локального shared_credentials_file

provider "aws" {
  region                  = "us-east-1"
  shared_credentials_file = "${pathexpand("~/.aws/credentials")}"
  profile                 = "default"

  assume_role {
    role_arn = "arn:aws:iam::1234567890:role/OrganizationAccountAccessRole"
  }
}

«profile» - это именованный профиль в ~ / .aws / credentials, который имеет ключи доступа AWS. Например.

[default]
region = us-east-1
aws_access_key_id = AKIAJXXXXXXXXXXXX
aws_secret_access_key = Aadxxxxxxxxxxxxxxxxxxxxxxxxxxxx    

Это не пользователь IAM в учетной записи, к которой вы хотите получить доступ. Он находится в «исходной» учетной записи (в какой-то момент вам понадобятся ключи для доступа к клику AWS).

«accept_role.role_arn» - это роль в учетной записи, которую вы хотите принять. Пользователю IAM в «профиле» нужно разрешить взять на себя эту роль.

person Darren O'Brien    schedule 18.03.2019
comment
Спасибо, Даррен. Я попробую и дам знать. - person Viet; 19.03.2019
comment
Я столкнулся с другой ошибкой. Я обновил свой вопрос выше. - person Viet; 19.03.2019
comment
Хмм хорошо. Вы подаете в суд на то, что профиль по умолчанию в вашем ~ / .aws / credentials содержит ключи AWS пользователя IAM, который может взять на себя роль в ‹OTHER-ACCOUNT›? Кроме того, я не уверен, должен ли регион иметь значение на этапе принятия роли, поскольку IAM - это глобальная служба, но в любом случае не помешает убедиться, что установлен правильный. - person Darren O'Brien; 20.03.2019
comment
Я уверен, что в профиле по умолчанию есть ключи, чтобы взять на себя роль в другой учетной записи, поскольку я могу использовать свою консоль для переключения на эту роль. Сервисы обоих аккаунтов находятся в одном регионе. Я тоже это проверил. - person Viet; 20.03.2019
comment
Я также создал новую роль и попробовал ее. Я столкнулся с другой ошибкой. Я обновлю свой вопрос. - person Viet; 20.03.2019
comment
Оператор accept_role поставщика aws рекомендуется из документации Terraform. - person Brandon Bradley; 18.03.2021

Если посмотреть на вашу политику доверительных отношений в другом аккаунте, ниже выделено условие многофакторной аутентификации. Таким образом, пользователь должен пройти двухфакторную аутентификацию, прежде чем принять на себя роль. Удалите это условие и попробуйте запустить код.

   "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        }
      }

person Naveen    schedule 23.08.2019
comment
Спасибо. Вы правы, и я поддержал ваш ответ. Теперь я оглядываюсь на свой вопрос, на самом деле ответ Роландо был правильным, поскольку он упомянул о пуленепробиваемости. Ваш ответ не сработает, если мне нужно будет применить МИД. - person Viet; 24.08.2019

Вообще говоря, вам нужно будет запустить целевую учетную запись. Как минимум, это означает создание роли, предполагаемой из роли конвейера, но которая может включать некоторые другие ресурсы.

person Aaron    schedule 13.03.2019
comment
Спасибо, Аарон. Знаете ли вы, есть ли где-нибудь документация / ответ / руководство / пример, в котором подробно рассматриваются подобные случаи? Например: как должна выглядеть ролевая политика. - person Viet; 13.03.2019
comment
Спасибо, @Aaron. - person Viet; 14.03.2019
comment
Я не знаю примера. Роль должна отражать все, что вы хотите, чтобы конвейер мог создавать / обновлять / удалять, поэтому это, вероятно, немного варьируется от клиента к клиенту, особенно если вы попытаетесь применить принцип наименьших привилегий. - person Aaron; 15.03.2019