web-dev-qa-db-fra.com

Exécuter Terraform appliquer avec AWS assumer le rôle

J'ai besoin d'exécuter un modèle Terraform pour provisionner l'infrastructure d'un compte AWS auquel je peux accéder en assumant un rôle.

Le problème que j'ai maintenant est que je n'ai pas d'utilisateur IAM dans ce compte AWS, donc je n'ai pas de aws_access_key_id ou un aws_secret_access_key pour configurer un autre profil nommé dans mon ~/.aws/credentials. Lorsque j'exécute la commande terraform apply, le modèle crée l'infrastructure pour mon compte, pas pour l'autre compte.

Comment exécuter le modèle Terraform en utilisant votre compte qui a un rôle pour accéder aux services d'un autre compte AWS?

Voici mon fichier 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}"
            }
        }
    }
}

Mettre à jour:

Après la réponse de Darren (cela a beaucoup de sens) ci-dessous, j'ai ajouté:

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>"
  }
}

Cependant, j'ai rencontré cette erreur:

  • provider.aws: le rôle "arn: aws: iam ::: role /" ne peut pas être utilisé.

    Il existe un certain nombre de causes possibles à cela - les plus courantes sont:

    • Les informations d'identification utilisées pour assumer le rôle ne sont pas valides
    • Les informations d'identification n'ont pas l'autorisation appropriée pour assumer le rôle
    • Le rôle ARN n'est pas valide

J'ai vérifié le rôle dans l'autre compte et je peux passer à ce rôle à l'aide de la console AWS à partir de mon compte. J'ai également vérifié le guide AWS ici

Donc: ce rôle ARN est valide, j'ai des informations d'identification pour assumer le rôle et toutes les autorisations dont j'ai besoin pour exécuter la pile.

Mettre à jour

J'ai également essayé avec un nouveau rôle qui a tous accès aux services . Cependant, j'ai rencontré cette erreur:

Erreur: Erreur d'actualisation: 2 erreur (s) s'est produite:

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

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

"InvalidInputException: ARN de projet non valide: l'ID de compte ne correspond pas au compte de l'appelant\n\tstatus code: 400, id de demande: ..." * aws_s3_bucket.build_artifact_bucket: 1 erreur (s) s'est produite:

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

configuration: AccessDenied: Code d'état d'accès refusé: 403, ID de demande: ..., ID d'hôte: ...

=====

MISE À JOUR 29 avril 2019:

Suite à la suggestion de @ Rolando, j'ai ajouté cette politique à l'utilisateur du COMPTE PRINCIPAL que j'essaie d'utiliser pour assumer le rôle de l'AUTRE COMPTE où je prévois d'exécuter terraform apply.

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

C'est le Trust Relationship du rôle admin appartient au AUTRE COMPTE:

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

Cependant, lorsque j'ai exécuté cette commande:

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

J'ai cette erreur:

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

J'ai une solution pare-balles à chaque fois que vous souhaitez exécuter des commandes en tant que rôle spécifique (y compris d'autres comptes). Je suppose que les outils de l'AWS CLI sont installés. Vous devrez également installer jq (outil simple pour analyser et extraire des données de json), bien que vous puissiez analyser les données comme vous le souhaitez.

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 '"')

La première ligne affecte la réponse du aws sts et la met dans une variable. Les 3 dernières lignes sélectionneront les valeurs de la première commande et les affecteront aux variables utilisées par le aws cli.

Considérations:

Si vous créez un script bash, ajoutez-y également vos commandes terraform. Vous pouvez également simplement créer un bash avec les lignes ci-dessus et l'exécuter avec un '.' devant (ie: . ./get-creds.sh). Cela créera les variables sur votre shell bash actuel.

Le rôle expire, gardez à l'esprit que les rôles expirent généralement une heure.

Votre Shell aura maintenant les trois variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN. Cela signifie qu'il remplacera votre ~/.aws/credentials. La chose la plus simple à faire pour effacer cela est de simplement démarrer une nouvelle session bash.

J'ai utilisé cet article comme source pour comprendre cela: https://docs.aws.Amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html

5
Rolando Cintron

En regardant votre politique de relation de confiance dans l'autre compte, une condition est appliquée authentification multifacteur ci-dessous mise en évidence. L'utilisateur doit donc 2 facteurs authentifiés avant d'assumer le rôle. Supprimez cette condition et essayez d'exécuter du code.

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

Vous devriez pouvoir le faire comme ceci: dans Terraform, configurez le fournisseur aws pour utiliser votre fichier shared_credentials_file local

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

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

"profil" est un profil nommé dans ~/.aws/credentials qui possède des clés d'accès AWS. Par exemple.

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

Ce n'est pas un utilisateur IAM dans le compte auquel vous souhaitez accéder. C'est dans le compte "source" (vous avez besoin de clés à un moment donné pour accéder à l'AWS cli).

"assume_role.role_arn" est le rôle dans le compte que vous souhaitez assumer. L'utilisateur IAM dans "profil" doit être autorisé à assumer ce rôle.

2
Darren O'Brien

De manière générale, vous devrez bootstrap le compte cible. Cela signifie au minimum créer un rôle qui est supposé à partir du rôle de pipeline, mais qui pourrait inclure d'autres ressources.

0
Aaron