From c00c18276cce9a92c6f466e50ca128959fecbbfe Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Mon, 17 Mar 2025 16:51:12 -0700 Subject: [PATCH 1/7] fix(storage): update instructions for adding/integrating with external S3 buckets --- .../storage/use-with-custom-s3/index.mdx | 223 +++++++++++++++--- 1 file changed, 195 insertions(+), 28 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 1ce67bd9ba3..030a054ba19 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -44,36 +44,41 @@ To do this, go to **Amazon S3 console** > **Select the S3 bucket** > **Permissio ![Showing Amplify console showing Storage tab selected](/images/gen2/storage/s3-console-permissions.png) -The policy will look something like this +The policy will look something like this: ```json { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "Statement1", - "Principal": { "AWS": "arn:aws:iam:::role/" }, - "Effect": "Allow", - "Action": [ - "s3:PutObject", - "s3:GetObject", - "s3:DeleteObject", - "s3:ListBucket" - ], - "Resource": [ - "arn:aws:s3:::/*" - ] - } - ] + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "Statement1", + "Principal": { "AWS": "arn:aws:iam:::role/" }, + "Effect": "Allow", + "Action": [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject", + "s3:ListBucket" + ], + "Resource": [ + "arn:aws:s3:::/", + "arn:aws:s3:::/*" + ] + } + ] } ``` Replace `` with your AWS account ID and `` with the IAM role associated with your Amplify Auth setup. Replace `` with the S3 bucket name. You can refer to [Amazon S3's Policies and Permissions documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html) for more ways to customize access to the bucket. + +In order to make calls to your S3 bucket from your application, you must also set up a CORS Policy for your S3 bucket. This applies only to manually-configured S3 buckets. Learn more about [setting up a CORS Policy for your S3 bucket](/[platform]/build-a-backend/storage/extend-s3-resources/#for-manually-configured-s3-resources). + + ### Specify S3 bucket in Amplify's backend config -Next, use the `addOutput` method from the backend definition object to define a custom s3 bucket by specifying the name and region of the bucket in your **amplify/backend.ts** file. +Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your **amplify/backend.ts** file. @@ -85,7 +90,10 @@ If you specify a custom S3 bucket, no sandbox storage resource will be created. -```javascript +Below is an example of configuring the backend to define a custom S3 bucket: + +{/* cSpell:disable */} +```ts title="amplify/backend.ts" import { defineBackend } from '@aws-amplify/backend'; import { auth } from './auth/resource'; @@ -100,12 +108,119 @@ const backend = defineBackend({ backend.addOutput({ storage: { aws_region: "", - bucket_name: "" + bucket_name: "", + // optional: `buckets` can be used when setting up more than one existing bucket + buckets: [ + { + aws_region: "", + bucket_name: "", + name: "", + // optional: `paths` can be used to set up access to specific bucket prefixes, + // and configure access to those prefixes for different user types + // @ts-expect-error: Amplify backend type issue — https://github.com/aws-amplify/amplify-backend/issues/2569 + paths: { + "public/*": { + guest: ["get", "list"], + authenticated: ["get", "list", "write", "delete"], + }, + "admin/*": { + groupsadmin: ["get", "list", "write", "delete"], + authenticated: ["get", "list", "write", "delete"], + }, + }, + } + ] }, }); -//highlight-end +// Define an inline policy to attach to Amplify's un-auth role +// This policy defines how unauthenticated users can access your existing bucket +const unauthPolicy = new Policy(backend.stack, 'customBucketUnauthPolicy', { + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['s3:GetObject'], + resources: [`arn:aws:s3:::${customBucketName}/public/*`], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ['s3:ListBucket'], + resources: [`arn:aws:s3:::${customBucketName}`], + conditions: { + StringLike: { + 's3:prefix': ['public/', 'public/*'], + }, + }, + }), + ], +}); + +// Define an inline policy to attach to Amplify's auth role +// This policy defines how authenticated users can access your existing bucket +const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + resources: [ + `arn:aws:s3:::${customBucketName}/public/*`, + `arn:aws:s3:::${customBucketName}/admin/*`, + ], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:ListBucket"], + resources: [ + `arn:aws:s3:::${customBucketName}`, + `arn:aws:s3:::${customBucketName}/*`, + ], + conditions: { + StringLike: { + "s3:prefix": ["public/*", "public/", "admin/*", "admin/"], + }, + }, + }), + ], +}); + +// Define an inline policy to attach to Admin user group role +// This policy defines how authenticated users with "admin" user group role can access your existing bucket +const adminPolicy = new Policy(backend.stack, "customBucketAdminPolicy", { + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + resources: [`arn:aws:s3:::${customBucketName}/admin/*`], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:ListBucket"], + resources: [ + `arn:aws:s3:::${customBucketName}`, + `arn:aws:s3:::${customBucketName}/*`, + ], + conditions: { + StringLike: { + "s3:prefix": ["admin/*", "admin/"], + }, + }, + }), + ], +}); + +// Add the policies to the unauthenticated user role +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( + unauthPolicy, +); + +// Add the policies to the authenticated user role +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); + +// Add the policies to the admin user group role +backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); +//highlight-end ``` +{/* cSpell:enable */} @@ -137,7 +252,7 @@ In addition to manually configuring your storage options, you will also need to ### Using Amplify configure Existing storage resource setup can be accomplished by passing the resource metadata to `Amplify.configure`. This will configure the Amplify Storage client library to interact with the additional resources. It's recommended to add the Amplify configuration step as early as possible in the application lifecycle, ideally at the root entry point. - +{/* cSpell:disable */} ```ts import { Amplify } from 'aws-amplify'; @@ -153,22 +268,40 @@ Amplify.configure({ buckets: { '': { bucketName: '', - region: '' + region: '', + // required: `paths` is needed to set up access to specific bucket prefixes, + // and configure access to those prefixes for different user types + // @ts-expect-error: Amplify backend type issue — https://github.com/aws-amplify/amplify-backend/issues/2569 + paths: { + 'public/*': { + guest: ["get", "list"], + authenticated: ["get", "list", "write", "delete"], + }, + "admin/*": { + groupsadmin: ["get", "list", "write", "delete"], + authenticated: ["get", "list", "write", "delete"], + }, + } }, '': { bucketName: '', - region: '' + region: '', + paths: { + // add more paths for the bucket + } } } } } }); - ``` +{/* cSpell:enable */} + ### Using Amplify outputs Alternatively, existing storage resources can be used by creating or modifying the `amplify_outputs.json` file directly. +{/* cSpell:disable */} ```ts title="amplify_outputs.json" { "auth": { @@ -182,15 +315,49 @@ Alternatively, existing storage resources can be used by creating or modifying t { "name": "", "bucket_name": "", - "aws_region": "" + "aws_region": "", + // required: `paths` is needed to set up access to specific bucket prefixes, + // and configure access to those prefixes for different user types + "paths": { + "public/*": { + "guest": [ + "get", + "list" + ], + "authenticated": [ + "get", + "list", + "write", + "delete" + ] + }, + "admin/*": { + "authenticated": [ + "get", + "list", + "write", + "delete" + ], + "groupsadmin": [ + "get", + "list", + "write", + "delete" + ] + } + } }, { "name": "", "bucket_name": "", - "aws_region": "" + "aws_region": "", + "paths": { + // add more paths for the bucket + } } ] } } ``` +{/* cSpell:enable */} From a0dff7f75f871c5152cc94a8680eea4015c2eb78 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Mon, 31 Mar 2025 19:10:48 -0700 Subject: [PATCH 2/7] rewrite examples, add user identity ID config, add examples for configuring storage without Amplify backend --- .../storage/use-with-custom-s3/index.mdx | 357 +++++++++++++++--- 1 file changed, 296 insertions(+), 61 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 030a054ba19..161189c1f7d 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -78,7 +78,9 @@ In order to make calls to your S3 bucket from your application, you must also se ### Specify S3 bucket in Amplify's backend config -Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your **amplify/backend.ts** file. +Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your **amplify/backend.ts** file. More options can be specified if more granular control over your custom S3 bucket is needed. + +Afterwards, set up the appropriate resources and IAM policies to be attached to the backend. @@ -90,20 +92,15 @@ If you specify a custom S3 bucket, no sandbox storage resource will be created. -Below is an example of configuring the backend to define a custom S3 bucket: +Below is an example of configuring the backend to define a custom S3 bucket where only authenticated (i.e. signed in) users have full access to a folder called `public/`: -{/* cSpell:disable */} ```ts title="amplify/backend.ts" - -import { defineBackend } from '@aws-amplify/backend'; -import { auth } from './auth/resource'; -import { data } from './data/resource'; +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; const backend = defineBackend({ auth, - data, }); - //highlight-start backend.addOutput({ storage: { @@ -116,15 +113,76 @@ backend.addOutput({ bucket_name: "", name: "", // optional: `paths` can be used to set up access to specific bucket prefixes, - // and configure access to those prefixes for different user types - // @ts-expect-error: Amplify backend type issue — https://github.com/aws-amplify/amplify-backend/issues/2569 + // and configure user access types to those prefixes paths: { "public/*": { - guest: ["get", "list"], authenticated: ["get", "list", "write", "delete"], }, - "admin/*": { - groupsadmin: ["get", "list", "write", "delete"], + }, + } + ] + }, +}); + +// Define an inline policy to attach to Amplify's auth role +// This policy defines how authenticated users can access your existing bucket +const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + resources: ["arn:aws:s3:::/public/*",], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:ListBucket"], + resources: [ + "arn:aws:s3:::", + "arn:aws:s3:::/*" + ], + conditions: { + StringLike: { + "s3:prefix": ["public/*", "public/"], + }, + }, + }), + ], +}); + +// Add the policies to the authenticated user role +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); +// highlight-end +``` + +From there, you can further configure the backend to add custom authorization rules for different user types. + + +The custom authorization rules defined in the examples below are able to be combined and follow the same rules used when working with Amplify-defined storage. For more information about the access types and access definition rules supported by Amplify, please refer to our documentation on [customizing authorization rules](/[platform]/build-a-backend/storage/authorization/). + + + + +Below is an example of expanding the original backend object to grant all guest (i.e. not signed in) users read access to files under `public/`: + +```ts title="amplify/backend.ts" + +// ... + +backend.addOutput({ + storage: { + aws_region: "", + bucket_name: "", + buckets: [ + { + aws_region: "", + bucket_name: "", + name: "", + paths: { + "public/*": { + // highlight-start + // "write" and "delete" can also be added depending on your use case + guest: ["get", "list"], + // highlight-end authenticated: ["get", "list", "write", "delete"], }, }, @@ -133,78 +191,230 @@ backend.addOutput({ }, }); +// ... Authenticated user policy and role attachment goes here ... + +// highlight-start // Define an inline policy to attach to Amplify's un-auth role -// This policy defines how unauthenticated users can access your existing bucket -const unauthPolicy = new Policy(backend.stack, 'customBucketUnauthPolicy', { +// This policy defines how un-authenticated users/guests can access your existing bucket +const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: ['s3:GetObject'], - resources: [`arn:aws:s3:::${customBucketName}/public/*`], + actions: ["s3:GetObject"], + resources: ["arn:aws:s3:::/public/*"], }), new PolicyStatement({ effect: Effect.ALLOW, - actions: ['s3:ListBucket'], - resources: [`arn:aws:s3:::${customBucketName}`], + actions: ["s3:ListBucket"], + resources: [ + "arn:aws:s3:::", + "arn:aws:s3:::/*" + ], conditions: { StringLike: { - 's3:prefix': ['public/', 'public/*'], + "s3:prefix": ["public/", "public/*"], }, }, }), ], }); -// Define an inline policy to attach to Amplify's auth role -// This policy defines how authenticated users can access your existing bucket -const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { +// Add the policies to the unauthenticated user role +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( + unauthPolicy, +); +// highlight-end +``` + + +Below is an example of expanding the original backend object to have an `admin/` folder that only users belonging to the "admin" user group can manage: +{/* cSpell:disable */} +```ts title="amplify/backend.ts" + +// ... + +backend.addOutput({ + storage: { + aws_region: "", + bucket_name: "", + buckets: [ + { + aws_region: "", + bucket_name: "", + name: "", + // @ts-expect-error: Amplify backend type issue + // https://github.com/aws-amplify/amplify-backend/issues/2569 + paths: { + "public/*": { + authenticated: ["get", "list", "write", "delete"], + }, + // highlight-start + "admin/*": { + groupsadmin: ["get", "list", "write", "delete"], + }, + // highlight-end + }, + } + ] + }, +}); + +// ... Authenticated user policy and role attachment goes here ... + +// highlight-start +// Define an inline policy to attach to "admin" user group role +// This policy defines how authenticated users with "admin" user group role can access your existing bucket +const adminPolicy = new Policy(backend.stack, "customBucketAdminPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + resources: ["arn:aws:s3:::/admin/*"], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:ListBucket"], + resources: [ + "arn:aws:s3:::", + "arn:aws:s3:::/*", + ], + conditions: { + StringLike: { + "s3:prefix": ["admin/*", "admin/"], + }, + }, + }), + ], +}); + +// Add the policies to the "admin" user group role +backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); +// highlight-end +``` +{/* cSpell:enable */} + + +While Amplify Storage uses the reserved token `entity_id` to represent the user's identity ID, that token is not available when configuring external buckets. Instead, you must use [the IAM role `${cognito-identity.amazonaws.com:sub}`](https://docs.aws.amazon.com/cognito/latest/developerguide/iam-roles.html#trust-policies-classic) to specify the user's identity ID. + +Below is an example of expanding the original backend object to define read access for guests to the `public/` folder, as well as defining a `protected/` folder where anyone can view uploaded files, but only the file owner can modify/delete them: + +{/* cSpell:disable */} +```ts title="amplify/backend.ts" + +// ... + +backend.addOutput({ + storage: { + aws_region: "", + bucket_name: "", + buckets: [ + { + aws_region: "", + bucket_name: "", + name: "", + // @ts-expect-error: Amplify backend type issue + // https://github.com/aws-amplify/amplify-backend/issues/2569 + paths: { + "public/*": { + guest: ["get", "list"], + authenticated: ["get", "list", "write", "delete"], + }, + // highlight-start + // allow guests and authenticated users to view all folders and files within `protected/` + "protected/*": { + guest: ["get", "list"], + authenticated: ["get", "list"], + }, + // allow owners full access to modify/delete their own files in their assigned subfolder + "protected/${cognito-identity.amazonaws.com:sub}/*": { + entityidentity: ["get", "list", "write", "delete"] + } + // highlight-end + }, + } + ] + }, +}); + +// highlight-start + +// Define an inline policy to attach to Amplify's un-auth role +// This policy defines how unauthenticated users/guests can access your existing bucket +const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { + statements: [ + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:GetObject"], resources: [ - `arn:aws:s3:::${customBucketName}/public/*`, - `arn:aws:s3:::${customBucketName}/admin/*`, + "arn:aws:s3:::/public/*", + "arn:aws:s3:::/protected/*" ], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - `arn:aws:s3:::${customBucketName}`, - `arn:aws:s3:::${customBucketName}/*`, + "arn:aws:s3:::", + "arn:aws:s3:::/*" ], conditions: { StringLike: { - "s3:prefix": ["public/*", "public/", "admin/*", "admin/"], + "s3:prefix": [ + "public/", + "public/*", + "protected/", + "protected/*" + ], }, }, }), ], }); -// Define an inline policy to attach to Admin user group role -// This policy defines how authenticated users with "admin" user group role can access your existing bucket -const adminPolicy = new Policy(backend.stack, "customBucketAdminPolicy", { +// Define an inline policy to attach to Amplify's auth role +// This policy defines how authenticated users can access your existing bucket +// and customizes owner access to their individual folder and any files inside +const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], - resources: [`arn:aws:s3:::${customBucketName}/admin/*`], + actions: ["s3:GetObject"], + resources: [ + "arn:aws:s3:::/public/*", + "arn:aws:s3:::/protected/*" + ], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - `arn:aws:s3:::${customBucketName}`, - `arn:aws:s3:::${customBucketName}/*`, + "arn:aws:s3:::", + "arn:aws:s3:::/*" ], conditions: { StringLike: { - "s3:prefix": ["admin/*", "admin/"], + "s3:prefix": [ + "public/", + "public/*", + "protected/", + "protected/*" + ], }, }, }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:PutObject"], + resources: [ + "arn:aws:s3:::/public/*", + "arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*" + ], + }), + new PolicyStatement({ + effect: Effect.ALLOW, + actions: ["s3:DeleteObject"], + resources: ["arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*"], + }), ], }); @@ -215,13 +425,11 @@ backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( // Add the policies to the authenticated user role backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); - -// Add the policies to the admin user group role -backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); -//highlight-end +// highlight-end ``` {/* cSpell:enable */} - + + ### Import latest amplify_outputs.json file @@ -254,7 +462,7 @@ Existing storage resource setup can be accomplished by passing the resource meta {/* cSpell:disable */} ```ts -import { Amplify } from 'aws-amplify'; +import { Amplify } from "aws-amplify"; Amplify.configure({ Auth: { @@ -262,32 +470,37 @@ Amplify.configure({ }, Storage: { S3: { - bucket: '', - region: '', + bucket: "", + region: "", // default bucket metadata should be duplicated below with any additional buckets buckets: { - '': { - bucketName: '', - region: '', - // required: `paths` is needed to set up access to specific bucket prefixes, - // and configure access to those prefixes for different user types - // @ts-expect-error: Amplify backend type issue — https://github.com/aws-amplify/amplify-backend/issues/2569 + "": { + bucketName: "", + region: "", paths: { - 'public/*': { + "public/*": { guest: ["get", "list"], authenticated: ["get", "list", "write", "delete"], + groupsadmin: ["get", "list", "write", "delete"] + }, + "protected/*": { + guest: ["get", "list"], + authenticated: ["get", "list"], + groupsadmin: ["get", "list", "write", "delete"] + } + "protected/${cognito-identity.amazonaws.com:sub}/*": { + entityidentity: ["get", "list", "write", "delete"] }, "admin/*": { groupsadmin: ["get", "list", "write", "delete"], - authenticated: ["get", "list", "write", "delete"], }, } }, - '': { - bucketName: '', - region: '', + "": { + bucketName: "", + region: "", paths: { - // add more paths for the bucket + // ... } } } @@ -316,8 +529,6 @@ Alternatively, existing storage resources can be used by creating or modifying t "name": "", "bucket_name": "", "aws_region": "", - // required: `paths` is needed to set up access to specific bucket prefixes, - // and configure access to those prefixes for different user types "paths": { "public/*": { "guest": [ @@ -329,15 +540,39 @@ Alternatively, existing storage resources can be used by creating or modifying t "list", "write", "delete" + ], + "groupsadmin": [ + "get", + "list", + "write", + "delete" ] }, - "admin/*": { + "protected/*": { + "guest": [ + "get", + "list" + ], "authenticated": [ + "get", + "list" + ], + "groupsadmin": [ "get", "list", "write", "delete" - ], + ] + }, + "protected/${cognito-identity.amazonaws.com:sub}/*": { + "entityidentity": [ + "get", + "list", + "write", + "delete" + ] + }, + "admin/*": { "groupsadmin": [ "get", "list", From 9f54e9147f2c47c838436a96a9f0266e8a62ddf7 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Tue, 1 Apr 2025 17:06:29 -0700 Subject: [PATCH 3/7] address feedback, align some wording --- .../storage/use-with-custom-s3/index.mdx | 153 +++++++++++------- 1 file changed, 98 insertions(+), 55 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 161189c1f7d..8212a5c51b5 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -52,7 +52,9 @@ The policy will look something like this: "Statement": [ { "Sid": "Statement1", - "Principal": { "AWS": "arn:aws:iam:::role/" }, + "Principal": { + "AWS": "arn:aws:iam:::role/" + }, "Effect": "Allow", "Action": [ "s3:PutObject", @@ -61,7 +63,7 @@ The policy will look something like this: "s3:ListBucket" ], "Resource": [ - "arn:aws:s3:::/", + "arn:aws:s3:::", "arn:aws:s3:::/*" ] } @@ -73,27 +75,26 @@ Replace `` with your AWS account ID and `` with the I You can refer to [Amazon S3's Policies and Permissions documentation](https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-policy-language-overview.html) for more ways to customize access to the bucket. -In order to make calls to your S3 bucket from your application, you must also set up a CORS Policy for your S3 bucket. This applies only to manually-configured S3 buckets. Learn more about [setting up a CORS Policy for your S3 bucket](/[platform]/build-a-backend/storage/extend-s3-resources/#for-manually-configured-s3-resources). +In order to make calls to your manually configured S3 bucket from your application, you must also set up a [CORS Policy](/[platform]/build-a-backend/storage/extend-s3-resources/#for-manually-configured-s3-resources) for the bucket. ### Specify S3 bucket in Amplify's backend config -Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your **amplify/backend.ts** file. More options can be specified if more granular control over your custom S3 bucket is needed. - -Afterwards, set up the appropriate resources and IAM policies to be attached to the backend. +Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your `amplify/backend.ts` file. You must also set up the appropriate resources and IAM policies to be attached to the backend. -**Important** - -You cannot use both a storage backend configured through Amplify and a custom S3 bucket at the same time. +**Important:** You cannot use both a storage backend configured through Amplify and a custom S3 bucket at the same time. If you specify a custom S3 bucket, no sandbox storage resource will be created. The provided custom S3 bucket will be used, even in the sandbox environment. -Below is an example of configuring the backend to define a custom S3 bucket where only authenticated (i.e. signed in) users have full access to a folder called `public/`: +Below are several examples of configuring the backend to define a custom S3 bucket: + + +Below is an example of expanding the original backend object to grant all authenticated (i.e. signed in) users with full access to files under `public/`: ```ts title="amplify/backend.ts" import { defineBackend } from "@aws-amplify/backend"; import { auth } from "./auth/resource"; @@ -101,7 +102,7 @@ import { auth } from "./auth/resource"; const backend = defineBackend({ auth, }); -//highlight-start +// highlight-start backend.addOutput({ storage: { aws_region: "", @@ -112,8 +113,10 @@ backend.addOutput({ aws_region: "", bucket_name: "", name: "", - // optional: `paths` can be used to set up access to specific bucket prefixes, - // and configure user access types to those prefixes + /* + optional: `paths` can be used to set up access to specific + bucket prefixes and configure user access types to them + */ paths: { "public/*": { authenticated: ["get", "list", "write", "delete"], @@ -124,13 +127,19 @@ backend.addOutput({ }, }); -// Define an inline policy to attach to Amplify's auth role -// This policy defines how authenticated users can access your existing bucket +/* + Define an inline policy to attach to Amplify's auth role + This policy defines how authenticated users can access your existing bucket +*/ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + actions: [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject" + ], resources: ["arn:aws:s3:::/public/*",], }), new PolicyStatement({ @@ -153,20 +162,17 @@ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); // highlight-end ``` - -From there, you can further configure the backend to add custom authorization rules for different user types. - - -The custom authorization rules defined in the examples below are able to be combined and follow the same rules used when working with Amplify-defined storage. For more information about the access types and access definition rules supported by Amplify, please refer to our documentation on [customizing authorization rules](/[platform]/build-a-backend/storage/authorization/). - - - + Below is an example of expanding the original backend object to grant all guest (i.e. not signed in) users read access to files under `public/`: ```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; -// ... +const backend = defineBackend({ + auth, +}); backend.addOutput({ storage: { @@ -192,10 +198,11 @@ backend.addOutput({ }); // ... Authenticated user policy and role attachment goes here ... - // highlight-start -// Define an inline policy to attach to Amplify's un-auth role -// This policy defines how un-authenticated users/guests can access your existing bucket +/* + Define an inline policy to attach to Amplify's un-auth role + This policy defines how unauthenticated/guest users can access your existing bucket +*/ const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { statements: [ new PolicyStatement({ @@ -227,11 +234,15 @@ backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( ``` -Below is an example of expanding the original backend object to have an `admin/` folder that only users belonging to the "admin" user group can manage: +Below is an example of expanding the original backend object to have an `admin/` folder that authenticated users can read, but only users belonging to the "admin" user group can manage: {/* cSpell:disable */} ```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; -// ... +const backend = defineBackend({ + auth, +}); backend.addOutput({ storage: { @@ -242,14 +253,17 @@ backend.addOutput({ aws_region: "", bucket_name: "", name: "", - // @ts-expect-error: Amplify backend type issue - // https://github.com/aws-amplify/amplify-backend/issues/2569 + /* + @ts-expect-error: Amplify backend type issue + https://github.com/aws-amplify/amplify-backend/issues/2569 + */ paths: { "public/*": { authenticated: ["get", "list", "write", "delete"], }, // highlight-start "admin/*": { + authenticated: ["get", "list"], groupsadmin: ["get", "list", "write", "delete"], }, // highlight-end @@ -260,15 +274,21 @@ backend.addOutput({ }); // ... Authenticated user policy and role attachment goes here ... - // highlight-start -// Define an inline policy to attach to "admin" user group role -// This policy defines how authenticated users with "admin" user group role can access your existing bucket +/* + Define an inline policy to attach to "admin" user group role + This policy defines how authenticated users with + "admin" user group role can access your existing bucket +*/ const adminPolicy = new Policy(backend.stack, "customBucketAdminPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], + actions: [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject" + ], resources: ["arn:aws:s3:::/admin/*"], }), new PolicyStatement({ @@ -294,14 +314,18 @@ backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); {/* cSpell:enable */} -While Amplify Storage uses the reserved token `entity_id` to represent the user's identity ID, that token is not available when configuring external buckets. Instead, you must use [the IAM role `${cognito-identity.amazonaws.com:sub}`](https://docs.aws.amazon.com/cognito/latest/developerguide/iam-roles.html#trust-policies-classic) to specify the user's identity ID. +Amplify allows scoping file access to individual users via the user's identity ID. To specify the user's identity ID, you can use the token `${cognito-identity.amazonaws.com:sub}`. Below is an example of expanding the original backend object to define read access for guests to the `public/` folder, as well as defining a `protected/` folder where anyone can view uploaded files, but only the file owner can modify/delete them: {/* cSpell:disable */} ```ts title="amplify/backend.ts" +import { defineBackend } from "@aws-amplify/backend"; +import { auth } from "./auth/resource"; -// ... +const backend = defineBackend({ + auth, +}); backend.addOutput({ storage: { @@ -312,20 +336,22 @@ backend.addOutput({ aws_region: "", bucket_name: "", name: "", - // @ts-expect-error: Amplify backend type issue - // https://github.com/aws-amplify/amplify-backend/issues/2569 + /* + @ts-expect-error: Amplify backend type issue + https://github.com/aws-amplify/amplify-backend/issues/2569 + */ paths: { "public/*": { guest: ["get", "list"], authenticated: ["get", "list", "write", "delete"], }, // highlight-start - // allow guests and authenticated users to view all folders and files within `protected/` + // allow all users to view all folders/files within `protected/` "protected/*": { guest: ["get", "list"], authenticated: ["get", "list"], }, - // allow owners full access to modify/delete their own files in their assigned subfolder + // allow owners to get/modify/delete their own files in assigned subfolder "protected/${cognito-identity.amazonaws.com:sub}/*": { entityidentity: ["get", "list", "write", "delete"] } @@ -338,8 +364,11 @@ backend.addOutput({ // highlight-start -// Define an inline policy to attach to Amplify's un-auth role -// This policy defines how unauthenticated users/guests can access your existing bucket +/* + Define an inline policy to attach to Amplify's un-auth role + This policy defines how unauthenticated users/guests + can access your existing bucket +*/ const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { statements: [ new PolicyStatement({ @@ -371,9 +400,11 @@ const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { ], }); -// Define an inline policy to attach to Amplify's auth role -// This policy defines how authenticated users can access your existing bucket -// and customizes owner access to their individual folder and any files inside +/* + Define an inline policy to attach to Amplify's auth role + This policy defines how authenticated users can access your + existing bucket and customizes owner access to their individual folder +*/ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { statements: [ new PolicyStatement({ @@ -413,7 +444,9 @@ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:DeleteObject"], - resources: ["arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*"], + resources: [ + "arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*" + ], }), ], }); @@ -430,11 +463,16 @@ backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); {/* cSpell:enable */} + + +The custom authorization rules defined in the examples are able to be combined, and follow the same rules used when working with Amplify-defined storage. For more information about the access types and access definition rules supported by Amplify, please refer to our documentation on [customizing authorization rules](/[platform]/build-a-backend/storage/authorization/). + + -### Import latest amplify_outputs.json file +### Import latest `amplify_outputs.json` file -To ensure the local **amplify_outputs.json** file is up-to-date, you can run [the npx ampx generate outputs command](/[platform]/reference/cli-commands/#npx-ampx-generate-outputs) or download the latest **amplify_outputs.json** from the Amplify console as shown below. +To ensure the local `amplify_outputs.json` file is up-to-date, you can run [the `npx ampx generate outputs` command](/[platform]/reference/cli-commands/#npx-ampx-generate-outputs) or download the latest `amplify_outputs.json` from the Amplify console as shown below. ![](/images/gen2/getting-started/react/amplify-outputs-download.png) @@ -442,9 +480,9 @@ To ensure the local **amplify_outputs.json** file is up-to-date, you can run [th -### Import latest amplify_outputs.dart file +### Import latest `amplify_outputs.dart` file -To ensure the local **amplify_outputs.dart** file is up-to-date, you can run [the npx ampx generate outputs command](/[platform]/reference/cli-commands/#npx-ampx-generate-outputs). +To ensure the local `amplify_outputs.dart` file is up-to-date, you can run [the `npx ampx generate outputs` command](/[platform]/reference/cli-commands/#npx-ampx-generate-outputs). @@ -453,11 +491,11 @@ Now that you've configured the necessary permissions, you can start using the st ## Use storage resources without an Amplify backend -While using the Amplify Backend is the easiest way to get started, existing storage resources can also be integrated with Amplify Storage. +While using the Amplify backend is the easiest way to get started, existing storage resources can also be integrated with Amplify Storage. In addition to manually configuring your storage options, you will also need to ensure Amplify Auth is properly configured in your project and associated IAM roles have the necessary permissions to interact with your existing bucket. Read more about [using existing auth resources without an Amplify backend](/[platform]/build-a-backend/auth/use-existing-cognito-resources/#use-auth-resources-without-an-amplify-backend). -### Using Amplify configure +### Using `Amplify.configure` Existing storage resource setup can be accomplished by passing the resource metadata to `Amplify.configure`. This will configure the Amplify Storage client library to interact with the additional resources. It's recommended to add the Amplify configuration step as early as possible in the application lifecycle, ideally at the root entry point. {/* cSpell:disable */} @@ -492,6 +530,7 @@ Amplify.configure({ entityidentity: ["get", "list", "write", "delete"] }, "admin/*": { + authenticated: ["get", "list"], groupsadmin: ["get", "list", "write", "delete"], }, } @@ -510,7 +549,7 @@ Amplify.configure({ ``` {/* cSpell:enable */} -### Using Amplify outputs +### Using `amplify_outputs.json` Alternatively, existing storage resources can be used by creating or modifying the `amplify_outputs.json` file directly. @@ -573,6 +612,10 @@ Alternatively, existing storage resources can be used by creating or modifying t ] }, "admin/*": { + "authenticated": [ + "get", + "list" + ], "groupsadmin": [ "get", "list", From 19bfed9ed5fb4cd13f86331946bcd6b63f1987b7 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Wed, 2 Apr 2025 22:35:52 -0700 Subject: [PATCH 4/7] reorder guest and auth'd user examples, update backend code examples to use CDK bucket construct --- .../storage/use-with-custom-s3/index.mdx | 198 +++++++++++------- 1 file changed, 118 insertions(+), 80 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 8212a5c51b5..8611ee31998 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -93,33 +93,45 @@ If you specify a custom S3 bucket, no sandbox storage resource will be created. Below are several examples of configuring the backend to define a custom S3 bucket: - -Below is an example of expanding the original backend object to grant all authenticated (i.e. signed in) users with full access to files under `public/`: + +Below is an example of expanding the original backend object to grant all guest (i.e. not signed in) users read access to files under `public/`: + ```ts title="amplify/backend.ts" -import { defineBackend } from "@aws-amplify/backend"; +import { defineBackend } from '@aws-amplify/backend'; +import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; import { auth } from "./auth/resource"; const backend = defineBackend({ auth, }); // highlight-start +const customBucketStack = backend.createStack("custom-bucket-stack"); + +// Import existing bucket +const customBucket = Bucket.fromBucketAttributes(bucketStack, "MyCustomBucket", { + bucketArn: "arn:aws:s3:::", + region: "" +}); + backend.addOutput({ storage: { - aws_region: "", - bucket_name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, // optional: `buckets` can be used when setting up more than one existing bucket buckets: [ { - aws_region: "", - bucket_name: "", - name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, + name: customBucket.bucketName, /* optional: `paths` can be used to set up access to specific bucket prefixes and configure user access types to them */ paths: { "public/*": { - authenticated: ["get", "list", "write", "delete"], + // "write" and "delete" can also be added depending on your use case + guest: ["get", "list"], }, }, } @@ -128,68 +140,74 @@ backend.addOutput({ }); /* - Define an inline policy to attach to Amplify's auth role - This policy defines how authenticated users can access your existing bucket + Define an inline policy to attach to Amplify's unauth role + This policy defines how unauthenticated/guest users can access your existing bucket */ -const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { +const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: [ - "s3:GetObject", - "s3:PutObject", - "s3:DeleteObject" - ], - resources: ["arn:aws:s3:::/public/*",], + actions: ["s3:GetObject"], + resources: [`${customBucket.bucketArn}/public/*`], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - "arn:aws:s3:::", - "arn:aws:s3:::/*" - ], + `${customBucket.bucketArn}`, + `${customBucket.bucketArn}/*` + ], conditions: { StringLike: { - "s3:prefix": ["public/*", "public/"], + "s3:prefix": ["public/", "public/*"], }, }, }), ], }); -// Add the policies to the authenticated user role -backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); +// Add the policies to the unauthenticated user role +backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( + unauthPolicy, +); // highlight-end ``` - -Below is an example of expanding the original backend object to grant all guest (i.e. not signed in) users read access to files under `public/`: - + +Below is an example of expanding the original backend object to grant all authenticated (i.e. signed in) users with full access to files under `public/`: ```ts title="amplify/backend.ts" -import { defineBackend } from "@aws-amplify/backend"; +import { defineBackend } from '@aws-amplify/backend'; +import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; import { auth } from "./auth/resource"; const backend = defineBackend({ auth, }); +const customBucketStack = backend.createStack("custom-bucket-stack"); + +// Import existing bucket +const customBucket = Bucket.fromBucketAttributes(bucketStack, "MyCustomBucket", { + bucketArn: "arn:aws:s3:::", + region: "" +}); + backend.addOutput({ storage: { - aws_region: "", - bucket_name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, buckets: [ { - aws_region: "", - bucket_name: "", - name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, + name: customBucket.bucketName, paths: { "public/*": { + guest: ["get", "list"], // highlight-start - // "write" and "delete" can also be added depending on your use case - guest: ["get", "list"], - // highlight-end authenticated: ["get", "list", "write", "delete"], + // highlight-end }, }, } @@ -197,39 +215,41 @@ backend.addOutput({ }, }); -// ... Authenticated user policy and role attachment goes here ... +// ... Unauthenticated/guest user policies and role attachments go here ... // highlight-start /* - Define an inline policy to attach to Amplify's un-auth role - This policy defines how unauthenticated/guest users can access your existing bucket + Define an inline policy to attach to Amplify's auth role + This policy defines how authenticated users can access your existing bucket */ -const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { +const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { statements: [ new PolicyStatement({ effect: Effect.ALLOW, - actions: ["s3:GetObject"], - resources: ["arn:aws:s3:::/public/*"], + actions: [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject" + ], + resources: [`${customBucket.bucketArn}/public/*`,], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - "arn:aws:s3:::", - "arn:aws:s3:::/*" - ], + `${customBucket.bucketArn}`, + `${customBucket.bucketArn}/*` + ], conditions: { StringLike: { - "s3:prefix": ["public/", "public/*"], + "s3:prefix": ["public/*", "public/"], }, }, }), ], }); -// Add the policies to the unauthenticated user role -backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( - unauthPolicy, -); +// Add the policies to the authenticated user role +backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); // highlight-end ``` @@ -237,22 +257,32 @@ backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( Below is an example of expanding the original backend object to have an `admin/` folder that authenticated users can read, but only users belonging to the "admin" user group can manage: {/* cSpell:disable */} ```ts title="amplify/backend.ts" -import { defineBackend } from "@aws-amplify/backend"; -import { auth } from "./auth/resource"; +import { defineBackend } from '@aws-amplify/backend'; +import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { auth } from './auth/resource'; const backend = defineBackend({ auth, }); +const customBucketStack = backend.createStack("custom-bucket-stack"); + +// Import existing bucket +const customBucket = Bucket.fromBucketAttributes(bucketStack, "MyCustomBucket", { + bucketArn: "arn:aws:s3:::", + region: "" +}); + backend.addOutput({ storage: { - aws_region: "", - bucket_name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, buckets: [ { - aws_region: "", - bucket_name: "", - name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, + name: customBucket.bucketName, /* @ts-expect-error: Amplify backend type issue https://github.com/aws-amplify/amplify-backend/issues/2569 @@ -289,14 +319,14 @@ const adminPolicy = new Policy(backend.stack, "customBucketAdminPolicy", { "s3:PutObject", "s3:DeleteObject" ], - resources: ["arn:aws:s3:::/admin/*"], + resources: [ `${customBucket.bucketArn}/admin/*`], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - "arn:aws:s3:::", - "arn:aws:s3:::/*", + `${customBucket.bucketArn}` + `${customBucket.bucketArn}/*` ], conditions: { StringLike: { @@ -320,22 +350,32 @@ Below is an example of expanding the original backend object to define read acce {/* cSpell:disable */} ```ts title="amplify/backend.ts" -import { defineBackend } from "@aws-amplify/backend"; +import { defineBackend } from '@aws-amplify/backend'; +import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; +import { Bucket } from 'aws-cdk-lib/aws-s3'; import { auth } from "./auth/resource"; const backend = defineBackend({ auth, }); +const customBucketStack = backend.createStack("custom-bucket-stack"); + +// Import existing bucket +const customBucket = s3.Bucket.fromBucketAttributes(bucketStack, "MyCustomBucket", { + bucketArn: "arn:aws:s3:::", + region: "" +}); + backend.addOutput({ storage: { - aws_region: "", - bucket_name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, buckets: [ { - aws_region: "", - bucket_name: "", - name: "", + aws_region: customBucket.env.region, + bucket_name: customBucket.bucketName, + name: customBucket.bucketName, /* @ts-expect-error: Amplify backend type issue https://github.com/aws-amplify/amplify-backend/issues/2569 @@ -361,11 +401,9 @@ backend.addOutput({ ] }, }); - // highlight-start - /* - Define an inline policy to attach to Amplify's un-auth role + Define an inline policy to attach to Amplify's unauth role This policy defines how unauthenticated users/guests can access your existing bucket */ @@ -375,16 +413,16 @@ const unauthPolicy = new Policy(backend.stack, "customBucketUnauthPolicy", { effect: Effect.ALLOW, actions: ["s3:GetObject"], resources: [ - "arn:aws:s3:::/public/*", - "arn:aws:s3:::/protected/*" + `${customBucket.bucketArn}/public/*` + `${customBucket.bucketArn}/protected/*` ], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - "arn:aws:s3:::", - "arn:aws:s3:::/*" + `${customBucket.bucketArn}` + `${customBucket.bucketArn}/*` ], conditions: { StringLike: { @@ -411,16 +449,16 @@ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { effect: Effect.ALLOW, actions: ["s3:GetObject"], resources: [ - "arn:aws:s3:::/public/*", - "arn:aws:s3:::/protected/*" + `${customBucket.bucketArn}/public/*` + `${customBucket.bucketArn}/protected/*` ], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:ListBucket"], resources: [ - "arn:aws:s3:::", - "arn:aws:s3:::/*" + `${customBucket.bucketArn}` + `${customBucket.bucketArn}/*` ], conditions: { StringLike: { @@ -437,15 +475,15 @@ const authPolicy = new Policy(backend.stack, "customBucketAuthPolicy", { effect: Effect.ALLOW, actions: ["s3:PutObject"], resources: [ - "arn:aws:s3:::/public/*", - "arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*" + `${customBucket.bucketArn}/public/*` + `${customBucket.bucketArn}/protected/${cognito-identity.amazonaws.com:sub}/*` ], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ["s3:DeleteObject"], resources: [ - "arn:aws:s3:::/protected/${cognito-identity.amazonaws.com:sub}/*" + `${customBucket.bucketArn}/protected/${cognito-identity.amazonaws.com:sub}/*` ], }), ], From 730e80d61f589ee33735ad3d66e71da6459d6bc2 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Fri, 4 Apr 2025 17:53:42 -0700 Subject: [PATCH 5/7] address feedback --- .../storage/use-with-custom-s3/index.mdx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 8611ee31998..39c8f235b88 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -83,11 +83,7 @@ In order to make calls to your manually configured S3 bucket from your applicati Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your `amplify/backend.ts` file. You must also set up the appropriate resources and IAM policies to be attached to the backend. - -**Important:** You cannot use both a storage backend configured through Amplify and a custom S3 bucket at the same time. - -If you specify a custom S3 bucket, no sandbox storage resource will be created. The provided custom S3 bucket will be used, even in the sandbox environment. - +**Important:** You can use a storage backend configured through Amplify and a custom S3 bucket at the same time using this method. However, the Amplify-configured storage will be used as the default bucket, and automatically provide its name and region to `addOutput`. Below are several examples of configuring the backend to define a custom S3 bucket: @@ -254,7 +250,7 @@ backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); ``` -Below is an example of expanding the original backend object to have an `admin/` folder that authenticated users can read, but only users belonging to the "admin" user group can manage: +Below is an example of expanding the original backend object with user group permissions. Here, any authenticated users can read from `admin/` and `public/` and authenticated users belonging to the "admin" user group can only manage `admin/`: {/* cSpell:disable */} ```ts title="amplify/backend.ts" import { defineBackend } from '@aws-amplify/backend'; @@ -344,8 +340,9 @@ backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); {/* cSpell:enable */} + Amplify allows scoping file access to individual users via the user's identity ID. To specify the user's identity ID, you can use the token `${cognito-identity.amazonaws.com:sub}`. - + Below is an example of expanding the original backend object to define read access for guests to the `public/` folder, as well as defining a `protected/` folder where anyone can view uploaded files, but only the file owner can modify/delete them: {/* cSpell:disable */} @@ -391,7 +388,7 @@ backend.addOutput({ guest: ["get", "list"], authenticated: ["get", "list"], }, - // allow owners to get/modify/delete their own files in assigned subfolder + // allow owners to read, write and delete their own files in assigned subfolder "protected/${cognito-identity.amazonaws.com:sub}/*": { entityidentity: ["get", "list", "write", "delete"] } @@ -503,7 +500,7 @@ backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); -The custom authorization rules defined in the examples are able to be combined, and follow the same rules used when working with Amplify-defined storage. For more information about the access types and access definition rules supported by Amplify, please refer to our documentation on [customizing authorization rules](/[platform]/build-a-backend/storage/authorization/). +The custom authorization rules defined in the examples can be combined, and follow the same rules as Amplify-defined storage. Please refer to our documentation on [customizing authorization rules](/[platform]/build-a-backend/storage/authorization/) for more information. From 517e3e8d399125cb41d7a1486a569e931dc5ca15 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Mon, 7 Apr 2025 14:26:06 -0700 Subject: [PATCH 6/7] update callout for adding existing bucket and amplify storage together, revert callout on owners example section --- .../build-a-backend/storage/use-with-custom-s3/index.mdx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 39c8f235b88..563676ac1bc 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -83,7 +83,7 @@ In order to make calls to your manually configured S3 bucket from your applicati Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your `amplify/backend.ts` file. You must also set up the appropriate resources and IAM policies to be attached to the backend. -**Important:** You can use a storage backend configured through Amplify and a custom S3 bucket at the same time using this method. However, the Amplify-configured storage will be used as the default bucket, and automatically provide its name and region to `addOutput`. +**Important:** You can use a storage backend configured through Amplify and a custom S3 bucket at the same time using this method. However, the Amplify-configured storage will be used as the **default bucket** and the custom S3 bucket will only be used as an additional bucket. Below are several examples of configuring the backend to define a custom S3 bucket: @@ -340,9 +340,8 @@ backend.auth.resources.groups["admin"].role.attachInlinePolicy(adminPolicy); {/* cSpell:enable */} - Amplify allows scoping file access to individual users via the user's identity ID. To specify the user's identity ID, you can use the token `${cognito-identity.amazonaws.com:sub}`. - + Below is an example of expanding the original backend object to define read access for guests to the `public/` folder, as well as defining a `protected/` folder where anyone can view uploaded files, but only the file owner can modify/delete them: {/* cSpell:disable */} From 0f915692b94709739303617489945500130dec23 Mon Sep 17 00:00:00 2001 From: Tiffany Yeung Date: Mon, 7 Apr 2025 16:11:31 -0700 Subject: [PATCH 7/7] adjust headings, add heading before examples for better linking --- .../storage/use-with-custom-s3/index.mdx | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx index 563676ac1bc..285bd4854e5 100644 --- a/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/use-with-custom-s3/index.mdx @@ -78,7 +78,7 @@ You can refer to [Amazon S3's Policies and Permissions documentation](https://do In order to make calls to your manually configured S3 bucket from your application, you must also set up a [CORS Policy](/[platform]/build-a-backend/storage/extend-s3-resources/#for-manually-configured-s3-resources) for the bucket. -### Specify S3 bucket in Amplify's backend config +### Specify the S3 bucket in Amplify's backend config Next, use the `addOutput` method from the backend definition object to define a custom S3 bucket by specifying the name and region of the bucket in your `amplify/backend.ts` file. You must also set up the appropriate resources and IAM policies to be attached to the backend. @@ -86,6 +86,8 @@ Next, use the `addOutput` method from the backend definition object to define a **Important:** You can use a storage backend configured through Amplify and a custom S3 bucket at the same time using this method. However, the Amplify-configured storage will be used as the **default bucket** and the custom S3 bucket will only be used as an additional bucket. +#### Configure the S3 bucket + Below are several examples of configuring the backend to define a custom S3 bucket: @@ -93,9 +95,9 @@ Below are several examples of configuring the backend to define a custom S3 buck Below is an example of expanding the original backend object to grant all guest (i.e. not signed in) users read access to files under `public/`: ```ts title="amplify/backend.ts" -import { defineBackend } from '@aws-amplify/backend'; -import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { defineBackend } from "@aws-amplify/backend"; +import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Bucket } from "aws-cdk-lib/aws-s3"; import { auth } from "./auth/resource"; const backend = defineBackend({ @@ -172,9 +174,9 @@ backend.auth.resources.unauthenticatedUserIamRole.attachInlinePolicy( Below is an example of expanding the original backend object to grant all authenticated (i.e. signed in) users with full access to files under `public/`: ```ts title="amplify/backend.ts" -import { defineBackend } from '@aws-amplify/backend'; -import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { defineBackend } from "@aws-amplify/backend"; +import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Bucket } from "aws-cdk-lib/aws-s3"; import { auth } from "./auth/resource"; const backend = defineBackend({ @@ -253,10 +255,10 @@ backend.auth.resources.authenticatedUserIamRole.attachInlinePolicy(authPolicy); Below is an example of expanding the original backend object with user group permissions. Here, any authenticated users can read from `admin/` and `public/` and authenticated users belonging to the "admin" user group can only manage `admin/`: {/* cSpell:disable */} ```ts title="amplify/backend.ts" -import { defineBackend } from '@aws-amplify/backend'; -import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { Bucket } from 'aws-cdk-lib/aws-s3'; -import { auth } from './auth/resource'; +import { defineBackend } from "@aws-amplify/backend"; +import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Bucket } from "aws-cdk-lib/aws-s3"; +import { auth } from "./auth/resource"; const backend = defineBackend({ auth, @@ -346,9 +348,9 @@ Below is an example of expanding the original backend object to define read acce {/* cSpell:disable */} ```ts title="amplify/backend.ts" -import { defineBackend } from '@aws-amplify/backend'; -import { Effect, Policy, PolicyStatement } from 'aws-cdk-lib/aws-iam'; -import { Bucket } from 'aws-cdk-lib/aws-s3'; +import { defineBackend } from "@aws-amplify/backend"; +import { Effect, Policy, PolicyStatement } from "aws-cdk-lib/aws-iam"; +import { Bucket } from "aws-cdk-lib/aws-s3"; import { auth } from "./auth/resource"; const backend = defineBackend({