使用AWS角色假设执行Terraform应用程序

26
我需要执行一个Terraform模板,为一个AWS账户提供基础设施,我可以通过扮演角色来访问该账户。
我现在遇到的问题是,在那个AWS账户中我没有IAM用户,因此我没有aws_access_key_id或aws_secret_access_key来设置另一个命名配置文件。当我运行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}"
            }
        }
    }
}

更新:
根据以下 Darren 的答案(非常有道理),我补充了:
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/”的角色。

    可能有许多原因导致此问题-最常见的是:

    • 用于扮演角色的凭据无效
    • 凭据没有适当的权限来扮演角色
    • 角色ARN无效

我已经检查了其他帐户中的角色,并且可以使用AWS控制台从我的帐户切换到该角色。我还检查了AWS指南here

所以说:那个角色ARN是有效的,我确实有凭据来扮演这个角色,并且拥有运行堆栈所需的所有权限。

更新

我还尝试了一个具有所有服务访问权限的新角色。 然而,我遇到了这个错误:

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 CORS

configuration: AccessDenied: Access Denied status code: 403, request id: ..., host id: ...

=====

2019年4月29日更新:

根据@Rolando的建议,我已将此策略添加到我尝试使用的主账户的用户中,以扮演我计划执行terraform apply的其他账户的角色。

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

这是角色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
4个回答

30

我有一个防弹解决方案,随时可以让您以特定角色(包括其他帐户)运行命令。

我假设您已经安装了AWS CLI工具。您还需要安装jq(一种轻松解析和提取JSON数据的工具),虽然您可以按任何方式解析数据。

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

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 命令的响应赋值给一个变量。 最后三行代码将从第一个命令中选择的值分配给aws cli使用的变量。

注意事项:

如果您创建了一个bash脚本,请在其中添加terraform命令。您也可以只创建一个包含上述行的bash,并在前面加上“.”运行它(例如:. ./get-creds.sh)。这将在当前的bash shell中创建变量。

角色有过期时间,通常为一小时,请注意。

您的shell现在将拥有三个变量AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN。这意味着它会覆盖您的~/.aws/credentials文件。清除此操作最简单的方法是启动一个新的bash会话。

我使用了这篇文章来查找这些信息:https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html


我按照您发布的链接并授予了角色访问权限(在另一个帐户上),链接如下:https://docs.aws.amazon.com/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html。然而,当我运行命令:`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`。 - Viet
1
这意味着用户扮演的角色没有扮演该角色的访问权限。 检查用户尝试扮演的角色的信任关系,应该类似于以下内容:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::1234567890:root" }, "Action": "sts:AssumeRole" } ] } 其中1234567890是用户所属帐户的编号... - Rolando Cintron
还要检查用户是否能够扮演角色。如果您是管理员,那么应该可以,但如果不是,则需要添加一个允许您扮演角色的策略。 - Rolando Cintron
我已经更新了上面的问题。我添加了策略,以便我的主账户中的用户可以扮演其他账户的角色。此外,我确认其他账户具有主账户的受信实体。 - Viet
2
你可以使用 jq -r 来消除管道到 tr 的步骤,例如:export AWS_ACCESS_KEY_ID=$(echo $aws_credentials | jq -r '.Credentials.AccessKeyId') - mm689

8
您应该按照以下方式进行操作: 在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”是您想扮演的账户中的角色。在“profile”中的IAM用户需要被允许扮演该角色。


谢谢,Darren。我会试一下并告诉你结果。 - Viet
我又遇到了一个错误。我已经更新了我的问题。 - Viet
嗯,好的。您确定您的 ~/.aws/credentials 文件中的 "default" 配置文件包含了可以在 <OTHER-ACCOUNT> 中扮演角色的 IAM 用户的 AWS 密钥吗?此外,在扮演角色步骤中是否应该设置“区域”并不确定,因为 IAM 是全球服务,但确保正确设置也无妨。 - Darren O'Brien
我相信“默认”配置文件中有假定在另一个帐户中扮演角色的密钥,因为我可以使用我的控制台切换到该角色。两个帐户的服务都在同一地区。我也已经检查过了。 - Viet
我也创建了一个新角色并尝试了它,但又遇到了另一个错误。我会更新我的问题。 - Viet
Terraform文档推荐使用AWS提供程序的assume_role语句。 - Brandon Bradley

2

查看您在其他帐户中的信任关系政策,下面突出显示了一个应用多因素认证的条件。因此,在扮演角色之前,用户应进行两步验证。删除此条件并尝试运行代码。

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


谢谢。你是正确的,我已经为你的答案投了赞成票。现在我回头看我的问题,实际上Rolando的答案是正确的,因为他提到了“防弹”。如果我需要强制执行MFA,你的答案将不起作用。 - Viet

0

一般来说,您需要引导目标帐户。最少需要创建一个可从管道角色承担的角色,但也可以包括其他一些资源。


谢谢,亚伦。你知道有没有关于这种情况的详细文档/答案/指南/示例之类的东西吗?例如:角色策略应该是什么样子的。 - Viet
谢谢你,@Aaron。 - Viet
1
我不知道有任何示例。该角色需要反映出您希望管道能够创建/更新/删除的所有内容,因此这可能因客户而异 - 尤其是如果您试图应用最小特权原则。 - Aaron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接