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

Last updated: 2020-06-24

I want to deploy artifacts to Amazon Simple Storage Service (Amazon S3) in a different account using AWS CodePipeline with an S3 deploy action provider. I also want to set the owner of the artifacts as the target account.

Short description

The following resolution is based on an example scenario that assumes the following:

  • You have two accounts: a development (dev) account and a production (prod) account.
  • The input bucket in the dev account is called codepipeline-input-bucket (with versioning enabled).
  • The default artifact bucket in the dev account is called codepipeline-us-east-1-0123456789.
  • The output bucket in the prod account is called codepipeline-output-bucket.
  • You're deploying artifacts from the dev account to an S3 bucket in the prod account.
  • You want to assume a cross-account role created in the prod account, and then deploy the artifacts. The role sets the object owner of artifacts as the target prod account instead of the dev account.

Resolution

Create an AWS Key Management Service (AWS KMS) key to use with CodePipeline in the dev account

1.    Open the AWS KMS console in the dev 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, and then choose Next.

7.    For Alias, enter s3deploykey.

Note: Replace s3deploykey with the alias for your key.

8.    Choose Next.

9.    In the Key administrators section of the Define key administrative permissions page, select an AWS Identity and Access Management (IAM) user or role as your key administrator, and then choose Next.

10.    In the Other AWS accounts section of the Define key usage permissions page, choose Add another AWS account.

11.    In the text box that appears, add the account ID of the prod account, and then choose Next.

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

12.    Review the key policy, and then choose Finish.

Important: You must use the 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 target account.

Create a CodePipeline in the dev account

1.    Open the CodePipeline console, and then choose Create pipeline.

2.    For Pipeline name, enter crossaccountdeploy.

Note: Replace crossaccountdeploy with the name of your pipeline.

The Role name text box is auto populated with the service role name AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy. You can also choose an 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 scenario.

5.    For Encryption key, select Customer Managed Key.

6.    For KMS customer master key, select s3deploykey from the list, and then choose Next.

Important: Replace s3deploykey with the alias of your key.

7.    On the Add source stage page, for Source provider, choose Amazon S3.

8.    For Bucket, enter codepipeline-input-bucket.

Note: Replace codepipeline-input-bucket with the name of your input bucket.

Important: The input bucket must have versioning enabled 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), and then choose Next.

11.    On the Add build stage page, choose Skip build stage, and then choose Skip.

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

13.    For Region, choose US East (N. Virginia).

Important: Replace US East (N. Virginia) with the AWS Region of your output bucket.

14.    For Bucket, enter the name of the prod bucket codepipeline-output-bucket.

Note: Replace codepipeline-output-bucket with the name of the output bucket in your prod account.

15.    Select the Extract file before deploy check box.

Note: If required, enter a path for Deployment path.

16.    Choose Next.

17.    Choose Create pipeline.

Now, your pipeline is triggered, but the source stage fails. Then, you receive the following error:

The object with key 'sample-website.zip' does not exist.

The Upload the sample website to the input bucket section shows you how to resolve this error later on.

Update the KMS usage policy in the dev account

Important: Skip this section if you're using an existing CodePipeline service role and already added that role as a key user in the Create an AWS Key Management Service (AWS KMS) key to use with CodePipeline in the dev account section.

1.    Open the AWS KMS console in the dev account, and then choose s3deploykey.

Important: Replace s3deploykey with the alias of your key.

2.    In the Key users section, choose Add.

3.    In the search box, enter the service role AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy, and then choose Add.

Configure a cross-account role in the prod account

To create policies:

1.    Open the IAM console in the prod account.

2.    In the navigation pane, choose Policies, and then choose Create policy.

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

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

Note: Replace codepipeline-output-bucket with the name of the output bucket in your prod account.

4.    Choose Review policy.

5.    For Name, enter outputbucketfullaccess.

6.    Choose Create policy.

7.    To create another policy, choose Create policy.

8.    Choose the JSON tab, and then enter the following policy in the JSON editor:

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

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 your dev account.

9.    Choose Review policy.

10.    For Name, enter devkmss3access.

11.    Choose Create policy.

To create a role:

1.    Open the IAM console in the prod account.

2.    In the navigation pane, choose Roles, and then choose Create role.

3.    Choose Another AWS account.

4.    For Account ID, enter the dev account id.

5.    Choose Next: Permissions.

6.    From the list of policies, select outputbucketfullaccess 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 tab, and then choose Edit Trust relationship.

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

{
    "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": {}
        }
    ]
}

Note: Replace dev-account-id with the account ID of your dev environment and the service role for your pipeline.

14.    Choose Update Trust Policy.

Configure the CodePipeline artifact bucket and service role in the dev account

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

2.    In the Bucket name list, choose codepipeline-us-east-1-0123456789.

Note: Replace codepipeline-us-east-1-0123456789 with the name of your artifact bucket.

3.    Choose Permissions, and then choose Bucket Policy.

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

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

Important: To align with proper JSON formatting, add a comma after the existing statements.

Note: Replace prod-account-id with the account ID of your prod environment. Replace codepipeline-us-east-1-0123456789 with your artifact bucket name.

5.    Choose Save.

6.    Open the IAM console in the dev account.

7.    In the navigation pane, choose Policies, and then choose Create policy.

8.    Choose the JSON tab, and then enter the following policy in the JSON editor:

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

Note: Replace prod-account-id with the account ID of your prod environment.

9.    Choose Review policy.

10.    For Name, enter assumeprods3role.

11.    Choose Create policy.

12.    In the navigation pane, choose Roles, and then choose AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy.

Note: Replace AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy with your service role if applicable.

13.    Choose Attach Policies, and then select assumeprods3role.

14.    Choose Attach Policy.

Update the CodePipeline to use a cross-account role in the dev account

1.    To get the pipeline definition into a file called codepipeline.json, run the following command:

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

Note: Replace crossaccountdeploy with the name of your pipeline.

2.    Update the deploy section in codepipeline.json to include the roleArn. See the following example:

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

To add the roleArn, make the following updates:

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

Note: Replace the prod-account-id with the account ID of your prod environment.

3.    Remove the metadata section at the end of your codepipeline.json file. For example:

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

Important: To align with proper JSON formatting, remove the comma before the metadata section.

4.    To update the pipeline, run 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 dev account.

2.    In the Bucket name list, choose codepipeline-input-bucket.

Note: Replace codepipeline-input-bucket with the name of your input bucket.

3.    Choose Upload, and then choose Add files.

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

5.    Choose Upload.

Now, CodePipeline is triggered and the following happens:

1.    The source action selects the sample-website.zip from codepipeline-input-bucket, and then places the website as a source artifact inside the codepipeline-us-east-1-0123456789 artifact bucket.

2.    In the deploy action, the CodePipeline service role AWSCodePipelineServiceRole-us-east-1-crossaccountdeploy assumes the prods3role of the prod account.

3.    CodePipeline uses the prods3role access for the KMS key and artifact bucket in the dev account to get the artifacts, and then deploys the extracted files to the codepipeline-output-bucket in the prod account.

The extracted objects in codepipeline-output-bucket now have the prod account as the owner.


Did this article help you?

Anything we could improve?


Need more help?