执行 Terraform apply with AWS assume role
Execute Terraform apply with AWS assume role
我需要执行一个 Terraform 模板来为我可以通过担任角色访问的 AWS 账户配置基础设施。
我现在遇到的问题是我在该 AWS 账户中没有 IAM 用户,所以我没有 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 指南 here
因此:该角色 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 CORS
configuration: 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"
}
}
这是角色 admin
的 Trust Relationship
属于其他帐户:
{
"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
一般来说,您需要 bootstrap 目标帐户。这至少意味着创建一个可以从管道角色中担任的角色,但可以包括一些其他资源。
你应该可以这样做:
在 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 用户。它位于 "source" 帐户中(您有时需要密钥才能访问 AWS cli)。
"assume_role.role_arn" 是您要担任的帐户中的角色。需要允许 "profile" 中的 IAM 用户担任该角色。
我有一个防弹解决方案,只要您想 运行 作为特定角色(包括其他帐户)执行命令。
我假设您已经安装了 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
命令的响应并将其放入变量中。
最后 3 行将 select 来自第一个命令的值并将它们分配给 aws
cli 使用的变量。
注意事项:
如果您创建 bash 脚本,请在其中添加您的 terraform 命令。您也可以只使用上面的行创建一个 bash,然后使用 '.' 创建 运行。在前面(即:. ./get-creds.sh
)。这将在您当前的 bash shell.
上创建变量
角色到期,请记住角色通常有一个小时的到期时间。
您的 shell 现在将具有三个变量 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
查看您在其他帐户中的信任关系策略,应用了一个条件 多因素身份验证,在下面突出显示。因此,用户在承担该角色之前应该进行 2 因素身份验证。删除此条件并尝试 运行 代码。
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
我需要执行一个 Terraform 模板来为我可以通过担任角色访问的 AWS 账户配置基础设施。
我现在遇到的问题是我在该 AWS 账户中没有 IAM 用户,所以我没有 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 指南 here
因此:该角色 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 CORS
configuration: 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"
}
}
这是角色 admin
的 Trust Relationship
属于其他帐户:
{
"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
一般来说,您需要 bootstrap 目标帐户。这至少意味着创建一个可以从管道角色中担任的角色,但可以包括一些其他资源。
你应该可以这样做: 在 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 用户。它位于 "source" 帐户中(您有时需要密钥才能访问 AWS cli)。
"assume_role.role_arn" 是您要担任的帐户中的角色。需要允许 "profile" 中的 IAM 用户担任该角色。
我有一个防弹解决方案,只要您想 运行 作为特定角色(包括其他帐户)执行命令。 我假设您已经安装了 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
命令的响应并将其放入变量中。
最后 3 行将 select 来自第一个命令的值并将它们分配给 aws
cli 使用的变量。
注意事项:
如果您创建 bash 脚本,请在其中添加您的 terraform 命令。您也可以只使用上面的行创建一个 bash,然后使用 '.' 创建 运行。在前面(即:. ./get-creds.sh
)。这将在您当前的 bash shell.
角色到期,请记住角色通常有一个小时的到期时间。
您的 shell 现在将具有三个变量 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
查看您在其他帐户中的信任关系策略,应用了一个条件 多因素身份验证,在下面突出显示。因此,用户在承担该角色之前应该进行 2 因素身份验证。删除此条件并尝试 运行 代码。
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}