{
 "Resources": {
  "tests3lambdaLambdaFunctionServiceRoleA74F4427": {
   "Type": "AWS::IAM::Role",
   "Properties": {
    "AssumeRolePolicyDocument": {
     "Statement": [
      {
       "Action": "sts:AssumeRole",
       "Effect": "Allow",
       "Principal": {
        "Service": "lambda.amazonaws.com"
       }
      }
     ],
     "Version": "2012-10-17"
    },
    "Policies": [
     {
      "PolicyDocument": {
       "Statement": [
        {
         "Action": [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
         ],
         "Effect": "Allow",
         "Resource": {
          "Fn::Join": [
           "",
           [
            "arn:",
            {
             "Ref": "AWS::Partition"
            },
            ":logs:",
            {
             "Ref": "AWS::Region"
            },
            ":",
            {
             "Ref": "AWS::AccountId"
            },
            ":log-group:/aws/lambda/*"
           ]
          ]
         }
        }
       ],
       "Version": "2012-10-17"
      },
      "PolicyName": "LambdaFunctionServiceRolePolicy"
     }
    ]
   },
   "Metadata": {
    "guard": {
     "SuppressedRules": [
      "IAM_NO_INLINE_POLICY_CHECK"
     ]
    }
   }
  },
  "tests3lambdaLambdaFunctionB56B7023": {
   "Type": "AWS::Lambda::Function",
   "Properties": {
    "Code": {
     "ZipFile": "exports.handler = async (event) => { console.log(event); return {statusCode: 200, body: \"\"}; }"
    },
    "Environment": {
     "Variables": {
      "AWS_NODEJS_CONNECTION_REUSE_ENABLED": "1"
     }
    },
    "Handler": "index.handler",
    "Role": {
     "Fn::GetAtt": [
      "tests3lambdaLambdaFunctionServiceRoleA74F4427",
      "Arn"
     ]
    },
    "Runtime": "nodejs22.x",
    "TracingConfig": {
     "Mode": "Active"
    }
   },
   "DependsOn": [
    "tests3lambdaLambdaFunctionServiceRoleA74F4427"
   ],
   "Metadata": {
    "cfn_nag": {
     "rules_to_suppress": [
      {
       "id": "W58",
       "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
      },
      {
       "id": "W89",
       "reason": "This is not a rule for the general case, just for specific use cases/industries"
      },
      {
       "id": "W92",
       "reason": "Impossible for us to define the correct concurrency for clients"
      }
     ]
    }
   }
  },
  "tests3lambdaLambdaFunctioninlinePolicyAddedToExecutionRole0D4909C11": {
   "Type": "AWS::IAM::Policy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": [
        "xray:PutTelemetryRecords",
        "xray:PutTraceSegments"
       ],
       "Effect": "Allow",
       "Resource": "*"
      }
     ],
     "Version": "2012-10-17"
    },
    "PolicyName": "tests3lambdaLambdaFunctioninlinePolicyAddedToExecutionRole0D4909C11",
    "Roles": [
     {
      "Ref": "tests3lambdaLambdaFunctionServiceRoleA74F4427"
     }
    ]
   }
  },
  "tests3lambdaS3LoggingBucket0C3BBFDC": {
   "Type": "AWS::S3::Bucket",
   "Properties": {
    "BucketEncryption": {
     "ServerSideEncryptionConfiguration": [
      {
       "ServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
       }
      }
     ]
    },
    "PublicAccessBlockConfiguration": {
     "BlockPublicAcls": true,
     "BlockPublicPolicy": true,
     "IgnorePublicAcls": true,
     "RestrictPublicBuckets": true
    },
    "Tags": [
     {
      "Key": "aws-cdk:auto-delete-objects",
      "Value": "true"
     }
    ],
    "VersioningConfiguration": {
     "Status": "Enabled"
    }
   },
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete",
   "Metadata": {
    "cfn_nag": {
     "rules_to_suppress": [
      {
       "id": "W35",
       "reason": "This S3 bucket is used as the access logging bucket for another bucket"
      }
     ]
    }
   }
  },
  "tests3lambdaS3LoggingBucketPolicyC349F74C": {
   "Type": "AWS::S3::BucketPolicy",
   "Properties": {
    "Bucket": {
     "Ref": "tests3lambdaS3LoggingBucket0C3BBFDC"
    },
    "PolicyDocument": {
     "Statement": [
      {
       "Action": "s3:*",
       "Condition": {
        "Bool": {
         "aws:SecureTransport": "false"
        }
       },
       "Effect": "Deny",
       "Principal": {
        "AWS": "*"
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "tests3lambdaS3LoggingBucket0C3BBFDC",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "tests3lambdaS3LoggingBucket0C3BBFDC",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      },
      {
       "Action": [
        "s3:DeleteObject*",
        "s3:GetBucket*",
        "s3:List*",
        "s3:PutBucketPolicy"
       ],
       "Effect": "Allow",
       "Principal": {
        "AWS": {
         "Fn::GetAtt": [
          "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
          "Arn"
         ]
        }
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "tests3lambdaS3LoggingBucket0C3BBFDC",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "tests3lambdaS3LoggingBucket0C3BBFDC",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      },
      {
       "Action": "s3:PutObject",
       "Condition": {
        "ArnLike": {
         "aws:SourceArn": {
          "Fn::GetAtt": [
           "tests3lambdaS3BucketBE7C1B8E",
           "Arn"
          ]
         }
        },
        "StringEquals": {
         "aws:SourceAccount": {
          "Ref": "AWS::AccountId"
         }
        }
       },
       "Effect": "Allow",
       "Principal": {
        "Service": "logging.s3.amazonaws.com"
       },
       "Resource": {
        "Fn::Join": [
         "",
         [
          {
           "Fn::GetAtt": [
            "tests3lambdaS3LoggingBucket0C3BBFDC",
            "Arn"
           ]
          },
          "/*"
         ]
        ]
       }
      }
     ],
     "Version": "2012-10-17"
    }
   }
  },
  "tests3lambdaS3LoggingBucketAutoDeleteObjectsCustomResource323E9779": {
   "Type": "Custom::S3AutoDeleteObjects",
   "Properties": {
    "ServiceToken": {
     "Fn::GetAtt": [
      "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F",
      "Arn"
     ]
    },
    "BucketName": {
     "Ref": "tests3lambdaS3LoggingBucket0C3BBFDC"
    }
   },
   "DependsOn": [
    "tests3lambdaS3LoggingBucketPolicyC349F74C"
   ],
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete"
  },
  "tests3lambdaS3BucketBE7C1B8E": {
   "Type": "AWS::S3::Bucket",
   "Properties": {
    "BucketEncryption": {
     "ServerSideEncryptionConfiguration": [
      {
       "ServerSideEncryptionByDefault": {
        "SSEAlgorithm": "AES256"
       }
      }
     ]
    },
    "LifecycleConfiguration": {
     "Rules": [
      {
       "NoncurrentVersionTransitions": [
        {
         "StorageClass": "GLACIER",
         "TransitionInDays": 90
        }
       ],
       "Status": "Enabled"
      }
     ]
    },
    "LoggingConfiguration": {
     "DestinationBucketName": {
      "Ref": "tests3lambdaS3LoggingBucket0C3BBFDC"
     }
    },
    "PublicAccessBlockConfiguration": {
     "BlockPublicAcls": true,
     "BlockPublicPolicy": true,
     "IgnorePublicAcls": true,
     "RestrictPublicBuckets": true
    },
    "VersioningConfiguration": {
     "Status": "Enabled"
    }
   },
   "UpdateReplacePolicy": "Delete",
   "DeletionPolicy": "Delete",
   "Metadata": {
    "cfn_nag": {
     "rules_to_suppress": [
      {
       "id": "W35",
       "reason": "This S3 bucket is created for unit/ integration testing purposes only."
      }
     ]
    }
   }
  },
  "tests3lambdaS3BucketPolicyE0402ABD": {
   "Type": "AWS::S3::BucketPolicy",
   "Properties": {
    "Bucket": {
     "Ref": "tests3lambdaS3BucketBE7C1B8E"
    },
    "PolicyDocument": {
     "Statement": [
      {
       "Action": "s3:*",
       "Condition": {
        "Bool": {
         "aws:SecureTransport": "false"
        }
       },
       "Effect": "Deny",
       "Principal": {
        "AWS": "*"
       },
       "Resource": [
        {
         "Fn::GetAtt": [
          "tests3lambdaS3BucketBE7C1B8E",
          "Arn"
         ]
        },
        {
         "Fn::Join": [
          "",
          [
           {
            "Fn::GetAtt": [
             "tests3lambdaS3BucketBE7C1B8E",
             "Arn"
            ]
           },
           "/*"
          ]
         ]
        }
       ]
      }
     ],
     "Version": "2012-10-17"
    }
   }
  },
  "tests3lambdaS3BucketNotificationsHandlerPolicy9A4507C7": {
   "Type": "AWS::IAM::Policy",
   "Properties": {
    "PolicyDocument": {
     "Statement": [
      {
       "Action": "s3:PutBucketNotification",
       "Effect": "Allow",
       "Resource": {
        "Fn::GetAtt": [
         "tests3lambdaS3BucketBE7C1B8E",
         "Arn"
        ]
       }
      }
     ],
     "Version": "2012-10-17"
    },
    "PolicyName": "tests3lambdaS3BucketNotificationsHandlerPolicy9A4507C7",
    "Roles": [
     {
      "Ref": "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC"
     }
    ]
   }
  },
  "tests3lambdaS3BucketNotifications1943E9B3": {
   "Type": "Custom::S3BucketNotifications",
   "Properties": {
    "ServiceToken": {
     "Fn::GetAtt": [
      "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691",
      "Arn"
     ]
    },
    "BucketName": {
     "Ref": "tests3lambdaS3BucketBE7C1B8E"
    },
    "NotificationConfiguration": {
     "LambdaFunctionConfigurations": [
      {
       "Events": [
        "s3:ObjectCreated:*"
       ],
       "LambdaFunctionArn": {
        "Fn::GetAtt": [
         "tests3lambdaLambdaFunctionB56B7023",
         "Arn"
        ]
       }
      }
     ]
    },
    "Managed": true,
    "SkipDestinationValidation": false
   },
   "DependsOn": [
    "tests3lambdaS3BucketAllowBucketNotificationsTos3lamnoargumentstests3lambdaLambdaFunction7F4DB5A1D2077883",
    "tests3lambdaS3BucketNotificationsHandlerPolicy9A4507C7",
    "tests3lambdaS3BucketPolicyE0402ABD"
   ]
  },
  "tests3lambdaS3BucketAllowBucketNotificationsTos3lamnoargumentstests3lambdaLambdaFunction7F4DB5A1D2077883": {
   "Type": "AWS::Lambda::Permission",
   "Properties": {
    "Action": "lambda:InvokeFunction",
    "FunctionName": {
     "Fn::GetAtt": [
      "tests3lambdaLambdaFunctionB56B7023",
      "Arn"
     ]
    },
    "Principal": "s3.amazonaws.com",
    "SourceAccount": {
     "Ref": "AWS::AccountId"
    },
    "SourceArn": {
     "Fn::GetAtt": [
      "tests3lambdaS3BucketBE7C1B8E",
      "Arn"
     ]
    }
   }
  },
  "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092": {
   "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"
     }
    ]
   }
  },
  "CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F": {
   "Type": "AWS::Lambda::Function",
   "Properties": {
    "Code": {
     "S3Bucket": {
      "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
     },
     "S3Key": "faa95a81ae7d7373f3e1f242268f904eb748d8d0fdd306e8a6fe515a1905a7d6.zip"
    },
    "Timeout": 900,
    "MemorySize": 128,
    "Handler": "index.handler",
    "Role": {
     "Fn::GetAtt": [
      "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092",
      "Arn"
     ]
    },
    "Runtime": "nodejs24.x",
    "Description": {
     "Fn::Join": [
      "",
      [
       "Lambda function for auto-deleting objects in ",
       {
        "Ref": "tests3lambdaS3LoggingBucket0C3BBFDC"
       },
       " S3 bucket."
      ]
     ]
    }
   },
   "DependsOn": [
    "CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092"
   ],
   "Metadata": {
    "cfn_nag": {
     "rules_to_suppress": [
      {
       "id": "W58",
       "reason": "CDK generated custom resource"
      },
      {
       "id": "W89",
       "reason": "CDK generated custom resource"
      },
      {
       "id": "W92",
       "reason": "CDK generated custom resource"
      }
     ]
    }
   }
  },
  "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC": {
   "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"
       ]
      ]
     }
    ]
   }
  },
  "BucketNotificationsHandler050a0587b7544547bf325f094a3db8347ECC3691": {
   "Type": "AWS::Lambda::Function",
   "Properties": {
    "Description": "AWS CloudFormation handler for \"Custom::S3BucketNotifications\" resources (@aws-cdk/aws-s3)",
    "Code": {
     "ZipFile": "import boto3  # type: ignore\nimport json\nimport logging\nimport urllib.request\n\ns3 = boto3.client(\"s3\")\n\nEVENTBRIDGE_CONFIGURATION = 'EventBridgeConfiguration'\nCONFIGURATION_TYPES = [\"TopicConfigurations\", \"QueueConfigurations\", \"LambdaFunctionConfigurations\"]\n\ndef handler(event: dict, context):\n  response_status = \"SUCCESS\"\n  error_message = \"\"\n  try:\n    props = event[\"ResourceProperties\"]\n    notification_configuration = props[\"NotificationConfiguration\"]\n    managed = props.get('Managed', 'true').lower() == 'true'\n    skipDestinationValidation = props.get('SkipDestinationValidation', 'false').lower() == 'true'\n    stack_id = event['StackId']\n    old = event.get(\"OldResourceProperties\", {}).get(\"NotificationConfiguration\", {})\n    if managed:\n      config = handle_managed(event[\"RequestType\"], notification_configuration)\n    else:\n      config = handle_unmanaged(props[\"BucketName\"], stack_id, event[\"RequestType\"], notification_configuration, old)\n    s3.put_bucket_notification_configuration(Bucket=props[\"BucketName\"], NotificationConfiguration=config, SkipDestinationValidation=skipDestinationValidation)\n  except Exception as e:\n    logging.exception(\"Failed to put bucket notification configuration\")\n    response_status = \"FAILED\"\n    error_message = f\"Error: {str(e)}. \"\n  finally:\n    submit_response(event, context, response_status, error_message)\n\ndef handle_managed(request_type, notification_configuration):\n  if request_type == 'Delete':\n    return {}\n  return notification_configuration\n\ndef handle_unmanaged(bucket, stack_id, request_type, notification_configuration, old):\n  def get_id(n):\n    n['Id'] = ''\n    sorted_notifications = sort_filter_rules(n)\n    strToHash=json.dumps(sorted_notifications, sort_keys=True).replace('\"Name\": \"prefix\"', '\"Name\": \"Prefix\"').replace('\"Name\": \"suffix\"', '\"Name\": \"Suffix\"')\n    return f\"{stack_id}-{hash(strToHash)}\"\n  def with_id(n):\n    n['Id'] = get_id(n)\n    return n\n\n  external_notifications = {}\n  existing_notifications = s3.get_bucket_notification_configuration(Bucket=bucket)\n  for t in CONFIGURATION_TYPES:\n    if request_type == 'Update':\n        old_incoming_ids = [get_id(n) for n in old.get(t, [])]\n        external_notifications[t] = [n for n in existing_notifications.get(t, []) if not get_id(n) in old_incoming_ids]      \n    elif request_type == 'Delete':\n        external_notifications[t] = [n for n in existing_notifications.get(t, []) if not n['Id'].startswith(f\"{stack_id}-\")]\n    elif request_type == 'Create':\n        external_notifications[t] = [n for n in existing_notifications.get(t, [])]\n  if EVENTBRIDGE_CONFIGURATION in existing_notifications:\n    external_notifications[EVENTBRIDGE_CONFIGURATION] = existing_notifications[EVENTBRIDGE_CONFIGURATION]\n\n  if request_type == 'Delete':\n    return external_notifications\n\n  notifications = {}\n  for t in CONFIGURATION_TYPES:\n    external = external_notifications.get(t, [])\n    incoming = [with_id(n) for n in notification_configuration.get(t, [])]\n    notifications[t] = external + incoming\n\n  if EVENTBRIDGE_CONFIGURATION in notification_configuration:\n    notifications[EVENTBRIDGE_CONFIGURATION] = notification_configuration[EVENTBRIDGE_CONFIGURATION]\n  elif EVENTBRIDGE_CONFIGURATION in external_notifications:\n    notifications[EVENTBRIDGE_CONFIGURATION] = external_notifications[EVENTBRIDGE_CONFIGURATION]\n\n  return notifications\n\ndef submit_response(event: dict, context, response_status: str, error_message: str):\n  response_body = json.dumps(\n    {\n      \"Status\": response_status,\n      \"Reason\": f\"{error_message}See the details in CloudWatch Log Stream: {context.log_stream_name}\",\n      \"PhysicalResourceId\": event.get(\"PhysicalResourceId\") or event[\"LogicalResourceId\"],\n      \"StackId\": event[\"StackId\"],\n      \"RequestId\": event[\"RequestId\"],\n      \"LogicalResourceId\": event[\"LogicalResourceId\"],\n      \"NoEcho\": False,\n    }\n  ).encode(\"utf-8\")\n  headers = {\"content-type\": \"\", \"content-length\": str(len(response_body))}\n  try:\n    req = urllib.request.Request(url=event[\"ResponseURL\"], headers=headers, data=response_body, method=\"PUT\")\n    with urllib.request.urlopen(req) as response:\n      print(response.read().decode(\"utf-8\"))\n    print(\"Status code: \" + response.reason)\n  except Exception as e:\n      print(\"send(..) failed executing request.urlopen(..): \" + str(e))\n\ndef sort_filter_rules(json_obj):\n  if not isinstance(json_obj, dict):\n      return json_obj\n  for key, value in json_obj.items():\n      if isinstance(value, dict):\n          json_obj[key] = sort_filter_rules(value)\n      elif isinstance(value, list):\n          json_obj[key] = [sort_filter_rules(item) for item in value]\n  if \"Filter\" in json_obj and \"Key\" in json_obj[\"Filter\"] and \"FilterRules\" in json_obj[\"Filter\"][\"Key\"]:\n      filter_rules = json_obj[\"Filter\"][\"Key\"][\"FilterRules\"]\n      sorted_filter_rules = sorted(filter_rules, key=lambda x: x[\"Name\"])\n      json_obj[\"Filter\"][\"Key\"][\"FilterRules\"] = sorted_filter_rules\n  return json_obj"
    },
    "Handler": "index.handler",
    "Role": {
     "Fn::GetAtt": [
      "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC",
      "Arn"
     ]
    },
    "Runtime": "python3.13",
    "Timeout": 300
   },
   "DependsOn": [
    "BucketNotificationsHandler050a0587b7544547bf325f094a3db834RoleB6FB88EC"
   ],
   "Metadata": {
    "cfn_nag": {
     "rules_to_suppress": [
      {
       "id": "W58",
       "reason": "Lambda functions has the required permission to write CloudWatch Logs. It uses custom policy instead of arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole with tighter permissions."
      },
      {
       "id": "W89",
       "reason": "This is not a rule for the general case, just for specific use cases/industries"
      },
      {
       "id": "W92",
       "reason": "Impossible for us to define the correct concurrency for clients"
      }
     ]
    }
   }
  }
 },
 "Parameters": {
  "BootstrapVersion": {
   "Type": "AWS::SSM::Parameter::Value<String>",
   "Default": "/cdk-bootstrap/hnb659fds/version",
   "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
  }
 },
 "Rules": {
  "CheckBootstrapVersion": {
   "Assertions": [
    {
     "Assert": {
      "Fn::Not": [
       {
        "Fn::Contains": [
         [
          "1",
          "2",
          "3",
          "4",
          "5"
         ],
         {
          "Ref": "BootstrapVersion"
         }
        ]
       }
      ]
     },
     "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
    }
   ]
  }
 }
}