diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md
index 2422fbe40a5fe..32d44938c8eed 100644
--- a/packages/@aws-cdk/aws-appsync/README.md
+++ b/packages/@aws-cdk/aws-appsync/README.md
@@ -334,6 +334,29 @@ new route53.CnameRecord(this, `CnameApiRecord`, {
 });
 ```
 
+## Log Group
+
+AppSync automatically create a log group with the name `/aws/appsync/apis/<graphql_api_id>` upon deployment with
+log data set to never expire. If you want to set a different expiration period, use the `logConfig.retention` property.
+
+To obtain the GraphQL API's log group as a `logs.ILogGroup` use the `logGroup` property of the
+`GraphqlApi` construct.
+
+```ts
+import * as logs from '@aws-cdk/aws-logs';
+
+const logConfig: appsync.LogConfig = {
+  retention: logs.RetentionDays.ONE_WEEK,
+};
+
+new appsync.GraphqlApi(this, 'api', {
+  authorizationConfig: {},
+  name: 'myApi',
+  schema: appsync.Schema.fromAsset(path.join(__dirname, 'myApi.graphql')),
+  logConfig,
+});
+```
+
 ## Schema
 
 Every GraphQL Api needs a schema to define the Api. CDK offers `appsync.Schema`
@@ -427,7 +450,7 @@ new appsync.GraphqlApi(this, 'api', {
     defaultAuthorization: {
       authorizationType: appsync.AuthorizationType.LAMBDA,
       lambdaAuthorizerConfig: {
-        handler: authFunction, 
+        handler: authFunction,
         // can also specify `resultsCacheTtl` and `validationRegex`.
       },
     },
diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
index ab740aac4cfb2..d384713310581 100644
--- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
+++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts
@@ -2,6 +2,7 @@ import { ICertificate } from '@aws-cdk/aws-certificatemanager';
 import { IUserPool } from '@aws-cdk/aws-cognito';
 import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam';
 import { IFunction } from '@aws-cdk/aws-lambda';
+import { ILogGroup, LogGroup, LogRetention, RetentionDays } from '@aws-cdk/aws-logs';
 import { ArnFormat, CfnResource, Duration, Expiration, IResolvable, Stack } from '@aws-cdk/core';
 import { Construct } from 'constructs';
 import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema, CfnDomainName, CfnDomainNameApiAssociation } from './appsync.generated';
@@ -248,6 +249,16 @@ export interface LogConfig {
    * @default - None
    */
   readonly role?: IRole;
+
+  /**
+  * The number of days log events are kept in CloudWatch Logs.
+  * By default AppSync keeps the logs infinitely. When updating this property,
+  * unsetting it doesn't remove the log retention policy.
+  * To remove the retention policy, set the value to `INFINITE`
+  *
+  * @default RetentionDays.INFINITE
+  */
+  readonly retention?: RetentionDays
 }
 
 /**
@@ -459,6 +470,11 @@ export class GraphqlApi extends GraphqlApiBase {
    */
   public readonly apiKey?: string;
 
+  /**
+   * the CloudWatch Log Group for this API
+   */
+  public readonly logGroup: ILogGroup;
+
   private schemaResource: CfnGraphQLSchema;
   private api: CfnGraphQLApi;
   private apiKeyResource?: CfnApiKey;
@@ -527,6 +543,16 @@ export class GraphqlApi extends GraphqlApiBase {
       });
     }
 
+    const logGroupName = `/aws/appsync/apis/${this.apiId}`;
+
+    this.logGroup = LogGroup.fromLogGroupName(this, 'LogGroup', logGroupName);
+
+    if (props.logConfig?.retention) {
+      new LogRetention(this, 'LogRetention', {
+        logGroupName: this.logGroup.logGroupName,
+        retention: props.logConfig.retention,
+      });
+    };
   }
 
   /**
@@ -620,10 +646,11 @@ export class GraphqlApi extends GraphqlApiBase {
         ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSAppSyncPushToCloudWatchLogs'),
       ],
     }).roleArn;
+    const fieldLogLevel: FieldLogLevel = config.fieldLogLevel ?? FieldLogLevel.NONE;
     return {
       cloudWatchLogsRoleArn: logsRoleArn,
       excludeVerboseContent: config.excludeVerboseContent,
-      fieldLogLevel: config.fieldLogLevel,
+      fieldLogLevel: fieldLogLevel,
     };
   }
 
diff --git a/packages/@aws-cdk/aws-appsync/package.json b/packages/@aws-cdk/aws-appsync/package.json
index 480e781af2bbc..b0e9b8c1320d8 100644
--- a/packages/@aws-cdk/aws-appsync/package.json
+++ b/packages/@aws-cdk/aws-appsync/package.json
@@ -84,6 +84,7 @@
     "@aws-cdk/aws-stepfunctions": "0.0.0",
     "@aws-cdk/cdk-build-tools": "0.0.0",
     "@aws-cdk/integ-runner": "0.0.0",
+    "@aws-cdk/integ-tests": "0.0.0",
     "@aws-cdk/cfn2ts": "0.0.0",
     "@aws-cdk/pkglint": "0.0.0",
     "@types/jest": "^27.5.2",
@@ -97,6 +98,7 @@
     "@aws-cdk/aws-elasticsearch": "0.0.0",
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-lambda": "0.0.0",
+    "@aws-cdk/aws-logs": "0.0.0",
     "@aws-cdk/aws-opensearchservice": "0.0.0",
     "@aws-cdk/aws-rds": "0.0.0",
     "@aws-cdk/aws-s3-assets": "0.0.0",
@@ -113,6 +115,7 @@
     "@aws-cdk/aws-elasticsearch": "0.0.0",
     "@aws-cdk/aws-iam": "0.0.0",
     "@aws-cdk/aws-lambda": "0.0.0",
+    "@aws-cdk/aws-logs": "0.0.0",
     "@aws-cdk/aws-opensearchservice": "0.0.0",
     "@aws-cdk/aws-rds": "0.0.0",
     "@aws-cdk/aws-s3-assets": "0.0.0",
diff --git a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts
index 350fbff6229db..1ed2a95b6aeae 100644
--- a/packages/@aws-cdk/aws-appsync/test/appsync.test.ts
+++ b/packages/@aws-cdk/aws-appsync/test/appsync.test.ts
@@ -2,6 +2,7 @@ import * as path from 'path';
 import { Template } from '@aws-cdk/assertions';
 import { Certificate } from '@aws-cdk/aws-certificatemanager';
 import * as iam from '@aws-cdk/aws-iam';
+import * as logs from '@aws-cdk/aws-logs';
 import * as cdk from '@aws-cdk/core';
 import * as appsync from '../lib';
 
@@ -191,3 +192,49 @@ test('appsync GraphqlApi should be configured with custom domain when specified'
     DomainName: domainName,
   });
 });
+
+test('log retention should be configured with given retention time when specified', () => {
+  // GIVEN
+  const retentionTime = logs.RetentionDays.ONE_WEEK;
+
+  // WHEN
+  new appsync.GraphqlApi(stack, 'log-retention', {
+    authorizationConfig: {},
+    name: 'log-retention',
+    schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')),
+    logConfig: {
+      retention: retentionTime,
+    },
+  });
+
+  // THEN
+  Template.fromStack(stack).hasResourceProperties('Custom::LogRetention', {
+    LogGroupName: {
+      'Fn::Join': [
+        '',
+        [
+          '/aws/appsync/apis/',
+          {
+            'Fn::GetAtt': [
+              'logretentionB69DFB48',
+              'ApiId',
+            ],
+          },
+        ],
+      ],
+    },
+    RetentionInDays: 7,
+  });
+});
+
+test('log retention should not appear when no retention time is specified', () => {
+  // WHEN
+  new appsync.GraphqlApi(stack, 'no-log-retention', {
+    authorizationConfig: {},
+    name: 'no-log-retention',
+    schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')),
+  });
+
+  // THEN
+  Template.fromStack(stack).resourceCountIs('Custom::LogRetention', 0);
+});
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts b/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts
new file mode 100644
index 0000000000000..664c28ceee237
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/integ.log-retention.ts
@@ -0,0 +1,40 @@
+import { join } from 'path';
+import { RetentionDays } from '@aws-cdk/aws-logs';
+import { App, Stack } from '@aws-cdk/core';
+import { ExpectedResult, IntegTest } from '@aws-cdk/integ-tests';
+import { GraphqlApi, LogConfig, Schema } from '../lib';
+
+const app = new App();
+const stack = new Stack(app, 'AppSyncIntegLogRetention');
+
+
+const retentionTime = RetentionDays.ONE_WEEK;
+const logConfig: LogConfig = {
+  retention: retentionTime,
+};
+
+const api = new GraphqlApi(stack, 'GraphqlApi', {
+  authorizationConfig: {},
+  name: 'IntegLogRetention',
+  schema: Schema.fromAsset(join(__dirname, 'appsync.test.graphql')),
+  logConfig,
+});
+
+const integ = new IntegTest(app, 'Integ', { testCases: [stack] });
+
+const describe = integ.assertions.awsApiCall('CloudWatchLogs',
+  'describeLogGroups',
+  {
+    logGroupNamePrefix: api.logGroup.logGroupName,
+  });
+
+describe.expect(ExpectedResult.objectLike({
+  logGroups: [
+    {
+      logGroupName: api.logGroup.logGroupName,
+      retentionInDays: retentionTime,
+    },
+  ],
+}));
+
+app.synth();
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/AppSyncIntegLogRetention.template.json b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/AppSyncIntegLogRetention.template.json
new file mode 100644
index 0000000000000..1dc3cc6f7eaa2
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/AppSyncIntegLogRetention.template.json
@@ -0,0 +1,240 @@
+{
+ "Resources": {
+  "GraphqlApiApiLogsRoleBB9E6BAD": {
+   "Type": "AWS::IAM::Role",
+   "Properties": {
+    "AssumeRolePolicyDocument": {
+     "Statement": [
+      {
+       "Action": "sts:AssumeRole",
+       "Effect": "Allow",
+       "Principal": {
+        "Service": "appsync.amazonaws.com"
+       }
+      }
+     ],
+     "Version": "2012-10-17"
+    },
+    "ManagedPolicyArns": [
+     {
+      "Fn::Join": [
+       "",
+       [
+        "arn:",
+        {
+         "Ref": "AWS::Partition"
+        },
+        ":iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs"
+       ]
+      ]
+     }
+    ]
+   }
+  },
+  "GraphqlApi1B6CF24C": {
+   "Type": "AWS::AppSync::GraphQLApi",
+   "Properties": {
+    "AuthenticationType": "API_KEY",
+    "Name": "IntegLogRetention",
+    "LogConfig": {
+     "CloudWatchLogsRoleArn": {
+      "Fn::GetAtt": [
+       "GraphqlApiApiLogsRoleBB9E6BAD",
+       "Arn"
+      ]
+     },
+     "FieldLogLevel": "NONE"
+    }
+   }
+  },
+  "GraphqlApiSchema1B71CE4F": {
+   "Type": "AWS::AppSync::GraphQLSchema",
+   "Properties": {
+    "ApiId": {
+     "Fn::GetAtt": [
+      "GraphqlApi1B6CF24C",
+      "ApiId"
+     ]
+    },
+    "Definition": "type test {\n  version: String!\n}\ntype Query {\n  getTests: [test]!\n}\ntype Mutation {\n  addTest(version: String!): test\n}\n"
+   }
+  },
+  "GraphqlApiDefaultApiKey47EE7128": {
+   "Type": "AWS::AppSync::ApiKey",
+   "Properties": {
+    "ApiId": {
+     "Fn::GetAtt": [
+      "GraphqlApi1B6CF24C",
+      "ApiId"
+     ]
+    }
+   },
+   "DependsOn": [
+    "GraphqlApiSchema1B71CE4F"
+   ]
+  },
+  "GraphqlApiLogRetention082A7017": {
+   "Type": "Custom::LogRetention",
+   "Properties": {
+    "ServiceToken": {
+     "Fn::GetAtt": [
+      "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A",
+      "Arn"
+     ]
+    },
+    "LogGroupName": {
+     "Fn::Join": [
+      "",
+      [
+       "/aws/appsync/apis/",
+       {
+        "Fn::GetAtt": [
+         "GraphqlApi1B6CF24C",
+         "ApiId"
+        ]
+       }
+      ]
+     ]
+    },
+    "RetentionInDays": 7
+   }
+  },
+  "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB": {
+   "Type": "AWS::IAM::Role",
+   "Properties": {
+    "AssumeRolePolicyDocument": {
+     "Statement": [
+      {
+       "Action": "sts:AssumeRole",
+       "Effect": "Allow",
+       "Principal": {
+        "Service": "lambda.amazonaws.com"
+       }
+      }
+     ],
+     "Version": "2012-10-17"
+    },
+    "ManagedPolicyArns": [
+     {
+      "Fn::Join": [
+       "",
+       [
+        "arn:",
+        {
+         "Ref": "AWS::Partition"
+        },
+        ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+       ]
+      ]
+     }
+    ]
+   }
+  },
+  "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB": {
+   "Type": "AWS::IAM::Policy",
+   "Properties": {
+    "PolicyDocument": {
+     "Statement": [
+      {
+       "Action": [
+        "logs:DeleteRetentionPolicy",
+        "logs:PutRetentionPolicy"
+       ],
+       "Effect": "Allow",
+       "Resource": "*"
+      }
+     ],
+     "Version": "2012-10-17"
+    },
+    "PolicyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
+    "Roles": [
+     {
+      "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB"
+     }
+    ]
+   }
+  },
+  "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A": {
+   "Type": "AWS::Lambda::Function",
+   "Properties": {
+    "Handler": "index.handler",
+    "Runtime": "nodejs14.x",
+    "Code": {
+     "S3Bucket": {
+      "Ref": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3BucketB4787383"
+     },
+     "S3Key": {
+      "Fn::Join": [
+       "",
+       [
+        {
+         "Fn::Select": [
+          0,
+          {
+           "Fn::Split": [
+            "||",
+            {
+             "Ref": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3VersionKey8CF8E820"
+            }
+           ]
+          }
+         ]
+        },
+        {
+         "Fn::Select": [
+          1,
+          {
+           "Fn::Split": [
+            "||",
+            {
+             "Ref": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3VersionKey8CF8E820"
+            }
+           ]
+          }
+         ]
+        }
+       ]
+      ]
+     }
+    },
+    "Role": {
+     "Fn::GetAtt": [
+      "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB",
+      "Arn"
+     ]
+    }
+   },
+   "DependsOn": [
+    "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
+    "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB"
+   ]
+  }
+ },
+ "Parameters": {
+  "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3BucketB4787383": {
+   "Type": "String",
+   "Description": "S3 bucket for asset \"c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a\""
+  },
+  "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3VersionKey8CF8E820": {
+   "Type": "String",
+   "Description": "S3 key for asset version \"c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a\""
+  },
+  "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aArtifactHashC9560B34": {
+   "Type": "String",
+   "Description": "Artifact hash for asset \"c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a\""
+  }
+ },
+ "Outputs": {
+  "ExportsOutputFnGetAttGraphqlApi1B6CF24CApiIdE34D50AD": {
+   "Value": {
+    "Fn::GetAtt": [
+     "GraphqlApi1B6CF24C",
+     "ApiId"
+    ]
+   },
+   "Export": {
+    "Name": "AppSyncIntegLogRetention:ExportsOutputFnGetAttGraphqlApi1B6CF24CApiIdE34D50AD"
+   }
+  }
+ }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json
new file mode 100644
index 0000000000000..ddbbd027583b6
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/IntegDefaultTestDeployAssert4E6713E1.template.json
@@ -0,0 +1,183 @@
+{
+ "Resources": {
+  "AwsApiCallCloudWatchLogsdescribeLogGroups": {
+   "Type": "Custom::DeployAssert@SdkCallCloudWatchLogsdescribeLogGroups",
+   "Properties": {
+    "ServiceToken": {
+     "Fn::GetAtt": [
+      "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F",
+      "Arn"
+     ]
+    },
+    "service": "CloudWatchLogs",
+    "api": "describeLogGroups",
+    "parameters": {
+     "logGroupNamePrefix": {
+      "Fn::Join": [
+       "",
+       [
+        "/aws/appsync/apis/",
+        {
+         "Fn::ImportValue": "AppSyncIntegLogRetention:ExportsOutputFnGetAttGraphqlApi1B6CF24CApiIdE34D50AD"
+        }
+       ]
+      ]
+     }
+    },
+    "flattenResponse": "false",
+    "salt": "1659609543121"
+   },
+   "UpdateReplacePolicy": "Delete",
+   "DeletionPolicy": "Delete"
+  },
+  "AwsApiCallCloudWatchLogsdescribeLogGroupsAssertEqualsCloudWatchLogsdescribeLogGroupsD2E2CD37": {
+   "Type": "Custom::DeployAssert@AssertEquals",
+   "Properties": {
+    "ServiceToken": {
+     "Fn::GetAtt": [
+      "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F",
+      "Arn"
+     ]
+    },
+    "actual": {
+     "Fn::GetAtt": [
+      "AwsApiCallCloudWatchLogsdescribeLogGroups",
+      "apiCallResponse"
+     ]
+    },
+    "expected": {
+     "Fn::Join": [
+      "",
+      [
+       "{\"$ObjectLike\":{\"logGroups\":[{\"logGroupName\":\"/aws/appsync/apis/",
+       {
+        "Fn::ImportValue": "AppSyncIntegLogRetention:ExportsOutputFnGetAttGraphqlApi1B6CF24CApiIdE34D50AD"
+       },
+       "\",\"retentionInDays\":7}]}}"
+      ]
+     ]
+    },
+    "salt": "1659609543122"
+   },
+   "UpdateReplacePolicy": "Delete",
+   "DeletionPolicy": "Delete"
+  },
+  "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73": {
+   "Type": "AWS::IAM::Role",
+   "Properties": {
+    "AssumeRolePolicyDocument": {
+     "Version": "2012-10-17",
+     "Statement": [
+      {
+       "Action": "sts:AssumeRole",
+       "Effect": "Allow",
+       "Principal": {
+        "Service": "lambda.amazonaws.com"
+       }
+      }
+     ]
+    },
+    "ManagedPolicyArns": [
+     {
+      "Fn::Sub": "arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+     }
+    ],
+    "Policies": [
+     {
+      "PolicyName": "Inline",
+      "PolicyDocument": {
+       "Version": "2012-10-17",
+       "Statement": [
+        {
+         "Action": [
+          "logs:DescribeLogGroups"
+         ],
+         "Effect": "Allow",
+         "Resource": [
+          "*"
+         ]
+        }
+       ]
+      }
+     }
+    ]
+   }
+  },
+  "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F": {
+   "Type": "AWS::Lambda::Function",
+   "Properties": {
+    "Runtime": "nodejs14.x",
+    "Code": {
+     "S3Bucket": {
+      "Ref": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3BucketF94385B7"
+     },
+     "S3Key": {
+      "Fn::Join": [
+       "",
+       [
+        {
+         "Fn::Select": [
+          0,
+          {
+           "Fn::Split": [
+            "||",
+            {
+             "Ref": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3VersionKey66DB0F9E"
+            }
+           ]
+          }
+         ]
+        },
+        {
+         "Fn::Select": [
+          1,
+          {
+           "Fn::Split": [
+            "||",
+            {
+             "Ref": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3VersionKey66DB0F9E"
+            }
+           ]
+          }
+         ]
+        }
+       ]
+      ]
+     }
+    },
+    "Timeout": 120,
+    "Handler": "index.handler",
+    "Role": {
+     "Fn::GetAtt": [
+      "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73",
+      "Arn"
+     ]
+    }
+   }
+  }
+ },
+ "Outputs": {
+  "AssertionResultsAssertEqualsCloudWatchLogsdescribeLogGroups": {
+   "Value": {
+    "Fn::GetAtt": [
+     "AwsApiCallCloudWatchLogsdescribeLogGroupsAssertEqualsCloudWatchLogsdescribeLogGroupsD2E2CD37",
+     "data"
+    ]
+   }
+  }
+ },
+ "Parameters": {
+  "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3BucketF94385B7": {
+   "Type": "String",
+   "Description": "S3 bucket for asset \"0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd\""
+  },
+  "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3VersionKey66DB0F9E": {
+   "Type": "String",
+   "Description": "S3 key for asset version \"0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd\""
+  },
+  "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdArtifactHash2AC894D9": {
+   "Type": "String",
+   "Description": "Artifact hash for asset \"0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd\""
+  }
+ }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd.bundle/index.js b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd.bundle/index.js
new file mode 100644
index 0000000000000..b3ec1b8c53d30
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd.bundle/index.js
@@ -0,0 +1,612 @@
+"use strict";
+var __create = Object.create;
+var __defProp = Object.defineProperty;
+var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
+var __getOwnPropNames = Object.getOwnPropertyNames;
+var __getProtoOf = Object.getPrototypeOf;
+var __hasOwnProp = Object.prototype.hasOwnProperty;
+var __export = (target, all) => {
+  for (var name in all)
+    __defProp(target, name, { get: all[name], enumerable: true });
+};
+var __copyProps = (to, from, except, desc) => {
+  if (from && typeof from === "object" || typeof from === "function") {
+    for (let key of __getOwnPropNames(from))
+      if (!__hasOwnProp.call(to, key) && key !== except)
+        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
+  }
+  return to;
+};
+var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
+  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
+  mod
+));
+var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
+
+// lib/assertions/providers/lambda-handler/index.ts
+var lambda_handler_exports = {};
+__export(lambda_handler_exports, {
+  handler: () => handler
+});
+module.exports = __toCommonJS(lambda_handler_exports);
+
+// ../assertions/lib/matcher.ts
+var Matcher = class {
+  static isMatcher(x) {
+    return x && x instanceof Matcher;
+  }
+};
+var MatchResult = class {
+  constructor(target) {
+    this.failures = [];
+    this.captures = /* @__PURE__ */ new Map();
+    this.finalized = false;
+    this.target = target;
+  }
+  push(matcher, path, message) {
+    return this.recordFailure({ matcher, path, message });
+  }
+  recordFailure(failure) {
+    this.failures.push(failure);
+    return this;
+  }
+  hasFailed() {
+    return this.failures.length !== 0;
+  }
+  get failCount() {
+    return this.failures.length;
+  }
+  compose(id, inner) {
+    const innerF = inner.failures;
+    this.failures.push(...innerF.map((f) => {
+      return { path: [id, ...f.path], message: f.message, matcher: f.matcher };
+    }));
+    inner.captures.forEach((vals, capture) => {
+      vals.forEach((value) => this.recordCapture({ capture, value }));
+    });
+    return this;
+  }
+  finished() {
+    if (this.finalized) {
+      return this;
+    }
+    if (this.failCount === 0) {
+      this.captures.forEach((vals, cap) => cap._captured.push(...vals));
+    }
+    this.finalized = true;
+    return this;
+  }
+  toHumanStrings() {
+    return this.failures.map((r) => {
+      const loc = r.path.length === 0 ? "" : ` at ${r.path.join("")}`;
+      return "" + r.message + loc + ` (using ${r.matcher.name} matcher)`;
+    });
+  }
+  recordCapture(options) {
+    let values = this.captures.get(options.capture);
+    if (values === void 0) {
+      values = [];
+    }
+    values.push(options.value);
+    this.captures.set(options.capture, values);
+  }
+};
+
+// ../assertions/lib/private/matchers/absent.ts
+var AbsentMatch = class extends Matcher {
+  constructor(name) {
+    super();
+    this.name = name;
+  }
+  test(actual) {
+    const result = new MatchResult(actual);
+    if (actual !== void 0) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Received ${actual}, but key should be absent`
+      });
+    }
+    return result;
+  }
+};
+
+// ../assertions/lib/private/type.ts
+function getType(obj) {
+  return Array.isArray(obj) ? "array" : typeof obj;
+}
+
+// ../assertions/lib/match.ts
+var Match = class {
+  static absent() {
+    return new AbsentMatch("absent");
+  }
+  static arrayWith(pattern) {
+    return new ArrayMatch("arrayWith", pattern);
+  }
+  static arrayEquals(pattern) {
+    return new ArrayMatch("arrayEquals", pattern, { subsequence: false });
+  }
+  static exact(pattern) {
+    return new LiteralMatch("exact", pattern, { partialObjects: false });
+  }
+  static objectLike(pattern) {
+    return new ObjectMatch("objectLike", pattern);
+  }
+  static objectEquals(pattern) {
+    return new ObjectMatch("objectEquals", pattern, { partial: false });
+  }
+  static not(pattern) {
+    return new NotMatch("not", pattern);
+  }
+  static serializedJson(pattern) {
+    return new SerializedJson("serializedJson", pattern);
+  }
+  static anyValue() {
+    return new AnyMatch("anyValue");
+  }
+  static stringLikeRegexp(pattern) {
+    return new StringLikeRegexpMatch("stringLikeRegexp", pattern);
+  }
+};
+var LiteralMatch = class extends Matcher {
+  constructor(name, pattern, options = {}) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+    this.partialObjects = options.partialObjects ?? false;
+    if (Matcher.isMatcher(this.pattern)) {
+      throw new Error("LiteralMatch cannot directly contain another matcher. Remove the top-level matcher or nest it more deeply.");
+    }
+  }
+  test(actual) {
+    if (Array.isArray(this.pattern)) {
+      return new ArrayMatch(this.name, this.pattern, { subsequence: false, partialObjects: this.partialObjects }).test(actual);
+    }
+    if (typeof this.pattern === "object") {
+      return new ObjectMatch(this.name, this.pattern, { partial: this.partialObjects }).test(actual);
+    }
+    const result = new MatchResult(actual);
+    if (typeof this.pattern !== typeof actual) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected type ${typeof this.pattern} but received ${getType(actual)}`
+      });
+      return result;
+    }
+    if (actual !== this.pattern) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected ${this.pattern} but received ${actual}`
+      });
+    }
+    return result;
+  }
+};
+var ArrayMatch = class extends Matcher {
+  constructor(name, pattern, options = {}) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+    this.subsequence = options.subsequence ?? true;
+    this.partialObjects = options.partialObjects ?? false;
+  }
+  test(actual) {
+    if (!Array.isArray(actual)) {
+      return new MatchResult(actual).recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected type array but received ${getType(actual)}`
+      });
+    }
+    if (!this.subsequence && this.pattern.length !== actual.length) {
+      return new MatchResult(actual).recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected array of length ${this.pattern.length} but received ${actual.length}`
+      });
+    }
+    let patternIdx = 0;
+    let actualIdx = 0;
+    const result = new MatchResult(actual);
+    while (patternIdx < this.pattern.length && actualIdx < actual.length) {
+      const patternElement = this.pattern[patternIdx];
+      const matcher = Matcher.isMatcher(patternElement) ? patternElement : new LiteralMatch(this.name, patternElement, { partialObjects: this.partialObjects });
+      const matcherName = matcher.name;
+      if (this.subsequence && (matcherName == "absent" || matcherName == "anyValue")) {
+        throw new Error(`The Matcher ${matcherName}() cannot be nested within arrayWith()`);
+      }
+      const innerResult = matcher.test(actual[actualIdx]);
+      if (!this.subsequence || !innerResult.hasFailed()) {
+        result.compose(`[${actualIdx}]`, innerResult);
+        patternIdx++;
+        actualIdx++;
+      } else {
+        actualIdx++;
+      }
+    }
+    for (; patternIdx < this.pattern.length; patternIdx++) {
+      const pattern = this.pattern[patternIdx];
+      const element = Matcher.isMatcher(pattern) || typeof pattern === "object" ? " " : ` [${pattern}] `;
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Missing element${element}at pattern index ${patternIdx}`
+      });
+    }
+    return result;
+  }
+};
+var ObjectMatch = class extends Matcher {
+  constructor(name, pattern, options = {}) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+    this.partial = options.partial ?? true;
+  }
+  test(actual) {
+    if (typeof actual !== "object" || Array.isArray(actual)) {
+      return new MatchResult(actual).recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected type object but received ${getType(actual)}`
+      });
+    }
+    const result = new MatchResult(actual);
+    if (!this.partial) {
+      for (const a of Object.keys(actual)) {
+        if (!(a in this.pattern)) {
+          result.recordFailure({
+            matcher: this,
+            path: [`/${a}`],
+            message: "Unexpected key"
+          });
+        }
+      }
+    }
+    for (const [patternKey, patternVal] of Object.entries(this.pattern)) {
+      if (!(patternKey in actual) && !(patternVal instanceof AbsentMatch)) {
+        result.recordFailure({
+          matcher: this,
+          path: [`/${patternKey}`],
+          message: `Missing key '${patternKey}' among {${Object.keys(actual).join(",")}}`
+        });
+        continue;
+      }
+      const matcher = Matcher.isMatcher(patternVal) ? patternVal : new LiteralMatch(this.name, patternVal, { partialObjects: this.partial });
+      const inner = matcher.test(actual[patternKey]);
+      result.compose(`/${patternKey}`, inner);
+    }
+    return result;
+  }
+};
+var SerializedJson = class extends Matcher {
+  constructor(name, pattern) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+  }
+  test(actual) {
+    const result = new MatchResult(actual);
+    if (getType(actual) !== "string") {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected JSON as a string but found ${getType(actual)}`
+      });
+      return result;
+    }
+    let parsed;
+    try {
+      parsed = JSON.parse(actual);
+    } catch (err) {
+      if (err instanceof SyntaxError) {
+        result.recordFailure({
+          matcher: this,
+          path: [],
+          message: `Invalid JSON string: ${actual}`
+        });
+        return result;
+      } else {
+        throw err;
+      }
+    }
+    const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern);
+    const innerResult = matcher.test(parsed);
+    result.compose(`(${this.name})`, innerResult);
+    return result;
+  }
+};
+var NotMatch = class extends Matcher {
+  constructor(name, pattern) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+  }
+  test(actual) {
+    const matcher = Matcher.isMatcher(this.pattern) ? this.pattern : new LiteralMatch(this.name, this.pattern);
+    const innerResult = matcher.test(actual);
+    const result = new MatchResult(actual);
+    if (innerResult.failCount === 0) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Found unexpected match: ${JSON.stringify(actual, void 0, 2)}`
+      });
+    }
+    return result;
+  }
+};
+var AnyMatch = class extends Matcher {
+  constructor(name) {
+    super();
+    this.name = name;
+  }
+  test(actual) {
+    const result = new MatchResult(actual);
+    if (actual == null) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: "Expected a value but found none"
+      });
+    }
+    return result;
+  }
+};
+var StringLikeRegexpMatch = class extends Matcher {
+  constructor(name, pattern) {
+    super();
+    this.name = name;
+    this.pattern = pattern;
+  }
+  test(actual) {
+    const result = new MatchResult(actual);
+    const regex = new RegExp(this.pattern, "gm");
+    if (typeof actual !== "string") {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `Expected a string, but got '${typeof actual}'`
+      });
+    }
+    if (!regex.test(actual)) {
+      result.recordFailure({
+        matcher: this,
+        path: [],
+        message: `String '${actual}' did not match pattern '${this.pattern}'`
+      });
+    }
+    return result;
+  }
+};
+
+// lib/assertions/providers/lambda-handler/base.ts
+var https = __toESM(require("https"));
+var url = __toESM(require("url"));
+var CustomResourceHandler = class {
+  constructor(event, context) {
+    this.event = event;
+    this.context = context;
+    this.timedOut = false;
+    this.timeout = setTimeout(async () => {
+      await this.respond({
+        status: "FAILED",
+        reason: "Lambda Function Timeout",
+        data: this.context.logStreamName
+      });
+      this.timedOut = true;
+    }, context.getRemainingTimeInMillis() - 1200);
+    this.event = event;
+    this.physicalResourceId = extractPhysicalResourceId(event);
+  }
+  async handle() {
+    try {
+      console.log(`Event: ${JSON.stringify({ ...this.event, ResponseURL: "..." })}`);
+      const response = await this.processEvent(this.event.ResourceProperties);
+      console.log(`Event output : ${JSON.stringify(response)}`);
+      await this.respond({
+        status: "SUCCESS",
+        reason: "OK",
+        data: response
+      });
+    } catch (e) {
+      console.log(e);
+      await this.respond({
+        status: "FAILED",
+        reason: e.message ?? "Internal Error"
+      });
+    } finally {
+      clearTimeout(this.timeout);
+    }
+  }
+  respond(response) {
+    if (this.timedOut) {
+      return;
+    }
+    const cfResponse = {
+      Status: response.status,
+      Reason: response.reason,
+      PhysicalResourceId: this.physicalResourceId,
+      StackId: this.event.StackId,
+      RequestId: this.event.RequestId,
+      LogicalResourceId: this.event.LogicalResourceId,
+      NoEcho: false,
+      Data: response.data
+    };
+    const responseBody = JSON.stringify(cfResponse);
+    console.log("Responding to CloudFormation", responseBody);
+    const parsedUrl = url.parse(this.event.ResponseURL);
+    const requestOptions = {
+      hostname: parsedUrl.hostname,
+      path: parsedUrl.path,
+      method: "PUT",
+      headers: { "content-type": "", "content-length": responseBody.length }
+    };
+    return new Promise((resolve, reject) => {
+      try {
+        const request2 = https.request(requestOptions, resolve);
+        request2.on("error", reject);
+        request2.write(responseBody);
+        request2.end();
+      } catch (e) {
+        reject(e);
+      }
+    });
+  }
+};
+function extractPhysicalResourceId(event) {
+  switch (event.RequestType) {
+    case "Create":
+      return event.LogicalResourceId;
+    case "Update":
+    case "Delete":
+      return event.PhysicalResourceId;
+  }
+}
+
+// lib/assertions/providers/lambda-handler/assertion.ts
+var AssertionHandler = class extends CustomResourceHandler {
+  async processEvent(request2) {
+    let actual = decodeCall(request2.actual);
+    const expected = decodeCall(request2.expected);
+    let result;
+    const matcher = new MatchCreator(expected).getMatcher();
+    console.log(`Testing equality between ${JSON.stringify(request2.actual)} and ${JSON.stringify(request2.expected)}`);
+    const matchResult = matcher.test(actual);
+    matchResult.finished();
+    if (matchResult.hasFailed()) {
+      result = {
+        data: JSON.stringify({
+          status: "fail",
+          message: [
+            ...matchResult.toHumanStrings(),
+            JSON.stringify(matchResult.target, void 0, 2)
+          ].join("\n")
+        })
+      };
+      if (request2.failDeployment) {
+        throw new Error(result.data);
+      }
+    } else {
+      result = {
+        data: JSON.stringify({
+          status: "pass"
+        })
+      };
+    }
+    return result;
+  }
+};
+var MatchCreator = class {
+  constructor(obj) {
+    this.parsedObj = {
+      matcher: obj
+    };
+  }
+  getMatcher() {
+    try {
+      const final = JSON.parse(JSON.stringify(this.parsedObj), function(_k, v) {
+        const nested = Object.keys(v)[0];
+        switch (nested) {
+          case "$ArrayWith":
+            return Match.arrayWith(v[nested]);
+          case "$ObjectLike":
+            return Match.objectLike(v[nested]);
+          case "$StringLike":
+            return Match.stringLikeRegexp(v[nested]);
+          default:
+            return v;
+        }
+      });
+      if (Matcher.isMatcher(final.matcher)) {
+        return final.matcher;
+      }
+      return Match.exact(final.matcher);
+    } catch {
+      return Match.exact(this.parsedObj.matcher);
+    }
+  }
+};
+function decodeCall(call) {
+  if (!call) {
+    return void 0;
+  }
+  try {
+    const parsed = JSON.parse(call);
+    return parsed;
+  } catch (e) {
+    return call;
+  }
+}
+
+// lib/assertions/providers/lambda-handler/utils.ts
+function decode(object) {
+  return JSON.parse(JSON.stringify(object), (_k, v) => {
+    switch (v) {
+      case "TRUE:BOOLEAN":
+        return true;
+      case "FALSE:BOOLEAN":
+        return false;
+      default:
+        return v;
+    }
+  });
+}
+
+// lib/assertions/providers/lambda-handler/sdk.ts
+function flatten(object) {
+  return Object.assign(
+    {},
+    ...function _flatten(child, path = []) {
+      return [].concat(...Object.keys(child).map((key) => {
+        const childKey = Buffer.isBuffer(child[key]) ? child[key].toString("utf8") : child[key];
+        return typeof childKey === "object" && childKey !== null ? _flatten(childKey, path.concat([key])) : { [path.concat([key]).join(".")]: childKey };
+      }));
+    }(object)
+  );
+}
+var AwsApiCallHandler = class extends CustomResourceHandler {
+  async processEvent(request2) {
+    const AWS = require("aws-sdk");
+    console.log(`AWS SDK VERSION: ${AWS.VERSION}`);
+    const service = new AWS[request2.service]();
+    const response = await service[request2.api](request2.parameters && decode(request2.parameters)).promise();
+    console.log(`SDK response received ${JSON.stringify(response)}`);
+    delete response.ResponseMetadata;
+    const respond = {
+      apiCallResponse: response
+    };
+    const flatData = {
+      ...flatten(respond)
+    };
+    return request2.flattenResponse === "true" ? flatData : respond;
+  }
+};
+
+// lib/assertions/providers/lambda-handler/types.ts
+var ASSERT_RESOURCE_TYPE = "Custom::DeployAssert@AssertEquals";
+var SDK_RESOURCE_TYPE_PREFIX = "Custom::DeployAssert@SdkCall";
+
+// lib/assertions/providers/lambda-handler/index.ts
+async function handler(event, context) {
+  const provider = createResourceHandler(event, context);
+  await provider.handle();
+}
+function createResourceHandler(event, context) {
+  if (event.ResourceType.startsWith(SDK_RESOURCE_TYPE_PREFIX)) {
+    return new AwsApiCallHandler(event, context);
+  }
+  switch (event.ResourceType) {
+    case ASSERT_RESOURCE_TYPE:
+      return new AssertionHandler(event, context);
+    default:
+      throw new Error(`Unsupported resource type "${event.ResourceType}`);
+  }
+}
+// Annotate the CommonJS export names for ESM import in node:
+0 && (module.exports = {
+  handler
+});
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.d.ts b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.d.ts
new file mode 100644
index 0000000000000..9bbf5854684b6
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.d.ts
@@ -0,0 +1 @@
+export declare function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context): Promise<void>;
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.js b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.js
new file mode 100644
index 0000000000000..a844de880012a
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.js
@@ -0,0 +1,174 @@
+"use strict";
+/* eslint-disable no-console */
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.handler = void 0;
+// eslint-disable-next-line import/no-extraneous-dependencies
+const AWS = require("aws-sdk");
+/**
+ * Creates a log group and doesn't throw if it exists.
+ *
+ * @param logGroupName the name of the log group to create.
+ * @param region to create the log group in
+ * @param options CloudWatch API SDK options.
+ */
+async function createLogGroupSafe(logGroupName, region, options) {
+    // If we set the log retention for a lambda, then due to the async nature of
+    // Lambda logging there could be a race condition when the same log group is
+    // already being created by the lambda execution. This can sometime result in
+    // an error "OperationAbortedException: A conflicting operation is currently
+    // in progress...Please try again."
+    // To avoid an error, we do as requested and try again.
+    let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries;
+    const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base;
+    do {
+        try {
+            const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options });
+            await cloudwatchlogs.createLogGroup({ logGroupName }).promise();
+            return;
+        }
+        catch (error) {
+            if (error.code === 'ResourceAlreadyExistsException') {
+                // The log group is already created by the lambda execution
+                return;
+            }
+            if (error.code === 'OperationAbortedException') {
+                if (retryCount > 0) {
+                    retryCount--;
+                    await new Promise(resolve => setTimeout(resolve, delay));
+                    continue;
+                }
+                else {
+                    // The log group is still being created by another execution but we are out of retries
+                    throw new Error('Out of attempts to create a logGroup');
+                }
+            }
+            throw error;
+        }
+    } while (true); // exit happens on retry count check
+}
+/**
+ * Puts or deletes a retention policy on a log group.
+ *
+ * @param logGroupName the name of the log group to create
+ * @param region the region of the log group
+ * @param options CloudWatch API SDK options.
+ * @param retentionInDays the number of days to retain the log events in the specified log group.
+ */
+async function setRetentionPolicy(logGroupName, region, options, retentionInDays) {
+    // The same as in createLogGroupSafe(), here we could end up with the race
+    // condition where a log group is either already being created or its retention
+    // policy is being updated. This would result in an OperationAbortedException,
+    // which we will try to catch and retry the command a number of times before failing
+    let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries;
+    const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base;
+    do {
+        try {
+            const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options });
+            if (!retentionInDays) {
+                await cloudwatchlogs.deleteRetentionPolicy({ logGroupName }).promise();
+            }
+            else {
+                await cloudwatchlogs.putRetentionPolicy({ logGroupName, retentionInDays }).promise();
+            }
+            return;
+        }
+        catch (error) {
+            if (error.code === 'OperationAbortedException') {
+                if (retryCount > 0) {
+                    retryCount--;
+                    await new Promise(resolve => setTimeout(resolve, delay));
+                    continue;
+                }
+                else {
+                    // The log group is still being created by another execution but we are out of retries
+                    throw new Error('Out of attempts to create a logGroup');
+                }
+            }
+            throw error;
+        }
+    } while (true); // exit happens on retry count check
+}
+async function handler(event, context) {
+    try {
+        console.log(JSON.stringify({ ...event, ResponseURL: '...' }));
+        // The target log group
+        const logGroupName = event.ResourceProperties.LogGroupName;
+        // The region of the target log group
+        const logGroupRegion = event.ResourceProperties.LogGroupRegion;
+        // Parse to AWS SDK retry options
+        const retryOptions = parseRetryOptions(event.ResourceProperties.SdkRetry);
+        if (event.RequestType === 'Create' || event.RequestType === 'Update') {
+            // Act on the target log group
+            await createLogGroupSafe(logGroupName, logGroupRegion, retryOptions);
+            await setRetentionPolicy(logGroupName, logGroupRegion, retryOptions, parseInt(event.ResourceProperties.RetentionInDays, 10));
+            if (event.RequestType === 'Create') {
+                // Set a retention policy of 1 day on the logs of this very function.
+                // Due to the async nature of the log group creation, the log group for this function might
+                // still be not created yet at this point. Therefore we attempt to create it.
+                // In case it is being created, createLogGroupSafe will handle the conflict.
+                const region = process.env.AWS_REGION;
+                await createLogGroupSafe(`/aws/lambda/${context.functionName}`, region, retryOptions);
+                // If createLogGroupSafe fails, the log group is not created even after multiple attempts.
+                // In this case we have nothing to set the retention policy on but an exception will skip
+                // the next line.
+                await setRetentionPolicy(`/aws/lambda/${context.functionName}`, region, retryOptions, 1);
+            }
+        }
+        await respond('SUCCESS', 'OK', logGroupName);
+    }
+    catch (e) {
+        console.log(e);
+        await respond('FAILED', e.message, event.ResourceProperties.LogGroupName);
+    }
+    function respond(responseStatus, reason, physicalResourceId) {
+        const responseBody = JSON.stringify({
+            Status: responseStatus,
+            Reason: reason,
+            PhysicalResourceId: physicalResourceId,
+            StackId: event.StackId,
+            RequestId: event.RequestId,
+            LogicalResourceId: event.LogicalResourceId,
+            Data: {
+                // Add log group name as part of the response so that it's available via Fn::GetAtt
+                LogGroupName: event.ResourceProperties.LogGroupName,
+            },
+        });
+        console.log('Responding', responseBody);
+        // eslint-disable-next-line @typescript-eslint/no-require-imports
+        const parsedUrl = require('url').parse(event.ResponseURL);
+        const requestOptions = {
+            hostname: parsedUrl.hostname,
+            path: parsedUrl.path,
+            method: 'PUT',
+            headers: { 'content-type': '', 'content-length': responseBody.length },
+        };
+        return new Promise((resolve, reject) => {
+            try {
+                // eslint-disable-next-line @typescript-eslint/no-require-imports
+                const request = require('https').request(requestOptions, resolve);
+                request.on('error', reject);
+                request.write(responseBody);
+                request.end();
+            }
+            catch (e) {
+                reject(e);
+            }
+        });
+    }
+    function parseRetryOptions(rawOptions) {
+        const retryOptions = {};
+        if (rawOptions) {
+            if (rawOptions.maxRetries) {
+                retryOptions.maxRetries = parseInt(rawOptions.maxRetries, 10);
+            }
+            if (rawOptions.base) {
+                retryOptions.retryOptions = {
+                    base: parseInt(rawOptions.base, 10),
+                };
+            }
+        }
+        return retryOptions;
+    }
+}
+exports.handler = handler;
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0JBQStCOzs7QUFFL0IsNkRBQTZEO0FBQzdELCtCQUErQjtBQVMvQjs7Ozs7O0dBTUc7QUFDSCxLQUFLLFVBQVUsa0JBQWtCLENBQUMsWUFBb0IsRUFBRSxNQUFlLEVBQUUsT0FBeUI7SUFDaEcsNEVBQTRFO0lBQzVFLDRFQUE0RTtJQUM1RSw2RUFBNkU7SUFDN0UsNEVBQTRFO0lBQzVFLG1DQUFtQztJQUNuQyx1REFBdUQ7SUFDdkQsSUFBSSxVQUFVLEdBQUcsT0FBTyxFQUFFLFVBQVUsSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQztJQUM1RSxNQUFNLEtBQUssR0FBRyxPQUFPLEVBQUUsWUFBWSxFQUFFLElBQUksSUFBSSxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUM7SUFDeEYsR0FBRztRQUNELElBQUk7WUFDRixNQUFNLGNBQWMsR0FBRyxJQUFJLEdBQUcsQ0FBQyxjQUFjLENBQUMsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxHQUFHLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDaEcsTUFBTSxjQUFjLENBQUMsY0FBYyxDQUFDLEVBQUUsWUFBWSxFQUFFLENBQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNoRSxPQUFPO1NBQ1I7UUFBQyxPQUFPLEtBQUssRUFBRTtZQUNkLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxnQ0FBZ0MsRUFBRTtnQkFDbkQsMkRBQTJEO2dCQUMzRCxPQUFPO2FBQ1I7WUFDRCxJQUFJLEtBQUssQ0FBQyxJQUFJLEtBQUssMkJBQTJCLEVBQUU7Z0JBQzlDLElBQUksVUFBVSxHQUFHLENBQUMsRUFBRTtvQkFDbEIsVUFBVSxFQUFF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QUFlO1FBQ3hDLE1BQU0sWUFBWSxHQUFvQixFQUFFLENBQUM7UUFDekMsSUFBSSxVQUFVLEVBQUU7WUFDZCxJQUFJLFVBQVUsQ0FBQyxVQUFVLEVBQUU7Z0JBQ3pCLFlBQVksQ0FBQyxVQUFVLEdBQUcsUUFBUSxDQUFDLFVBQVUsQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLENBQUM7YUFDL0Q7WUFDRCxJQUFJLFVBQVUsQ0FBQyxJQUFJLEVBQUU7Z0JBQ25CLFlBQVksQ0FBQyxZQUFZLEdBQUc7b0JBQzFCLElBQUksRUFBRSxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUM7aUJBQ3BDLENBQUM7YUFDSDtTQUNGO1FBQ0QsT0FBTyxZQUFZLENBQUM7SUFDdEIsQ0FBQztBQUNILENBQUM7QUEzRkQsMEJBMkZDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSAqL1xuXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzXG5pbXBvcnQgKiBhcyBBV1MgZnJvbSAnYXdzLXNkayc7XG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzXG5pbXBvcnQgdHlwZSB7IFJldHJ5RGVsYXlPcHRpb25zIH0gZnJvbSAnYXdzLXNkay9saWIvY29uZmlnLWJhc2UnO1xuXG5pbnRlcmZhY2UgU2RrUmV0cnlPcHRpb25zIHtcbiAgbWF4UmV0cmllcz86IG51bWJlcjtcbiAgcmV0cnlPcHRpb25zPzogUmV0cnlEZWxheU9wdGlvbnM7XG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIGxvZyBncm91cCBhbmQgZG9lc24ndCB0aHJvdyBpZiBpdCBleGlzdHMuXG4gKlxuICogQHBhcmFtIGxvZ0dyb3VwTmFtZSB0aGUgbmFtZSBvZiB0aGUgbG9nIGdyb3VwIHRvIGNyZWF0ZS5cbiAqIEBwYXJhbSByZWdpb24gdG8gY3JlYXRlIHRoZSBsb2cgZ3JvdXAgaW5cbiAqIEBwYXJhbSBvcHRpb25zIENsb3VkV2F0Y2ggQVBJIFNESyBvcHRpb25zLlxuICovXG5hc3luYyBmdW5jdGlvbiBjcmVhdGVMb2dHcm91cFNhZmUobG9nR3JvdXBOYW1lOiBzdHJpbmcsIHJlZ2lvbj86IHN0cmluZywgb3B0aW9ucz86IFNka1JldHJ5T3B0aW9ucykge1xuICAvLyBJZiB3ZSBzZXQgdGhlIGxvZyByZXRlbnRpb24gZm9yIGEgbGFtYmRhLCB0aGVuIGR1ZSB0byB0aGUgYXN5bmMgbmF0dXJlIG9mXG4gIC8vIExhbWJkYSBsb2dnaW5nIHRoZXJlIGNvdWxkIGJlIGEgcmFjZSBjb25kaXRpb24gd2hlbiB0aGUgc2FtZSBsb2cgZ3JvdXAgaXNcbiAgLy8gYWxyZWFkeSBiZWluZyBjcmVhdGVkIGJ5IHRoZSBsYW1iZGEgZXhlY3V0aW9uLiBUaGlzIGNhbiBzb21ldGltZSByZXN1bHQgaW5cbiAgLy8gYW4gZXJyb3IgXCJPcGVyYXRpb25BYm9ydGVkRXhjZXB0aW9uOiBBIGNvbmZsaWN0aW5nIG9wZXJhdGlvbiBpcyBjdXJyZW50bHlcbiAgLy8gaW4gcHJvZ3Jlc3MuLi5QbGVhc2UgdHJ5IGFnYWluLlwiXG4gIC8vIFRvIGF2b2lkIGFuIGVycm9yLCB3ZSBkbyBhcyByZXF1ZXN0ZWQgYW5kIHRyeSBhZ2Fpbi5cbiAgbGV0IHJldHJ5Q291bnQgPSBvcHRpb25zPy5tYXhSZXRyaWVzID09IHVuZGVmaW5lZCA/IDEwIDogb3B0aW9ucy5tYXhSZXRyaWVzO1xuICBjb25zdCBkZWxheSA9IG9wdGlvbnM/LnJldHJ5T3B0aW9ucz8uYmFzZSA9PSB1bmRlZmluZWQgPyAxMCA6IG9wdGlvbnMucmV0cnlPcHRpb25zLmJhc2U7XG4gIGRvIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgY2xvdWR3YXRjaGxvZ3MgPSBuZXcgQVdTLkNsb3VkV2F0Y2hMb2dzKHsgYXBpVmVyc2lvbjogJzIwMTQtMDMtMjgnLCByZWdpb24sIC4uLm9wdGlvbnMgfSk7XG4gICAgICBhd2FpdCBjbG91ZHdhdGNobG9ncy5jcmVhdGVMb2dHcm91cCh7IGxvZ0dyb3VwTmFtZSB9KS5wcm9taXNlKCk7XG4gICAgICByZXR1cm47XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvci5jb2RlID09PSAnUmVzb3VyY2VBbHJlYWR5RXhpc3RzRXhjZXB0aW9uJykge1xuICAgICAgICAvLyBUaGUgbG9nIGdyb3VwIGlzIGFscmVhZHkgY3JlYXRlZCBieSB0aGUgbGFtYmRhIGV4ZWN1dGlvblxuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBpZiAoZXJyb3IuY29kZSA9PT0gJ09wZXJhdGlvbkFib3J0ZWRFeGNlcHRpb24nKSB7XG4gICAgICAgIGlmIChyZXRyeUNvdW50ID4gMCkge1xuICAgICAgICAgIHJldHJ5Q291bnQtLTtcbiAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXNvbHZlID0+IHNldFRpbWVvdXQocmVzb2x2ZSwgZGVsYXkpKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyBUaGUgbG9nIGdyb3VwIGlzIHN0aWxsIGJlaW5nIGNyZWF0ZWQgYnkgYW5vdGhlciBleGVjdXRpb24gYnV0IHdlIGFyZSBvdXQgb2YgcmV0cmllc1xuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignT3V0IG9mIGF0dGVtcHRzIHRvIGNyZWF0ZSBhIGxvZ0dyb3VwJyk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfSB3aGlsZSAodHJ1ZSk7IC8vIGV4aXQgaGFwcGVucyBvbiByZXRyeSBjb3VudCBjaGVja1xufVxuXG4vKipcbiAqIFB1dHMgb3IgZGVsZXRlcyBhIHJldGVudGlvbiBwb2xpY3kgb24gYSBsb2cgZ3JvdXAuXG4gKlxuICogQHBhcmFtIGxvZ0dyb3VwTmFtZSB0aGUgbmFtZSBvZiB0aGUgbG9nIGdyb3VwIHRvIGNyZWF0ZVxuICogQHBhcmFtIHJlZ2lvbiB0aGUgcmVnaW9uIG9mIHRoZSBsb2cgZ3JvdXBcbiAqIEBwYXJhbSBvcHRpb25zIENsb3VkV2F0Y2ggQVBJIFNESyBvcHRpb25zLlxuICogQHBhcmFtIHJldGVudGlvbkluRGF5cyB0aGUgbnVtYmVyIG9mIGRheXMgdG8gcmV0YWluIHRoZSBsb2cgZXZlbnRzIGluIHRoZSBzcGVjaWZpZWQgbG9nIGdyb3VwLlxuICovXG5hc3luYyBmdW5jdGlvbiBzZXRSZXRlbnRpb25Qb2xpY3kobG9nR3JvdXBOYW1lOiBzdHJpbmcsIHJlZ2lvbj86IHN0cmluZywgb3B0aW9ucz86IFNka1JldHJ5T3B0aW9ucywgcmV0ZW50aW9uSW5EYXlzPzogbnVtYmVyKSB7XG4gIC8vIFRoZSBzYW1lIGFzIGluIGNyZWF0ZUxvZ0dyb3VwU2FmZSgpLCBoZXJlIHdlIGNvdWxkIGVuZCB1cCB3aXRoIHRoZSByYWNlXG4gIC8vIGNvbmRpdGlvbiB3aGVyZSBhIGxvZyBncm91cCBpcyBlaXRoZXIgYWxyZWFkeSBiZWluZyBjcmVhdGVkIG9yIGl0cyByZXRlbnRpb25cbiAgLy8gcG9saWN5IGlzIGJlaW5nIHVwZGF0ZWQuIFRoaXMgd291bGQgcmVzdWx0IGluIGFuIE9wZXJhdGlvbkFib3J0ZWRFeGNlcHRpb24sXG4gIC8vIHdoaWNoIHdlIHdpbGwgdHJ5IHRvIGNhdGNoIGFuZCByZXRyeSB0aGUgY29tbWFuZCBhIG51bWJlciBvZiB0aW1lcyBiZWZvcmUgZmFpbGluZ1xuICBsZXQgcmV0cnlDb3VudCA9IG9wdGlvbnM/Lm1heFJldHJpZXMgPT0gdW5kZWZpbmVkID8gMTAgOiBvcHRpb25zLm1heFJldHJpZXM7XG4gIGNvbnN0IGRlbGF5ID0gb3B0aW9ucz8ucmV0cnlPcHRpb25zPy5iYXNlID09IHVuZGVmaW5lZCA/IDEwIDogb3B0aW9ucy5yZXRyeU9wdGlvbnMuYmFzZTtcbiAgZG8ge1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBjbG91ZHdhdGNobG9ncyA9IG5ldyBBV1MuQ2xvdWRXYXRjaExvZ3MoeyBhcGlWZXJzaW9uOiAnMjAxNC0wMy0yOCcsIHJlZ2lvbiwgLi4ub3B0aW9ucyB9KTtcbiAgICAgIGlmICghcmV0ZW50aW9uSW5EYXlzKSB7XG4gICAgICAgIGF3YWl0IGNsb3Vkd2F0Y2hsb2dzLmRlbGV0ZVJldGVudGlvblBvbGljeSh7IGxvZ0dyb3VwTmFtZSB9KS5wcm9taXNlKCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhd2FpdCBjbG91ZHdhdGNobG9ncy5wdXRSZXRlbnRpb25Qb2xpY3koeyBsb2dHcm91cE5hbWUsIHJldGVudGlvbkluRGF5cyB9KS5wcm9taXNlKCk7XG4gICAgICB9XG4gICAgICByZXR1cm47XG5cbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKGVycm9yLmNvZGUgPT09ICdPcGVyYXRpb25BYm9ydGVkRXhjZXB0aW9uJykge1xuICAgICAgICBpZiAocmV0cnlDb3VudCA+IDApIHtcbiAgICAgICAgICByZXRyeUNvdW50LS07XG4gICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzb2x2ZSA9PiBzZXRUaW1lb3V0KHJlc29sdmUsIGRlbGF5KSk7XG4gICAgICAgICAgY29udGludWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgLy8gVGhlIGxvZyBncm91cCBpcyBzdGlsbCBiZWluZyBjcmVhdGVkIGJ5IGFub3RoZXIgZXhlY3V0aW9uIGJ1dCB3ZSBhcmUgb3V0IG9mIHJldHJpZXNcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ091dCBvZiBhdHRlbXB0cyB0byBjcmVhdGUgYSBsb2dHcm91cCcpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gIH0gd2hpbGUgKHRydWUpOyAvLyBleGl0IGhhcHBlbnMgb24gcmV0cnkgY291bnQgY2hlY2tcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGhhbmRsZXIoZXZlbnQ6IEFXU0xhbWJkYS5DbG91ZEZvcm1hdGlvbkN1c3RvbVJlc291cmNlRXZlbnQsIGNvbnRleHQ6IEFXU0xhbWJkYS5Db250ZXh0KSB7XG4gIHRyeSB7XG4gICAgY29uc29sZS5sb2coSlNPTi5zdHJpbmdpZnkoeyAuLi5ldmVudCwgUmVzcG9uc2VVUkw6ICcuLi4nIH0pKTtcblxuICAgIC8vIFRoZSB0YXJnZXQgbG9nIGdyb3VwXG4gICAgY29uc3QgbG9nR3JvdXBOYW1lID0gZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkxvZ0dyb3VwTmFtZTtcblxuICAgIC8vIFRoZSByZWdpb24gb2YgdGhlIHRhcmdldCBsb2cgZ3JvdXBcbiAgICBjb25zdCBsb2dHcm91cFJlZ2lvbiA9IGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5Mb2dHcm91cFJlZ2lvbjtcblxuICAgIC8vIFBhcnNlIHRvIEFXUyBTREsgcmV0cnkgb3B0aW9uc1xuICAgIGNvbnN0IHJldHJ5T3B0aW9ucyA9IHBhcnNlUmV0cnlPcHRpb25zKGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5TZGtSZXRyeSk7XG5cbiAgICBpZiAoZXZlbnQuUmVxdWVzdFR5cGUgPT09ICdDcmVhdGUnIHx8IGV2ZW50LlJlcXVlc3RUeXBlID09PSAnVXBkYXRlJykge1xuICAgICAgLy8gQWN0IG9uIHRoZSB0YXJnZXQgbG9nIGdyb3VwXG4gICAgICBhd2FpdCBjcmVhdGVMb2dHcm91cFNhZmUobG9nR3JvdXBOYW1lLCBsb2dHcm91cFJlZ2lvbiwgcmV0cnlPcHRpb25zKTtcbiAgICAgIGF3YWl0IHNldFJldGVudGlvblBvbGljeShsb2dHcm91cE5hbWUsIGxvZ0dyb3VwUmVnaW9uLCByZXRyeU9wdGlvbnMsIHBhcnNlSW50KGV2ZW50LlJlc291cmNlUHJvcGVydGllcy5SZXRlbnRpb25JbkRheXMsIDEwKSk7XG5cbiAgICAgIGlmIChldmVudC5SZXF1ZXN0VHlwZSA9PT0gJ0NyZWF0ZScpIHtcbiAgICAgICAgLy8gU2V0IGEgcmV0ZW50aW9uIHBvbGljeSBvZiAxIGRheSBvbiB0aGUgbG9ncyBvZiB0aGlzIHZlcnkgZnVuY3Rpb24uXG4gICAgICAgIC8vIER1ZSB0byB0aGUgYXN5bmMgbmF0dXJlIG9mIHRoZSBsb2cgZ3JvdXAgY3JlYXRpb24sIHRoZSBsb2cgZ3JvdXAgZm9yIHRoaXMgZnVuY3Rpb24gbWlnaHRcbiAgICAgICAgLy8gc3RpbGwgYmUgbm90IGNyZWF0ZWQgeWV0IGF0IHRoaXMgcG9pbnQuIFRoZXJlZm9yZSB3ZSBhdHRlbXB0IHRvIGNyZWF0ZSBpdC5cbiAgICAgICAgLy8gSW4gY2FzZSBpdCBpcyBiZWluZyBjcmVhdGVkLCBjcmVhdGVMb2dHcm91cFNhZmUgd2lsbCBoYW5kbGUgdGhlIGNvbmZsaWN0LlxuICAgICAgICBjb25zdCByZWdpb24gPSBwcm9jZXNzLmVudi5BV1NfUkVHSU9OO1xuICAgICAgICBhd2FpdCBjcmVhdGVMb2dHcm91cFNhZmUoYC9hd3MvbGFtYmRhLyR7Y29udGV4dC5mdW5jdGlvbk5hbWV9YCwgcmVnaW9uLCByZXRyeU9wdGlvbnMpO1xuICAgICAgICAvLyBJZiBjcmVhdGVMb2dHcm91cFNhZmUgZmFpbHMsIHRoZSBsb2cgZ3JvdXAgaXMgbm90IGNyZWF0ZWQgZXZlbiBhZnRlciBtdWx0aXBsZSBhdHRlbXB0cy5cbiAgICAgICAgLy8gSW4gdGhpcyBjYXNlIHdlIGhhdmUgbm90aGluZyB0byBzZXQgdGhlIHJldGVudGlvbiBwb2xpY3kgb24gYnV0IGFuIGV4Y2VwdGlvbiB3aWxsIHNraXBcbiAgICAgICAgLy8gdGhlIG5leHQgbGluZS5cbiAgICAgICAgYXdhaXQgc2V0UmV0ZW50aW9uUG9saWN5KGAvYXdzL2xhbWJkYS8ke2NvbnRleHQuZnVuY3Rpb25OYW1lfWAsIHJlZ2lvbiwgcmV0cnlPcHRpb25zLCAxKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICBhd2FpdCByZXNwb25kKCdTVUNDRVNTJywgJ09LJywgbG9nR3JvdXBOYW1lKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGNvbnNvbGUubG9nKGUpO1xuXG4gICAgYXdhaXQgcmVzcG9uZCgnRkFJTEVEJywgZS5tZXNzYWdlLCBldmVudC5SZXNvdXJjZVByb3BlcnRpZXMuTG9nR3JvdXBOYW1lKTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlc3BvbmQocmVzcG9uc2VTdGF0dXM6IHN0cmluZywgcmVhc29uOiBzdHJpbmcsIHBoeXNpY2FsUmVzb3VyY2VJZDogc3RyaW5nKSB7XG4gICAgY29uc3QgcmVzcG9uc2VCb2R5ID0gSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgU3RhdHVzOiByZXNwb25zZVN0YXR1cyxcbiAgICAgIFJlYXNvbjogcmVhc29uLFxuICAgICAgUGh5c2ljYWxSZXNvdXJjZUlkOiBwaHlzaWNhbFJlc291cmNlSWQsXG4gICAgICBTdGFja0lkOiBldmVudC5TdGFja0lkLFxuICAgICAgUmVxdWVzdElkOiBldmVudC5SZXF1ZXN0SWQsXG4gICAgICBMb2dpY2FsUmVzb3VyY2VJZDogZXZlbnQuTG9naWNhbFJlc291cmNlSWQsXG4gICAgICBEYXRhOiB7XG4gICAgICAgIC8vIEFkZCBsb2cgZ3JvdXAgbmFtZSBhcyBwYXJ0IG9mIHRoZSByZXNwb25zZSBzbyB0aGF0IGl0J3MgYXZhaWxhYmxlIHZpYSBGbjo6R2V0QXR0XG4gICAgICAgIExvZ0dyb3VwTmFtZTogZXZlbnQuUmVzb3VyY2VQcm9wZXJ0aWVzLkxvZ0dyb3VwTmFtZSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zb2xlLmxvZygnUmVzcG9uZGluZycsIHJlc3BvbnNlQm9keSk7XG5cbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXJlcXVpcmUtaW1wb3J0c1xuICAgIGNvbnN0IHBhcnNlZFVybCA9IHJlcXVpcmUoJ3VybCcpLnBhcnNlKGV2ZW50LlJlc3BvbnNlVVJMKTtcbiAgICBjb25zdCByZXF1ZXN0T3B0aW9ucyA9IHtcbiAgICAgIGhvc3RuYW1lOiBwYXJzZWRVcmwuaG9zdG5hbWUsXG4gICAgICBwYXRoOiBwYXJzZWRVcmwucGF0aCxcbiAgICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgICBoZWFkZXJzOiB7ICdjb250ZW50LXR5cGUnOiAnJywgJ2NvbnRlbnQtbGVuZ3RoJzogcmVzcG9uc2VCb2R5Lmxlbmd0aCB9LFxuICAgIH07XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1yZXF1aXJlLWltcG9ydHNcbiAgICAgICAgY29uc3QgcmVxdWVzdCA9IHJlcXVpcmUoJ2h0dHBzJykucmVxdWVzdChyZXF1ZXN0T3B0aW9ucywgcmVzb2x2ZSk7XG4gICAgICAgIHJlcXVlc3Qub24oJ2Vycm9yJywgcmVqZWN0KTtcbiAgICAgICAgcmVxdWVzdC53cml0ZShyZXNwb25zZUJvZHkpO1xuICAgICAgICByZXF1ZXN0LmVuZCgpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICByZWplY3QoZSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBmdW5jdGlvbiBwYXJzZVJldHJ5T3B0aW9ucyhyYXdPcHRpb25zOiBhbnkpOiBTZGtSZXRyeU9wdGlvbnMge1xuICAgIGNvbnN0IHJldHJ5T3B0aW9uczogU2RrUmV0cnlPcHRpb25zID0ge307XG4gICAgaWYgKHJhd09wdGlvbnMpIHtcbiAgICAgIGlmIChyYXdPcHRpb25zLm1heFJldHJpZXMpIHtcbiAgICAgICAgcmV0cnlPcHRpb25zLm1heFJldHJpZXMgPSBwYXJzZUludChyYXdPcHRpb25zLm1heFJldHJpZXMsIDEwKTtcbiAgICAgIH1cbiAgICAgIGlmIChyYXdPcHRpb25zLmJhc2UpIHtcbiAgICAgICAgcmV0cnlPcHRpb25zLnJldHJ5T3B0aW9ucyA9IHtcbiAgICAgICAgICBiYXNlOiBwYXJzZUludChyYXdPcHRpb25zLmJhc2UsIDEwKSxcbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHJldHJ5T3B0aW9ucztcbiAgfVxufVxuIl19
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.ts b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.ts
new file mode 100644
index 0000000000000..b78be3cb5c1ec
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/index.ts
@@ -0,0 +1,186 @@
+/* eslint-disable no-console */
+
+// eslint-disable-next-line import/no-extraneous-dependencies
+import * as AWS from 'aws-sdk';
+// eslint-disable-next-line import/no-extraneous-dependencies
+import type { RetryDelayOptions } from 'aws-sdk/lib/config-base';
+
+interface SdkRetryOptions {
+  maxRetries?: number;
+  retryOptions?: RetryDelayOptions;
+}
+
+/**
+ * Creates a log group and doesn't throw if it exists.
+ *
+ * @param logGroupName the name of the log group to create.
+ * @param region to create the log group in
+ * @param options CloudWatch API SDK options.
+ */
+async function createLogGroupSafe(logGroupName: string, region?: string, options?: SdkRetryOptions) {
+  // If we set the log retention for a lambda, then due to the async nature of
+  // Lambda logging there could be a race condition when the same log group is
+  // already being created by the lambda execution. This can sometime result in
+  // an error "OperationAbortedException: A conflicting operation is currently
+  // in progress...Please try again."
+  // To avoid an error, we do as requested and try again.
+  let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries;
+  const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base;
+  do {
+    try {
+      const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options });
+      await cloudwatchlogs.createLogGroup({ logGroupName }).promise();
+      return;
+    } catch (error) {
+      if (error.code === 'ResourceAlreadyExistsException') {
+        // The log group is already created by the lambda execution
+        return;
+      }
+      if (error.code === 'OperationAbortedException') {
+        if (retryCount > 0) {
+          retryCount--;
+          await new Promise(resolve => setTimeout(resolve, delay));
+          continue;
+        } else {
+          // The log group is still being created by another execution but we are out of retries
+          throw new Error('Out of attempts to create a logGroup');
+        }
+      }
+      throw error;
+    }
+  } while (true); // exit happens on retry count check
+}
+
+/**
+ * Puts or deletes a retention policy on a log group.
+ *
+ * @param logGroupName the name of the log group to create
+ * @param region the region of the log group
+ * @param options CloudWatch API SDK options.
+ * @param retentionInDays the number of days to retain the log events in the specified log group.
+ */
+async function setRetentionPolicy(logGroupName: string, region?: string, options?: SdkRetryOptions, retentionInDays?: number) {
+  // The same as in createLogGroupSafe(), here we could end up with the race
+  // condition where a log group is either already being created or its retention
+  // policy is being updated. This would result in an OperationAbortedException,
+  // which we will try to catch and retry the command a number of times before failing
+  let retryCount = options?.maxRetries == undefined ? 10 : options.maxRetries;
+  const delay = options?.retryOptions?.base == undefined ? 10 : options.retryOptions.base;
+  do {
+    try {
+      const cloudwatchlogs = new AWS.CloudWatchLogs({ apiVersion: '2014-03-28', region, ...options });
+      if (!retentionInDays) {
+        await cloudwatchlogs.deleteRetentionPolicy({ logGroupName }).promise();
+      } else {
+        await cloudwatchlogs.putRetentionPolicy({ logGroupName, retentionInDays }).promise();
+      }
+      return;
+
+    } catch (error) {
+      if (error.code === 'OperationAbortedException') {
+        if (retryCount > 0) {
+          retryCount--;
+          await new Promise(resolve => setTimeout(resolve, delay));
+          continue;
+        } else {
+          // The log group is still being created by another execution but we are out of retries
+          throw new Error('Out of attempts to create a logGroup');
+        }
+      }
+      throw error;
+    }
+  } while (true); // exit happens on retry count check
+}
+
+export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) {
+  try {
+    console.log(JSON.stringify({ ...event, ResponseURL: '...' }));
+
+    // The target log group
+    const logGroupName = event.ResourceProperties.LogGroupName;
+
+    // The region of the target log group
+    const logGroupRegion = event.ResourceProperties.LogGroupRegion;
+
+    // Parse to AWS SDK retry options
+    const retryOptions = parseRetryOptions(event.ResourceProperties.SdkRetry);
+
+    if (event.RequestType === 'Create' || event.RequestType === 'Update') {
+      // Act on the target log group
+      await createLogGroupSafe(logGroupName, logGroupRegion, retryOptions);
+      await setRetentionPolicy(logGroupName, logGroupRegion, retryOptions, parseInt(event.ResourceProperties.RetentionInDays, 10));
+
+      if (event.RequestType === 'Create') {
+        // Set a retention policy of 1 day on the logs of this very function.
+        // Due to the async nature of the log group creation, the log group for this function might
+        // still be not created yet at this point. Therefore we attempt to create it.
+        // In case it is being created, createLogGroupSafe will handle the conflict.
+        const region = process.env.AWS_REGION;
+        await createLogGroupSafe(`/aws/lambda/${context.functionName}`, region, retryOptions);
+        // If createLogGroupSafe fails, the log group is not created even after multiple attempts.
+        // In this case we have nothing to set the retention policy on but an exception will skip
+        // the next line.
+        await setRetentionPolicy(`/aws/lambda/${context.functionName}`, region, retryOptions, 1);
+      }
+    }
+
+    await respond('SUCCESS', 'OK', logGroupName);
+  } catch (e) {
+    console.log(e);
+
+    await respond('FAILED', e.message, event.ResourceProperties.LogGroupName);
+  }
+
+  function respond(responseStatus: string, reason: string, physicalResourceId: string) {
+    const responseBody = JSON.stringify({
+      Status: responseStatus,
+      Reason: reason,
+      PhysicalResourceId: physicalResourceId,
+      StackId: event.StackId,
+      RequestId: event.RequestId,
+      LogicalResourceId: event.LogicalResourceId,
+      Data: {
+        // Add log group name as part of the response so that it's available via Fn::GetAtt
+        LogGroupName: event.ResourceProperties.LogGroupName,
+      },
+    });
+
+    console.log('Responding', responseBody);
+
+    // eslint-disable-next-line @typescript-eslint/no-require-imports
+    const parsedUrl = require('url').parse(event.ResponseURL);
+    const requestOptions = {
+      hostname: parsedUrl.hostname,
+      path: parsedUrl.path,
+      method: 'PUT',
+      headers: { 'content-type': '', 'content-length': responseBody.length },
+    };
+
+    return new Promise((resolve, reject) => {
+      try {
+        // eslint-disable-next-line @typescript-eslint/no-require-imports
+        const request = require('https').request(requestOptions, resolve);
+        request.on('error', reject);
+        request.write(responseBody);
+        request.end();
+      } catch (e) {
+        reject(e);
+      }
+    });
+  }
+
+  function parseRetryOptions(rawOptions: any): SdkRetryOptions {
+    const retryOptions: SdkRetryOptions = {};
+    if (rawOptions) {
+      if (rawOptions.maxRetries) {
+        retryOptions.maxRetries = parseInt(rawOptions.maxRetries, 10);
+      }
+      if (rawOptions.base) {
+        retryOptions.retryOptions = {
+          base: parseInt(rawOptions.base, 10),
+        };
+      }
+    }
+    return retryOptions;
+  }
+}
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/cdk.out b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/cdk.out
new file mode 100644
index 0000000000000..588d7b269d34f
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/cdk.out
@@ -0,0 +1 @@
+{"version":"20.0.0"}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/integ.json b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/integ.json
new file mode 100644
index 0000000000000..2e53939a47b06
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/integ.json
@@ -0,0 +1,11 @@
+{
+  "version": "20.0.0",
+  "testCases": {
+    "Integ/DefaultTest": {
+      "stacks": [
+        "AppSyncIntegLogRetention"
+      ],
+      "assertionStack": "IntegDefaultTestDeployAssert4E6713E1"
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/manifest.json b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/manifest.json
new file mode 100644
index 0000000000000..f5d5110397c91
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/manifest.json
@@ -0,0 +1,184 @@
+{
+  "version": "20.0.0",
+  "artifacts": {
+    "Tree": {
+      "type": "cdk:tree",
+      "properties": {
+        "file": "tree.json"
+      }
+    },
+    "AppSyncIntegLogRetention": {
+      "type": "aws:cloudformation:stack",
+      "environment": "aws://unknown-account/unknown-region",
+      "properties": {
+        "templateFile": "AppSyncIntegLogRetention.template.json",
+        "validateOnSynth": false
+      },
+      "metadata": {
+        "/AppSyncIntegLogRetention": [
+          {
+            "type": "aws:cdk:asset",
+            "data": {
+              "path": "asset.c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a",
+              "id": "c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a",
+              "packaging": "zip",
+              "sourceHash": "c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a",
+              "s3BucketParameter": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3BucketB4787383",
+              "s3KeyParameter": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3VersionKey8CF8E820",
+              "artifactHashParameter": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aArtifactHashC9560B34"
+            }
+          }
+        ],
+        "/AppSyncIntegLogRetention/GraphqlApi/ApiLogsRole/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "GraphqlApiApiLogsRoleBB9E6BAD"
+          }
+        ],
+        "/AppSyncIntegLogRetention/GraphqlApi/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "GraphqlApi1B6CF24C"
+          }
+        ],
+        "/AppSyncIntegLogRetention/GraphqlApi/Schema": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "GraphqlApiSchema1B71CE4F"
+          }
+        ],
+        "/AppSyncIntegLogRetention/GraphqlApi/DefaultApiKey": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "GraphqlApiDefaultApiKey47EE7128"
+          }
+        ],
+        "/AppSyncIntegLogRetention/GraphqlApi/LogRetention/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "GraphqlApiLogRetention082A7017"
+          }
+        ],
+        "/AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB"
+          }
+        ],
+        "/AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB"
+          }
+        ],
+        "/AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Resource": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A"
+          }
+        ],
+        "/AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/S3Bucket": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3BucketB4787383"
+          }
+        ],
+        "/AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/S3VersionKey": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aS3VersionKey8CF8E820"
+          }
+        ],
+        "/AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/ArtifactHash": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParametersc70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13aArtifactHashC9560B34"
+          }
+        ],
+        "/AppSyncIntegLogRetention/Exports/Output{\"Fn::GetAtt\":[\"GraphqlApi1B6CF24C\",\"ApiId\"]}": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "ExportsOutputFnGetAttGraphqlApi1B6CF24CApiIdE34D50AD"
+          }
+        ]
+      },
+      "displayName": "AppSyncIntegLogRetention"
+    },
+    "IntegDefaultTestDeployAssert4E6713E1": {
+      "type": "aws:cloudformation:stack",
+      "environment": "aws://unknown-account/unknown-region",
+      "properties": {
+        "templateFile": "IntegDefaultTestDeployAssert4E6713E1.template.json",
+        "validateOnSynth": false
+      },
+      "dependencies": [
+        "AppSyncIntegLogRetention"
+      ],
+      "metadata": {
+        "/Integ/DefaultTest/DeployAssert": [
+          {
+            "type": "aws:cdk:asset",
+            "data": {
+              "path": "asset.0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd.bundle",
+              "id": "0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd",
+              "packaging": "zip",
+              "sourceHash": "0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd",
+              "s3BucketParameter": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3BucketF94385B7",
+              "s3KeyParameter": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3VersionKey66DB0F9E",
+              "artifactHashParameter": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdArtifactHash2AC894D9"
+            }
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/Default/Default": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AwsApiCallCloudWatchLogsdescribeLogGroups"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/Default/Default": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AwsApiCallCloudWatchLogsdescribeLogGroupsAssertEqualsCloudWatchLogsdescribeLogGroupsD2E2CD37"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/AssertionResults": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssertionResultsAssertEqualsCloudWatchLogsdescribeLogGroups"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "SingletonFunction1488541a7b23466481b69b4408076b81Role37ABCE73"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "SingletonFunction1488541a7b23466481b69b4408076b81HandlerCD40AE9F"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/S3Bucket": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3BucketF94385B7"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/S3VersionKey": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdS3VersionKey66DB0F9E"
+          }
+        ],
+        "/Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/ArtifactHash": [
+          {
+            "type": "aws:cdk:logicalId",
+            "data": "AssetParameters0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbdArtifactHash2AC894D9"
+          }
+        ]
+      },
+      "displayName": "Integ/DefaultTest/DeployAssert"
+    }
+  }
+}
\ No newline at end of file
diff --git a/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/tree.json b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/tree.json
new file mode 100644
index 0000000000000..cd73d74dee554
--- /dev/null
+++ b/packages/@aws-cdk/aws-appsync/test/log-retention.integ.snapshot/tree.json
@@ -0,0 +1,592 @@
+{
+  "version": "tree-0.1",
+  "tree": {
+    "id": "App",
+    "path": "",
+    "children": {
+      "Tree": {
+        "id": "Tree",
+        "path": "Tree",
+        "constructInfo": {
+          "fqn": "constructs.Construct",
+          "version": "10.1.63"
+        }
+      },
+      "AppSyncIntegLogRetention": {
+        "id": "AppSyncIntegLogRetention",
+        "path": "AppSyncIntegLogRetention",
+        "children": {
+          "GraphqlApi": {
+            "id": "GraphqlApi",
+            "path": "AppSyncIntegLogRetention/GraphqlApi",
+            "children": {
+              "ApiLogsRole": {
+                "id": "ApiLogsRole",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/ApiLogsRole",
+                "children": {
+                  "Resource": {
+                    "id": "Resource",
+                    "path": "AppSyncIntegLogRetention/GraphqlApi/ApiLogsRole/Resource",
+                    "attributes": {
+                      "aws:cdk:cloudformation:type": "AWS::IAM::Role",
+                      "aws:cdk:cloudformation:props": {
+                        "assumeRolePolicyDocument": {
+                          "Statement": [
+                            {
+                              "Action": "sts:AssumeRole",
+                              "Effect": "Allow",
+                              "Principal": {
+                                "Service": "appsync.amazonaws.com"
+                              }
+                            }
+                          ],
+                          "Version": "2012-10-17"
+                        },
+                        "managedPolicyArns": [
+                          {
+                            "Fn::Join": [
+                              "",
+                              [
+                                "arn:",
+                                {
+                                  "Ref": "AWS::Partition"
+                                },
+                                ":iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs"
+                              ]
+                            ]
+                          }
+                        ]
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/aws-iam.CfnRole",
+                      "version": "0.0.0"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-iam.Role",
+                  "version": "0.0.0"
+                }
+              },
+              "Resource": {
+                "id": "Resource",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/Resource",
+                "attributes": {
+                  "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLApi",
+                  "aws:cdk:cloudformation:props": {
+                    "authenticationType": "API_KEY",
+                    "name": "IntegLogRetention",
+                    "logConfig": {
+                      "cloudWatchLogsRoleArn": {
+                        "Fn::GetAtt": [
+                          "GraphqlApiApiLogsRoleBB9E6BAD",
+                          "Arn"
+                        ]
+                      },
+                      "fieldLogLevel": "NONE"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-appsync.CfnGraphQLApi",
+                  "version": "0.0.0"
+                }
+              },
+              "Schema": {
+                "id": "Schema",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/Schema",
+                "attributes": {
+                  "aws:cdk:cloudformation:type": "AWS::AppSync::GraphQLSchema",
+                  "aws:cdk:cloudformation:props": {
+                    "apiId": {
+                      "Fn::GetAtt": [
+                        "GraphqlApi1B6CF24C",
+                        "ApiId"
+                      ]
+                    },
+                    "definition": "type test {\n  version: String!\n}\ntype Query {\n  getTests: [test]!\n}\ntype Mutation {\n  addTest(version: String!): test\n}\n"
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-appsync.CfnGraphQLSchema",
+                  "version": "0.0.0"
+                }
+              },
+              "DefaultApiKey": {
+                "id": "DefaultApiKey",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/DefaultApiKey",
+                "attributes": {
+                  "aws:cdk:cloudformation:type": "AWS::AppSync::ApiKey",
+                  "aws:cdk:cloudformation:props": {
+                    "apiId": {
+                      "Fn::GetAtt": [
+                        "GraphqlApi1B6CF24C",
+                        "ApiId"
+                      ]
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-appsync.CfnApiKey",
+                  "version": "0.0.0"
+                }
+              },
+              "LogGroup": {
+                "id": "LogGroup",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/LogGroup",
+                "constructInfo": {
+                  "fqn": "@aws-cdk/core.Resource",
+                  "version": "0.0.0"
+                }
+              },
+              "LogRetention": {
+                "id": "LogRetention",
+                "path": "AppSyncIntegLogRetention/GraphqlApi/LogRetention",
+                "children": {
+                  "Resource": {
+                    "id": "Resource",
+                    "path": "AppSyncIntegLogRetention/GraphqlApi/LogRetention/Resource",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/core.CfnResource",
+                      "version": "0.0.0"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-logs.LogRetention",
+                  "version": "0.0.0"
+                }
+              }
+            },
+            "constructInfo": {
+              "fqn": "@aws-cdk/aws-appsync.GraphqlApi",
+              "version": "0.0.0"
+            }
+          },
+          "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a": {
+            "id": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a",
+            "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a",
+            "children": {
+              "Code": {
+                "id": "Code",
+                "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code",
+                "children": {
+                  "Stage": {
+                    "id": "Stage",
+                    "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code/Stage",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/core.AssetStaging",
+                      "version": "0.0.0"
+                    }
+                  },
+                  "AssetBucket": {
+                    "id": "AssetBucket",
+                    "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Code/AssetBucket",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/aws-s3.BucketBase",
+                      "version": "0.0.0"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-s3-assets.Asset",
+                  "version": "0.0.0"
+                }
+              },
+              "ServiceRole": {
+                "id": "ServiceRole",
+                "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole",
+                "children": {
+                  "Resource": {
+                    "id": "Resource",
+                    "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/Resource",
+                    "attributes": {
+                      "aws:cdk:cloudformation:type": "AWS::IAM::Role",
+                      "aws:cdk:cloudformation:props": {
+                        "assumeRolePolicyDocument": {
+                          "Statement": [
+                            {
+                              "Action": "sts:AssumeRole",
+                              "Effect": "Allow",
+                              "Principal": {
+                                "Service": "lambda.amazonaws.com"
+                              }
+                            }
+                          ],
+                          "Version": "2012-10-17"
+                        },
+                        "managedPolicyArns": [
+                          {
+                            "Fn::Join": [
+                              "",
+                              [
+                                "arn:",
+                                {
+                                  "Ref": "AWS::Partition"
+                                },
+                                ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
+                              ]
+                            ]
+                          }
+                        ]
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/aws-iam.CfnRole",
+                      "version": "0.0.0"
+                    }
+                  },
+                  "DefaultPolicy": {
+                    "id": "DefaultPolicy",
+                    "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy",
+                    "children": {
+                      "Resource": {
+                        "id": "Resource",
+                        "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy/Resource",
+                        "attributes": {
+                          "aws:cdk:cloudformation:type": "AWS::IAM::Policy",
+                          "aws:cdk:cloudformation:props": {
+                            "policyDocument": {
+                              "Statement": [
+                                {
+                                  "Action": [
+                                    "logs:DeleteRetentionPolicy",
+                                    "logs:PutRetentionPolicy"
+                                  ],
+                                  "Effect": "Allow",
+                                  "Resource": "*"
+                                }
+                              ],
+                              "Version": "2012-10-17"
+                            },
+                            "policyName": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB",
+                            "roles": [
+                              {
+                                "Ref": "LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB"
+                              }
+                            ]
+                          }
+                        },
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/aws-iam.CfnPolicy",
+                          "version": "0.0.0"
+                        }
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/aws-iam.Policy",
+                      "version": "0.0.0"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/aws-iam.Role",
+                  "version": "0.0.0"
+                }
+              },
+              "Resource": {
+                "id": "Resource",
+                "path": "AppSyncIntegLogRetention/LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/Resource",
+                "constructInfo": {
+                  "fqn": "@aws-cdk/core.CfnResource",
+                  "version": "0.0.0"
+                }
+              }
+            },
+            "constructInfo": {
+              "fqn": "constructs.Construct",
+              "version": "10.1.63"
+            }
+          },
+          "AssetParameters": {
+            "id": "AssetParameters",
+            "path": "AppSyncIntegLogRetention/AssetParameters",
+            "children": {
+              "c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a": {
+                "id": "c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a",
+                "path": "AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a",
+                "children": {
+                  "S3Bucket": {
+                    "id": "S3Bucket",
+                    "path": "AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/S3Bucket",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/core.CfnParameter",
+                      "version": "0.0.0"
+                    }
+                  },
+                  "S3VersionKey": {
+                    "id": "S3VersionKey",
+                    "path": "AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/S3VersionKey",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/core.CfnParameter",
+                      "version": "0.0.0"
+                    }
+                  },
+                  "ArtifactHash": {
+                    "id": "ArtifactHash",
+                    "path": "AppSyncIntegLogRetention/AssetParameters/c70c1d1695771af4771fd98971e16bb3e6443c62c2994b002b2c3d76e707b13a/ArtifactHash",
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/core.CfnParameter",
+                      "version": "0.0.0"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "constructs.Construct",
+                  "version": "10.1.63"
+                }
+              }
+            },
+            "constructInfo": {
+              "fqn": "constructs.Construct",
+              "version": "10.1.63"
+            }
+          },
+          "Exports": {
+            "id": "Exports",
+            "path": "AppSyncIntegLogRetention/Exports",
+            "children": {
+              "Output{\"Fn::GetAtt\":[\"GraphqlApi1B6CF24C\",\"ApiId\"]}": {
+                "id": "Output{\"Fn::GetAtt\":[\"GraphqlApi1B6CF24C\",\"ApiId\"]}",
+                "path": "AppSyncIntegLogRetention/Exports/Output{\"Fn::GetAtt\":[\"GraphqlApi1B6CF24C\",\"ApiId\"]}",
+                "constructInfo": {
+                  "fqn": "@aws-cdk/core.CfnOutput",
+                  "version": "0.0.0"
+                }
+              }
+            },
+            "constructInfo": {
+              "fqn": "constructs.Construct",
+              "version": "10.1.63"
+            }
+          }
+        },
+        "constructInfo": {
+          "fqn": "@aws-cdk/core.Stack",
+          "version": "0.0.0"
+        }
+      },
+      "Integ": {
+        "id": "Integ",
+        "path": "Integ",
+        "children": {
+          "DefaultTest": {
+            "id": "DefaultTest",
+            "path": "Integ/DefaultTest",
+            "children": {
+              "Default": {
+                "id": "Default",
+                "path": "Integ/DefaultTest/Default",
+                "constructInfo": {
+                  "fqn": "constructs.Construct",
+                  "version": "10.1.63"
+                }
+              },
+              "DeployAssert": {
+                "id": "DeployAssert",
+                "path": "Integ/DefaultTest/DeployAssert",
+                "children": {
+                  "AwsApiCallCloudWatchLogsdescribeLogGroups": {
+                    "id": "AwsApiCallCloudWatchLogsdescribeLogGroups",
+                    "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups",
+                    "children": {
+                      "SdkProvider": {
+                        "id": "SdkProvider",
+                        "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/SdkProvider",
+                        "children": {
+                          "AssertionsProvider": {
+                            "id": "AssertionsProvider",
+                            "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/SdkProvider/AssertionsProvider",
+                            "constructInfo": {
+                              "fqn": "constructs.Construct",
+                              "version": "10.1.63"
+                            }
+                          }
+                        },
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/integ-tests.AssertionsProvider",
+                          "version": "0.0.0"
+                        }
+                      },
+                      "Default": {
+                        "id": "Default",
+                        "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/Default",
+                        "children": {
+                          "Default": {
+                            "id": "Default",
+                            "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/Default/Default",
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CfnResource",
+                              "version": "0.0.0"
+                            }
+                          }
+                        },
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/core.CustomResource",
+                          "version": "0.0.0"
+                        }
+                      },
+                      "AssertEqualsCloudWatchLogsdescribeLogGroups": {
+                        "id": "AssertEqualsCloudWatchLogsdescribeLogGroups",
+                        "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups",
+                        "children": {
+                          "AssertionProvider": {
+                            "id": "AssertionProvider",
+                            "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/AssertionProvider",
+                            "children": {
+                              "AssertionsProvider": {
+                                "id": "AssertionsProvider",
+                                "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/AssertionProvider/AssertionsProvider",
+                                "constructInfo": {
+                                  "fqn": "constructs.Construct",
+                                  "version": "10.1.63"
+                                }
+                              }
+                            },
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/integ-tests.AssertionsProvider",
+                              "version": "0.0.0"
+                            }
+                          },
+                          "Default": {
+                            "id": "Default",
+                            "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/Default",
+                            "children": {
+                              "Default": {
+                                "id": "Default",
+                                "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/Default/Default",
+                                "constructInfo": {
+                                  "fqn": "@aws-cdk/core.CfnResource",
+                                  "version": "0.0.0"
+                                }
+                              }
+                            },
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CustomResource",
+                              "version": "0.0.0"
+                            }
+                          },
+                          "AssertionResults": {
+                            "id": "AssertionResults",
+                            "path": "Integ/DefaultTest/DeployAssert/AwsApiCallCloudWatchLogsdescribeLogGroups/AssertEqualsCloudWatchLogsdescribeLogGroups/AssertionResults",
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CfnOutput",
+                              "version": "0.0.0"
+                            }
+                          }
+                        },
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/integ-tests.EqualsAssertion",
+                          "version": "0.0.0"
+                        }
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "@aws-cdk/integ-tests.AwsApiCall",
+                      "version": "0.0.0"
+                    }
+                  },
+                  "SingletonFunction1488541a7b23466481b69b4408076b81": {
+                    "id": "SingletonFunction1488541a7b23466481b69b4408076b81",
+                    "path": "Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81",
+                    "children": {
+                      "Staging": {
+                        "id": "Staging",
+                        "path": "Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Staging",
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/core.AssetStaging",
+                          "version": "0.0.0"
+                        }
+                      },
+                      "Role": {
+                        "id": "Role",
+                        "path": "Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Role",
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/core.CfnResource",
+                          "version": "0.0.0"
+                        }
+                      },
+                      "Handler": {
+                        "id": "Handler",
+                        "path": "Integ/DefaultTest/DeployAssert/SingletonFunction1488541a7b23466481b69b4408076b81/Handler",
+                        "constructInfo": {
+                          "fqn": "@aws-cdk/core.CfnResource",
+                          "version": "0.0.0"
+                        }
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "constructs.Construct",
+                      "version": "10.1.63"
+                    }
+                  },
+                  "AssetParameters": {
+                    "id": "AssetParameters",
+                    "path": "Integ/DefaultTest/DeployAssert/AssetParameters",
+                    "children": {
+                      "0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd": {
+                        "id": "0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd",
+                        "path": "Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd",
+                        "children": {
+                          "S3Bucket": {
+                            "id": "S3Bucket",
+                            "path": "Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/S3Bucket",
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CfnParameter",
+                              "version": "0.0.0"
+                            }
+                          },
+                          "S3VersionKey": {
+                            "id": "S3VersionKey",
+                            "path": "Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/S3VersionKey",
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CfnParameter",
+                              "version": "0.0.0"
+                            }
+                          },
+                          "ArtifactHash": {
+                            "id": "ArtifactHash",
+                            "path": "Integ/DefaultTest/DeployAssert/AssetParameters/0d8d96305807ac805d23c6d7b279eef238715605efad63c839ad1e7e8236bdbd/ArtifactHash",
+                            "constructInfo": {
+                              "fqn": "@aws-cdk/core.CfnParameter",
+                              "version": "0.0.0"
+                            }
+                          }
+                        },
+                        "constructInfo": {
+                          "fqn": "constructs.Construct",
+                          "version": "10.1.63"
+                        }
+                      }
+                    },
+                    "constructInfo": {
+                      "fqn": "constructs.Construct",
+                      "version": "10.1.63"
+                    }
+                  }
+                },
+                "constructInfo": {
+                  "fqn": "@aws-cdk/core.Stack",
+                  "version": "0.0.0"
+                }
+              }
+            },
+            "constructInfo": {
+              "fqn": "@aws-cdk/integ-tests.IntegTestCase",
+              "version": "0.0.0"
+            }
+          }
+        },
+        "constructInfo": {
+          "fqn": "@aws-cdk/integ-tests.IntegTest",
+          "version": "0.0.0"
+        }
+      }
+    },
+    "constructInfo": {
+      "fqn": "@aws-cdk/core.App",
+      "version": "0.0.0"
+    }
+  }
+}
\ No newline at end of file