How do I deploy artifacts to Amazon S3 in a different AWS account using CodePipeline?

Last updated: 2022-02-14

I want to deploy artifacts to an Amazon Simple Storage Service (Amazon S3) bucket in a different account. I also want to set the destination account as the object owner. Is there a way to do that using AWS CodePipeline with an Amazon S3 deploy action provider?

Resolution

Note: The following example procedure assumes the following:

  • You have two accounts: a development account and a production account.
  • The input bucket in the development account is named codepipeline-input-bucket (with versioning activated).
  • The default artifact bucket in the development account is named codepipeline-us-east-1-0123456789.
  • The output bucket in the production account is named codepipeline-output-bucket.
  • You're deploying artifacts from the development account to an S3 bucket in the production account.
  • You're assuming a cross-account role created in the production account to deploy the artifacts. The role makes the production account the object owner instead of the development account. To provide the bucket owner in the production account with access to the objects owned by the development account, see the following article: How do I deploy artifacts to Amazon S3 in a different AWS account using CodePipeline and a canned ACL?

Create an AWS KMS key to use with CodePipeline in the development account

Important: You must use the AWS Key Management Service (AWS KMS) customer managed key for cross-account deployments. If the key isn't configured, then CodePipeline encrypts the objects with default encryption, which can't be decrypted by the role in the destination account.

1.    Open the AWS KMS console in the development account.

2.    In the navigation pane, choose Customer managed keys.

3.    Choose Create Key.

4.    For Key type, choose Symmetric Key.

5.    Expand Advanced Options.

6.    For Key material origin, choose KMS. Then, choose Next.

7.    For Alias, enter your key's alias. For example: s3deploykey.

8.    Choose Next. The Define key administrative permissions page opens.

9.    In the Key administrators section, select an AWS Identity and Access Management (IAM) user or role as your key administrator.

10. Choose Next. The Define key usage permissions page opens.

11.    In the Other AWS accounts section, choose Add another AWS account.

12.    In the text box that appears, add the account ID of the production account. Then, choose Next.

Note: You can also select an existing service role in the This Account section. If you select an existing service role, skip the steps in the Update the KMS usage policy in the development account section.

13.    Review the key policy. Then, choose Finish.

Create a CodePipeline in the development account

1.    Open the CodePipeline console. Then, choose Create pipeline.

2.    For Pipeline name, enter a name for your pipeline. For example: crossaccountdeploy.

Note: The Role name text box is populated automatically with the service role name AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy. You can also choose another, existing service role with access to the KMS key.

3.    Expand the Advanced settings section.

4.    For Artifact store, select Default location.
Note: You can select Custom location if that's required for your use case.

5.    For Encryption key, select Customer Managed Key.

6.    For KMS customer managed key, select your key's alias from the list (s3deploykey, for this example).Then, choose Next. The Add source stage page opens.

7. For Source provider, choose Amazon S3.

8.    For Bucket, enter the name of your development input S3 bucket. For example: codepipeline-input-bucket.

Important: The input bucket must have versioning activated to work with CodePipeline.

9.    For S3 object key, enter sample-website.zip.

Important: To use a sample AWS website instead of your own website, see Tutorial: Create a pipeline that uses Amazon S3 as a deployment provider. Then, search for "sample static website" in the Prerequisites of the 1: Deploy Static Website Files to Amazon S3 section.

10.    For Change detection options, choose Amazon CloudWatch Events (recommended). Then, choose Next.

11.    On the Add build stage page, choose Skip build stage. Then, choose Skip.

12.    On the Add deploy stage page, for Deploy provider, choose Amazon S3.

13.    For Region, choose the AWS Region that your production output S3 bucket is in. For example: US East (N. Virginia).

Important: If the production output bucket's Region is different than your pipeline's Region, then you must also verify the following:

14.    For Bucket, enter the name of your production output S3 bucket. For example: codepipeline-output-bucket.

15.    Select the Extract file before deploy check box.
Note: If needed, enter a path for Deployment path.

