5
votes

I am trying to create a CloudFormation Stack using the AWS CLI by running the following command:

aws cloudformation create-stack --debug --stack-name ${stackName} --template-url ${s3TemplatePath} --parameters '${parameters}' --region eu-west-1

The template resides in an S3 bucket in the another account, lets call this account 456. The bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Example permissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "arn:aws:iam::123:root"
                ]
            },
            "Action": [
                "s3:*"
            ],
            "Resource": "arn:aws:s3:::cloudformation.template.eberry.digital/*"
        }
    ]
}

("Action: * " is for debugging).

Now for a twist. I am logged into account 456 and I run

aws sts assume-role --role-arn arn:aws:iam::123:role/delegate-access-to-infrastructure-account-role --role-session-name jenkins

and the set the correct environment variables to access 123. The policy attached to the role that I assume allow the user Administrator access while I debug - which still doesn't work.

aws s3api list-buckets

then display the buckets in account 123.

To summarize:

  • Specifying a template in an S3 bucket owned by account 456, into CloufFormation in the console, while logged into account 123 works.
  • Specifying a template in an S3 bucket owned by account 123, using the CLI, works.
  • Specifying a template in an S3 bucket owned by account 456, using the CLI, doesn't work.

The error:

An error occurred (ValidationError) when calling the CreateStack operation: S3 error: Access Denied For more information check http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html

I don't understand what I am doing wrong and would by thankful for any ideas. In the meantime I will upload the template to all accounts that will use it.

1
After calling aws sts assume-role ... and setting environment. Can you call aws sts get-caller-identity to prove that you really using temp permissions?Michał Zaborowski
I was not aware of that command, thanks for the tip. I'm not sure it proves I'm using temp permissions? They do change however. Before the switch the identity returned is in account 456, after the switch the identity account is 123.Jonatan
if that changed, then you did it right. You can play around with aws s3 ls / aws s3 ls bucket_name - maybe that way something pop up...Michał Zaborowski
I used aws s3api head --bucket --key from the and was able to get the details of the file from the CLI. Still, referencing it in --template-url doesn't work.Jonatan
Sure, aws s3 ls is more convenient from command line. Well it complains about S3 permissions, and you clearly provided admin access. Looks like the bug. Please report a bug - forums.aws.amazon.com - looked there, but no luck...Michał Zaborowski

1 Answers

8
votes

Amazon S3 provides cross-account access through the use of bucket policies. These are IAM resource policies (which are applied to resources—in this case an S3 bucket—rather than IAM principals: users, groups, or roles). You can read more about how Amazon S3 authorises access in the Amazon S3 Developer Guide.

I was a little confused about which account is which, so instead I'll just say that you need this bucket policy when you want to deploy a template in a bucket owned by one AWS account as a stack in a different AWS account. For example, the template is in a bucket owned by AWS account 111111111111 and you want to use that template to deploy a stack in AWS account 222222222222. In this case, you'll need to be logged in to account 222222222222 and specify that account as the principal in the bucket policy.

The following is an example bucket policy that provides access to another AWS account; I use this on my own CloudFormation templates bucket.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::AWS_ACCOUNT_ID_WITHOUT_HYPHENS:root"
      },
      "Action": [
        "s3:GetBucketLocation",
        "s3:GetObject",
        "s3:GetObjectTagging",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::S3_BUCKET_NAME",
        "arn:aws:s3:::S3_BUCKET_NAME/*"
      ]
    }
  ]
}

You'll need to use the 12-digit account identifier for the AWS account you want to provide access to, and the name of the S3 bucket (you can probably use "Resource": "*", but I haven't tested this).