AWS S3 Authentication
Which option to choose
- Assume Role (recommended): you create an IAM role in your account that trusts the WCH identity. WCH requests temporary one-hour credentials for each publish. Nothing long-lived is stored in WCH, and you can cut access by editing the role.
- Resource Policy: you attach a bucket policy that names the WCH identity. No role, no keys, and no secrets change hands. WCH shows you the exact policy to paste. Quickest to set up, and the best choice for very large sites, because there is no one-hour session that can expire mid-upload.
- Access Key: you create an IAM user and paste its access key and secret into WCH. Simplest to understand, but the key is a long-lived secret stored in WCH. Use it only when the other two are not an option.
What every option has in common
The WCH identity
WCH uploads with one AWS identity. Assume Role and Resource Policy both grant that identity access to your bucket, so you never receive credentials from WCH.
- WCH AWS Account ID:
985542660797 - WCH IAM user ARN:
arn:aws:iam::985542660797:user/wes-assume-role
You can copy the live principal ARN from inside WCH: on the AWS destination form, select Resource Policy and expand AWS setup instructions. If WCH support has set your account up with a dedicated identity, use the ARN they gave you instead.
The permissions WCH needs
WCH uploads one zip per publish using an S3 multipart upload. It never reads, lists, or deletes objects. These four actions, on objects inside the bucket, are all it needs:
s3:PutObject
s3:PutObjectTagging
s3:AbortMultipartUpload
s3:ListMultipartUploadParts
All four are object-level, so the resource must end in /*, for example arn:aws:s3:::your-bucket-name/*. A bare bucket ARN matches nothing. To restrict WCH to one folder, narrow the resource to arn:aws:s3:::your-bucket-name/releases/* and put that folder in the File Name field in WCH.
How the upload is written
Objects are written with the STANDARD_IA storage class and SSE-S3 (AES256) server-side encryption, with no ACL header. A bucket policy that requires SSE-KMS, denies STANDARD_IA, or adds an s3:x-amz-acl condition will block every upload.
Option 1: Assume Role
Step 1: Create the role
- Sign in to the AWS Console for the account that owns your S3 bucket.
- Go to IAM, then Roles, then Create role.
- For Trusted entity type, choose Custom trust policy and paste the trust policy from Step 2.
- On the next screen, attach the permissions policy from Step 3.
- Name the role, for example
wch-s3-upload, and create it. - Copy the finished role's ARN. It looks like
arn:aws:iam::123456789012:role/wch-s3-upload, where 123456789012 is your own account ID.
Step 2: Set the trust policy
The trust policy tells your role who may assume it. It must name the WCH identity, not your own account.
Recommended, with an External ID. An External ID is a shared secret that stops your role being assumed on someone else's behalf. Choose any hard-to-guess value, such as a UUID, and enter the same value in WCH.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::985542660797:user/wes-assume-role"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "your-chosen-external-id"
}
}
}
]
}
The External ID is optional. If you leave it out, remove the whole Condition block.
Use an External ID whenever you can. Without it, any project that could get the WCH identity to assume your role ARN could reach your bucket. The External ID makes sure the role is only assumed on your behalf.
Step 3: Attach the permissions policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectTagging",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
Step 4: Fill in the destination in WCH
- Authentication Type: Assume Role.
- Role ARN: the ARN from Step 1.
- External ID (Optional): the exact value from your trust policy, or blank if you did not use one.
What happens when you publish
- WCH builds and zips your project.
- WCH assumes your role with the WCH identity, passing the External ID if one is set.
- AWS returns temporary credentials that expire after one hour.
- WCH uploads the zip to your bucket in the region and under the file name you chose.
Option 2: Resource Policy
With a resource policy, the permission lives on your bucket instead of on a role. WCH authenticates as its own identity, and your bucket policy says that identity may upload.
- In WCH, add the AWS destination, set Authentication Type to Resource Policy, and enter your Region and Bucket Name. The policy below updates to match the bucket name you type.
- Expand AWS setup instructions. Under 1. WES principal, click Copy to copy the principal ARN. Under 2. Bucket policy, click Copy JSON.
- In the AWS Console, open S3, open your bucket, go to Permissions, then Bucket policy, then Edit. Paste the JSON and save. If the bucket already has a policy, add the new statement to its Statement list instead of replacing the policy.
- Back in WCH, click Test connection. WCH starts and immediately aborts a multipart upload to prove the policy works, without leaving a file behind. Success shows "Successfully authenticated to s3://your-bucket".
- Save the destination.
The generated policy looks like this:
{
"Version": "2012-10-17",
"Id": "WESDeployAccess",
"Statement": [
{
"Sid": "AllowWESDeployUpload",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::985542660797:user/wes-assume-role"
},
"Action": [
"s3:PutObject",
"s3:PutObjectTagging",
"s3:AbortMultipartUpload",
"s3:ListMultipartUploadParts"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
Optional but recommended: add a lifecycle rule on the bucket that aborts incomplete multipart uploads after 7 days. It needs no permission from WCH and cleans up parts stranded by a failed upload.
Policies that look right but deny WCH
Do not add an
s3:x-amz-aclcondition. WCH sends no ACL header, so a condition such as"s3:x-amz-acl": "bucket-owner-full-control"fails and denies every upload. This snippet appears in older AWS cross-account examples. Leave it out. On buckets with ACLs disabled, the default since April 2023, you already own every object WCH writes.
- Resource Control Policies: if your organization applies RCPs, they can deny external principals regardless of the bucket policy. Add an exception for AWS account
985542660797. - Encryption conditions: WCH requests SSE-S3 (AES256). A policy that denies anything other than
aws:kmsblocks the upload before any data transfers. Contact WCH support if SSE-KMS is required. - Conditions that lock WCH out:
aws:SourceArn,aws:SourceVpc,aws:SourceVpce, andaws:PrincipalOrgIDare absent from a direct IAM user request, so any condition requiring them denies WCH. Conditions onaws:SecureTransportands3:x-amz-storage-classare safe.
Option 3: Access Key
- In IAM, open Users and click Create user. Name it, for example
wch-uploader. - Choose Attach policies directly, then Create policy, and paste the permissions policy from Option 1, Step 3, with your bucket name.
- Attach the policy and finish creating the user.
- Open the user, go to Security credentials, and click Create access key.
- Save the Access Key ID and Secret Access Key. The secret is shown only once.
In WCH, set Authentication Type to Access Key and fill in Access Key ID and Secret Access Key.
Rotate the key if it is ever exposed. An access key never expires on its own. If you rotate it in IAM, update the destination in WCH before the next publish, or the upload will fail with an access denied error.
Fields shared by every option
Whichever Authentication Type you pick, the rest of the AWS destination form is the same:
- Region: the AWS region of your bucket, for example
us-east-1. - Bucket Name: your S3 bucket name, with no
s3://prefix. - File Name: the object name to write, by default
project.zip. Include a folder if you want, for examplereleases/project.zip. Tick Use project name to name the file after your project. - Is Production?: turn on for your production target. A project can have one production AWS destination.
Test connection is available for every option. Use it after saving credentials and again after any change to your bucket policy or role.
Troubleshooting
- Access denied when assuming the role: the trust policy does not name
arn:aws:iam::985542660797:user/wes-assume-role, or the External ID in WCH does not exactly match the trust policy. - Authenticated, but the upload fails: the permissions policy or bucket policy does not allow
s3:PutObjecton the object path, the resource does not end in/*, or a condition blocks the STANDARD_IA storage class or AES256 encryption. - Wrong region error: the Region in WCH does not match where the bucket lives.
- File never appears: check that the File Name and any folder prefix in your policy line up with where you are looking.
- Upload fails partway on a very large site: the one-hour Assume Role session expired mid-upload. Switch the destination to Resource Policy.
- Test connection fails with a policy that looks correct: check the list of conditions above, and any Resource Control Policy or Service Control Policy applied to your account.
Revoking WCH's access
- Assume Role: remove the WCH principal from the trust policy, change the External ID, or delete the role.
- Resource Policy: remove the WCH statement from the bucket policy.
- Access Key: deactivate or delete the access key in IAM.
In every case you can also delete the destination in WCH.