16.    Choose Next.

17.    Choose Create pipeline. The pipeline runs, but the source stage fails. The following error appears: "The object with key 'sample-website.zip' does not exist."

The Upload the sample website to the input bucket section of this article describes how to resolve this error.

Update the KMS usage policy in the development account

Important: Skip this section if you're using an existing CodePipeline service role.

1.    Open the AWS KMS console in the development account.

2.    Select your key's alias (s3deploykey, for this example).

3.    In the Key users section, choose Add.

4.    In the search box, enter the service role AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy.

5.    Choose Add.

Configure a cross-account role in the production account

Create an IAM policy for the role that grants Amazon S3 permissions to your production output S3 bucket

1.    Open the IAM console in the production account.

2.    In the navigation pane, choose Policies. Then, choose Create policy.

3.    Choose the JSON tab. Then, enter the following policy in the JSON editor:

Important: Replace codepipeline-output-bucket with your production output S3 bucket's name.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:Put*"
      ],
      "Resource": [
        "arn:aws:s3:::codepipeline-output-bucket/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::codepipeline-output-bucket"
      ]
    }
  ]
}

4.    Choose Review policy.

5.    For Name, enter a name for the policy. For example: outputbucketdeployaccess.

6.    Choose Create policy.

Create an IAM policy for the role that grants the required KMS permissions

1.    In the IAM console, choose Create policy.

2.    Choose the JSON tab. Then, enter the following policy in the JSON editor:

Note: Replace the ARN of the KMS key that you created. Replace codepipeline-us-east-1-0123456789 with the name of the artifact bucket in the development account.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "kms:DescribeKey",
        "kms:GenerateDataKey*",
        "kms:Encrypt",
        "kms:ReEncrypt*",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:kms:us-east-1:<dev-account-id>:key/<key id>"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:Get*"
      ],
      "Resource": [
        "arn:aws:s3:::codepipeline-us-east-1-0123456789/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::codepipeline-us-east-1-0123456789"
      ]
    }
  ]
}

3.    Choose Review policy.

4.    For Name, enter a name for the policy. For example: devkmss3access.

5.    Choose Create policy.

Create a cross-account role that the development account can assume to deploy the artifacts

1.    Open the IAM console in the production account.

2.    In the navigation pane, choose Roles. Then, choose Create role.

3.    Choose Another AWS account.

4.    For Account ID, enter the development account's AWS account ID.

5.    Choose Next: Permissions.

6.    From the list of policies, select outputbucketdeployaccess and devkmss3access.

7.    Choose Next: Tags.

8.    (Optional) Add tags, and then choose Next: Review.

9.    For Role name, enter prods3role.

10.    Choose Create role.

11.    From the list of roles, choose prods3role.

12.    Choose the Trust relationship. Then, choose Edit Trust relationship.

13.    In the Policy Document editor, enter the following policy:

Important: Replace dev-account-id with your development account's AWS account ID. Replace AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy with the name of the service role for your pipeline.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<dev-account-id>:role/service-role/AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy"
        ]
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}

14.    Choose Update Trust Policy.

Update the bucket policy for the CodePipeline artifact bucket in the development account

1.    Open the Amazon S3 console in the development account.

2.    In the Bucket name list, choose the name of your artifact bucket in your development account (for this example, codepipeline-us-east-1-0123456789).

3.    Choose Permissions. Then, choose Bucket Policy.

4.    In the text editor, update your existing policy to include the following policy statements:

Important: To align with proper JSON formatting, add a comma after the existing statements. Replace prod-account-id with your production account's AWS account ID. Replace codepipeline-us-east-1-0123456789 with your artifact bucket's name.

{
    "Sid": "",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::<prod-account-id>:root"
    },
    "Action": [
        "s3:Get*",
        "s3:Put*"
    ],
    "Resource": "arn:aws:s3:::codepipeline-us-east-1-0123456789/*"
},
{
    "Sid": "",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::<prod-account-id>:root"
    },
    "Action": "s3:ListBucket",
    "Resource": "arn:aws:s3:::codepipeline-us-east-1-0123456789"
}

