From 20169eec73aac39c7a76b4c3be6a5ad41c6e0731 Mon Sep 17 00:00:00 2001 From: Tim Schmelter Date: Thu, 12 Dec 2024 15:41:58 -0800 Subject: [PATCH 1/4] fix: fix policies with overridden table names --- packages/amplify-category-api/package.json | 2 +- .../model-transformer-override.test.ts.snap | 784 +++++++++++------- .../package.json | 2 +- .../dynamo-model-resource-generator.ts | 110 +-- 4 files changed, 554 insertions(+), 344 deletions(-) diff --git a/packages/amplify-category-api/package.json b/packages/amplify-category-api/package.json index 0433283603..296c7cd23f 100644 --- a/packages/amplify-category-api/package.json +++ b/packages/amplify-category-api/package.json @@ -103,7 +103,7 @@ "coverageProvider": "v8", "coverageThreshold": { "global": { - "branches": 68, + "branches": 67, "functions": 42, "lines": 40 } diff --git a/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap b/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap index 853fb0e4a9..145dbddbb0 100644 --- a/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap +++ b/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap @@ -304,6 +304,85 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, + "DynamoDBAccess71ABE5AE": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess71ABE5AE", + "Roles": Array [ + Object { + "Ref": "PostIAMRole83BF708F", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "GetPostResolver": Object { "Properties": Object { "ApiId": Object { @@ -664,79 +743,6 @@ $util.toJson({})", ], "Version": "2012-10-17", }, - "Policies": Array [ - Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess", - }, - ], "RoleName": Object { "Fn::Join": Array [ "", @@ -755,6 +761,50 @@ $util.toJson({})", }, "Type": "AWS::IAM::Role", }, + "PostIAMRoleDefaultPolicy04190CA0": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:ConditionCheckItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "PostTable", + "Arn", + ], + }, + Object { + "Ref": "AWS::NoValue", + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "PostIAMRoleDefaultPolicy04190CA0", + "Roles": Array [ + Object { + "Ref": "PostIAMRole83BF708F", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "PostTable": Object { "DeletionPolicy": "Delete", "Properties": Object { @@ -1434,79 +1484,6 @@ Object { ], "Version": "2012-10-17", }, - "Policies": Array [ - Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess", - }, - ], "RoleName": Object { "Fn::Join": Array [ "", @@ -1525,6 +1502,50 @@ Object { }, "Type": "AWS::IAM::Role", }, + "CommentIAMRoleDefaultPolicyA8D6F6B5": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:ConditionCheckItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "CommentTable", + "Arn", + ], + }, + Object { + "Ref": "AWS::NoValue", + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "CommentIAMRoleDefaultPolicyA8D6F6B5", + "Roles": Array [ + Object { + "Ref": "CommentIAMRoleD5EC5F51", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "CommentTable": Object { "DeletionPolicy": "Delete", "Properties": Object { @@ -1710,6 +1731,85 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, + "DynamoDBAccess71ABE5AE": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess71ABE5AE", + "Roles": Array [ + Object { + "Ref": "CommentIAMRoleD5EC5F51", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "GetCommentResolver": Object { "Properties": Object { "ApiId": Object { @@ -2555,6 +2655,85 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, + "DynamoDBAccess71ABE5AE": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess71ABE5AE", + "Roles": Array [ + Object { + "Ref": "PostIAMRole83BF708F", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "GetPostResolver": Object { "Properties": Object { "ApiId": Object { @@ -2915,79 +3094,6 @@ $util.toJson({})", ], "Version": "2012-10-17", }, - "Policies": Array [ - Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess", - }, - ], "RoleName": Object { "Fn::Join": Array [ "", @@ -3006,6 +3112,50 @@ $util.toJson({})", }, "Type": "AWS::IAM::Role", }, + "PostIAMRoleDefaultPolicy04190CA0": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:ConditionCheckItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "PostTable", + "Arn", + ], + }, + Object { + "Ref": "AWS::NoValue", + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "PostIAMRoleDefaultPolicy04190CA0", + "Roles": Array [ + Object { + "Ref": "PostIAMRole83BF708F", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "PostTable": Object { "DeletionPolicy": "Delete", "Properties": Object { @@ -3657,79 +3807,6 @@ Object { ], "Version": "2012-10-17", }, - "Policies": Array [ - Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess", - }, - ], "RoleName": Object { "Fn::Join": Array [ "", @@ -3748,6 +3825,50 @@ Object { }, "Type": "AWS::IAM::Role", }, + "CommentIAMRoleDefaultPolicyA8D6F6B5": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:Query", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:ConditionCheckItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:UpdateItem", + "dynamodb:DeleteItem", + "dynamodb:DescribeTable", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::GetAtt": Array [ + "CommentTable", + "Arn", + ], + }, + Object { + "Ref": "AWS::NoValue", + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "CommentIAMRoleDefaultPolicyA8D6F6B5", + "Roles": Array [ + Object { + "Ref": "CommentIAMRoleD5EC5F51", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "CommentTable": Object { "DeletionPolicy": "Delete", "Properties": Object { @@ -3925,6 +4046,85 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, + "DynamoDBAccess71ABE5AE": Object { + "Properties": Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess71ABE5AE", + "Roles": Array [ + Object { + "Ref": "CommentIAMRoleD5EC5F51", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, "GetCommentResolver": Object { "Properties": Object { "ApiId": Object { diff --git a/packages/amplify-graphql-auth-transformer/package.json b/packages/amplify-graphql-auth-transformer/package.json index a2f57819ea..93b98955f9 100644 --- a/packages/amplify-graphql-auth-transformer/package.json +++ b/packages/amplify-graphql-auth-transformer/package.json @@ -70,7 +70,7 @@ "coverageProvider": "v8", "coverageThreshold": { "global": { - "branches": 88, + "branches": 87, "functions": 90, "lines": 90 } diff --git a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts index a348ba6dee..0bb790c7dd 100644 --- a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts +++ b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts @@ -231,7 +231,19 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { } /** - * createIAMRole + * Create a role, assumable by AppSync, with a policy statement named `DynamoDBAccess`. Policy actions are scoped to the table named + * according to Amplify's default convention (`{modelName}-{apiId}-{envName}`). + * + * In most cases, this will duplicate the `...IAMRoleDefaultPolicy` that is automatically generated by the AppSync CDK's + * `addDynamoDbDataSource` API in {@link createModelTableDataSource}. We create it anyway for 3 reasons: + * + * 1. The `...IAMRoleDefaultPolicy` respects [table name + * overrides](https://docs.amplify.aws/gen1/javascript/build-a-backend/graphqlapi/modify-amplify-generated-resources/#customize-amplify-generated-resources-for-model-directive). + * Overrides are not available to us during creation of this role. + * 2. The `DynamoDBAccess` policy is exposed as an Amplify-generated resource for overrides. Existing customers may be using this and + * depending on the policy statements + * 3. The `DynamoDBAccess` policy includes statements enabling Lambda sync flows, if enabled. Note that the sync policy does not rely on + * table name, so it does not need to be aware of table name overrides. */ createIAMRole = (context: TransformerContextProvider, def: ObjectTypeDefinitionNode, scope: Construct, tableName: string): iam.IRole => { const roleName = context.resourceHelper.generateIAMRoleName(ModelResourceIDs.ModelTableIAMRoleID(def!.name.value)); @@ -239,54 +251,53 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { const role = new iam.Role(scope, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), { roleName, assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'), - // Use an inline policy here to prevent unnecessary policy resources from being generated - // and slowing down deployments. - inlinePolicies: { - DynamoDBAccess: new iam.PolicyDocument({ - statements: [ - new iam.PolicyStatement({ - effect: iam.Effect.ALLOW, - actions: [ - 'dynamodb:BatchGetItem', - 'dynamodb:BatchWriteItem', - 'dynamodb:PutItem', - 'dynamodb:DeleteItem', - 'dynamodb:GetItem', - 'dynamodb:Scan', - 'dynamodb:Query', - 'dynamodb:UpdateItem', - 'dynamodb:ConditionCheckItem', - 'dynamodb:DescribeTable', - 'dynamodb:GetRecords', - 'dynamodb:GetShardIterator', - ], - resources: [ - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { - tablename: tableName, - }), - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { - tablename: tableName, - }), - ...(context.isProjectUsingDataStore() - ? [ - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { - tablename: amplifyDataStoreTableName, - }), - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { - tablename: amplifyDataStoreTableName, - }), - ] - : []), - ], - }), - ], - }), - }, }); + + role.attachInlinePolicy( + new iam.Policy(scope, 'DynamoDBAccess', { + statements: [ + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + 'dynamodb:BatchGetItem', + 'dynamodb:BatchWriteItem', + 'dynamodb:PutItem', + 'dynamodb:DeleteItem', + 'dynamodb:GetItem', + 'dynamodb:Scan', + 'dynamodb:Query', + 'dynamodb:UpdateItem', + 'dynamodb:ConditionCheckItem', + 'dynamodb:DescribeTable', + 'dynamodb:GetRecords', + 'dynamodb:GetShardIterator', + ], + resources: [ + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { + tablename: tableName, + }), + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { + tablename: tableName, + }), + ...(context.isProjectUsingDataStore() + ? [ + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { + tablename: amplifyDataStoreTableName, + }), + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { + tablename: amplifyDataStoreTableName, + }), + ] + : []), + ], + }), + ], + }), + ); setResourceName(role, { name: ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), setOnDefaultChild: true }); const syncConfig = SyncUtils.getSyncConfig(context, def!.name.value); @@ -296,7 +307,6 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { ); } - // return an `IRole` to prevent modification and default policy generation. - return role.withoutPolicyUpdates(); + return role; }; } From 1cb4188ddbe981c02f019cc321c5ca6ed4932319 Mon Sep 17 00:00:00 2001 From: Tim Schmelter Date: Fri, 13 Dec 2024 16:00:17 -0800 Subject: [PATCH 2/4] e2e test --- .../amplify-e2e-core/src/utils/sdk-calls.ts | 30 ++++- .../api-override-ddb-table-name.test.ts | 119 ++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 packages/amplify-e2e-tests/src/__tests__/api-override-ddb-table-name.test.ts diff --git a/packages/amplify-e2e-core/src/utils/sdk-calls.ts b/packages/amplify-e2e-core/src/utils/sdk-calls.ts index 7d4c2e8e1f..3444ca430d 100644 --- a/packages/amplify-e2e-core/src/utils/sdk-calls.ts +++ b/packages/amplify-e2e-core/src/utils/sdk-calls.ts @@ -201,7 +201,35 @@ export const getAmplifyBackendJobStatus = async (jobId: string, appId: string, e .promise(); }; -export const listRolePolicies = async (roleName: string, region: string) => { +export const listRoleNamesContaining = async (searchString: string, region: string): Promise => { + const service = new IAM({ region }); + + const roles: string[] = []; + let isTruncated = true; + let marker: string | undefined; + + while (isTruncated) { + const params = marker ? { Marker: marker } : {}; + const response = await service.listRoles(params).promise(); + + const matchingRoles = response.Roles.filter((role) => role.RoleName.includes(searchString)); + roles.push(...matchingRoles.map((r) => r.RoleName)); + + isTruncated = response.IsTruncated; + marker = response.Marker; + } + + return roles; +}; + +export const getRolePolicy = async (roleName: string, policyName: string, region: string): Promise => { + const service = new IAM({ region }); + const rawDocument = (await service.getRolePolicy({ PolicyName: policyName, RoleName: roleName }).promise()).PolicyDocument; + const decodedDocument = decodeURIComponent(rawDocument); + return JSON.parse(decodedDocument); +}; + +export const listRolePolicies = async (roleName: string, region: string): Promise => { const service = new IAM({ region }); return (await service.listRolePolicies({ RoleName: roleName }).promise()).PolicyNames; }; diff --git a/packages/amplify-e2e-tests/src/__tests__/api-override-ddb-table-name.test.ts b/packages/amplify-e2e-tests/src/__tests__/api-override-ddb-table-name.test.ts new file mode 100644 index 0000000000..8884f9cfa5 --- /dev/null +++ b/packages/amplify-e2e-tests/src/__tests__/api-override-ddb-table-name.test.ts @@ -0,0 +1,119 @@ +import path from 'path'; +import * as fs from 'fs-extra'; +import { + addApiWithoutSchema, + amplifyOverrideApi, + amplifyPush, + amplifyPushOverride, + createNewProjectDir, + deleteProject, + deleteProjectDir, + getAppSyncApi, + getDDBTable, + getProjectMeta, + getRolePolicy, + initJSProjectWithProfile, + listRolePolicies, + listRoleNamesContaining, + replaceOverrideFileWithProjectInfo, + updateApiSchema, + updateSchema, +} from 'amplify-category-api-e2e-core'; + +describe('Override table name', () => { + let projRoot: string; + let projFolderName: string; + beforeEach(async () => { + projFolderName = 'overridename'; + projRoot = await createNewProjectDir(projFolderName); + }); + + afterEach(async () => { + const metaFilePath = path.join(projRoot, 'amplify', '#current-cloud-backend', 'amplify-meta.json'); + if (fs.existsSync(metaFilePath)) { + await deleteProject(projRoot); + } + deleteProjectDir(projRoot); + }); + + it('Generates correct permissions policies for DynamoDB tables with overridden names', async () => { + const now = Math.floor(Date.now() / 1000); + const modelName = `Override${now}`; + + const schema = /* GraphQL */ ` + type ${modelName} @model { + id: ID! + content: String + } + `; + + const envName = 'integtest'; + const projName = 'overridetest'; + const cliInputsFilePath = path.join(projRoot, 'amplify', 'backend', 'api', `${projName}`, 'cli-inputs.json'); + await initJSProjectWithProfile(projRoot, { name: projName, envName }); + await addApiWithoutSchema(projRoot); + + updateSchema(projRoot, projName, schema); + expect(fs.existsSync(cliInputsFilePath)).toBe(true); + + await amplifyPush(projRoot); + + await amplifyOverrideApi(projRoot, {}); + + const overriddenTableName = `OverrideTest${now}Custom`; + const overrideCode = /* TypeScript */ ` + export function override(props: any) { + props.models['${modelName}'].modelDDBTable.tableName = '${overriddenTableName}'; + } + `; + const destOverrideFilePath = path.join(projRoot, 'amplify', 'backend', 'api', `${projName}`, 'override.ts'); + fs.writeFileSync(destOverrideFilePath, overrideCode); + + await amplifyPushOverride(projRoot); + + const meta = getProjectMeta(projRoot); + const region = meta.providers.awscloudformation.Region; + const { output } = meta.api.overridetest; + const { GraphQLAPIIdOutput, GraphQLAPIEndpointOutput, GraphQLAPIKeyOutput } = output; + const { graphqlApi } = await getAppSyncApi(GraphQLAPIIdOutput, region); + + expect(graphqlApi).toBeDefined(); + expect(graphqlApi.apiId).toEqual(GraphQLAPIIdOutput); + expect(GraphQLAPIIdOutput).toBeDefined(); + expect(GraphQLAPIEndpointOutput).toBeDefined(); + expect(GraphQLAPIKeyOutput).toBeDefined(); + + const defaultTableName = `${modelName}-${graphqlApi.apiId}-${envName}`; + const error = { message: null }; + try { + const defaultTable = await getDDBTable(defaultTableName, region); + expect(defaultTable).toBeUndefined(); + } catch (ex) { + Object.assign(error, ex); + } + expect(error).toBeDefined(); + expect(error.message).toContain(`${defaultTableName} not found`); + + const actualTable = await getDDBTable(overriddenTableName, region); + expect(actualTable).toBeDefined(); + + // Validate policy. The role will be created with the prefix {modelName}IAMRole. It should have 2 policies: one created by Amplify, one + // created by AppSync's CDK call during the `addDynamoDbDataSource` flow. We expect the policy statements for the latter to refer to the + // overridden table name. + const matchingRoleNames = await listRoleNamesContaining(modelName, region); + expect(matchingRoleNames).toBeDefined(); + expect(matchingRoleNames.length).toEqual(1); + const roleName = matchingRoleNames[0]; + + const policies = await listRolePolicies(roleName, region); + expect(policies).toBeDefined(); + expect(policies.length).toBe(2); + + const defaultPolicy = policies.find((p) => p.startsWith(`${modelName}IAMRoleDefault`)); + expect(defaultPolicy).toBeDefined(); + + const policyObject = await getRolePolicy(roleName, defaultPolicy, region); + expect(policyObject).toBeDefined(); + expect(policyObject.Statement[0].Resource[0]).toContain(overriddenTableName); + }); +}); From aa286c1465881fb7bca22aa2230edf7f5c540540 Mon Sep 17 00:00:00 2001 From: Tim Schmelter Date: Mon, 16 Dec 2024 15:14:34 -0800 Subject: [PATCH 3/4] Revert 'addInlinePolicy' statement --- .../model-transformer-override.test.ts.snap | 608 +++++++++--------- .../dynamo-model-resource-generator.ts | 91 ++- 2 files changed, 337 insertions(+), 362 deletions(-) diff --git a/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap b/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap index 145dbddbb0..5bf5f98768 100644 --- a/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap +++ b/packages/amplify-category-api/src/__tests__/graphql-transformer/override/__snapshots__/model-transformer-override.test.ts.snap @@ -304,85 +304,6 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, - "DynamoDBAccess71ABE5AE": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess71ABE5AE", - "Roles": Array [ - Object { - "Ref": "PostIAMRole83BF708F", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "GetPostResolver": Object { "Properties": Object { "ApiId": Object { @@ -743,6 +664,79 @@ $util.toJson({})", ], "Version": "2012-10-17", }, + "Policies": Array [ + Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess", + }, + ], "RoleName": Object { "Fn::Join": Array [ "", @@ -1484,6 +1478,79 @@ Object { ], "Version": "2012-10-17", }, + "Policies": Array [ + Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess", + }, + ], "RoleName": Object { "Fn::Join": Array [ "", @@ -1731,85 +1798,6 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, - "DynamoDBAccess71ABE5AE": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess71ABE5AE", - "Roles": Array [ - Object { - "Ref": "CommentIAMRoleD5EC5F51", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "GetCommentResolver": Object { "Properties": Object { "ApiId": Object { @@ -2655,85 +2643,6 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, - "DynamoDBAccess71ABE5AE": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Post-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess71ABE5AE", - "Roles": Array [ - Object { - "Ref": "PostIAMRole83BF708F", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "GetPostResolver": Object { "Properties": Object { "ApiId": Object { @@ -3094,6 +3003,79 @@ $util.toJson({})", ], "Version": "2012-10-17", }, + "Policies": Array [ + Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Post-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess", + }, + ], "RoleName": Object { "Fn::Join": Array [ "", @@ -3807,6 +3789,79 @@ Object { ], "Version": "2012-10-17", }, + "Policies": Array [ + Object { + "PolicyDocument": Object { + "Statement": Array [ + Object { + "Action": Array [ + "dynamodb:BatchGetItem", + "dynamodb:BatchWriteItem", + "dynamodb:PutItem", + "dynamodb:DeleteItem", + "dynamodb:GetItem", + "dynamodb:Scan", + "dynamodb:Query", + "dynamodb:UpdateItem", + "dynamodb:ConditionCheckItem", + "dynamodb:DescribeTable", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + ], + "Effect": "Allow", + "Resource": Array [ + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + Object { + "Fn::Sub": Array [ + "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", + Object { + "tablename": Object { + "Fn::Join": Array [ + "", + Array [ + "Comment-", + Object { + "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", + }, + "-", + Object { + "Ref": "referencetotransformerrootstackenv10C5A902Ref", + }, + ], + ], + }, + }, + ], + }, + ], + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "DynamoDBAccess", + }, + ], "RoleName": Object { "Fn::Join": Array [ "", @@ -4046,85 +4101,6 @@ $util.toJson({})", }, "Type": "AWS::AppSync::Resolver", }, - "DynamoDBAccess71ABE5AE": Object { - "Properties": Object { - "PolicyDocument": Object { - "Statement": Array [ - Object { - "Action": Array [ - "dynamodb:BatchGetItem", - "dynamodb:BatchWriteItem", - "dynamodb:PutItem", - "dynamodb:DeleteItem", - "dynamodb:GetItem", - "dynamodb:Scan", - "dynamodb:Query", - "dynamodb:UpdateItem", - "dynamodb:ConditionCheckItem", - "dynamodb:DescribeTable", - "dynamodb:GetRecords", - "dynamodb:GetShardIterator", - ], - "Effect": "Allow", - "Resource": Array [ - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - Object { - "Fn::Sub": Array [ - "arn:aws:dynamodb:\${AWS::Region}:\${AWS::AccountId}:table/\${tablename}/*", - Object { - "tablename": Object { - "Fn::Join": Array [ - "", - Array [ - "Comment-", - Object { - "Ref": "referencetotransformerrootstackGraphQLAPI20497F53ApiId", - }, - "-", - Object { - "Ref": "referencetotransformerrootstackenv10C5A902Ref", - }, - ], - ], - }, - }, - ], - }, - ], - }, - ], - "Version": "2012-10-17", - }, - "PolicyName": "DynamoDBAccess71ABE5AE", - "Roles": Array [ - Object { - "Ref": "CommentIAMRoleD5EC5F51", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, "GetCommentResolver": Object { "Properties": Object { "ApiId": Object { diff --git a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts index 0bb790c7dd..0cfc4cf667 100644 --- a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts +++ b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts @@ -251,53 +251,52 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { const role = new iam.Role(scope, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), { roleName, assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'), + inlinePolicies: { + DynamoDBAccess: new iam.PolicyDocument({ + statements: [ + new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: [ + 'dynamodb:BatchGetItem', + 'dynamodb:BatchWriteItem', + 'dynamodb:PutItem', + 'dynamodb:DeleteItem', + 'dynamodb:GetItem', + 'dynamodb:Scan', + 'dynamodb:Query', + 'dynamodb:UpdateItem', + 'dynamodb:ConditionCheckItem', + 'dynamodb:DescribeTable', + 'dynamodb:GetRecords', + 'dynamodb:GetShardIterator', + ], + resources: [ + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { + tablename: tableName, + }), + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { + tablename: tableName, + }), + ...(context.isProjectUsingDataStore() + ? [ + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { + tablename: amplifyDataStoreTableName, + }), + // eslint-disable-next-line no-template-curly-in-string + cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { + tablename: amplifyDataStoreTableName, + }), + ] + : []), + ], + }), + ], + }), + }, }); - - role.attachInlinePolicy( - new iam.Policy(scope, 'DynamoDBAccess', { - statements: [ - new iam.PolicyStatement({ - effect: iam.Effect.ALLOW, - actions: [ - 'dynamodb:BatchGetItem', - 'dynamodb:BatchWriteItem', - 'dynamodb:PutItem', - 'dynamodb:DeleteItem', - 'dynamodb:GetItem', - 'dynamodb:Scan', - 'dynamodb:Query', - 'dynamodb:UpdateItem', - 'dynamodb:ConditionCheckItem', - 'dynamodb:DescribeTable', - 'dynamodb:GetRecords', - 'dynamodb:GetShardIterator', - ], - resources: [ - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { - tablename: tableName, - }), - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { - tablename: tableName, - }), - ...(context.isProjectUsingDataStore() - ? [ - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}', { - tablename: amplifyDataStoreTableName, - }), - // eslint-disable-next-line no-template-curly-in-string - cdk.Fn.sub('arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${tablename}/*', { - tablename: amplifyDataStoreTableName, - }), - ] - : []), - ], - }), - ], - }), - ); setResourceName(role, { name: ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), setOnDefaultChild: true }); const syncConfig = SyncUtils.getSyncConfig(context, def!.name.value); From 7ac809572626105eb6896f16ce465f6827eae839 Mon Sep 17 00:00:00 2001 From: Tim Schmelter Date: Mon, 16 Dec 2024 15:18:07 -0800 Subject: [PATCH 4/4] Restore comment --- .../src/resources/dynamo-model-resource-generator.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts index 0cfc4cf667..363d5b5716 100644 --- a/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts +++ b/packages/amplify-graphql-model-transformer/src/resources/dynamo-model-resource-generator.ts @@ -251,6 +251,8 @@ export class DynamoModelResourceGenerator extends ModelResourceGenerator { const role = new iam.Role(scope, ModelResourceIDs.ModelTableIAMRoleID(def!.name.value), { roleName, assumedBy: new iam.ServicePrincipal('appsync.amazonaws.com'), + // Use an inline policy here to prevent unnecessary policy CloudFormation resources from being generated. Note that CDK will still + // create a CFN resource for `IAMRoleDefaultPolicy` inlinePolicies: { DynamoDBAccess: new iam.PolicyDocument({ statements: [