Мне нужно выполнить шаблон 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}"
}
}
}
}
Update:
После ответа Даррена (это имеет большой смысл) ниже я добавил:
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: The role "arn:aws:iam:::role/" cannot be assumed.
There are a number of possible causes of this - the most common are:
- The credentials used in order to assume the role are invalid
- The credentials do not have appropriate permission to assume the role
- The role ARN is not valid
Я проверил роль в другой учетной записи и могу переключиться на эту роль с помощью консоли AWS из своей учетной записи. Я также проверил руководство AWS здесь.
Итак: эта роль ARN действительна, у меня есть учетные данные для принятия роли и все разрешения, необходимые для запуска стека.
Update
Я также пробовал с новая роль, имеющая полный доступ к сервисам. Однако я столкнулся с этой ошибкой:
Error: Error refreshing state: 2 error(s) occurred:
* aws_codebuild_project.build_project: 1 error(s) occurred: * aws_codebuild_project.build_project: aws_codebuild_project.build_project: Error retreiving Projects:"InvalidInputException: Invalid project ARN: account ID does not match caller's account\n\tstatus code: 400, request id: ..." * aws_s3_bucket.build_artifact_bucket: 1 error(s) occurred:
* aws_s3_bucket.build_artifact_bucket: aws_s3_bucket.build_artifact_bucket: error getting S3 Bucket CORSconfiguration: AccessDenied: Access Denied status code: 403, request id: ..., host id: ...
=====
UPDATE 29 Apr 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





Вообще говоря, вам нужно будет загрузить целевую учетную запись. Как минимум, это означает создание роли, которая будет исходить из роли конвейера, но может включать в себя некоторые другие ресурсы.
Спасибо, @Аарон.
Я не знаю примера. Роль должна отражать все, что вы хотите, чтобы конвейер мог создавать/обновлять/удалять, поэтому это, вероятно, сильно различается от клиента к клиенту, особенно если вы пытаетесь применить принцип наименьших привилегий.
Вы должны быть в состоянии сделать это следующим образом: В 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 cli).
«assume_role.role_arn» — это роль в учетной записи, которую вы хотите взять на себя. Пользователю IAM в «профиле» необходимо разрешить взять на себя эту роль.
Спасибо, Даррен. Я попробую и дам вам знать.
Я столкнулся с другой ошибкой. Я обновил свой вопрос выше.
Хмм хорошо. Вы настаиваете на том, что профиль «по умолчанию» в вашем ~/.aws/credentials имеет ключи AWS пользователя IAM, который может взять на себя роль в <OTHER-ACCOUNT>? Кроме того, я не уверен, должен ли «регион» иметь значение на этапе принятия роли, поскольку IAM является глобальной службой, но в любом случае не помешает убедиться, что установлен правильный.
Я уверен, что в профиле «по умолчанию» есть ключи для роли в другой учетной записи, поскольку я могу использовать свою консоль для переключения на эту роль. Сервисы обоих аккаунтов находятся в одном регионе. Я также проверил это.
Я также создал новую роль и попробовал ее. Я столкнулся с другой ошибкой. Я обновлю свой вопрос.
Оператор accept_role поставщика aws рекомендуется из документации Terraform.
У меня есть пуленепробиваемое решение в любое время, когда вы хотите запускать команды в качестве определенной роли (включая другие учетные записи). Я предполагаю, что у вас установлены инструменты 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
Я перешел по ссылке, которую вы разместили, и предоставил доступ к роли (в другой учетной записи) следующим образом: 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
Это означает, что пользователь, принимающий на себя роль, не имеет доступа для принятия этой роли. Проверьте доверительные отношения роли, которую пытается взять на себя пользователь, должно выглядеть примерно так: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::1234567890:root" }, "Action": "sts:AssumeRole" } ] } Где 1234567890 — это учетная запись, к которой принадлежит пользователь...
Также убедитесь, что пользователь может принимать роли. Если вы являетесь администратором, у вас должна быть такая возможность, но если нет, то вам придется добавить политику, позволяющую вам принимать на себя роли.
У меня есть. Я только что обновил вопрос выше. Я добавил политику, чтобы мой пользователь в ОСНОВНОЙ УЧЕТНОЙ ЗАПИСИ мог взять на себя роль ДРУГОЙ УЧЕТНОЙ ЗАПИСИ. Также я подтверждаю, что ДРУГАЯ УЧЕТНАЯ ЗАПИСЬ имеет доверенных лиц ОСНОВНОЙ УЧЕТНОЙ ЗАПИСИ.
К вашему сведению, вы можете использовать jq -r, чтобы исключить трубу к tr, например. export AWS_ACCESS_KEY_ID=$(echo $aws_credentials | jq -r '.Credentials.AccessKeyId')
Глядя на вашу политику доверительных отношений в другой учетной записи, ниже выделено условие многофакторная аутентификация. Таким образом, пользователь должен пройти двухэтапную аутентификацию, прежде чем брать на себя роль. Удалите это условие и попробуйте запустить код.
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}Спасибо. Вы правы, и я проголосовал за ваш ответ. Теперь я оглядываюсь назад на свой вопрос, на самом деле ответ Роландо был правильным, поскольку он упомянул «пуленепробиваемый». Ваш ответ не сработает, если мне нужно применить MFA.
Спасибо, Аарон. Знаете ли вы, есть ли где-нибудь документация/ответ/руководство/пример, в котором подробно описаны такие случаи? Например: как должна выглядеть ролевая политика.