5.    Choose Save.

Attach a policy to your CodePipeline service role in the development account that allows it to assume the cross-account role that you created

1.    Open the IAM console in the development account.

2.    In the navigation pane, choose Policies. Then, choose Create policy.

3.    Choose the JSON tab. Then, enter the following policy in the JSON editor:

Important: Replace prod-account-id with your production account's AWS account ID.

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": [
      "arn:aws:iam::<prod-account-id>:role/prods3role"
    ]
  }
}

4.    Choose Review policy.

5.    For Name, enter assumeprods3role.

6.    Choose Create policy.

7.    In the navigation pane, choose Roles. Then, choose the name of the service role for your pipeline (for this example, AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy).

8.    Choose Attach Policies. Then, select assumeprods3role.

9.    Choose Attach Policy.

Update your pipeline to use the cross-account role in the development account

Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you're using the most recent AWS CLI version.

1.    Retrieve the pipeline definition as a file named codepipeline.json by running the following AWS CLI command:

Important: Replace crossaccountdeploy with your pipeline's name.

aws codepipeline get-pipeline --name crossaccountdeploy > codepipeline.json

2.    Add the cross-account IAM role ARN (roleArn) to the deploy action section of the codepipeline.json file. For more information, see the CodePipeline pipeline structure reference in the CodePipeline User Guide.

Example cross-account IAM roleArn

"roleArn": "arn:aws:iam::your-prod-account id:role/prods3role",

Example deploy action that includes a cross-account IAM role ARN

Important: Replace the prod-account-id with your production account's AWS account ID.

{
  "name": "Deploy",
  "actions": [
    {
      "name": "Deploy",
      "actionTypeId": {
        "category": "Deploy",
        "owner": "AWS",
        "provider": "S3",
        "version": "1"
      },
      "runOrder": 1,
      "configuration": {
        "BucketName": "codepipeline-output-bucket",
        "Extract": "true"
      },
      "outputArtifacts": [],
      "inputArtifacts": [
        {
          "name": "SourceArtifact"
        }
      ],
      "roleArn": "arn:aws:iam::<prod-account-id>:role/prods3role",
      "region": "us-east-1",
      "namespace": "DeployVariables"
    }
  ]
}

3.    Remove the metadata section at the end of the codepipeline.json file.

Important: Make sure that you also remove the comma that's before the metadata section.

Example metadata section

"metadata": {
    "pipelineArn": "arn:aws:codepipeline:us-east-1:<dev-account-id>:crossaccountdeploy",
    "created": 1587527378.629,
    "updated": 1587534327.983
}

4.    Update the pipeline by running the following command:

aws codepipeline update-pipeline --cli-input-json file://codepipeline.json

Upload the sample website to the input bucket

1.    Open the Amazon S3 console in the development account.

2.    In the Bucket name list, choose your development input S3 bucket. For example: codepipeline-input-bucket.

3.    Choose Upload. Then, choose Add files.

4.    Select the sample-website.zip file that you downloaded earlier.

5.    Choose Upload to run the pipeline. When the pipeline runs, the following occurs:

  • The source action selects the sample-website.zip from the development input S3 bucket (codepipeline-input-bucket). Then, the source action places the zip file as a source artifact inside the artifact bucket in the development account ( codepipeline-us-east-1-0123456789).
  • In the deploy action, the CodePipeline service role (AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy) assumes the cross-account role (prods3role) in the production account.
  • CodePipeline uses the cross account role (prods3role) to access the KMS key and artifact bucket in the development account. Then, CodePipeline deploys the extracted files to the production output S3 bucket (codepipeline-output-bucket) in the production account.

Note: The production account is the owner of the extracted objects in the production output S3 bucket (codepipeline-output-bucket).


Did this article help?


Do you need billing or technical support?