AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AWS GenAI IDP Accelerator (uksb-r8evguc4p9) (SO9027) (v0.5.4)
Mappings:
  AppSyncLogLevelMap:
    DEBUG:
      FieldLogLevel: ALL
    INFO:
      FieldLogLevel: ALL
    WARN:
      FieldLogLevel: ERROR
    ERROR:
      FieldLogLevel: ERROR
    CRITICAL:
      FieldLogLevel: ERROR
  CrawlerScheduleMap:
    Manual:
      ScheduleExpression: ''
    Every15m:
      ScheduleExpression: cron(0/15 * * * ? *)
    EveryHour:
      ScheduleExpression: cron(0 * * * ? *)
    EveryDay:
      ScheduleExpression: cron(0 0 * * ? *)
  ConfigurationMap:
    lending-package-sample:
      ConfigPath: lending-package-sample
    rvl-cdip:
      ConfigPath: rvl-cdip
    rvl-cdip-with-few-shot-examples:
      ConfigPath: rvl-cdip-with-few-shot-examples
    bank-statement-sample:
      ConfigPath: bank-statement-sample
    docsplit:
      ConfigPath: docsplit
    realkie-fcc-verified:
      ConfigPath: realkie-fcc-verified
    ocr-benchmark:
      ConfigPath: ocr-benchmark
    rule-validation:
      ConfigPath: rule-validation
    rule-extraction:
      ConfigPath: rule-extraction
Parameters:
  AdminEmail:
    Type: String
    Description: Email address of the admin user (e.g. jdoe+admin@example.com).  An
      initial temporary password is automatically sent to this user via email.
    AllowedPattern: ^[\w.+-]+@([\w-]+\.)+[\w-]{2,6}$
  AllowedSignUpEmailDomain:
    Type: String
    Default: ''
    Description: Email address domain (example.com) or comma separated list of email
      domains (example1.com, example2.com) allowed to signup using the web UI. If
      left empty, signup via the web UI is disabled and users will have to be created
      using Cognito.
    AllowedPattern: ^(|([\w-]+\.)+[\w-]{2,6}(, *([\w-]+\.)+[\w-]{2,6})*)$
    ConstraintDescription: Must be empty or a list of comma separated email domains
      (example1.com, example2.com)
  CustomConfigPath:
    Type: String
    Default: ''
    Description: S3 URI pointing to your custom configuration YAML file. When provided,
      this configuration overrides the selected pattern preset and applies to all
      processing patterns.  Leave blank to use the selected pattern configuration
      preset. For example s3://my-bucket/custom-config/config.yaml
    AllowedPattern: ^(|s3://.*)$
    ConstraintDescription: Must be empty or a valid S3 URI (e.g., s3://my-bucket/custom-config/config.yaml)
  ConfigurationPreset:
    Type: String
    Default: lending-package-sample
    AllowedValues:
    - lending-package-sample
    - rvl-cdip
    - rvl-cdip-with-few-shot-examples
    - bank-statement-sample
    - docsplit
    - realkie-fcc-verified
    - ocr-benchmark
    - rule-validation
    - rule-extraction
    Description: 'Select the configuration preset. Each configuration contains pre-tuned
      settings for specific document processing scenarios - see https://github.com/aws-samples/sample-genai-idp/blob/main/config_library/README.md.
      Note: Custom Configuration Path overrides this when provided.'
  Pattern2CustomClassificationModelARN:
    Type: String
    Default: ''
    Description: Specify Custom Fine Tuned Classification Model ARN. Leave blank to
      use pretrained model specified in the selected configuration.
  Pattern2CustomExtractionModelARN:
    Type: String
    Default: ''
    Description: Specify Custom Fine Tuned Extraction Model ARN. Leave blank to use
      pretrained model specified in the selected configuration.
  EvaluationBaselineBucketName:
    Type: String
    Default: ''
    Description: '(Optional) Existing bucket used for baseline (ground truth) data
      for processed documents. Baseline data is used to asess the accuracy  of the
      processing pattern outputs.  Provide the name of an existing bucket here. Hint:
      you can use the Output bucket of another GenAIIDP stack to compare the outputs
      from  different patterns and prompts.  Leave blank to automatically create an
      empty bucket where you can (optionally) place your validated baseline data later.'
  ReportingBucketName:
    Type: String
    Default: ''
    Description: (Optional) Existing bucket used for analytics. (Currently used for
      evaluation analytics) Provide the name of an existing bucket here or leave blank
      to automatically create a new bucket.
  DocumentSectionsCrawlerFrequency:
    Type: String
    Default: EveryDay
    AllowedValues:
    - Manual
    - Every15m
    - EveryHour
    - EveryDay
    Description: Frequency for the Glue Crawler to run and discover new document section
      tables and partitions. Manual requires manual execution, other options run automatically
      at the specified interval.
  EnableXRayTracing:
    Type: String
    Default: 'true'
    AllowedValues:
    - 'true'
    - 'false'
    Description: Enable X-Ray tracing
  EnableMCP:
    Type: String
    Default: 'true'
    AllowedValues:
    - 'true'
    - 'false'
    Description: Enable Model Context Protocol (MCP) integration for external application
      access via AWS Bedrock AgentCore Gateway
  EnableMLflow:
    Type: String
    Default: 'false'
    AllowedValues:
    - 'true'
    - 'false'
    Description: Enable MLflow experiment tracking integration. MLflow is an open-source
      platform for managing the ML lifecycle, including experiment tracking, metrics
      logging, and model performance monitoring. When enabled, document processing
      metrics and model performance data are logged to a SageMaker MLflow tracking
      server for analysis and comparison.
  MlflowTrackingURI:
    Type: String
    Default: ''
    Description: '(Required if EnableMLflow is true) SageMaker MLflow tracking server
      ARN for logging metrics and experiments. Format: arn:<partition>:sagemaker:<region>:<account-id>:mlflow-tracking-server/<server-name>
      where <partition> is ''aws'' for standard regions or ''aws-us-gov'' for GovCloud.'
    AllowedPattern: ^(|arn:aws[a-z-]*:sagemaker:[a-z0-9-]+:[0-9]{12}:mlflow-tracking-server/.+)$
    ConstraintDescription: Must be empty or a valid SageMaker MLflow tracking server
      ARN
  DocumentKnowledgeBase:
    Default: BEDROCK_KNOWLEDGE_BASE (Create)
    Type: String
    AllowedValues:
    - DISABLED
    - BEDROCK_KNOWLEDGE_BASE (Create)
    Description: Use document processing results as knowledge base content. Model
      access for amazon.titan-embed-text-v2:0 MUST be enabled in Amazon Bedrock.
  KnowledgeBaseVectorStore:
    Type: String
    Default: S3_VECTORS
    AllowedValues:
    - OPENSEARCH_SERVERLESS
    - S3_VECTORS
    Description: Choose vector store type for Bedrock Knowledge Base. S3 Vectors (default)
      for cost-effective storage with sub-second latency, OpenSearch Serverless for
      sub-millisecond queries.
  KnowledgeBaseModelId:
    Type: String
    Default: us.amazon.nova-pro-v1:0
    AllowedValues:
    - us.amazon.nova-lite-v1:0
    - us.amazon.nova-pro-v1:0
    - us.amazon.nova-premier-v1:0
    - us.amazon.nova-2-lite-v1:0
    - us.amazon.nova-2-lite-v1:0:priority
    - us.amazon.nova-2-lite-v1:0:flex
    - global.amazon.nova-2-lite-v1:0
    - global.amazon.nova-2-lite-v1:0:priority
    - global.amazon.nova-2-lite-v1:0:flex
    - us.anthropic.claude-3-haiku-20240307-v1:0
    - us.anthropic.claude-3-5-haiku-20241022-v1:0
    - us.anthropic.claude-3-5-sonnet-20241022-v2:0
    - us.anthropic.claude-3-7-sonnet-20250219-v1:0
    - us.anthropic.claude-sonnet-4-20250514-v1:0
    - us.anthropic.claude-sonnet-4-5-20250929-v1:0
    - eu.amazon.nova-lite-v1:0
    - eu.amazon.nova-pro-v1:0
    - eu.amazon.nova-2-lite-v1:0
    - eu.amazon.nova-2-lite-v1:0:priority
    - eu.amazon.nova-2-lite-v1:0:flex
    - eu.anthropic.claude-3-haiku-20240307-v1:0
    - eu.anthropic.claude-3-5-sonnet-20241022-v2:0
    - eu.anthropic.claude-3-7-sonnet-20250219-v1:0
    - eu.anthropic.claude-sonnet-4-20250514-v1:0
    - eu.anthropic.claude-sonnet-4-5-20250929-v1:0
    - qwen.qwen3-vl-235b-a22b
    - global.amazon.nova-2-lite-v1:0
    - global.anthropic.claude-haiku-4-5-v1:0
    - global.anthropic.claude-sonnet-4-5-v1:0
    Description: Model to use for optional Document Knowledge Base. Model access MUST
      be enabled in Amazon Bedrock. Note - Nova Premier may not be available in all
      EU regions.
  PostProcessingLambdaHookFunctionArn:
    Default: ''
    Type: String
    AllowedPattern: ^(|arn:aws[a-z-]*:lambda:.*)$
    Description: '(Optional) The specified Lambda function is invoked by EventBridge
      after the document processing workflow is processed. This function can implement
      custom logic to initiate downstream processing on the output of the processing
      pattern.

      '
  BedrockGuardrailId:
    Type: String
    Default: ''
    AllowedPattern: ^(|[a-z0-9]+)$
    Description: 'Optionally provide the *Id* (not name) of an *existing* Bedrock
      Guardrail (looks like: wxyz3ab12x34) to be used for all Bedrock and Bedrock
      Knowledge Base interactions.

      '
  BedrockGuardrailVersion:
    Type: String
    Default: DRAFT
    AllowedPattern: ^(|(([1-9][0-9]{0,7})|(DRAFT)))$
    Description: 'If you provided a Bedrock Guardrail Id above, provide the corresponding
      Guardrail version here (e.g. DRAFT|1|2|...).

      '
  MaxConcurrentWorkflows:
    Type: Number
    Default: 100
    Description: Maximum number of concurrent workflow executions allowed
    MinValue: 1
  DataRetentionInDays:
    Type: Number
    Default: 365
    Description: Number of days to retain documents (S3) and tracking records (DynamoDB)
    MinValue: 1
  ErrorThreshold:
    Type: Number
    Default: 1
    Description: Number of workflow errors that triggers an alert (per 5 minutes)
    MinValue: 1
  ExecutionTimeThresholdMs:
    Type: Number
    Default: 300000
    Description: Maximum acceptable execution time in milliseconds before alerting
      (default 300000 = 300 seconds)
    MinValue: 1000
  PermissionsBoundaryArn:
    Type: String
    Default: ''
    Description: '(Optional) ARN of an existing IAM Permissions Boundary policy to
      attach to all IAM roles. Required by some organizations with Service Control
      Policies (SCPs). Format: arn:partition:iam::account-id:policy/policy-name Leave
      blank if no Permissions Boundary is required.'
    AllowedPattern: ^(|arn:aws[a-z-]*:iam::[0-9]{12}:policy/.+)$
    ConstraintDescription: Must be empty or a valid IAM policy ARN
  EnableECRImageScanning:
    Type: String
    Default: 'false'
    AllowedValues:
    - 'true'
    - 'false'
    Description: 'Enable automatic vulnerability scanning for Lambda container images
      in ECR (applies to Pattern-1 and Pattern-2).  Disabling may improve deployment
      reliability but reduces security posture.  Recommended: true for production,
      false only if experiencing deployment issues.'
  LogLevel:
    Type: String
    Default: INFO
    AllowedValues:
    - DEBUG
    - INFO
    - WARN
    - ERROR
    Description: Default logging level for all logs
  LogRetentionDays:
    Type: Number
    Default: 30
    Description: Number of days to retain CloudWatch logs
    AllowedValues:
    - 1
    - 3
    - 5
    - 7
    - 14
    - 30
    - 60
    - 90
    - 120
    - 150
    - 180
    - 365
    - 400
    - 545
    - 731
    - 1827
    - 3653
  WebUIHosting:
    Type: String
    Default: CloudFront
    AllowedValues:
    - CloudFront
    - ALB
    Description: Frontend hosting method. Use CloudFront for public/standard deployments.
      Use ALB for GovCloud or private network deployments that require VPC-based hosting.
  ALBVpcId:
    Type: String
    Default: ''
    Description: (Required when WebUIHosting=ALB) VPC ID for the ALB and S3 VPC endpoint.
  ALBSubnetIds:
    Type: CommaDelimitedList
    Default: ''
    Description: (Required when WebUIHosting=ALB) Comma-separated subnet IDs for the
      ALB (minimum 2 subnets in different AZs).
  ALBCertificateArn:
    Type: String
    Default: ''
    Description: (Required when WebUIHosting=ALB) ACM certificate ARN for the ALB
      HTTPS listener. Can be ACM-issued or imported (including self-signed). Use scripts/generate_self_signed_cert.sh
      for demo/testing.
  ALBScheme:
    Type: String
    Default: internal
    AllowedValues:
    - internal
    - internet-facing
    Description: (ALB hosting only) ALB scheme. Use 'internal' for private network
      access, 'internet-facing' for public access.
  ALBAllowedCIDRs:
    Type: String
    Default: ''
    Description: (ALB hosting only) Comma-separated CIDR ranges allowed to access
      the ALB. Leave empty to automatically use the VPC CIDR range (recommended for
      internal ALBs). Specify explicit CIDRs to override.
  CloudFrontPriceClass:
    Type: String
    Default: PriceClass_100
    Description: (CloudFront hosting only) Specify the CloudFront price class. See
      https://aws.amazon.com/cloudfront/pricing/ for a description of each price class.
    AllowedValues:
    - PriceClass_100
    - PriceClass_200
    - PriceClass_All
    ConstraintDescription: Allowed Price Classes PriceClass_100 PriceClass_200 and
      PriceClass_All
  CloudFrontAllowedGeos:
    Type: String
    Default: ''
    Description: 'Specify a comma separated list of two letter country codes (uppercase
      ISO 3166-1) that are allowed to access the web user interface via CloudFront.
      For example: US,CA. Leave empty if you do not want geo restrictions to be applied.'
    AllowedPattern: ^(|[A-Z]{2}(,[A-Z]{2})*)$
    ConstraintDescription: Comma separated list of uppercase two letter country codes
      or empty
  WAFAllowedIPv4Ranges:
    Type: String
    Default: '0.0.0.0/0'
    Description: 'Comma-separated list of IPv4 CIDR ranges to allow. Default (0.0.0.0/0)
      disables WAF and allows all IPs. Example to restrict: "192.168.1.0/24, 10.0.0.0/16"'
    AllowedPattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}(,\s*([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2})*$
    ConstraintDescription: Must be valid comma-separated list of IPv4 CIDR ranges
Rules:
  MLflowTrackingURIRequired:
    RuleCondition:
      Fn::Equals:
      - Ref: EnableMLflow
      - 'true'
    Assertions:
    - Assert:
        Fn::Not:
        - Fn::Equals:
          - Ref: MlflowTrackingURI
          - ''
      AssertDescription: MlflowTrackingURI is required when EnableMLflow is true
Conditions:
  HasGuardrailConfig:
    Fn::And:
    - Fn::Not:
      - Fn::Equals:
        - Ref: BedrockGuardrailId
        - ''
    - Fn::Not:
      - Fn::Equals:
        - Ref: BedrockGuardrailVersion
        - ''
  ShouldAllowSignUpEmailDomain:
    Fn::Not:
    - Fn::Equals:
      - Ref: AllowedSignUpEmailDomain
      - ''
  ShouldEnableGeoRestriction:
    Fn::Not:
    - Fn::Equals:
      - Ref: CloudFrontAllowedGeos
      - ''
  ShouldEnablePostProcessingLambdaHook:
    Fn::Not:
    - Fn::Equals:
      - Ref: PostProcessingLambdaHookFunctionArn
      - ''
  ShouldCreateEvaluationBaselineBucket:
    Fn::Equals:
    - Ref: EvaluationBaselineBucketName
    - ''
  ShouldCreateReportingBucket:
    Fn::Equals:
    - Ref: ReportingBucketName
    - ''
  IsWafEnabled:
    Fn::Not:
    - Fn::Equals:
      - Ref: WAFAllowedIPv4Ranges
      - '0.0.0.0/0'
  ShouldCreateDocumentKnowledgeBase:
    Fn::Equals:
    - Ref: DocumentKnowledgeBase
    - BEDROCK_KNOWLEDGE_BASE (Create)
  ShouldUseDocumentKnowledgeBase:
    Condition: ShouldCreateDocumentKnowledgeBase
  DocumentSectionsCrawlerScheduleEnabled:
    Fn::Not:
    - Fn::Equals:
      - Ref: DocumentSectionsCrawlerFrequency
      - Manual
  HasPermissionsBoundary:
    Fn::Not:
    - Fn::Equals:
      - Ref: PermissionsBoundaryArn
      - ''
  HasCustomConfigPath:
    Fn::Not:
    - Fn::Equals:
      - Ref: CustomConfigPath
      - ''
  IsS3VectorsVectorStore:
    Fn::Equals:
    - Ref: KnowledgeBaseVectorStore
    - S3_VECTORS
  CreateAgentCoreLambda:
    Fn::Equals:
    - Ref: EnableMCP
    - 'true'
  CreateExternalAppClient:
    Fn::Equals:
    - Ref: EnableMCP
    - 'true'
  UseCloudFrontHosting:
    Fn::Equals:
    - Ref: WebUIHosting
    - CloudFront
  UseALBHosting:
    Fn::Equals:
    - Ref: WebUIHosting
    - ALB
  IsMLflowEnabled:
    Fn::Equals:
    - Ref: EnableMLflow
    - 'true'
Metadata:
  cfn-lint:
    config:
      ignore_checks:
      - W1030
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: User Authentication
      Parameters:
      - AdminEmail
      - AllowedSignUpEmailDomain
    - Label:
        default: Configuration
      Parameters:
      - ConfigurationPreset
      - CustomConfigPath
      - Pattern2CustomClassificationModelARN
      - Pattern2CustomExtractionModelARN
    - Label:
        default: Evaluation
      Parameters:
      - EvaluationBaselineBucketName
    - Label:
        default: Reporting
      Parameters:
      - ReportingBucketName
      - DocumentSectionsCrawlerFrequency
    - Label:
        default: Document Knowledge Base
      Parameters:
      - DocumentKnowledgeBase
      - KnowledgeBaseVectorStore
      - KnowledgeBaseModelId
    - Label:
        default: Post Processing
      Parameters:
      - PostProcessingLambdaHookFunctionArn
    - Label:
        default: Bedrock Guardrails
      Parameters:
      - BedrockGuardrailId
      - BedrockGuardrailVersion
    - Label:
        default: Security Configuration
      Parameters:
      - PermissionsBoundaryArn
      - WAFAllowedIPv4Ranges
    - Label:
        default: MCP Integration
      Parameters:
      - EnableMCP
    - Label:
        default: MLflow Integration
      Parameters:
      - EnableMLflow
      - MlflowTrackingURI
    - Label:
        default: Web UI Hosting
      Parameters:
      - WebUIHosting
      - CloudFrontPriceClass
      - CloudFrontAllowedGeos
    - Label:
        default: ALB Hosting (when WebUIHosting=ALB)
      Parameters:
      - ALBVpcId
      - ALBSubnetIds
      - ALBCertificateArn
      - ALBScheme
      - ALBAllowedCIDRs
    - Label:
        default: General Configuration
      Parameters:
      - MaxConcurrentWorkflows
      - DataRetentionInDays
      - ErrorThreshold
      - ExecutionTimeThresholdMs
      - LogLevel
      - LogRetentionDays
    ParameterLabels:
      AdminEmail:
        default: Admin Email Address
      AllowedSignUpEmailDomain:
        default: Allowed Sign Up Email Domain
      Pattern2CustomClassificationModelARN:
        default: Custom Classification Model ARN
      Pattern2CustomExtractionModelARN:
        default: Custom Extraction Model ARN
      ConfigurationPreset:
        default: Configuration Preset
      CustomConfigPath:
        default: Custom Configuration Path
      PostProcessingLambdaHookFunctionArn:
        default: Post Processing Lambda Hook Function ARN
      EvaluationBaselineBucketName:
        default: Evaluation Baseline Bucket Name
      ReportingBucketName:
        default: Reporting Bucket Name
      DocumentKnowledgeBase:
        default: Document Knowledge Base Configuration
      KnowledgeBaseModelId:
        default: Knowledge Base Model Id
      BedrockGuardrailId:
        default: Bedrock Guardrail Id
      BedrockGuardrailVersion:
        default: Bedrock Guardrail Version
      PermissionsBoundaryArn:
        default: Permissions Boundary ARN
      MaxConcurrentWorkflows:
        default: Maximum Concurrent Workflows
      DataRetentionInDays:
        default: Data Retention Period (days)
      ErrorThreshold:
        default: Error Alert Threshold
      ExecutionTimeThresholdMs:
        default: Execution Time Threshold (ms)
      LogLevel:
        default: Logging Level
      LogRetentionDays:
        default: Log Retention Period (days)
      CloudFrontPriceClass:
        default: CloudFront Price Class
      CloudFrontAllowedGeos:
        default: CloudFront Allowed Geos
      WAFAllowedIPv4Ranges:
        default: WAF Allowed IPv4 Ranges
      EnableMCP:
        default: Enable MCP Integration
      EnableMLflow:
        default: Enable MLflow Integration
      MlflowTrackingURI:
        default: MLflow Tracking Server URI
      WebUIHosting:
        default: Web UI Hosting Method
      ALBVpcId:
        default: ALB - VPC ID
      ALBSubnetIds:
        default: ALB - Subnet IDs
      ALBCertificateArn:
        default: ALB - ACM Certificate ARN
      ALBScheme:
        default: ALB - Scheme (internal/internet-facing)
      ALBAllowedCIDRs:
        default: ALB - Allowed CIDR Ranges
Resources:
  StacknameCheckFunction:
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it has no network access
        - id: W92
          reason: Reserved concurrency not required
      SamResourceId: StacknameCheckFunction
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      InlineCode: "import cfnresponse\nimport time\nimport json\ndef handler(event,\
        \ context):\n    print(json.dumps(event))\n    input = event['ResourceProperties'].get('InputString',\
        \ '')\n    max_length = int(event['ResourceProperties'].get('MaxLength', 0))\n\
        \    status = cfnresponse.SUCCESS\n    reason = f\"Stack Name Length under\
        \ {max_length} - OK\"\n    if event['RequestType'] == \"Create\":\n      if\
        \ len(input) > max_length:\n        status = cfnresponse.FAILED\n        reason\
        \ = f\"Stack Name ({input}) length ({len(input)}) too long - max length {max_length}\
        \ - FAILED\"\n    else:\n      print(f\"Request type is {event['RequestType']}\
        \ - skipping\")\n    cfnresponse.send(event, context, status, {}, reason=reason)\n"
      Environment:
        Variables:
          LOG_LEVEL: INFO
      LoggingConfig:
        LogGroup:
          Ref: StacknameCheckFunctionLogGroup
  StacknameCheckFunctionLogGroup:
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W84
          reason: LogGroup does not require KMS Key Id, since it's only logging checks
            on stack name length
      SamResourceId: StacknameCheckFunctionLogGroup
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays:
        Ref: LogRetentionDays
  IsStacknameLengthOK:
    Type: Custom::StacknameCheck
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - StacknameCheckFunction
        - Arn
      InputString:
        Ref: AWS::StackName
      MaxLength: 25
    Metadata:
      SamResourceId: IsStacknameLengthOK
  ConfigurationCopyFunction:
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with S3
        - id: W92
          reason: Reserved concurrency not required
      SamResourceId: ConfigurationCopyFunction
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 300
      InlineCode: "import boto3\nimport cfnresponse\nimport json\nimport logging\n\
        \nlogger = logging.getLogger()\nlogger.setLevel(logging.INFO)\n\ndef handler(event,\
        \ context):\n    logger.info(json.dumps(event))\n\n    try:\n        source_bucket\
        \ = event['ResourceProperties']['SourceBucket']\n        source_prefix = event['ResourceProperties']['SourcePrefix']\n\
        \        target_bucket = event['ResourceProperties']['TargetBucket']\n   \
        \     target_prefix = event['ResourceProperties'].get('TargetPrefix', '')\n\
        \n        file_list = event['ResourceProperties'].get('FileList', [])\n\n\
        \        s3_client = boto3.client('s3')\n\n        if event['RequestType']\
        \ == 'Create' or event['RequestType'] == 'Update':\n            # Copy files\
        \ explicitly from the provided list\n            copied_count = 0\n\n    \
        \        for relative_file_path in file_list:\n                # Skip empty\
        \ entries\n                if not relative_file_path.strip():\n          \
        \          continue\n\n                # Construct source key\n          \
        \      source_key = f\"{source_prefix}/{relative_file_path}\"\n\n        \
        \        # Construct target key with optional target prefix\n            \
        \    if target_prefix:\n                    target_key = f\"{target_prefix}/{relative_file_path}\"\
        \n                else:\n                    target_key = relative_file_path\n\
        \n                logger.info(f\"Copying {source_bucket}/{source_key} to {target_bucket}/{target_key}\"\
        )\n\n                try:\n                    copy_source = {'Bucket': source_bucket,\
        \ 'Key': source_key}\n                    s3_client.copy_object(\n       \
        \                 CopySource=copy_source,\n                        Bucket=target_bucket,\n\
        \                        Key=target_key\n                    )\n         \
        \           copied_count += 1\n                except Exception as copy_error:\n\
        \                    logger.warning(f\"Failed to copy {source_key}: {str(copy_error)}\"\
        )\n                    # Continue with other files instead of failing the\
        \ entire operation\n\n            logger.info(f\"Successfully copied {copied_count}\
        \ configuration files\")\n            cfnresponse.send(event, context, cfnresponse.SUCCESS,\
        \ {\n                'CopiedFiles': copied_count\n            }, reason=f\"\
        Successfully copied {copied_count} configuration files\")\n\n        elif\
        \ event['RequestType'] == 'Delete':\n            # For delete, we don't need\
        \ to clean up the configuration files\n            # as they may be needed\
        \ by other resources\n            logger.info(\"Delete request - no action\
        \ needed for configuration files\")\n            cfnresponse.send(event, context,\
        \ cfnresponse.SUCCESS, {}, \n                           reason=\"Delete completed\
        \ - configuration files retained\")\n\n    except Exception as e:\n      \
        \  logger.error(f\"Error: {str(e)}\")\n        cfnresponse.send(event, context,\
        \ cfnresponse.FAILED, {}, \n                       reason=f\"Error copying\
        \ configuration files: {str(e)}\")\n"
      Environment:
        Variables:
          LOG_LEVEL: INFO
      LoggingConfig:
        LogGroup:
          Ref: ConfigurationCopyFunctionLogGroup
      Policies:
      - S3ReadPolicy:
          BucketName: aws-ml-blog-us-east-1
      - S3WritePolicy:
          BucketName:
            Ref: ConfigurationBucket
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  ConfigurationCopyFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays:
        Ref: LogRetentionDays
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
    Metadata:
      SamResourceId: ConfigurationCopyFunctionLogGroup
  CopyConfigurationFiles:
    Type: Custom::ConfigurationCopy
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - ConfigurationCopyFunction
        - Arn
      SourceBucket: aws-ml-blog-us-east-1
      SourcePrefix: artifacts/genai-idp/0.5.4/config_library
      TargetBucket:
        Ref: ConfigurationBucket
      TargetPrefix: config_library
      ConfigLibraryHash: afb96e02447200f6
      FileList:
      - README.md
      - TEMPLATE_README.md
      - __pycache__/test_config_library.cpython-313-pytest-8.4.1.pyc
      - __pycache__/test_config_library.cpython-313-pytest-9.0.2.pyc
      - managed_config/docsplit/config.yaml
      - managed_config/fake-w2/config.yaml
      - managed_config/ocr-benchmark/config.yaml
      - managed_config/realkie-fcc-verified/config.yaml
      - pricing.yaml
      - test_config_library.py
      - unified/README.md
      - unified/bank-statement-sample/README.md
      - unified/bank-statement-sample/config.yaml
      - unified/docsplit/README.md
      - unified/docsplit/config.yaml
      - unified/fake-w2/README.md
      - unified/fake-w2/config.yaml
      - unified/healthcare-multisection-package/README.md
      - unified/healthcare-multisection-package/config.yaml
      - unified/lending-package-sample-govcloud/README.md
      - unified/lending-package-sample-govcloud/config.yaml
      - unified/lending-package-sample/README.md
      - unified/lending-package-sample/config.yaml
      - unified/ocr-benchmark/README.md
      - unified/ocr-benchmark/config.yaml
      - unified/realkie-fcc-verified/README.md
      - unified/realkie-fcc-verified/config.yaml
      - unified/rule-extraction/README.md
      - unified/rule-extraction/config.yaml
      - unified/rule-validation/README.md
      - unified/rule-validation/config.yaml
      - unified/rvl-cdip-with-few-shot-examples/README.md
      - unified/rvl-cdip-with-few-shot-examples/config.yaml
      - unified/rvl-cdip-with-few-shot-examples/example-images/bank-statement-pages/image1.jpg
      - unified/rvl-cdip-with-few-shot-examples/example-images/bank-statement-pages/image2.jpg
      - unified/rvl-cdip-with-few-shot-examples/example-images/bank-statement-pages/image3.jpg
      - unified/rvl-cdip-with-few-shot-examples/example-images/email1.jpg
      - unified/rvl-cdip-with-few-shot-examples/example-images/letter1.jpg
      - unified/rvl-cdip-with-few-shot-examples/example-images/letter2.png
      - unified/rvl-cdip/README.md
      - unified/rvl-cdip/config.yaml
    Metadata:
      SamResourceId: CopyConfigurationFiles
  DOCUMENTKB:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::CloudFormation::Stack
    Condition: ShouldCreateDocumentKnowledgeBase
    Properties:
      TemplateURL: https://s3.amazonaws.com/aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/8aa7205f414265e002fb492f19b5f37f.template
      Parameters:
        pKnowledgeBaseBucketName:
          Ref: OutputBucket
        pInputDocumentUploadFolderPrefix: ''
        pS3SyncScheduleExpression: cron(0/30 * ? * * *)
        pCustomerManagedEncryptionKeyArn:
          Fn::GetAtt:
          - CustomerManagedEncryptionKey
          - Arn
        pChunkingStrategy: Fixed-size chunking
        pMaxTokens: 8192
        pVectorStoreType:
          Ref: KnowledgeBaseVectorStore
        LogLevel:
          Ref: LogLevel
        PermissionsBoundaryArn:
          Ref: PermissionsBoundaryArn
    Metadata:
      SamResourceId: DOCUMENTKB
  AgentCoreMCPHandlerLogGroup:
    Type: AWS::Logs::LogGroup
    Condition: CreateAgentCoreLambda
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: AgentCoreMCPHandlerLogGroup
  AgentCoreMCPHandlerFunction:
    Type: AWS::Serverless::Function
    Condition: CreateAgentCoreLambda
    Metadata:
      BuildMethod: python3.12
      SamResourceId: AgentCoreMCPHandlerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: bedrock-agentcore:InvokeAgentRuntime, aws-marketplace, and logs:DescribeLogGroups
            APIs do not support resource-level permissions
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      FunctionName:
        Fn::Sub: ${AWS::StackName}-agentcore-mcp-handler
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/47710615ba14ac9dad276a3423eae094
      Handler: index.lambda_handler
      Runtime: python3.12
      MemorySize: 1024
      Timeout: 900
      Layers:
      - Ref: IDPCommonAgentsLayer
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
          TRACKING_TABLE_NAME:
            Ref: TrackingTable
          LOOKUP_FUNCTION_NAME:
            Ref: LookupFunction
          STRANDS_LOG_LEVEL:
            Ref: LogLevel
          AGENT_TABLE:
            Ref: AgentTable
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          ATHENA_DATABASE:
            Ref: ReportingDatabase
          ATHENA_WORKGROUP: primary
          QUERY_RESULTS_BUCKET:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
          ATHENA_OUTPUT_LOCATION:
            Fn::Sub:
            - s3://${Bucket}/athena-results/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          AWS_STACK_NAME:
            Ref: AWS::StackName
          GUARDRAIL_ID_AND_VERSION:
            Fn::If:
            - HasGuardrailConfig
            - Fn::Sub: ${BedrockGuardrailId}:${BedrockGuardrailVersion}
            - ''
          CLOUDWATCH_LOG_GROUP_PREFIX:
            Fn::Sub: /aws/lambda/${AWS::StackName}
          SETTINGS_PARAMETER_NAME:
            Ref: SettingsParameter
          MCP_SERVER_BUCKET:
            Ref: MCPContentBucket
      LoggingConfig:
        LogGroup:
          Ref: AgentCoreMCPHandlerLogGroup
      Policies:
      - Statement:
          Effect: Allow
          Action: bedrock-agentcore:InvokeAgentRuntime
          Resource: '*'
      - Statement:
          Effect: Allow
          Action: appsync:GraphQL
          Resource:
            Fn::Sub: arn:${AWS::Partition}:appsync:${AWS::Region}:${AWS::AccountId}:apis/${GraphQLApi.ApiId}/*
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ChatMessagesTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: IdHelperChatMemoryTable
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - S3CrudPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
      - Statement:
        - Effect: Allow
          Action:
          - athena:StartQueryExecution
          - athena:GetQueryExecution
          - athena:GetQueryResults
          - athena:StopQueryExecution
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/primary
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:datacatalog/*
        - Effect: Allow
          Action:
          - s3:ListBucket
          - s3:GetBucketLocation
          - s3:GetBucketVersioning
          Resource:
          - Fn::Sub:
            - ${BucketArn}
            - BucketArn:
                Fn::If:
                - ShouldCreateReportingBucket
                - Fn::GetAtt:
                  - ReportingBucket
                  - Arn
                - Fn::Sub: arn:${AWS::Partition}:s3:::${ReportingBucketName}
          - Fn::GetAtt:
            - InputBucket
            - Arn
          - Fn::GetAtt:
            - OutputBucket
            - Arn
          - Fn::GetAtt:
            - MCPContentBucket
            - Arn
        - Effect: Allow
          Action:
          - s3:GetObject
          - s3:PutObject
          - s3:DeleteObject
          - s3:AbortMultipartUpload
          - s3:ListMultipartUploadParts
          Resource:
          - Fn::Sub:
            - ${BucketArn}/*
            - BucketArn:
                Fn::If:
                - ShouldCreateReportingBucket
                - Fn::GetAtt:
                  - ReportingBucket
                  - Arn
                - Fn::Sub: arn:${AWS::Partition}:s3:::${ReportingBucketName}
          - Fn::Sub: ${InputBucket.Arn}/*
          - Fn::Sub: ${OutputBucket.Arn}/*
          - Fn::Sub: ${MCPContentBucket.Arn}/*
        - Effect: Allow
          Action:
          - glue:GetTable
          - glue:GetTables
          - glue:GetDatabase
          - glue:GetDatabases
          - glue:GetPartitions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:database/${ReportingDatabase}
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${ReportingDatabase}/*
        - Effect: Allow
          Action:
          - bedrock:InvokeModel
          - bedrock:InvokeModelWithResponseStream
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock:*::foundation-model/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*
        - Effect: Allow
          Action:
          - aws-marketplace:Subscribe
          - aws-marketplace:Unsubscribe
          - aws-marketplace:ViewSubscriptions
          Resource: '*'
        - Fn::If:
          - HasGuardrailConfig
          - Effect: Allow
            Action:
            - bedrock:ApplyGuardrail
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:guardrail/${BedrockGuardrailId}
          - Ref: AWS::NoValue
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - bedrock-agentcore:StartCodeInterpreterSession
          - bedrock-agentcore:StopCodeInterpreterSession
          - bedrock-agentcore:InvokeCodeInterpreter
          - bedrock-agentcore:GetCodeInterpreterSession
          - bedrock-agentcore:ListCodeInterpreterSessions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock-agentcore:*:aws:code-interpreter/*
        - Effect: Allow
          Action:
          - secretsmanager:GetSecretValue
          Resource:
          - Ref: ExternalMCPAgentsSecret
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          - logs:DescribeLogStreams
          - logs:FilterLogEvents
          - logs:GetLogEvents
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/vendedlogs/states/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${AWS::StackName}*
          - Fn::GetAtt:
            - QueueSenderLogGroup
            - Arn
          - Fn::GetAtt:
            - QueueProcessorLogGroup
            - Arn
          - Fn::GetAtt:
            - WorkflowTrackerLogGroup
            - Arn
          - Fn::GetAtt:
            - SaveReportingDataFunctionV2LogGroup
            - Arn
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          Resource:
          - '*'
        - Effect: Allow
          Action:
          - dynamodb:DescribeTable
          - dynamodb:Scan
          - dynamodb:Query
          - dynamodb:GetItem
          - dynamodb:Query
          Resource:
          - Fn::GetAtt:
            - TrackingTable
            - Arn
          - Fn::GetAtt:
            - ConfigurationTable
            - Arn
          - Fn::GetAtt:
            - AgentTable
            - Arn
        - Effect: Allow
          Action:
          - cloudformation:DescribeStackResources
          - cloudformation:DescribeStacks
          - cloudformation:ListStackResources
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}*
        - Effect: Allow
          Action:
          - states:DescribeExecution
          - states:GetExecutionHistory
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:${AWS::StackName}*
        - Effect: Allow
          Action:
          - ssm:GetParameter
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
  AgentCoreGatewayManagerFunction:
    Type: AWS::Serverless::Function
    Condition: CreateAgentCoreLambda
    Metadata:
      SamResourceId: AgentCoreGatewayManagerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: F38
          reason: PassRole permission is required to delegate the execution role to
            the Bedrock AgentCore Gateway service securely
        - id: F3
          reason: Wildcard actions required for AgentCore Gateway lifecycle management
            (create, update, delete) and CloudWatch Logs delivery configuration
        - id: W11
          reason: Wildcard resource required as AgentCore Gateway ARNs are dynamically
            generated and unknown at deployment time
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/59124fd81f28100305868f51967849e3
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 512
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
      LoggingConfig:
        LogGroup:
          Ref: AgentCoreGatewayManagerLogGroup
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - bedrock-agent:*
          - bedrock-agentcore:*
          - bedrock-agentcore-control:*
          - logs:CreateLogGroup
          - logs:PutLogEvents
          - logs:DeleteLogGroup
          - logs:PutDeliverySource
          - logs:DeleteDeliverySource
          - logs:PutDeliveryDestination
          - logs:DeleteDeliveryDestination
          - logs:DescribeDeliveryDestinations
          - logs:DescribeDeliverySources
          - iam:PassRole
          - iam:CreateRole
          - iam:AttachRolePolicy
          - iam:GetRole
          - cognito-idp:DescribeUserPool
          Resource: '*'
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  AgentCoreGatewayManagerLogGroup:
    Type: AWS::Logs::LogGroup
    Condition: CreateAgentCoreLambda
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: AgentCoreGatewayManagerLogGroup
  AgentCoreGatewayExecutionRole:
    Type: AWS::IAM::Role
    Condition: CreateAgentCoreLambda
    Properties:
      RoleName:
        Fn::Sub: ${AWS::StackName}-AgentCoreGatewayExecutionRole
      Description: Execution role for AgentCore Gateway
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              Fn::Sub: bedrock-agentcore.${AWS::URLSuffix}
          Action: sts:AssumeRole
          Condition:
            StringEquals:
              aws:SourceAccount:
                Ref: AWS::AccountId
            ArnLike:
              aws:SourceArn:
                Fn::Sub: arn:${AWS::Partition}:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:*
      Policies:
      - PolicyName: InvokeLambdaPolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource:
              Fn::GetAtt:
              - AgentCoreMCPHandlerFunction
              - Arn
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/CloudWatchLogsFullAccess
      Tags:
      - Key: Name
        Value:
          Fn::Sub: ${AWS::StackName}-AgentCoreGatewayExecutionRole
    Metadata:
      SamResourceId: AgentCoreGatewayExecutionRole
  AgentCoreGateway:
    Type: Custom::AgentCoreGateway
    Condition: CreateAgentCoreLambda
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - AgentCoreGatewayManagerFunction
        - Arn
      StackName:
        Ref: AWS::StackName
      Region:
        Ref: AWS::Region
      LambdaArn:
        Fn::GetAtt:
        - AgentCoreMCPHandlerFunction
        - Arn
      UserPoolId:
        Ref: UserPool
      ClientId:
        Ref: ExternalAppClient
      ClientSecret:
        Fn::GetAtt:
        - ExternalAppClient
        - ClientSecret
      ConnectorClientId:
        Ref: MCPConnectorClient
      ExecutionRoleArn:
        Fn::GetAtt:
        - AgentCoreGatewayExecutionRole
        - Arn
      SourceCodeHash: a45b737c604d71e3
    Metadata:
      SamResourceId: AgentCoreGateway
  ReadPreviousIDPPatternFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: ReadPreviousIDPPatternFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Reserved concurrency not required
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 10
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/872c3097072a1614f72cfceaff7a85fb
      Environment:
        Variables:
          LOG_LEVEL: INFO
      LoggingConfig:
        LogGroup:
          Ref: ReadPreviousIDPPatternFunctionLogGroup
      Policies:
      - Statement:
        - Effect: Allow
          Action: ssm:GetParameter
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
  ReadPreviousIDPPatternFunctionLogGroup:
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W84
          reason: LogGroup does not require KMS Key Id
      SamResourceId: ReadPreviousIDPPatternFunctionLogGroup
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays:
        Ref: LogRetentionDays
  PreviousIDPPatternValue:
    Type: Custom::ReadPreviousIDPPattern
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - ReadPreviousIDPPatternFunction
        - Arn
      SettingsParameterName:
        Ref: SettingsParameter
    Metadata:
      SamResourceId: PreviousIDPPatternValue
  PATTERNSTACK:
    DependsOn:
    - IsStacknameLengthOK
    - CopyConfigurationFiles
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/cf4923caa62ab959d2eb9143a87c35de.template
      Parameters:
        StackName:
          Ref: AWS::StackName
        InputBucket:
          Ref: InputBucket
        DiscoveryBucket:
          Ref: DiscoveryBucket
        DiscoveryTrackingTable:
          Ref: DiscoveryTrackingTable
        ConfigurationBucket:
          Ref: ConfigurationBucket
        WorkingBucket:
          Ref: WorkingBucket
        OutputBucket:
          Ref: OutputBucket
        TrackingTable:
          Ref: TrackingTable
        CustomerManagedEncryptionKeyArn:
          Fn::GetAtt:
          - CustomerManagedEncryptionKey
          - Arn
        LogRetentionDays:
          Ref: LogRetentionDays
        LogLevel:
          Ref: LogLevel
        ExecutionTimeThresholdMs:
          Ref: ExecutionTimeThresholdMs
        BedrockGuardrailId:
          Ref: BedrockGuardrailId
        BedrockGuardrailVersion:
          Ref: BedrockGuardrailVersion
        UpdateConfigurationFunctionArn:
          Fn::GetAtt:
          - UpdateConfigurationFunction
          - Arn
        ConfigurationTable:
          Ref: ConfigurationTable
        AppSyncApiUrl:
          Fn::GetAtt:
          - GraphQLApi
          - GraphQLUrl
        AppSyncApiArn:
          Fn::GetAtt:
          - GraphQLApi
          - Arn
        ConfigurationDefaultS3Uri:
          Fn::If:
          - HasCustomConfigPath
          - Ref: CustomConfigPath
          - Fn::Sub:
            - s3://${ConfigurationBucket}/config_library/unified/${ConfigPath}/config.yaml
            - ConfigPath:
                Fn::FindInMap:
                - ConfigurationMap
                - Ref: ConfigurationPreset
                - ConfigPath
        ConfigLibraryHash: afb96e02447200f6
        EnableXRayTracing:
          Ref: EnableXRayTracing
        PermissionsBoundaryArn:
          Ref: PermissionsBoundaryArn
        ReportingBucket:
          Fn::If:
          - ShouldCreateReportingBucket
          - Ref: ReportingBucket
          - Ref: ReportingBucketName
        BaselineBucket:
          Fn::If:
          - ShouldCreateEvaluationBaselineBucket
          - Ref: EvaluationBaselineBucket
          - Ref: EvaluationBaselineBucketName
        SaveReportingFunctionName:
          Ref: SaveReportingDataFunctionV2
        ImageVersion: 74e22237
        ArtifactBucket: aws-ml-blog-us-east-1
        ArtifactPrefix: artifacts/genai-idp/0.5.4
        SourceZipfile: unified-source-74e22237.zip
        CustomClassificationModelARN:
          Ref: Pattern2CustomClassificationModelARN
        CustomExtractionModelARN:
          Ref: Pattern2CustomExtractionModelARN
        EnableECRImageScanning:
          Ref: EnableECRImageScanning
        PreviousIDPPattern:
          Fn::GetAtt:
          - PreviousIDPPatternValue
          - IDPPattern
        EnableMLflow:
          Ref: EnableMLflow
        MlflowTrackingURI:
          Ref: MlflowTrackingURI
    Metadata:
      SamResourceId: PATTERNSTACK
  APPSYNCSTACK:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/b45dc7b35386488640c6d693c454bc30.template
      Parameters:
        GraphQLApiId:
          Fn::GetAtt:
          - GraphQLApi
          - ApiId
        GraphQLApiArn:
          Fn::GetAtt:
          - GraphQLApi
          - Arn
        GraphQLApiUrl:
          Fn::GetAtt:
          - GraphQLApi
          - GraphQLUrl
        AgentChatProcessorFunctionArn:
          Fn::GetAtt:
          - AgentChatProcessorFunction
          - Arn
        AgentProcessorFunctionArn:
          Fn::GetAtt:
          - AgentProcessorFunction
          - Arn
        TestExecutionAggregationFunctionArn:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.TestExecutionAggregationFunctionArn
        MLflowLoggerFunctionArn:
          Fn::If:
          - IsMLflowEnabled
          - Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.MLflowLoggerFunctionArn
          - ''
        TrackingTableName:
          Ref: TrackingTable
        ConfigurationTableName:
          Ref: ConfigurationTable
        AgentTableName:
          Ref: AgentTable
        ChatMessagesTableName:
          Ref: ChatMessagesTable
        ChatSessionsTableName:
          Ref: ChatSessionsTable
        IdHelperChatMemoryTableName:
          Ref: IdHelperChatMemoryTable
        DiscoveryTrackingTableName:
          Ref: DiscoveryTrackingTable
        InputBucketName:
          Ref: InputBucket
        OutputBucketName:
          Ref: OutputBucket
        ConfigurationBucketName:
          Ref: ConfigurationBucket
        TestSetBucketName:
          Ref: TestSetBucket
        EvaluationBaselineBucketName:
          Fn::If:
          - ShouldCreateEvaluationBaselineBucket
          - Ref: EvaluationBaselineBucket
          - Ref: EvaluationBaselineBucketName
        ReportingBucketName:
          Fn::If:
          - ShouldCreateReportingBucket
          - Ref: ReportingBucket
          - Ref: ReportingBucketName
        WorkingBucketName:
          Ref: WorkingBucket
        DiscoveryBucketName:
          Ref: DiscoveryBucket
        DocumentQueueUrl:
          Ref: DocumentQueue
        DiscoveryQueueUrl:
          Ref: DiscoveryQueue
        TestFileCopyQueueUrl:
          Ref: TestFileCopyQueue
        TestSetFileCopyQueueUrl:
          Ref: TestSetFileCopyQueue
        TestResultCacheUpdateQueueUrl:
          Ref: TestResultCacheUpdateQueue
        TestResultCacheUpdateQueueArn:
          Fn::GetAtt:
          - TestResultCacheUpdateQueue
          - Arn
        DocumentQueueName:
          Fn::GetAtt:
          - DocumentQueue
          - QueueName
        DiscoveryQueueName:
          Fn::GetAtt:
          - DiscoveryQueue
          - QueueName
        TestFileCopyQueueName:
          Fn::GetAtt:
          - TestFileCopyQueue
          - QueueName
        TestSetFileCopyQueueName:
          Fn::GetAtt:
          - TestSetFileCopyQueue
          - QueueName
        TestResultCacheUpdateQueueName:
          Fn::GetAtt:
          - TestResultCacheUpdateQueue
          - QueueName
        CustomerManagedEncryptionKeyArn:
          Fn::GetAtt:
          - CustomerManagedEncryptionKey
          - Arn
        ReportingDatabaseName:
          Ref: ReportingDatabase
        LookupFunctionName:
          Ref: LookupFunction
        SaveReportingFunctionName:
          Ref: SaveReportingDataFunctionV2
        SettingsParameterName:
          Ref: SettingsParameter
        StackName:
          Ref: AWS::StackName
        LogRetentionDays:
          Ref: LogRetentionDays
        LogLevel:
          Ref: LogLevel
        DataRetentionInDays:
          Ref: DataRetentionInDays
        PermissionsBoundaryArn:
          Ref: PermissionsBoundaryArn
        ExecutionTimeThresholdMs:
          Ref: ExecutionTimeThresholdMs
        HasGuardrailConfig:
          Fn::If:
          - HasGuardrailConfig
          - 'true'
          - 'false'
        BedrockGuardrailId:
          Ref: BedrockGuardrailId
        BedrockGuardrailVersion:
          Ref: BedrockGuardrailVersion
        KnowledgeBaseModelId:
          Ref: KnowledgeBaseModelId
        ShouldCreateEvaluationBaselineBucket:
          Fn::If:
          - ShouldCreateEvaluationBaselineBucket
          - 'true'
          - 'false'
        ShouldCreateReportingBucket:
          Fn::If:
          - ShouldCreateReportingBucket
          - 'true'
          - 'false'
        IDPCommonBaseLayerArn:
          Ref: IDPCommonBaseLayer
        IDPCommonAgentsLayerArn:
          Ref: IDPCommonAgentsLayer
        UsersTableName:
          Ref: UsersTable
        UsersTableArn:
          Fn::GetAtt:
          - UsersTable
          - Arn
    Metadata:
      SamResourceId: APPSYNCSTACK
  CustomerManagedEncryptionKey:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::KMS::Key
    Metadata:
      security-matrix:
        rules_to_suppress:
        - id: IAM-005
          reason: No cross-account access - only same account root and AWS services
        - id: KMS-007
          reason: KMS monitoring not required for this IDP solution - comprehensive
            CloudWatch monitoring already in place
        - id: KMS-002
          reason: kms:* permission for account root is standard pattern for administrative
            access to KMS keys
      SamResourceId: CustomerManagedEncryptionKey
    Properties:
      Description: KMS key for DynamoDB encryption
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
        - Sid: Enable IAM User Permissions
          Effect: Allow
          Principal:
            AWS:
              Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:root
          Action: kms:*
          Resource: '*'
        - Sid: Allow lambda to access the Keys
          Effect: Allow
          Principal:
            AWS:
              Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:root
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource: '*'
        - Sid: Allow DynamoDB to use the key
          Effect: Allow
          Principal:
            Service:
              Fn::Sub: dynamodb.${AWS::URLSuffix}
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource: '*'
        - Sid: Allow CloudWatch Logs to use the key
          Effect: Allow
          Principal:
            Service:
              Fn::Sub: logs.${AWS::URLSuffix}
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource: '*'
        - Fn::If:
          - IsS3VectorsVectorStore
          - Sid: Allow S3 Vectors indexing service to use the key
            Effect: Allow
            Principal:
              Service:
                Fn::Sub: indexing.s3vectors.${AWS::URLSuffix}
            Action:
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey*
            - kms:DescribeKey
            Resource: '*'
          - Ref: AWS::NoValue
  CustomerManagedEncryptionKeyAlias:
    Type: AWS::KMS::Alias
    Properties:
      AliasName:
        Fn::Sub: alias/${AWS::StackName}-customer-encryption-key
      TargetKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: CustomerManagedEncryptionKeyAlias
  IDPCommonBaseLayer:
    Type: AWS::Lambda::LayerVersion
    Properties:
      LayerName:
        Fn::Sub: ${AWS::StackName}-idp-common-base
      Description: IDP Common base layer (core + docs_service + image)
      Content:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/genai-idp/0.5.4/layers/idp-common-base-4766f94e.zip
      CompatibleRuntimes:
      - python3.12
      - python3.13
    Metadata:
      SamResourceId: IDPCommonBaseLayer
  IDPCommonReportingLayer:
    Type: AWS::Lambda::LayerVersion
    Properties:
      LayerName:
        Fn::Sub: ${AWS::StackName}-idp-common-reporting
      Description: IDP Common reporting layer (core + docs_service + reporting)
      Content:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/genai-idp/0.5.4/layers/idp-common-reporting-4766f94e.zip
      CompatibleRuntimes:
      - python3.12
      - python3.13
    Metadata:
      SamResourceId: IDPCommonReportingLayer
  IDPCommonAgentsLayer:
    Type: AWS::Lambda::LayerVersion
    Properties:
      LayerName:
        Fn::Sub: ${AWS::StackName}-idp-common-agents
      Description: IDP Common agents layer (core + agents)
      Content:
        S3Bucket: aws-ml-blog-us-east-1
        S3Key: artifacts/genai-idp/0.5.4/layers/idp-common-agents-4766f94e.zip
      CompatibleRuntimes:
      - python3.12
      - python3.13
    Metadata:
      SamResourceId: IDPCommonAgentsLayer
  MCPContentBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: mcp-content-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteMCPTempFiles
          Status: Enabled
          Prefix: mcp-temp/
          ExpirationInDays: 7
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: MCPContentBucket
  MCPContentBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: MCPContentBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${MCPContentBucket.Arn}/*
          - Fn::Sub: ${MCPContentBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: MCPContentBucketPolicy
  LoggingBucket:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W35
          reason: This is the logging destination bucket - does not require its own
            access logging
      security-matrix:
        rules_to_suppress:
        - id: S3-001
          reason: This is the logging destination bucket - does not require its own
            access logging
      cdk_nag:
        rules_to_suppress:
        - id: NIST.800.53.R5-S3DefaultEncryptionKMS
          reason: Logging destination bucket requires SSE-S3; CloudFront and S3 access
            logging cannot write to KMS-encrypted buckets
      SamResourceId: LoggingBucket
    DeletionPolicy: Retain
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      OwnershipControls:
        Rules:
        - ObjectOwnership: BucketOwnerPreferred
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
        - Id: DeleteOldLogs
          Status: Enabled
          ExpirationInDays: 180
          Prefix: ''
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 7
  LoggingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Metadata:
      security-matrix:
        rules_to_suppress:
        - id: IAM-005
          reason: Already has aws:SourceAccount condition for confused deputy prevention
      SamResourceId: LoggingBucketPolicy
    Properties:
      Bucket:
        Ref: LoggingBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: AllowS3ServerAccessLogs
          Effect: Allow
          Principal:
            Service:
              Fn::Sub: logging.s3.${AWS::URLSuffix}
          Action:
          - s3:PutObject
          Resource:
            Fn::Sub: ${LoggingBucket.Arn}/*
          Condition:
            StringEquals:
              aws:SourceAccount:
                Ref: AWS::AccountId
        - Sid: AllowCloudFrontLogs
          Effect: Allow
          Principal:
            Service:
              Fn::Sub: cloudfront.${AWS::URLSuffix}
          Action:
          - s3:PutObject
          Resource:
            Fn::Sub: ${LoggingBucket.Arn}/*
          Condition:
            StringEquals:
              AWS:SourceAccount:
                Ref: AWS::AccountId
        - Fn::If:
          - UseALBHosting
          - Sid: AllowALBAccessLogs
            Effect: Allow
            Principal:
              Service:
                Fn::Sub: logdelivery.elasticloadbalancing.${AWS::URLSuffix}
            Action:
            - s3:PutObject
            Resource:
              Fn::Sub: ${LoggingBucket.Arn}/alb-access-logs/*
            Condition:
              StringEquals:
                aws:SourceAccount:
                  Ref: AWS::AccountId
          - Ref: AWS::NoValue
        - Fn::If:
          - UseALBHosting
          - Sid: AllowALBAccessLogsBucketACL
            Effect: Allow
            Principal:
              Service:
                Fn::Sub: logdelivery.elasticloadbalancing.${AWS::URLSuffix}
            Action:
            - s3:GetBucketAcl
            Resource:
              Fn::Sub: ${LoggingBucket.Arn}
            Condition:
              StringEquals:
                aws:SourceAccount:
                  Ref: AWS::AccountId
          - Ref: AWS::NoValue
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${LoggingBucket.Arn}/*
          - Fn::Sub: ${LoggingBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
  DiscoveryBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      NotificationConfiguration:
        EventBridgeConfiguration:
          EventBridgeEnabled: true
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: discov-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: DiscoveryBucket
  DiscoveryBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: DiscoveryBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${DiscoveryBucket.Arn}/*
          - Fn::Sub: ${DiscoveryBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: DiscoveryBucketPolicy
  InputBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      NotificationConfiguration:
        EventBridgeConfiguration:
          EventBridgeEnabled: true
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          - x-amz-meta-config-version
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: input-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: InputBucket
  InputBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: InputBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${InputBucket.Arn}/*
          - Fn::Sub: ${InputBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: InputBucketPolicy
  TestSetBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: test-set-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: TestSetBucket
  TestSetBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: TestSetBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${TestSetBucket.Arn}/*
          - Fn::Sub: ${TestSetBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: TestSetBucketPolicy
  TestSetBucketAutoDeleteFunction:
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
      SamResourceId: TestSetBucketAutoDeleteFunction
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 512
      InlineCode: "import boto3\nimport cfnresponse\nimport json\nimport logging\n\
        \nlogger = logging.getLogger()\nlogger.setLevel(logging.INFO)\n\ndef handler(event,\
        \ context):\n    logger.info(json.dumps(event))\n    \n    try:\n        bucket_name\
        \ = event['ResourceProperties']['BucketName']\n        \n        if event['RequestType']\
        \ == 'Delete':\n            logger.info(f\"Emptying bucket {bucket_name} before\
        \ deletion\")\n            s3 = boto3.resource('s3')\n            bucket =\
        \ s3.Bucket(bucket_name)\n            \n            # Delete all objects including\
        \ versions\n            bucket.object_versions.all().delete()\n          \
        \  logger.info(f\"Successfully emptied bucket {bucket_name}\")\n        \n\
        \        cfnresponse.send(event, context, cfnresponse.SUCCESS, {})\n     \
        \   \n    except Exception as e:\n        logger.error(f\"Error: {str(e)}\"\
        )\n        # On delete, we want to succeed even if there's an error\n    \
        \    # to avoid blocking stack deletion\n        if event['RequestType'] ==\
        \ 'Delete':\n            cfnresponse.send(event, context, cfnresponse.SUCCESS,\
        \ {}, \n                           reason=f\"Bucket cleanup attempted: {str(e)}\"\
        )\n        else:\n            cfnresponse.send(event, context, cfnresponse.SUCCESS,\
        \ {})\n"
      LoggingConfig:
        LogGroup:
          Ref: TestSetBucketAutoDeleteFunctionLogGroup
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - s3:DeleteObject
          - s3:DeleteObjectVersion
          - s3:GetBucketVersioning
          - s3:ListBucket
          - s3:ListBucketVersions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:s3:::${TestSetBucket}
          - Fn::Sub: arn:${AWS::Partition}:s3:::${TestSetBucket}/*
        - Effect: Allow
          Action:
          - kms:Decrypt
          - kms:GenerateDataKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  TestSetBucketAutoDeleteFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: TestSetBucketAutoDeleteFunctionLogGroup
  TestSetBucketAutoDelete:
    Type: Custom::S3AutoDelete
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - TestSetBucketAutoDeleteFunction
        - Arn
      BucketName:
        Ref: TestSetBucket
    Metadata:
      SamResourceId: TestSetBucketAutoDelete
  FccDatasetDeployerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: FccDatasetDeployerLogGroup
  FccDatasetDeployerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: FccDatasetDeployerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      FunctionName:
        Fn::Sub: ${AWS::StackName}-FccDatasetDeployer
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/e0dc5f1ad9fc8fd7befaaa65c57de298
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 2048
      LoggingConfig:
        LogGroup:
          Ref: FccDatasetDeployerLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TESTSET_BUCKET:
            Ref: TestSetBucket
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: TestSetBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  FccDatasetDeployment:
    Type: Custom::FccDatasetDeployer
    DependsOn:
    - TestSetBucket
    - TrackingTable
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - FccDatasetDeployerFunction
        - Arn
      DatasetVersion: '1.1'
      DatasetName: RealKIE-FCC-Verified
      DatasetDescription: Test set with single and multi-page invoices sourced from
        the Federal Communications Commission (FCC) to evaluate extraction performance.
      SourceCodeHash: 996bb194bfcf9133
    Metadata:
      SamResourceId: FccDatasetDeployment
  OcrBenchmarkDeployerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: OcrBenchmarkDeployerLogGroup
  OcrBenchmarkDeployerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: OcrBenchmarkDeployerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      FunctionName:
        Fn::Sub: ${AWS::StackName}-OcrBenchmarkDeployer
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/b0e05469c441bfea715d21f96058b68f
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 2048
      LoggingConfig:
        LogGroup:
          Ref: OcrBenchmarkDeployerLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TESTSET_BUCKET:
            Ref: TestSetBucket
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: TestSetBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  OcrBenchmarkDeployment:
    Type: Custom::OcrBenchmarkDeployer
    DependsOn:
    - TestSetBucket
    - TrackingTable
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - OcrBenchmarkDeployerFunction
        - Arn
      DatasetVersion: '1.0'
      DatasetName: OmniAI-OCR-Benchmark
      DatasetDescription: Test set with diverse document formats (tables, charts,
        forms, etc.) from the OmniAI OCR Benchmark, filtered for top format/schema
        pairs with at least 6 samples each.
      SourceCodeHash: 74220b4744eed7a2
    Metadata:
      SamResourceId: OcrBenchmarkDeployment
  RvlCdipNmpDeployerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: RvlCdipNmpDeployerLogGroup
  RvlCdipNmpDeployerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: RvlCdipNmpDeployerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      FunctionName:
        Fn::Sub: ${AWS::StackName}-RvlCdipNmpDeployer
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/2fd20470c62e23266eaf8563b05f09e6
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 3008
      EphemeralStorage:
        Size: 10240
      LoggingConfig:
        LogGroup:
          Ref: RvlCdipNmpDeployerLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TESTSET_BUCKET:
            Ref: TestSetBucket
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: TestSetBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  RvlCdipNmpDeployment:
    Type: Custom::RvlCdipNmpDeployer
    DependsOn:
    - TestSetBucket
    - TrackingTable
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - RvlCdipNmpDeployerFunction
        - Arn
      DatasetVersion: '0.1'
      DatasetName: RVL-CDIP-NMP-Packets
      DatasetDescription: Test set with 500 multi-page document packets combining
        13 document types (budget, email, form, handwritten, invoice, language, letter,
        memo, news article, questionnaire, resume, scientific publication, specification)
        for classification and document splitting evaluation.
      SourceCodeHash: 696eaeef6e5bd6be
    Metadata:
      SamResourceId: RvlCdipNmpDeployment
  W2DatasetDeployerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: W2DatasetDeployerLogGroup
  W2DatasetDeployerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: W2DatasetDeployerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      FunctionName:
        Fn::Sub: ${AWS::StackName}-W2DatasetDeployer
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/e049fcc2b4701e04df93a56c961a708b
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      MemorySize: 3008
      EphemeralStorage:
        Size: 10240
      LoggingConfig:
        LogGroup:
          Ref: W2DatasetDeployerLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TESTSET_BUCKET:
            Ref: TestSetBucket
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: TestSetBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  W2DatasetDeployment:
    Type: Custom::W2DatasetDeployer
    DependsOn:
    - TestSetBucket
    - TrackingTable
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - W2DatasetDeployerFunction
        - Arn
      DatasetVersion: '1.0'
      DatasetName: Fake-W2-Tax-Forms
      DatasetDescription: 'Test set with 2,000 synthetic US W-2 tax form images and
        45-field structured ground truth for extraction evaluation. Source: HuggingFace
        singhsays/fake-w2-us-tax-form-dataset (CC0: Public Domain).'
      SourceCodeHash: 677eb9dd0a6cd5d9
    Metadata:
      SamResourceId: W2DatasetDeployment
  ConfigurationBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Properties:
      NotificationConfiguration:
        EventBridgeConfiguration:
          EventBridgeEnabled: true
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: input-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: ConfigurationBucket
  ConfigurationBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: ConfigurationBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${ConfigurationBucket.Arn}/*
          - Fn::Sub: ${ConfigurationBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: ConfigurationBucketPolicy
  WorkingBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: working-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: LogRetentionDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: WorkingBucket
  WorkingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: WorkingBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${WorkingBucket.Arn}/*
          - Fn::Sub: ${WorkingBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: WorkingBucketPolicy
  OutputBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: output-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: OutputBucket
  OutputBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: OutputBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${OutputBucket.Arn}/*
          - Fn::Sub: ${OutputBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: OutputBucketPolicy
  ReportingBucket:
    Type: AWS::S3::Bucket
    Condition: ShouldCreateReportingBucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: reporting-bucket-logs/
      LifecycleConfiguration:
        Rules:
        - Id: DeleteAfterNDays
          Status: Enabled
          ExpirationInDays:
            Ref: DataRetentionInDays
          AbortIncompleteMultipartUpload:
            DaysAfterInitiation: 1
    Metadata:
      SamResourceId: ReportingBucket
  ReportingBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: ShouldCreateReportingBucket
    Properties:
      Bucket:
        Ref: ReportingBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${ReportingBucket.Arn}/*
          - Fn::Sub: ${ReportingBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: ReportingBucketPolicy
  GetLowercase:
    Type: Custom::GetLowercase
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - GetDomainLambda
        - Arn
      InputString:
        Ref: AWS::StackName
    Metadata:
      SamResourceId: GetLowercase
  ReportingDatabase:
    Type: AWS::Glue::Database
    Properties:
      CatalogId:
        Ref: AWS::AccountId
      DatabaseInput:
        Name:
          Fn::Sub: ${GetLowercase.Lowercase}-reporting-db
        Description: Database for evaluation results
    Metadata:
      SamResourceId: ReportingDatabase
  DocumentEvaluationsTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId:
        Ref: AWS::AccountId
      DatabaseName:
        Ref: ReportingDatabase
      TableInput:
        Name: document_evaluations
        Description: Table for document-level evaluation metrics
        TableType: EXTERNAL_TABLE
        Parameters:
          classification: parquet
          typeOfData: file
          projection.enabled: 'true'
          projection.date.type: date
          projection.date.format: yyyy-MM-dd
          projection.date.range: 2024-01-01,2030-12-31
          projection.date.interval: '1'
          projection.date.interval.unit: DAYS
          storage.location.template:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/document_metrics/date=${date}/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
              date: ${date}
        StorageDescriptor:
          Location:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/document_metrics/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          InputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat
          OutputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat
          SerdeInfo:
            SerializationLibrary: org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe
          Columns:
          - Name: document_id
            Type: string
          - Name: input_key
            Type: string
          - Name: evaluation_date
            Type: timestamp
          - Name: accuracy
            Type: double
          - Name: precision
            Type: double
          - Name: recall
            Type: double
          - Name: f1_score
            Type: double
          - Name: false_alarm_rate
            Type: double
          - Name: false_discovery_rate
            Type: double
          - Name: weighted_overall_score
            Type: double
          - Name: execution_time
            Type: double
          - Name: page_level_accuracy
            Type: double
          - Name: split_accuracy_without_order
            Type: double
          - Name: split_accuracy_with_order
            Type: double
          - Name: total_pages
            Type: int
          - Name: total_splits
            Type: int
          - Name: correctly_classified_pages
            Type: int
          - Name: correctly_split_without_order
            Type: int
          - Name: correctly_split_with_order
            Type: int
          Compressed: true
        PartitionKeys:
        - Name: date
          Type: string
    Metadata:
      SamResourceId: DocumentEvaluationsTable
  SectionEvaluationsTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId:
        Ref: AWS::AccountId
      DatabaseName:
        Ref: ReportingDatabase
      TableInput:
        Name: section_evaluations
        Description: Table for section-level evaluation metrics
        TableType: EXTERNAL_TABLE
        Parameters:
          classification: parquet
          typeOfData: file
          projection.enabled: 'true'
          projection.date.type: date
          projection.date.format: yyyy-MM-dd
          projection.date.range: 2024-01-01,2030-12-31
          projection.date.interval: '1'
          projection.date.interval.unit: DAYS
          storage.location.template:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/section_metrics/date=${date}/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
              date: ${date}
        StorageDescriptor:
          Location:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/section_metrics/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          InputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat
          OutputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat
          SerdeInfo:
            SerializationLibrary: org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe
          Columns:
          - Name: document_id
            Type: string
          - Name: section_id
            Type: string
          - Name: section_type
            Type: string
          - Name: accuracy
            Type: double
          - Name: precision
            Type: double
          - Name: recall
            Type: double
          - Name: f1_score
            Type: double
          - Name: false_alarm_rate
            Type: double
          - Name: false_discovery_rate
            Type: double
          - Name: weighted_overall_score
            Type: double
          - Name: evaluation_date
            Type: timestamp
          Compressed: true
        PartitionKeys:
        - Name: date
          Type: string
    Metadata:
      SamResourceId: SectionEvaluationsTable
  AttributeEvaluationsTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId:
        Ref: AWS::AccountId
      DatabaseName:
        Ref: ReportingDatabase
      TableInput:
        Name: attribute_evaluations
        Description: Table for attribute-level evaluation metrics
        TableType: EXTERNAL_TABLE
        Parameters:
          classification: parquet
          typeOfData: file
          projection.enabled: 'true'
          projection.date.type: date
          projection.date.format: yyyy-MM-dd
          projection.date.range: 2024-01-01,2030-12-31
          projection.date.interval: '1'
          projection.date.interval.unit: DAYS
          storage.location.template:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/attribute_metrics/date=${date}/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
              date: ${date}
        StorageDescriptor:
          Location:
            Fn::Sub:
            - s3://${Bucket}/evaluation_metrics/attribute_metrics/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          InputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat
          OutputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat
          SerdeInfo:
            SerializationLibrary: org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe
          Columns:
          - Name: document_id
            Type: string
          - Name: section_id
            Type: string
          - Name: section_type
            Type: string
          - Name: attribute_name
            Type: string
          - Name: expected
            Type: string
          - Name: actual
            Type: string
          - Name: matched
            Type: boolean
          - Name: score
            Type: double
          - Name: reason
            Type: string
          - Name: evaluation_method
            Type: string
          - Name: confidence
            Type: string
          - Name: confidence_threshold
            Type: string
          - Name: weight
            Type: double
          - Name: evaluation_date
            Type: timestamp
          Compressed: true
        PartitionKeys:
        - Name: date
          Type: string
    Metadata:
      SamResourceId: AttributeEvaluationsTable
  MeteringTable:
    Type: AWS::Glue::Table
    Properties:
      CatalogId:
        Ref: AWS::AccountId
      DatabaseName:
        Ref: ReportingDatabase
      TableInput:
        Name: metering
        Description: Table for document metering data
        TableType: EXTERNAL_TABLE
        Parameters:
          classification: parquet
          typeOfData: file
          projection.enabled: 'true'
          projection.date.type: date
          projection.date.format: yyyy-MM-dd
          projection.date.range: 2024-01-01,2030-12-31
          projection.date.interval: '1'
          projection.date.interval.unit: DAYS
          storage.location.template:
            Fn::Sub:
            - s3://${Bucket}/metering/date=${date}/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
              date: ${date}
        StorageDescriptor:
          Location:
            Fn::Sub:
            - s3://${Bucket}/metering/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          InputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat
          OutputFormat: org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat
          SerdeInfo:
            SerializationLibrary: org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe
          Columns:
          - Name: document_id
            Type: string
          - Name: context
            Type: string
          - Name: service_api
            Type: string
          - Name: unit
            Type: string
          - Name: value
            Type: double
          - Name: number_of_pages
            Type: int
          - Name: unit_cost
            Type: double
          - Name: estimated_cost
            Type: double
          - Name: timestamp
            Type: timestamp
          Compressed: true
        PartitionKeys:
        - Name: date
          Type: string
    Metadata:
      SamResourceId: MeteringTable
  DocumentSectionsCrawlerSecurityConfigurationV2:
    Type: AWS::Glue::SecurityConfiguration
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-document-sections-crawler-security-config-v2
      EncryptionConfiguration:
        S3Encryptions:
        - S3EncryptionMode: SSE-KMS
          KmsKeyArn:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
    Metadata:
      SamResourceId: DocumentSectionsCrawlerSecurityConfigurationV2
  DocumentSectionsCrawler:
    Type: AWS::Glue::Crawler
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-document-sections-crawler
      Description: Crawler to discover document section tables in the reporting bucket
        with conservative schema handling
      Role:
        Fn::GetAtt:
        - DocumentSectionsCrawlerRole
        - Arn
      DatabaseName:
        Ref: ReportingDatabase
      CrawlerSecurityConfiguration:
        Ref: DocumentSectionsCrawlerSecurityConfigurationV2
      Targets:
        S3Targets:
        - Path:
            Fn::Sub:
            - s3://${Bucket}/document_sections/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
      SchemaChangePolicy:
        UpdateBehavior: UPDATE_IN_DATABASE
        DeleteBehavior: LOG
      TablePrefix: document_sections_
      Configuration: "{\n  \"Version\": 1.0,\n  \"CrawlerOutput\": {\n    \"Partitions\"\
        : { \"AddOrUpdateBehavior\": \"InheritFromTable\" },\n    \"Tables\": { \"\
        AddOrUpdateBehavior\": \"MergeNewColumns\" }\n  },\n  \"Grouping\": { \"TableLevelConfiguration\"\
        : 3 },\n  \"CreatePartitionIndex\": true\n}\n"
      Schedule:
        Fn::If:
        - DocumentSectionsCrawlerScheduleEnabled
        - ScheduleExpression:
            Fn::FindInMap:
            - CrawlerScheduleMap
            - Ref: DocumentSectionsCrawlerFrequency
            - ScheduleExpression
        - Ref: AWS::NoValue
    Metadata:
      SamResourceId: DocumentSectionsCrawler
  DocumentSectionsCrawlerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              Fn::Sub: glue.${AWS::URLSuffix}
          Action: sts:AssumeRole
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSGlueServiceRole
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: DocumentSectionsCrawlerS3Access
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - s3:GetObject
            - s3:ListBucket
            Resource:
            - Fn::Sub:
              - arn:${AWS::Partition}:s3:::${Bucket}
              - Bucket:
                  Fn::If:
                  - ShouldCreateReportingBucket
                  - Ref: ReportingBucket
                  - Ref: ReportingBucketName
            - Fn::Sub:
              - arn:${AWS::Partition}:s3:::${Bucket}/document_sections/*
              - Bucket:
                  Fn::If:
                  - ShouldCreateReportingBucket
                  - Ref: ReportingBucket
                  - Ref: ReportingBucketName
          - Effect: Allow
            Action:
            - kms:Decrypt
            - kms:DescribeKey
            Resource:
              Fn::GetAtt:
              - CustomerManagedEncryptionKey
              - Arn
    Metadata:
      SamResourceId: DocumentSectionsCrawlerRole
  EvaluationBaselineBucket:
    Type: AWS::S3::Bucket
    Condition: ShouldCreateEvaluationBaselineBucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: RetainExceptOnCreate
    Properties:
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders:
          - Content-Type
          - x-amz-content-sha256
          - x-amz-date
          - Authorization
          - x-amz-security-token
          AllowedMethods:
          - PUT
          - POST
          AllowedOrigins:
          - Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}
            - Fn::GetAtt:
              - ALBHOSTINGSTACK
              - Outputs.WebUIURL
          - http://localhost:3000
          ExposedHeaders:
          - ETag
          - x-amz-server-side-encryption
          MaxAge: 3000
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID:
              Ref: CustomerManagedEncryptionKey
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: output-bucket-logs/
    Metadata:
      SamResourceId: EvaluationBaselineBucket
  EvaluationBaselineBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Condition: ShouldCreateEvaluationBaselineBucket
    Properties:
      Bucket:
        Ref: EvaluationBaselineBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: s3:*
          Resource:
          - Fn::Sub: ${EvaluationBaselineBucket.Arn}/*
          - Fn::Sub: ${EvaluationBaselineBucket.Arn}
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: EvaluationBaselineBucketPolicy
  WebUIBucket:
    Type: AWS::S3::Bucket
    UpdateReplacePolicy: Retain
    DeletionPolicy: Retain
    Metadata:
      cfn-lint:
        config:
          ignore_checks:
          - W3045
      cdk_nag:
        rules_to_suppress:
        - id: NIST.800.53.R5-S3DefaultEncryptionKMS
          reason: Static web assets bucket served via CloudFront/ALB; KMS encryption
            adds cost and latency with no security benefit for public web content
      SamResourceId: WebUIBucket
    Properties:
      AccessControl: Private
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      WebsiteConfiguration:
        Fn::If:
        - UseCloudFrontHosting
        - IndexDocument: index.html
          ErrorDocument: index.html
        - Ref: AWS::NoValue
      LoggingConfiguration:
        DestinationBucketName:
          Ref: LoggingBucket
        LogFilePrefix: webapp-bucket-logs/
  WebUIBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket:
        Ref: WebUIBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Fn::If:
          - UseCloudFrontHosting
          - Effect: Allow
            Principal:
              CanonicalUser:
                Fn::GetAtt:
                - CloudFrontOriginAccessIdentity
                - S3CanonicalUserId
            Action: s3:GetObject
            Resource:
              Fn::Sub: ${WebUIBucket.Arn}/*
          - Ref: AWS::NoValue
        - Fn::If:
          - UseALBHosting
          - Effect: Allow
            Principal: '*'
            Action: s3:GetObject
            Resource:
              Fn::Sub: ${WebUIBucket.Arn}/*
            Condition:
              StringEquals:
                aws:sourceVpce:
                  Fn::GetAtt:
                  - ALBHOSTINGSTACK
                  - Outputs.S3VPCEndpointId
          - Ref: AWS::NoValue
        - Effect: Deny
          Action:
          - s3:*
          Principal: '*'
          Resource:
          - Fn::GetAtt:
            - WebUIBucket
            - Arn
          - Fn::Sub: ${WebUIBucket.Arn}/*
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: WebUIBucketPolicy
  TrackingTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: PK
        AttributeType: S
      - AttributeName: SK
        AttributeType: S
      - AttributeName: InitialEventTime
        AttributeType: S
      - AttributeName: ItemType
        AttributeType: S
      KeySchema:
      - AttributeName: PK
        KeyType: HASH
      - AttributeName: SK
        KeyType: RANGE
      GlobalSecondaryIndexes:
      - IndexName: TypeDateIndex
        KeySchema:
        - AttributeName: ItemType
          KeyType: HASH
        - AttributeName: InitialEventTime
          KeyType: RANGE
        Projection:
          ProjectionType: INCLUDE
          NonKeyAttributes:
          - ObjectKey
          - ObjectStatus
          - CompletionTime
          - ConfigVersion
          - EvaluationStatus
          - NumPages
          - ConfidenceAlertCount
          - HITLTriggered
          - HITLCompleted
          - HITLStatus
          - HITLReviewOwner
          - HITLReviewedBy
          - TestRunId
          - TestSetName
          - CreatedAt
          - CompletedFiles
          - FailedFiles
          - FilesCount
          - Status
          - CompletedAt
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: TrackingTable
  BackfillWorkerFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: BackfillWorkerFunctionLogGroup
  BackfillWorkerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: BackfillWorkerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with DynamoDB
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.lambda_handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/8bee08827d88ba5e25b25468fb38f579
      Timeout: 900
      MemorySize: 512
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
      LoggingConfig:
        LogGroup:
          Ref: BackfillWorkerFunctionLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  BackfillStateMachineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              Fn::Sub: states.${AWS::URLSuffix}
          Action: sts:AssumeRole
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: BackfillStateMachinePolicy
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource:
              Fn::GetAtt:
              - BackfillWorkerFunction
              - Arn
    Metadata:
      SamResourceId: BackfillStateMachineRole
  BackfillStateMachine:
    Type: AWS::Serverless::StateMachine
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-GSIBackfill
      DefinitionUri:
        Bucket: aws-ml-blog-us-east-1
        Key: artifacts/genai-idp/0.5.4/99b30b34a6a85de005d4a64e580d3fcb
      DefinitionSubstitutions:
        BackfillWorkerFunctionArn:
          Fn::GetAtt:
          - BackfillWorkerFunction
          - Arn
      Role:
        Fn::GetAtt:
        - BackfillStateMachineRole
        - Arn
    Metadata:
      SamResourceId: BackfillStateMachine
  BackfillTriggerFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: BackfillTriggerFunctionLogGroup
  BackfillTriggerFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: BackfillTriggerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/8bee08827d88ba5e25b25468fb38f579
      Timeout: 60
      MemorySize: 256
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
      LoggingConfig:
        LogGroup:
          Ref: BackfillTriggerFunctionLogGroup
      Policies:
      - DynamoDBReadPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action: states:StartExecution
          Resource:
            Ref: BackfillStateMachine
        - Effect: Allow
          Action:
          - kms:Decrypt
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  BackfillCustomResource:
    Type: Custom::GSIBackfill
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - BackfillTriggerFunction
        - Arn
      TrackingTableName:
        Ref: TrackingTable
      BackfillStateMachineArn:
        Ref: BackfillStateMachine
      TotalSegments: '10'
      BackfillVersion: '3.0'
    Metadata:
      SamResourceId: BackfillCustomResource
  ConcurrencyTable:
    Type: AWS::DynamoDB::Table
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W78
          reason: Point-in-time recovery not required for concurrency tracking table
            as data is transient
      SamResourceId: ConcurrencyTable
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: counter_id
        AttributeType: S
      KeySchema:
      - AttributeName: counter_id
        KeyType: HASH
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
  InitializeConcurrencyTableLambda:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: InitializeConcurrencyTableLambda
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/53b0bedb4a5d96c39145cfdb31a200c9
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONCURRENCY_TABLE:
            Ref: ConcurrencyTable
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConcurrencyTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  InitializeConcurrencyTableCustomResource:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - InitializeConcurrencyTableLambda
        - Arn
      TableName:
        Ref: ConcurrencyTable
    Metadata:
      SamResourceId: InitializeConcurrencyTableCustomResource
  ConfigurationTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      KeySchema:
      - AttributeName: Configuration
        KeyType: HASH
      AttributeDefinitions:
      - AttributeName: Configuration
        AttributeType: S
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: ConfigurationTable
  DiscoveryTrackingTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: jobId
        AttributeType: S
      KeySchema:
      - AttributeName: jobId
        KeyType: HASH
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: DiscoveryTrackingTable
  DiscoveryQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 900
      MessageRetentionPeriod: 86400
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      RedrivePolicy:
        deadLetterTargetArn:
          Fn::GetAtt:
          - DiscoveryDLQ
          - Arn
        maxReceiveCount: 1000
    Metadata:
      SamResourceId: DiscoveryQueue
  DiscoveryDLQ:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 900
      MessageRetentionPeriod: 345600
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: DiscoveryDLQ
  UpdateConfigurationFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: UpdateConfigurationFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      Layers:
      - Ref: IDPCommonBaseLayer
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/8a607d3a104e78d371c0c50db4ef7028
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
      LoggingConfig:
        LogGroup:
          Ref: UpdateConfigurationFunctionLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConfigurationTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - s3:GetObject
          - s3:ListBucket
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1
          - Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/*
          - Fn::Sub: arn:${AWS::Partition}:s3:::${ConfigurationBucket}
          - Fn::Sub: arn:${AWS::Partition}:s3:::${ConfigurationBucket}/*
        - Fn::If:
          - HasCustomConfigPath
          - Effect: Allow
            Action:
            - s3:GetObject
            Resource:
              Fn::Sub:
              - arn:${AWS::Partition}:s3:::${Path}
              - Path:
                  Fn::Select:
                  - 1
                  - Fn::Split:
                    - s3://
                    - Ref: CustomConfigPath
          - Ref: AWS::NoValue
        - Fn::If:
          - HasCustomConfigPath
          - Effect: Allow
            Action:
            - s3:ListBucket
            Resource:
              Fn::Sub:
              - arn:${AWS::Partition}:s3:::${BucketName}
              - BucketName:
                  Fn::Select:
                  - 0
                  - Fn::Split:
                    - /
                    - Fn::Select:
                      - 1
                      - Fn::Split:
                        - s3://
                        - Ref: CustomConfigPath
          - Ref: AWS::NoValue
  UpdateConfigurationFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: UpdateConfigurationFunctionLogGroup
  SettingsParameter:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::SSM::Parameter
    Properties:
      Type: String
      Name:
        Fn::Sub: ${AWS::StackName}-Settings
      Value: '{}'
    Metadata:
      SamResourceId: SettingsParameter
  UpdateSettingsFunction:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: UpdateSettingsFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/63a2e36fe625f71c11671fac1e21c97a
      LoggingConfig:
        LogGroup:
          Ref: UpdateSettingsFunctionLogGroup
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - ssm:GetParameter
          - ssm:PutParameter
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
  UpdateSettingsFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: UpdateSettingsFunctionLogGroup
  UpdateSettingsValues:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - UpdateSettingsFunction
        - Arn
      SettingsName:
        Ref: SettingsParameter
      SettingsKeyValuePairs:
        StackName:
          Fn::Sub: ${AWS::StackName}
        Version: '0.5.4'
        BuildDateTime: '2026-03-27 22:07:17'
        IDPPattern: Unified
        InputBucket:
          Ref: InputBucket
        DiscoveryBucket:
          Ref: DiscoveryBucket
        ConfigurationBucket:
          Ref: ConfigurationBucket
        OutputBucket:
          Ref: OutputBucket
        ReportingBucket:
          Fn::If:
          - ShouldCreateReportingBucket
          - Ref: ReportingBucket
          - Ref: ReportingBucketName
        EvaluationBaselineBucket:
          Fn::If:
          - ShouldCreateEvaluationBaselineBucket
          - Ref: EvaluationBaselineBucket
          - Ref: EvaluationBaselineBucketName
        ShouldUseDocumentKnowledgeBase:
          Fn::If:
          - ShouldUseDocumentKnowledgeBase
          - true
          - false
        CloudWatchLogGroups:
          Fn::Join:
          - ','
          - - Ref: QueueSenderLogGroup
            - Ref: QueueProcessorLogGroup
            - Ref: WorkflowTrackerLogGroup
            - Ref: SaveReportingDataFunctionV2LogGroup
            - Fn::GetAtt:
              - PATTERNSTACK
              - Outputs.PatternLogGroups
        StateMachineArn:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.StateMachineArn
        StateMachineName:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.StateMachineName
        KnowledgeBaseId:
          Fn::If:
          - ShouldCreateDocumentKnowledgeBase
          - Fn::GetAtt:
            - DOCUMENTKB
            - Outputs.KnowledgeBaseID
          - ''
        AllowedSignUpEmailDomains:
          Ref: AllowedSignUpEmailDomain
    UpdateReplacePolicy: Delete
    DeletionPolicy: Delete
    Metadata:
      SamResourceId: UpdateSettingsValues
  DocumentQueueDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 30
      MessageRetentionPeriod: 345600
    Metadata:
      SamResourceId: DocumentQueueDLQ
  DocumentQueueDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: DocumentQueueDLQ
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - DocumentQueueDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: DocumentQueueDLQPolicy
  DocumentQueue:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 30
      MessageRetentionPeriod: 86400
      RedrivePolicy:
        maxReceiveCount: 1000
        deadLetterTargetArn:
          Fn::GetAtt:
          - DocumentQueueDLQ
          - Arn
    Metadata:
      SamResourceId: DocumentQueue
  DocumentQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: DocumentQueue
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - DocumentQueue
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: DocumentQueuePolicy
  QueueSenderDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 30
      MessageRetentionPeriod: 345600
    Metadata:
      SamResourceId: QueueSenderDLQ
  QueueSenderDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: QueueSenderDLQ
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - QueueSenderDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: QueueSenderDLQPolicy
  QueueSender:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: QueueSender
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/a241c57f7e21aa63af387498a883781f
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      Layers:
      - Ref: IDPCommonBaseLayer
      Tracing: Active
      DeadLetterQueue:
        Type: SQS
        TargetArn:
          Fn::GetAtt:
          - QueueSenderDLQ
          - Arn
      LoggingConfig:
        LogGroup:
          Ref: QueueSenderLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          QUEUE_URL:
            Ref: DocumentQueue
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          DATA_RETENTION_IN_DAYS:
            Ref: DataRetentionInDays
          OUTPUT_BUCKET:
            Ref: OutputBucket
          CONFIG_TABLE:
            Ref: ConfigurationTable
      Policies:
      - SQSSendMessagePolicy:
          QueueName:
            Fn::GetAtt:
            - DocumentQueue
            - QueueName
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - S3ReadPolicy:
          BucketName:
            Ref: InputBucket
      - Statement:
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
      Events:
        S3Event:
          Type: CloudWatchEvent
          Properties:
            Pattern:
              source:
              - aws.s3
              detail-type:
              - Object Created
              detail:
                bucket:
                  name:
                  - Ref: InputBucket
  QueueSenderLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: QueueSenderLogGroup
  QueueProcessor:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: QueueProcessor
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/ccaa9acb80f680c2283c62838b822dcb
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      Layers:
      - Ref: IDPCommonBaseLayer
      Tracing: Active
      LoggingConfig:
        LogGroup:
          Ref: QueueProcessorLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          STATE_MACHINE_ARN:
            Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineArn
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          CONCURRENCY_TABLE:
            Ref: ConcurrencyTable
          MAX_CONCURRENT:
            Ref: MaxConcurrentWorkflows
          WORKING_BUCKET:
            Ref: WorkingBucket
          CONFIG_TABLE:
            Ref: ConfigurationTable
      Policies:
      - SQSPollerPolicy:
          QueueName:
            Fn::GetAtt:
            - DocumentQueue
            - QueueName
      - StepFunctionsExecutionPolicy:
          StateMachineName:
            Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineName
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConcurrencyTable
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - S3CrudPolicy:
          BucketName:
            Ref: WorkingBucket
      - Statement:
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Query/*
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue:
              Fn::GetAtt:
              - DocumentQueue
              - Arn
            BatchSize: 50
            MaximumBatchingWindowInSeconds: 1
            FunctionResponseTypes:
            - ReportBatchItemFailures
  QueueProcessorLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: QueueProcessorLogGroup
  WorkflowTrackerDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 30
      MessageRetentionPeriod: 345600
    Metadata:
      SamResourceId: WorkflowTrackerDLQ
  WorkflowTrackerDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: WorkflowTrackerDLQ
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - WorkflowTrackerDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: WorkflowTrackerDLQPolicy
  WorkflowTracker:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: WorkflowTracker
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/df8aad8f2270d9795dbe7a7a2837c20d
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      Layers:
      - Ref: IDPCommonBaseLayer
      Tracing: Active
      DeadLetterQueue:
        Type: SQS
        TargetArn:
          Fn::GetAtt:
          - WorkflowTrackerDLQ
          - Arn
      LoggingConfig:
        LogGroup:
          Ref: WorkflowTrackerLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONCURRENCY_TABLE:
            Ref: ConcurrencyTable
          METRIC_NAMESPACE:
            Ref: AWS::StackName
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          OUTPUT_BUCKET:
            Ref: OutputBucket
          REPORTING_BUCKET:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
          SAVE_REPORTING_FUNCTION_NAME:
            Ref: SaveReportingDataFunctionV2
          WORKING_BUCKET:
            Ref: WorkingBucket
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConcurrencyTable
      - S3CrudPolicy:
          BucketName:
            Ref: WorkingBucket
      - Statement:
        - Effect: Allow
          Action:
          - cloudwatch:PutMetricData
          Resource: '*'
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
            Fn::GetAtt:
            - SaveReportingDataFunctionV2
            - Arn
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  WorkflowTrackerLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: WorkflowTrackerLogGroup
  WorkflowStateChangeRule:
    Type: AWS::Events::Rule
    Properties:
      EventPattern:
        source:
        - aws.states
        detail-type:
        - Step Functions Execution Status Change
        detail:
          stateMachineArn:
          - Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineArn
          status:
          - FAILED
          - TIMED_OUT
          - ABORTED
          - SUCCEEDED
      State: ENABLED
      Targets:
      - Arn:
          Fn::GetAtt:
          - WorkflowTracker
          - Arn
        Id: TrackWorkflowCompletion
        RetryPolicy:
          MaximumRetryAttempts: 3
    Metadata:
      SamResourceId: WorkflowStateChangeRule
  WorkflowTrackerPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: WorkflowTracker
      Principal:
        Fn::Sub: events.${AWS::URLSuffix}
      SourceArn:
        Fn::GetAtt:
        - WorkflowStateChangeRule
        - Arn
    Metadata:
      SamResourceId: WorkflowTrackerPermission
  PostProcessingDecompressorDLQ:
    Type: AWS::SQS::Queue
    Condition: ShouldEnablePostProcessingLambdaHook
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 30
      MessageRetentionPeriod: 345600
    Metadata:
      SamResourceId: PostProcessingDecompressorDLQ
  PostProcessingDecompressorDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Condition: ShouldEnablePostProcessingLambdaHook
    Properties:
      Queues:
      - Ref: PostProcessingDecompressorDLQ
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: EnforceSSLOnly
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - PostProcessingDecompressorDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: false
    Metadata:
      SamResourceId: PostProcessingDecompressorDLQPolicy
  PostProcessingDecompressor:
    Type: AWS::Serverless::Function
    Condition: ShouldEnablePostProcessingLambdaHook
    Metadata:
      SamResourceId: PostProcessingDecompressor
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/97e1dec293acc9260faff659edf22df7
      Handler: index.handler
      Runtime: python3.12
      Timeout: 300
      Layers:
      - Ref: IDPCommonBaseLayer
      MemorySize: 512
      Tracing: Active
      DeadLetterQueue:
        Type: SQS
        TargetArn:
          Fn::GetAtt:
          - PostProcessingDecompressorDLQ
          - Arn
      LoggingConfig:
        LogGroup:
          Ref: PostProcessingDecompressorLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CUSTOM_POST_PROCESSOR_ARN:
            Ref: PostProcessingLambdaHookFunctionArn
          WORKING_BUCKET:
            Ref: WorkingBucket
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: WorkingBucket
      - Statement:
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
            Ref: PostProcessingLambdaHookFunctionArn
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  PostProcessingDecompressorLogGroup:
    Type: AWS::Logs::LogGroup
    Condition: ShouldEnablePostProcessingLambdaHook
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: PostProcessingDecompressorLogGroup
  PostProcessingLambdaHookRule:
    Type: AWS::Events::Rule
    Condition: ShouldEnablePostProcessingLambdaHook
    Properties:
      EventPattern:
        source:
        - aws.states
        detail-type:
        - Step Functions Execution Status Change
        detail:
          stateMachineArn:
          - Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineArn
          status:
          - SUCCEEDED
      State: ENABLED
      Targets:
      - Arn:
          Fn::GetAtt:
          - PostProcessingDecompressor
          - Arn
        Id: PostProcessingDecompressor
        RetryPolicy:
          MaximumRetryAttempts: 3
    Metadata:
      SamResourceId: PostProcessingLambdaHookRule
  PostProcessingDecompressorPermission:
    Type: AWS::Lambda::Permission
    Condition: ShouldEnablePostProcessingLambdaHook
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: PostProcessingDecompressor
      Principal:
        Fn::Sub: events.${AWS::URLSuffix}
      SourceArn:
        Fn::GetAtt:
        - PostProcessingLambdaHookRule
        - Arn
    Metadata:
      SamResourceId: PostProcessingDecompressorPermission
  SaveReportingDataFunctionV2:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: SaveReportingDataFunctionV2
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/47e6977116c62c80c0d7bbaf46574adf
      Handler: index.handler
      Runtime: python3.12
      Timeout: 300
      Layers:
      - Ref: IDPCommonReportingLayer
      MemorySize: 1024
      Tracing: Active
      LoggingConfig:
        LogGroup:
          Ref: SaveReportingDataFunctionV2LogGroup
      Policies:
      - S3CrudPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
      - S3ReadPolicy:
          BucketName:
            Ref: OutputBucket
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - Statement:
        - Effect: Allow
          Action:
          - cloudwatch:PutMetricData
          Resource: '*'
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - glue:CreateTable
          - glue:GetTable
          - glue:UpdateTable
          - glue:GetDatabase
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:database/${GetLowercase.Lowercase}-reporting-db
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/document_sections_*
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/metering
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/document_evaluations
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/section_evaluations
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/attribute_evaluations
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${GetLowercase.Lowercase}-reporting-db/rule_validation_*
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          METRIC_NAMESPACE:
            Ref: AWS::StackName
          STACK_NAME:
            Ref: AWS::StackName
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
  SaveReportingDataFunctionV2LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: SaveReportingDataFunctionV2LogGroup
  AlertsTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName: Workflow Alerts
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: AlertsTopic
  WorkflowErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription:
        Fn::Sub: Alert when workflow errors exceed ${ErrorThreshold} in 5 minutes
      MetricName: ExecutionsFailedCount
      Namespace: AWS/States
      Statistic: Sum
      Period: 300
      EvaluationPeriods: 1
      Threshold:
        Ref: ErrorThreshold
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
      - Name: StateMachineArn
        Value:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.StateMachineArn
      AlarmActions:
      - Ref: AlertsTopic
    Metadata:
      SamResourceId: WorkflowErrorsAlarm
  SlowExecutionsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription:
        Fn::Sub: Alert when average execution time exceeds ${ExecutionTimeThresholdMs}
          milliseconds
      MetricName: ExecutionTime
      Namespace: AWS/States
      Statistic: Average
      Period: 300
      EvaluationPeriods: 1
      Threshold:
        Ref: ExecutionTimeThresholdMs
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
      - Name: StateMachineArn
        Value:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.StateMachineArn
      AlarmActions:
      - Ref: AlertsTopic
    Metadata:
      SamResourceId: SlowExecutionsAlarm
  MainTemplateSubsetDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName:
        Fn::Sub: ${AWS::StackName}-${AWS::Region}-MainTemplate-Subset
      DashboardBody:
        Fn::Sub:
        - "{\n  \"widgets\": [\n    {\n      \"type\": \"metric\",\n      \"x\": 0,\n\
          \      \"y\": 0,\n      \"width\": 8,\n      \"height\": 6,\n      \"properties\"\
          : {\n        \"metrics\": [\n          [\n            \"${AWS::StackName}\"\
          ,\n            \"QueueLatencyMilliseconds\",\n            {\n          \
          \    \"stat\": \"Average\"\n            }\n          ],\n          [\n \
          \           \".\",\n            \"QueueLatencyMilliseconds\",\n        \
          \    {\n              \"stat\": \"p90\"\n            }\n          ],\n \
          \         [\n            \".\",\n            \"QueueLatencyMilliseconds\"\
          ,\n            {\n              \"stat\": \"Maximum\"\n            }\n \
          \         ]\n        ],\n        \"region\": \"${AWS::Region}\",\n     \
          \   \"title\": \"Queue Latency\",\n        \"period\": 300,\n        \"\
          view\": \"timeSeries\",\n        \"annotations\": {\n          \"horizontal\"\
          : [\n            {\n              \"value\": ${ExecutionTimeThresholdMs\n\
          \              },\n              \"label\": \"Threshold (${ExecutionTimeThresholdMs}ms)\"\
          ,\n              \"color\": \"#ff0000\"\n            }\n          ]\n  \
          \      }\n      }\n    },\n    {\n      \"type\": \"metric\",\n      \"\
          x\": 8,\n      \"y\": 0,\n      \"width\": 8,\n      \"height\": 6,\n  \
          \    \"properties\": {\n        \"metrics\": [\n          [\n          \
          \  \"${AWS::StackName}\",\n            \"WorkflowLatencyMilliseconds\",\n\
          \            {\n              \"stat\": \"Average\"\n            }\n   \
          \       ],\n          [\n            \".\",\n            \"WorkflowLatencyMilliseconds\"\
          ,\n            {\n              \"stat\": \"p90\"\n            }\n     \
          \     ],\n          [\n            \".\",\n            \"WorkflowLatencyMilliseconds\"\
          ,\n            {\n              \"stat\": \"Maximum\"\n            }\n \
          \         ]\n        ],\n        \"region\": \"${AWS::Region}\",\n     \
          \   \"title\": \"Workflow Latency\",\n        \"period\": 300,\n       \
          \ \"view\": \"timeSeries\",\n        \"annotations\": {\n          \"horizontal\"\
          : [\n            {\n              \"value\": ${ExecutionTimeThresholdMs\n\
          \              },\n              \"label\": \"Threshold (${ExecutionTimeThresholdMs}ms)\"\
          ,\n              \"color\": \"#ff0000\"\n            }\n          ]\n  \
          \      }\n      }\n    },\n    {\n      \"type\": \"metric\",\n      \"\
          x\": 16,\n      \"y\": 0,\n      \"width\": 8,\n      \"height\": 6,\n \
          \     \"properties\": {\n        \"metrics\": [\n          [\n         \
          \   \"${AWS::StackName}\",\n            \"TotalLatencyMilliseconds\",\n\
          \            {\n              \"stat\": \"Average\"\n            }\n   \
          \       ],\n          [\n            \".\",\n            \"TotalLatencyMilliseconds\"\
          ,\n            {\n              \"stat\": \"p90\"\n            }\n     \
          \     ],\n          [\n            \".\",\n            \"TotalLatencyMilliseconds\"\
          ,\n            {\n              \"stat\": \"Maximum\"\n            }\n \
          \         ]\n        ],\n        \"region\": \"${AWS::Region}\",\n     \
          \   \"title\": \"Total Processing Latency\",\n        \"period\": 300,\n\
          \        \"view\": \"timeSeries\",\n        \"annotations\": {\n       \
          \   \"horizontal\": [\n            {\n              \"value\": ${ExecutionTimeThresholdMs\n\
          \              },\n              \"label\": \"Threshold (${ExecutionTimeThresholdMs}ms)\"\
          ,\n              \"color\": \"#ff0000\"\n            }\n          ]\n  \
          \      }\n      }\n    },\n    {\n      \"type\": \"metric\",\n      \"\
          x\": 0,\n      \"y\": 6,\n      \"width\": 8,\n      \"height\": 6,\n  \
          \    \"properties\": {\n        \"metrics\": [\n          [\n          \
          \  {\n              \"expression\": \"m1/PERIOD(m1)*60\",\n            \
          \  \"label\": \"Messages Received per Minute\",\n              \"id\": \"\
          e1\"\n            }\n          ],\n          [\n            {\n        \
          \      \"expression\": \"m2/PERIOD(m2)*60\",\n              \"label\": \"\
          Messages Deleted per Minute\",\n              \"id\": \"e2\"\n         \
          \   }\n          ],\n          [\n            \"AWS/SQS\",\n           \
          \ \"NumberOfMessagesReceived\",\n            \"QueueName\",\n          \
          \  \"${DocumentQueue.QueueName}\",\n            {\n              \"id\"\
          : \"m1\",\n              \"stat\": \"Sum\",\n              \"visible\":\
          \ false\n            }\n          ],\n          [\n            \".\",\n\
          \            \"NumberOfMessagesDeleted\",\n            \".\",\n        \
          \    \".\",\n            {\n              \"id\": \"m2\",\n            \
          \  \"stat\": \"Sum\",\n              \"visible\": false\n            }\n\
          \          ]\n        ],\n        \"region\": \"${AWS::Region}\",\n    \
          \    \"title\": \"SQS Queue Metrics (per Minute)\",\n        \"view\": \"\
          timeSeries\",\n        \"period\": 60,\n        \"yAxis\": {\n         \
          \ \"left\": {\n            \"label\": \"Count per Minute\"\n          }\n\
          \        }\n      }\n    },\n    {\n      \"type\": \"metric\",\n      \"\
          x\": 8,\n      \"y\": 6,\n      \"width\": 8,\n      \"height\": 6,\n  \
          \    \"properties\": {\n        \"metrics\": [\n          [\n          \
          \  {\n              \"expression\": \"m1/PERIOD(m1)*60\",\n            \
          \  \"label\": \"Started per Minute\",\n              \"id\": \"e1\"\n  \
          \          }\n          ],\n          [\n            {\n              \"\
          expression\": \"m2/PERIOD(m2)*60\",\n              \"label\": \"Succeeded\
          \ per Minute\",\n              \"id\": \"e2\"\n            }\n         \
          \ ],\n          [\n            {\n              \"expression\": \"m3/PERIOD(m3)*60\"\
          ,\n              \"label\": \"Failed per Minute\",\n              \"id\"\
          : \"e3\"\n            }\n          ],\n          [\n            \"AWS/States\"\
          ,\n            \"ExecutionsStarted\",\n            \"StateMachineArn\",\n\
          \            \"${StateMachineArn}\",\n            {\n              \"id\"\
          : \"m1\",\n              \"stat\": \"Sum\",\n              \"visible\":\
          \ false\n            }\n          ],\n          [\n            \".\",\n\
          \            \"ExecutionsSucceeded\",\n            \".\",\n            \"\
          .\",\n            {\n              \"id\": \"m2\",\n              \"stat\"\
          : \"Sum\",\n              \"visible\": false\n            }\n          ],\n\
          \          [\n            \".\",\n            \"ExecutionsFailed\",\n  \
          \          \".\",\n            \".\",\n            {\n              \"id\"\
          : \"m3\",\n              \"stat\": \"Sum\",\n              \"visible\":\
          \ false\n            }\n          ]\n        ],\n        \"region\": \"\
          ${AWS::Region}\",\n        \"title\": \"Workflow Executions (per Minute)\"\
          ,\n        \"view\": \"timeSeries\",\n        \"period\": 60,\n        \"\
          yAxis\": {\n          \"left\": {\n            \"label\": \"Count per Minute\"\
          \n          }\n        }\n      }\n    },\n    {\n      \"type\": \"log\"\
          ,\n      \"x\": 18,\n      \"y\": 6,\n      \"width\": 8,\n      \"height\"\
          : 6,\n      \"properties\": {\n        \"query\": \"SOURCE '${WorkflowTrackerLogGroup}'\
          \ | fields @timestamp, @message | filter @message like /Publishing latency\
          \ metrics/ | parse @message 'total: *ms' as totalTime |  filter totalTime\
          \ > ${ExecutionTimeThresholdMs} |  stats count(*) as high_latency_count\
          \ by bin(1m)\",\n        \"region\": \"${AWS::Region}\",\n        \"title\"\
          : \"Count of Workflow Executions over latency threshold (${ExecutionTimeThresholdMs}ms)\"\
          ,\n        \"stacked\": false,\n        \"view\": \"timeSeries\"\n     \
          \ }\n    },\n    {\n      \"type\": \"log\",\n      \"x\": 0,\n      \"\
          y\": 12,\n      \"width\": 24,\n      \"height\": 6,\n      \"properties\"\
          : {\n        \"query\": \"SOURCE '${StateMachineLogGroup}' | fields @timestamp,\
          \ @message | filter @message like /ExecutionFailed/ or @message like /TimedOut/\
          \ | parse @message /execution: (?<execution_arn>[^ ]*)/ | parse @message\
          \ /error: (?<error>[^\\\"]*)/| sort @timestamp desc | limit 20\",\n    \
          \    \"region\": \"${AWS::Region}\",\n        \"title\": \"Step Functions\
          \ Executions Failed\",\n        \"view\": \"table\"\n      }\n    },\n \
          \   {\n      \"type\": \"log\",\n      \"x\": 0,\n      \"y\": 18,\n   \
          \   \"width\": 12,\n      \"height\": 6,\n      \"properties\": {\n    \
          \    \"query\": \"SOURCE '${QueueSenderLogGroup}' | fields @timestamp, @logStream,\
          \ @message | filter @message like /ERROR/ | sort @timestamp desc | limit\
          \ 20\",\n        \"region\": \"${AWS::Region}\",\n        \"title\": \"\
          Queue Sender Lambda Errors\",\n        \"view\": \"table\"\n      }\n  \
          \  },\n    {\n      \"type\": \"log\",\n      \"x\": 12,\n      \"y\": 18,\n\
          \      \"width\": 12,\n      \"height\": 6,\n      \"properties\": {\n \
          \       \"query\": \"SOURCE '${QueueProcessorLogGroup}' | fields @timestamp,\
          \ @logStream, @message | filter @message like /ERROR/ | sort @timestamp\
          \ desc | limit 20\",\n        \"region\": \"${AWS::Region}\",\n        \"\
          title\": \"Queue Processor Lambda Errors\",\n        \"view\": \"table\"\
          \n      }\n    },\n    {\n      \"type\": \"log\",\n      \"x\": 0,\n  \
          \    \"y\": 24,\n      \"width\": 12,\n      \"height\": 6,\n      \"properties\"\
          : {\n        \"query\": \"SOURCE '${WorkflowTrackerLogGroup}' | fields @timestamp,\
          \ @logStream, @message | filter @message like /ERROR/ | sort @timestamp\
          \ desc | limit 20\",\n        \"region\": \"${AWS::Region}\",\n        \"\
          title\": \"Workflow Tracker Lambda Errors\",\n        \"view\": \"table\"\
          \n      }\n    },\n    {\n      \"type\": \"log\",\n      \"x\": 12,\n \
          \     \"y\": 24,\n      \"width\": 12,\n      \"height\": 6,\n      \"properties\"\
          : {\n        \"query\": \"SOURCE '${SaveReportingDataFunctionV2LogGroup}'\
          \ | fields @timestamp, @logStream, @message | filter @message like /ERROR/\
          \ | sort @timestamp desc | limit 20\",\n        \"region\": \"${AWS::Region}\"\
          ,\n        \"title\": \"Save Reporting Data Tracker Lambda Errors\",\n \
          \       \"view\": \"table\"\n      }\n    }\n  ]\n}\n"
        - StateMachineArn:
            Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineArn
          StateMachineLogGroup:
            Fn::GetAtt:
            - PATTERNSTACK
            - Outputs.StateMachineLogGroup
    Metadata:
      SamResourceId: MainTemplateSubsetDashboard
  DashboardMergerFunction:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: DashboardMergerFunction
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: Role requires * resource access for CloudWatch Dashboards
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/0896bb998ca0d52243d7c3ebd5a3ed83
      Handler: index.handler
      Runtime: python3.12
      Timeout: 60
      MemorySize: 128
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          STACK_NAME:
            Ref: AWS::StackName
      Policies:
      - Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - cloudwatch:GetDashboard
          - cloudwatch:ListDashboards
          - cloudwatch:PutDashboard
          - cloudwatch:DeleteDashboards
          Resource: '*'
  MergedDashboard:
    Type: Custom::DashboardMerger
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - DashboardMergerFunction
        - Arn
      Dashboard1Name:
        Ref: MainTemplateSubsetDashboard
      Dashboard2Name:
        Fn::GetAtt:
        - PATTERNSTACK
        - Outputs.DashboardName
      MergedDashboardName:
        Fn::Sub: ${AWS::StackName}-${AWS::Region}
      ChangeToForceUpdate: f9f261c7957fee7d
    Metadata:
      SamResourceId: MergedDashboard
  LookupFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: LookupFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/260bdc8311b041383298218aa67eb5b7
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      LoggingConfig:
        LogGroup:
          Ref: LookupFunctionLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - DynamoDBReadPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - states:DescribeExecution
          - states:GetExecutionHistory
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:${AWS::StackName}*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  LookupFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: LookupFunctionLogGroup
  AgentTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: PK
        AttributeType: S
      - AttributeName: SK
        AttributeType: S
      KeySchema:
      - AttributeName: PK
        KeyType: HASH
      - AttributeName: SK
        KeyType: RANGE
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: AgentTable
  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: PK
        AttributeType: S
      - AttributeName: SK
        AttributeType: S
      - AttributeName: email
        AttributeType: S
      KeySchema:
      - AttributeName: PK
        KeyType: HASH
      - AttributeName: SK
        KeyType: RANGE
      GlobalSecondaryIndexes:
      - IndexName: EmailIndex
        KeySchema:
        - AttributeName: email
          KeyType: HASH
        Projection:
          ProjectionType: ALL
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: UsersTable
  ExternalMCPAgentsSecret:
    Type: AWS::SecretsManager::Secret
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}/external-mcp-agents/credentials
      Description: 'Credentials for External MCP Agents - JSON array with required
        fields: mcp_url, cognito_user_pool_id, cognito_client_id, cognito_username,
        cognito_password. Optional fields: agent_name, agent_description'
      SecretString: '[]'
      KmsKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: ExternalMCPAgentsSecret
  AgentChatProcessorFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: AgentChatProcessorFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W58
          reason: DLQ not required for Cfn Custom Resource function
        - id: W76
          reason: 'Suppressing W76: SPCM for IAM policy document is higher than 25'
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/c0f8ce302693df29ce37d8288954bfc9
      Handler: index.handler
      Runtime: python3.12
      Timeout: 600
      Layers:
      - Ref: IDPCommonAgentsLayer
      MemorySize: 1024
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
          TRACKING_TABLE_NAME:
            Ref: TrackingTable
          LOOKUP_FUNCTION_NAME:
            Ref: LookupFunction
          STRANDS_LOG_LEVEL:
            Ref: LogLevel
          BEDROCK_REGION:
            Ref: AWS::Region
          CHAT_MESSAGES_TABLE:
            Ref: ChatMessagesTable
          ID_HELPER_CHAT_MEMORY_TABLE:
            Ref: IdHelperChatMemoryTable
          MEMORY_METHOD: dynamodb
          STREAMING_ENABLED: true
          MAX_CONVERSATION_TURNS: 20
          MAX_MESSAGE_SIZE_KB: 8.5
          DATA_RETENTION_DAYS:
            Ref: DataRetentionInDays
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          ATHENA_DATABASE:
            Ref: ReportingDatabase
          ATHENA_OUTPUT_LOCATION:
            Fn::Sub:
            - s3://${Bucket}/athena-results/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          AWS_STACK_NAME:
            Ref: AWS::StackName
          GUARDRAIL_ID_AND_VERSION:
            Fn::If:
            - HasGuardrailConfig
            - Fn::Sub: ${BedrockGuardrailId}:${BedrockGuardrailVersion}
            - ''
          CLOUDWATCH_LOG_GROUP_PREFIX:
            Fn::Sub: /aws/lambda/${AWS::StackName}
          SETTINGS_PARAMETER_NAME:
            Ref: SettingsParameter
      LoggingConfig:
        LogGroup:
          Ref: AgentChatProcessorLogGroup
      Policies:
      - Statement:
          Effect: Allow
          Action: bedrock-agentcore:InvokeAgentRuntime
          Resource: '*'
      - Statement:
          Effect: Allow
          Action: appsync:GraphQL
          Resource:
            Fn::Sub: arn:${AWS::Partition}:appsync:${AWS::Region}:${AWS::AccountId}:apis/${GraphQLApi.ApiId}/*
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ChatMessagesTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: IdHelperChatMemoryTable
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - S3CrudPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
      - Statement:
        - Effect: Allow
          Action:
          - athena:StartQueryExecution
          - athena:GetQueryExecution
          - athena:GetQueryResults
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/primary
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:datacatalog/*
        - Effect: Allow
          Action:
          - glue:GetTable
          - glue:GetTables
          - glue:GetDatabase
          - glue:GetDatabases
          - glue:GetPartitions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:database/${ReportingDatabase}
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${ReportingDatabase}/*
        - Effect: Allow
          Action:
          - bedrock:InvokeModel
          - bedrock:InvokeModelWithResponseStream
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock:*::foundation-model/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*
        - Effect: Allow
          Action:
          - aws-marketplace:Subscribe
          - aws-marketplace:Unsubscribe
          - aws-marketplace:ViewSubscriptions
          Resource: '*'
        - Fn::If:
          - HasGuardrailConfig
          - Effect: Allow
            Action:
            - bedrock:ApplyGuardrail
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:guardrail/${BedrockGuardrailId}
          - Ref: AWS::NoValue
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - bedrock-agentcore:StartCodeInterpreterSession
          - bedrock-agentcore:StopCodeInterpreterSession
          - bedrock-agentcore:InvokeCodeInterpreter
          - bedrock-agentcore:GetCodeInterpreterSession
          - bedrock-agentcore:ListCodeInterpreterSessions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock-agentcore:*:aws:code-interpreter/*
        - Effect: Allow
          Action:
          - secretsmanager:GetSecretValue
          Resource:
          - Ref: ExternalMCPAgentsSecret
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          - logs:DescribeLogStreams
          - logs:FilterLogEvents
          - logs:GetLogEvents
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/vendedlogs/states/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${AWS::StackName}*
          - Fn::GetAtt:
            - QueueSenderLogGroup
            - Arn
          - Fn::GetAtt:
            - QueueProcessorLogGroup
            - Arn
          - Fn::GetAtt:
            - WorkflowTrackerLogGroup
            - Arn
          - Fn::GetAtt:
            - SaveReportingDataFunctionV2LogGroup
            - Arn
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          Resource:
          - '*'
        - Effect: Allow
          Action:
          - dynamodb:DescribeTable
          - dynamodb:Scan
          - dynamodb:Query
          - dynamodb:GetItem
          - dynamodb:Query
          Resource:
          - Fn::GetAtt:
            - TrackingTable
            - Arn
        - Effect: Allow
          Action:
          - cloudformation:DescribeStackResources
          - cloudformation:DescribeStacks
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}*
        - Effect: Allow
          Action:
          - states:DescribeExecution
          - states:GetExecutionHistory
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:${AWS::StackName}*
        - Effect: Allow
          Action:
          - xray:GetTraceSummaries
          - xray:BatchGetTraces
          - xray:GetServiceGraph
          Resource: '*'
        - Effect: Allow
          Action:
          - ssm:GetParameter
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
  AgentChatProcessorLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: AgentChatProcessorLogGroup
  ChatSessionsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: userId
        AttributeType: S
      - AttributeName: sessionId
        AttributeType: S
      KeySchema:
      - AttributeName: userId
        KeyType: HASH
      - AttributeName: sessionId
        KeyType: RANGE
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: ChatSessionsTable
  ChatMessagesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: PK
        AttributeType: S
      - AttributeName: SK
        AttributeType: S
      KeySchema:
      - AttributeName: PK
        KeyType: HASH
      - AttributeName: SK
        KeyType: RANGE
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: ChatMessagesTable
  IdHelperChatMemoryTable:
    Type: AWS::DynamoDB::Table
    Properties:
      PointInTimeRecoverySpecification:
        PointInTimeRecoveryEnabled: true
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
      - AttributeName: PK
        AttributeType: S
      - AttributeName: SK
        AttributeType: S
      KeySchema:
      - AttributeName: PK
        KeyType: HASH
      - AttributeName: SK
        KeyType: RANGE
      TimeToLiveSpecification:
        AttributeName: ExpiresAfter
        Enabled: true
      SSESpecification:
        SSEEnabled: true
        SSEType: KMS
        KMSMasterKeyId:
          Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: IdHelperChatMemoryTable
  CalculateCapacityFunction:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: CalculateCapacityFunction
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: servicequotas:GetServiceQuota and ListServiceQuotas APIs do not
            support resource-level permissions
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.lambda_handler
      Runtime: python3.12
      Timeout: 300
      MemorySize: 512
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/090212afc65f8aa3f97140a06f097fad
      LoggingConfig:
        LogGroup:
          Ref: CalculateCapacityFunctionLogGroup
      Environment:
        Variables:
          TRACKING_TABLE:
            Ref: TrackingTable
          METERING_TABLE_NAME:
            Ref: TrackingTable
          LAMBDA_MEMORY_GB: '0.5'
          RECOMMENDATION_HIGH_COMPLEXITY_THRESHOLD: '2.5'
          RECOMMENDATION_MEDIUM_COMPLEXITY_THRESHOLD: '1.5'
          RECOMMENDATION_HIGH_LOAD_THRESHOLD: '3.0'
          RECOMMENDATION_MEDIUM_LOAD_THRESHOLD: '2.0'
          RECOMMENDATION_HIGH_LATENCY_THRESHOLD: '300'
          RECOMMENDATION_LARGE_DOC_THRESHOLD: '50000'
          RECOMMENDATION_HIGH_PAGE_THRESHOLD: '20'
          MEDIUM_COMPLEXITY_THRESHOLD: '5000'
          HIGH_COMPLEXITY_THRESHOLD: '15000'
          PAGE_COMPLEXITY_FACTOR: '0.1'
          HIGH_COMPLEXITY_MULTIPLIER: '2.0'
          MEDIUM_COMPLEXITY_MULTIPLIER: '1.5'
          MIN_TOKENS_PER_REQUEST: '500'
          BEDROCK_MODEL_QUOTA_CODES: '{"us.amazon.nova-lite-v1:0":"L-7C42E72A","us.amazon.nova-pro-v1:0":"L-C0326783","us.amazon.nova-premier-v1:0":"L-AA7FE948","us.amazon.nova-2-lite-v1:0":"L-C6F5908D","global.amazon.nova-2-lite-v1:0":"L-C6F5908D","us.anthropic.claude-3-haiku-20240307-v1:0":"L-DCADBC78","us.anthropic.claude-haiku-4-5-20251001-v1:0":"L-58BE175A","us.anthropic.claude-3-5-sonnet-20241022-v2:0":"L-FF8B4E28","us.anthropic.claude-3-7-sonnet-20250219-v1:0":"L-6E888CC2","us.anthropic.claude-sonnet-4-20250514-v1:0":"L-59759B4A","us.anthropic.claude-sonnet-4-20250514-v1:0:1m":"L-1FA095B8","us.anthropic.claude-sonnet-4-5-20250929-v1:0":"L-F4DDD3EB","us.anthropic.claude-sonnet-4-5-20250929-v1:0:1m":"L-F4DDD3EB","us.anthropic.claude-opus-4-20250514-v1:0":"L-29C2B0A3","us.anthropic.claude-opus-4-1-20250805-v1:0":"L-BD85BFCD","us.anthropic.claude-opus-4-5-20251101-v1:0":"L-7007E9C9","global.anthropic.claude-haiku-4-5-20251001-v1:0":"L-58BE175A","global.anthropic.claude-sonnet-4-5-20250929-v1:0":"L-F4DDD3EB","global.anthropic.claude-sonnet-4-5-20250929-v1:0:1m":"L-F4DDD3EB","global.anthropic.claude-opus-4-5-20251101-v1:0":"L-7007E9C9"}'
          BEDROCK_MODEL_RPM_QUOTA_CODES: '{"us.amazon.nova-lite-v1:0":"L-89F8391A","us.amazon.nova-pro-v1:0":"L-ED46B8C5","us.amazon.nova-premier-v1:0":"L-9AD981E7","us.amazon.nova-2-lite-v1:0":"L-F06F1187","global.amazon.nova-2-lite-v1:0":"L-F06F1187","us.anthropic.claude-3-haiku-20240307-v1:0":"L-616A3F5B","us.anthropic.claude-haiku-4-5-20251001-v1:0":"L-CCA5DF70","us.anthropic.claude-3-5-sonnet-20241022-v2:0":"L-1D3E59A3","us.anthropic.claude-3-7-sonnet-20250219-v1:0":"L-3D8CC480","us.anthropic.claude-sonnet-4-20250514-v1:0":"L-559DCC33","us.anthropic.claude-sonnet-4-20250514-v1:0:1m":"L-534E5E05","us.anthropic.claude-sonnet-4-5-20250929-v1:0":"L-4A6BFAB1","us.anthropic.claude-sonnet-4-5-20250929-v1:0:1m":"L-4A6BFAB1","us.anthropic.claude-opus-4-20250514-v1:0":"L-C99C7EF6","us.anthropic.claude-opus-4-1-20250805-v1:0":"L-7EC72A47","us.anthropic.claude-opus-4-5-20251101-v1:0":"L-27989F42","global.anthropic.claude-haiku-4-5-20251001-v1:0":"L-CCA5DF70","global.anthropic.claude-sonnet-4-5-20250929-v1:0":"L-4A6BFAB1","global.anthropic.claude-sonnet-4-5-20250929-v1:0:1m":"L-4A6BFAB1","global.anthropic.claude-opus-4-5-20251101-v1:0":"L-27989F42"}'
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - servicequotas:GetServiceQuota
          - servicequotas:ListServiceQuotas
          Resource: '*'
        - Effect: Allow
          Action:
          - dynamodb:Scan
          - dynamodb:Query
          Resource:
            Fn::GetAtt:
            - TrackingTable
            - Arn
        - Effect: Allow
          Action:
          - kms:Decrypt
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  CalculateCapacityFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /aws/lambda/${AWS::StackName}-CalculateCapacity
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: CalculateCapacityFunctionLogGroup
  CalculateCapacityResolverFunction:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: CalculateCapacityResolverFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.lambda_handler
      Runtime: python3.12
      Timeout: 60
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/9f7d1822ef4cc23dc99a51641e8d5545
      LoggingConfig:
        LogGroup:
          Ref: CalculateCapacityResolverFunctionLogGroup
      Environment:
        Variables:
          CALCULATE_CAPACITY_FUNCTION_NAME:
            Ref: CalculateCapacityFunction
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
            Fn::GetAtt:
            - CalculateCapacityFunction
            - Arn
  CalculateCapacityResolverFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /aws/lambda/${AWS::StackName}-CalculateCapacityResolver
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: CalculateCapacityResolverFunctionLogGroup
  CalculateCapacityDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      Name: CalculateCapacityDataSource
      Description: Lambda function for calculating capacity requirements
      Type: AWS_LAMBDA
      ServiceRoleArn:
        Fn::GetAtt:
        - HITLAppSyncServiceRole
        - Arn
      LambdaConfig:
        LambdaFunctionArn:
          Fn::GetAtt:
          - CalculateCapacityResolverFunction
          - Arn
    Metadata:
      SamResourceId: CalculateCapacityDataSource
  CalculateCapacityResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - CalculateCapacityDataSource
        - Name
      TypeName: Query
      FieldName: calculateCapacity
    Metadata:
      SamResourceId: CalculateCapacityResolver
  AgentProcessorLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName:
        Fn::Sub: /aws/lambda/${AWS::StackName}-AgentProcessor
      RetentionInDays:
        Ref: LogRetentionDays
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
    Metadata:
      SamResourceId: AgentProcessorLogGroup
  AgentProcessorFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: AgentProcessorFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W58
          reason: DLQ not required for Cfn Custom Resource function
        - id: W76
          reason: 'Suppressing W76: SPCM for IAM policy document is higher than 25'
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/4ffb0807a427b79bf019e538a17479d0
      Handler: index.handler
      Runtime: python3.12
      Timeout: 900
      Layers:
      - Ref: IDPCommonAgentsLayer
      MemorySize: 1024
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
          TRACKING_TABLE_NAME:
            Ref: TrackingTable
          LOOKUP_FUNCTION_NAME:
            Ref: LookupFunction
          STRANDS_LOG_LEVEL:
            Ref: LogLevel
          AGENT_TABLE:
            Ref: AgentTable
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          ATHENA_DATABASE:
            Ref: ReportingDatabase
          ATHENA_OUTPUT_LOCATION:
            Fn::Sub:
            - s3://${Bucket}/athena-results/
            - Bucket:
                Fn::If:
                - ShouldCreateReportingBucket
                - Ref: ReportingBucket
                - Ref: ReportingBucketName
          AWS_STACK_NAME:
            Ref: AWS::StackName
          GUARDRAIL_ID_AND_VERSION:
            Fn::If:
            - HasGuardrailConfig
            - Fn::Sub: ${BedrockGuardrailId}:${BedrockGuardrailVersion}
            - ''
          CLOUDWATCH_LOG_GROUP_PREFIX:
            Fn::Sub: /aws/lambda/${AWS::StackName}
          SETTINGS_PARAMETER_NAME:
            Ref: SettingsParameter
      LoggingConfig:
        LogGroup:
          Ref: AgentProcessorLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: AgentTable
      - DynamoDBReadPolicy:
          TableName:
            Ref: ConfigurationTable
      - S3CrudPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateReportingBucket
            - Ref: ReportingBucket
            - Ref: ReportingBucketName
      - Statement:
        - Effect: Allow
          Action:
          - athena:StartQueryExecution
          - athena:GetQueryExecution
          - athena:GetQueryResults
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:workgroup/primary
          - Fn::Sub: arn:${AWS::Partition}:athena:${AWS::Region}:${AWS::AccountId}:datacatalog/*
        - Effect: Allow
          Action:
          - glue:GetTable
          - glue:GetTables
          - glue:GetDatabase
          - glue:GetDatabases
          - glue:GetPartitions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:catalog
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:database/${ReportingDatabase}
          - Fn::Sub: arn:${AWS::Partition}:glue:${AWS::Region}:${AWS::AccountId}:table/${ReportingDatabase}/*
        - Effect: Allow
          Action:
          - bedrock:InvokeModel
          - bedrock:InvokeModelWithResponseStream
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock:*::foundation-model/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*
        - Effect: Allow
          Action:
          - aws-marketplace:Subscribe
          - aws-marketplace:Unsubscribe
          - aws-marketplace:ViewSubscriptions
          Resource: '*'
        - Fn::If:
          - HasGuardrailConfig
          - Effect: Allow
            Action:
            - bedrock:ApplyGuardrail
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:guardrail/${BedrockGuardrailId}
          - Ref: AWS::NoValue
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - bedrock-agentcore:StartCodeInterpreterSession
          - bedrock-agentcore:StopCodeInterpreterSession
          - bedrock-agentcore:InvokeCodeInterpreter
          - bedrock-agentcore:GetCodeInterpreterSession
          - bedrock-agentcore:ListCodeInterpreterSessions
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock-agentcore:*:aws:code-interpreter/*
        - Effect: Allow
          Action:
          - secretsmanager:GetSecretValue
          Resource:
          - Ref: ExternalMCPAgentsSecret
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          - logs:DescribeLogStreams
          - logs:FilterLogEvents
          - logs:GetLogEvents
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/vendedlogs/states/${AWS::StackName}*
          - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/${AWS::StackName}*
          - Fn::GetAtt:
            - QueueSenderLogGroup
            - Arn
          - Fn::GetAtt:
            - QueueProcessorLogGroup
            - Arn
          - Fn::GetAtt:
            - WorkflowTrackerLogGroup
            - Arn
          - Fn::GetAtt:
            - SaveReportingDataFunctionV2LogGroup
            - Arn
        - Effect: Allow
          Action:
          - logs:DescribeLogGroups
          Resource:
          - '*'
        - Effect: Allow
          Action:
          - dynamodb:DescribeTable
          - dynamodb:Scan
          - dynamodb:Query
          - dynamodb:GetItem
          - dynamodb:Query
          Resource:
          - Fn::GetAtt:
            - TrackingTable
            - Arn
        - Effect: Allow
          Action:
          - cloudformation:DescribeStackResources
          - cloudformation:DescribeStacks
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/${AWS::StackName}/*
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}*
        - Effect: Allow
          Action:
          - states:DescribeExecution
          - states:GetExecutionHistory
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:execution:${AWS::StackName}*
        - Effect: Allow
          Action:
          - xray:GetTraceSummaries
          - xray:BatchGetTraces
          - xray:GetServiceGraph
          Resource: '*'
        - Effect: Allow
          Action:
          - ssm:GetParameter
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
  ExternalAppClient:
    Type: AWS::Cognito::UserPoolClient
    Condition: CreateExternalAppClient
    Properties:
      ClientName: external-app-client
      UserPoolId:
        Ref: UserPool
      GenerateSecret: true
      ExplicitAuthFlows:
      - ALLOW_ADMIN_USER_PASSWORD_AUTH
      - ALLOW_USER_PASSWORD_AUTH
      - ALLOW_REFRESH_TOKEN_AUTH
      SupportedIdentityProviders:
      - COGNITO
      AllowedOAuthFlows:
      - code
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthScopes:
      - openid
      - email
      - profile
      CallbackURLs:
      - Fn::Sub: https://${GetDomain.OutputString}.auth.${AWS::Region}.amazoncognito.com
      - Fn::If:
        - UseCloudFrontHosting
        - Fn::Sub: https://${CloudFrontDistribution.DomainName}/
        - Fn::Sub: ${ALBHOSTINGSTACK.Outputs.WebUIURL}/
      - Fn::Sub: https://${AWS::Region}.quicksight.aws.amazon.com/sn/oauthcallback
      - https://us-east-1.quicksight.aws.amazon.com/sn/oauthcallback
      - https://us-west-2.quicksight.aws.amazon.com/sn/oauthcallback
      - https://eu-west-1.quicksight.aws.amazon.com/sn/oauthcallback
      - https://ap-southeast-2.quicksight.aws.amazon.com/sn/oauthcallback
      LogoutURLs:
      - Fn::Sub: https://${GetDomain.OutputString}.auth.${AWS::Region}.amazoncognito.com
      - Fn::If:
        - UseCloudFrontHosting
        - Fn::Sub: https://${CloudFrontDistribution.DomainName}/
        - Fn::Sub: ${ALBHOSTINGSTACK.Outputs.WebUIURL}/
      - Fn::Sub: https://${AWS::Region}.quicksight.aws.amazon.com/sn/oauthcallback
      - https://us-east-1.quicksight.aws.amazon.com/sn/oauthcallback
      - https://us-west-2.quicksight.aws.amazon.com/sn/oauthcallback
      - https://eu-west-1.quicksight.aws.amazon.com/sn/oauthcallback
      - https://ap-southeast-2.quicksight.aws.amazon.com/sn/oauthcallback
    Metadata:
      SamResourceId: ExternalAppClient
  MCPResourceServer:
    Type: AWS::Cognito::UserPoolResourceServer
    Condition: CreateAgentCoreLambda
    Properties:
      UserPoolId:
        Ref: UserPool
      Identifier: idp-mcp-connector
      Name: idp-mcp-connector
      Scopes:
      - ScopeName: access
        ScopeDescription: Full access to IDP MCP Connector
    Metadata:
      SamResourceId: MCPResourceServer
  MCPConnectorClient:
    Type: AWS::Cognito::UserPoolClient
    Condition: CreateAgentCoreLambda
    DependsOn: MCPResourceServer
    Properties:
      ClientName: mcp-connector-client
      UserPoolId:
        Ref: UserPool
      GenerateSecret: true
      AllowedOAuthFlows:
      - client_credentials
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthScopes:
      - idp-mcp-connector/access
      SupportedIdentityProviders:
      - COGNITO
    Metadata:
      SamResourceId: MCPConnectorClient
  UserPool:
    Type: AWS::Cognito::UserPool
    Metadata:
      cfn-nag:
        rules_to_suppress:
        - id: W57
          reason: MFA configurations specific to target environment - disabled by
            default
      SamResourceId: UserPool
    Properties:
      UserPoolName:
        Fn::Sub: ${AWS::StackName}-users
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly:
          Fn::If:
          - ShouldAllowSignUpEmailDomain
          - false
          - true
        InviteMessageTemplate:
          EmailSubject: Welcome to GenAI-IDP!
          EmailMessage:
            Fn::Sub:
            - '<p>Hello {username},</p>

              <p>Welcome to the AWS GenAIIDP Solution! Your temporary password is:
              <strong>{####}</strong></p>

              <p>When the CloudFormation stack is COMPLETE, use the link below to
              log in and set your permanent password.

              <p>     ${WebUIURL}

              <p>

              <p>Thanks,</p>

              <p>AWS GenAIIDP Team</p>

              <p>

              <p><em>Stack: ${AWS::StackName}, v0.5.4, 2026-03-27 22:07:17</em>'
            - WebUIURL:
                Fn::If:
                - UseCloudFrontHosting
                - Fn::Sub: https://${CloudFrontDistribution.DomainName}/
                - Fn::GetAtt:
                  - ALBHOSTINGSTACK
                  - Outputs.WebUIURL
      AutoVerifiedAttributes:
      - email
      EmailConfiguration:
        EmailSendingAccount: COGNITO_DEFAULT
      EmailVerificationMessage: Please verify your email to complete account registration.
        Confirmation Code {####}.
      EmailVerificationSubject: Account Verification
      LambdaConfig:
        Fn::If:
        - ShouldAllowSignUpEmailDomain
        - PreAuthentication:
            Fn::GetAtt:
            - CognitoUserPoolEmailDomainVerifyFunction
            - Arn
          PreSignUp:
            Fn::GetAtt:
            - CognitoUserPoolEmailDomainVerifyFunction
            - Arn
        - Ref: AWS::NoValue
      Policies:
        PasswordPolicy:
          MinimumLength: 8
          RequireLowercase: true
          RequireNumbers: true
          RequireSymbols: true
          RequireUppercase: true
      Schema:
      - Name: email
        AttributeDataType: String
        Mutable: false
        Required: true
      UsernameConfiguration:
        CaseSensitive: false
  UserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName:
        Fn::Sub: ${AWS::StackName}-Client
      AllowedOAuthFlows:
      - code
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthScopes:
      - openid
      - email
      - phone
      - profile
      CallbackURLs:
      - Fn::If:
        - UseCloudFrontHosting
        - Fn::Sub: https://${CloudFrontDistribution.DomainName}
        - Fn::GetAtt:
          - ALBHOSTINGSTACK
          - Outputs.WebUIURL
      AccessTokenValidity: 1
      EnableTokenRevocation: true
      ExplicitAuthFlows:
      - ALLOW_USER_SRP_AUTH
      - ALLOW_REFRESH_TOKEN_AUTH
      GenerateSecret: false
      IdTokenValidity: 1
      PreventUserExistenceErrors: ENABLED
      ReadAttributes:
      - email
      - email_verified
      - preferred_username
      RefreshTokenValidity: 30
      SupportedIdentityProviders:
      - COGNITO
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: UserPoolClient
  GetDomainLambda:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
      SamResourceId: GetDomainLambda
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Description: Converts Stackname to lowercase and adds unique timestamp
      Handler: index.handler
      Runtime: python3.12
      Timeout: 30
      MemorySize: 128
      LoggingConfig:
        LogGroup:
          Ref: GetDomainLambdaLogGroup
      InlineCode: "import cfnresponse\nimport time\ndef handler(event, context): \
        \                                                   \n    lowercase = event['ResourceProperties'].get('InputString',\
        \ '').lower()\n    lowercaseWithTimestamp = f\"{lowercase}-{time.time_ns()}\"\
        \ # make unique\n    responseData = {\n      'Lowercase': lowercase, \n  \
        \    'OutputString': lowercaseWithTimestamp, # don't change key - avoid UserPool\
        \ update errors\n    }                                            \n    cfnresponse.send(event,\
        \ context, cfnresponse.SUCCESS, responseData)\n"
  GetDomainLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: GetDomainLambdaLogGroup
  GetDomain:
    Type: Custom::GetDomain
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - GetDomainLambda
        - Arn
      InputString:
        Ref: AWS::StackName
    Metadata:
      SamResourceId: GetDomain
  UserPoolDomain:
    Type: AWS::Cognito::UserPoolDomain
    Properties:
      Domain:
        Fn::GetAtt:
        - GetDomain
        - OutputString
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: UserPoolDomain
  IdentityPool:
    Type: AWS::Cognito::IdentityPool
    Properties:
      IdentityPoolName:
        Fn::Sub: ${AWS::StackName}-IdentityPool
      AllowUnauthenticatedIdentities: false
      CognitoIdentityProviders:
      - ClientId:
          Ref: UserPoolClient
        ProviderName:
          Fn::GetAtt:
          - UserPool
          - ProviderName
    Metadata:
      SamResourceId: IdentityPool
  CognitoIdentityPoolSetRole:
    Type: AWS::Cognito::IdentityPoolRoleAttachment
    Properties:
      IdentityPoolId:
        Ref: IdentityPool
      Roles:
        authenticated:
          Fn::GetAtt:
          - CognitoAuthorizedRole
          - Arn
    Metadata:
      SamResourceId: CognitoIdentityPoolSetRole
  CognitoAuthorizedRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Federated: cognito-identity.amazonaws.com
          Action:
          - sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              cognito-identity.amazonaws.com:aud:
                Ref: IdentityPool
            ForAnyValue:StringLike:
              cognito-identity.amazonaws.com:amr: authenticated
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: S3
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - s3:GetObject
            - s3:ListBucket
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:s3:::${OutputBucket}
            - Fn::Sub: arn:${AWS::Partition}:s3:::${OutputBucket}/*
            - Fn::Sub: arn:${AWS::Partition}:s3:::${InputBucket}
            - Fn::Sub: arn:${AWS::Partition}:s3:::${InputBucket}/*
            - Fn::Sub: arn:${AWS::Partition}:s3:::${ConfigurationBucket}
            - Fn::Sub: arn:${AWS::Partition}:s3:::${ConfigurationBucket}/*
          - Effect: Allow
            Action:
            - kms:Encrypt
            - kms:Decrypt
            - kms:ReEncrypt*
            - kms:GenerateDataKey*
            - kms:DescribeKey
            Resource:
              Fn::GetAtt:
              - CustomerManagedEncryptionKey
              - Arn
      - PolicyName: ParameterStore
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - ssm:GetParameter
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SettingsParameter}
      - PolicyName: GraphQL
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - appsync:GraphQL
            Resource:
            - Fn::Sub: ${GraphQLApi.Arn}/types/Query/*
            - Fn::Sub: ${GraphQLApi.Arn}/types/Subscription/*
    Metadata:
      SamResourceId: CognitoAuthorizedRole
  AdminUser:
    Type: AWS::Cognito::UserPoolUser
    DependsOn: CognitoUserPoolEmailDomainVerifyPermissionReady
    Properties:
      DesiredDeliveryMediums:
      - EMAIL
      UserAttributes:
      - Name: email
        Value:
          Ref: AdminEmail
      Username:
        Ref: AdminEmail
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: AdminUser
  CognitoUserPoolEmailDomainVerifyPermissionReady:
    Type: AWS::CloudFormation::WaitConditionHandle
    Metadata:
      ConditionalDependency:
        Fn::If:
        - ShouldAllowSignUpEmailDomain
        - Ref: CognitoUserPoolEmailDomainVerifyPermission
        - ''
      SamResourceId: CognitoUserPoolEmailDomainVerifyPermissionReady
  AdminGroup:
    Type: AWS::Cognito::UserPoolGroup
    Properties:
      Description: Administrators
      GroupName: Admin
      Precedence: 0
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: AdminGroup
  AuthorGroup:
    Type: AWS::Cognito::UserPoolGroup
    Properties:
      Description: Authors - read/write access to documents and configuration
      GroupName: Author
      Precedence: 1
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: AuthorGroup
  ReviewerGroup:
    Type: AWS::Cognito::UserPoolGroup
    Properties:
      Description: Document Reviewers for HITL
      GroupName: Reviewer
      Precedence: 2
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: ReviewerGroup
  ViewerGroup:
    Type: AWS::Cognito::UserPoolGroup
    Properties:
      Description: Viewers - read-only access to documents and configuration
      GroupName: Viewer
      Precedence: 3
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: ViewerGroup
  AdminUserToGroupAttachment:
    Type: AWS::Cognito::UserPoolUserToGroupAttachment
    Properties:
      GroupName:
        Ref: AdminGroup
      Username:
        Ref: AdminUser
      UserPoolId:
        Ref: UserPool
    Metadata:
      SamResourceId: AdminUserToGroupAttachment
  CognitoUserPoolEmailDomainVerifyFunction:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
      SamResourceId: CognitoUserPoolEmailDomainVerifyFunction
    Condition: ShouldAllowSignUpEmailDomain
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Description: Verifies email address is in allowed domain
      Handler: index.lambda_handler
      Runtime: python3.12
      Timeout: 10
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          ALLOWED_SIGNUP_EMAIL_DOMAINS:
            Ref: AllowedSignUpEmailDomain
      InlineCode: "import os\nimport boto3\nimport json\ndef lambda_handler(event,\
        \ context):\n    print(json.dumps(event))\n    allowed_domains = [\n     \
        \   domain.strip() \n        for domain in os.environ.get('ALLOWED_SIGNUP_EMAIL_DOMAINS',\
        \ '').split(',')\n    ]\n    user_attributes = event.get('request', {}).get('userAttributes',\
        \ {})\n    email = user_attributes.get('email')\n    if not email or '@' not\
        \ in email:\n        raise ValueError('Username does not exist or invalid\
        \ email address')\n    email_domain = email.split('@')[1]\n    if not email_domain\
        \ or not allowed_domains:\n        raise ValueError('Server error - invalid\
        \ configuration')\n    if email_domain not in allowed_domains:\n        raise\
        \ ValueError('Invalid email address domain')\n    return event\n"
      LoggingConfig:
        LogGroup:
          Ref: CognitoUserPoolEmailDomainVerifyFunctionLogGroup
  CognitoUserPoolEmailDomainVerifyFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: CognitoUserPoolEmailDomainVerifyFunctionLogGroup
  CognitoUserPoolEmailDomainVerifyPermission:
    Type: AWS::Lambda::Permission
    Condition: ShouldAllowSignUpEmailDomain
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: CognitoUserPoolEmailDomainVerifyFunction
      Principal:
        Fn::Sub: cognito-idp.${AWS::URLSuffix}
      SourceAccount:
        Ref: AWS::AccountId
      SourceArn:
        Fn::GetAtt:
        - UserPool
        - Arn
    Metadata:
      SamResourceId: CognitoUserPoolEmailDomainVerifyPermission
  GraphQLApi:
    Type: AWS::AppSync::GraphQLApi
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-api
      AuthenticationType: AMAZON_COGNITO_USER_POOLS
      UserPoolConfig:
        UserPoolId:
          Ref: UserPool
        AwsRegion:
          Ref: AWS::Region
        DefaultAction: ALLOW
      AdditionalAuthenticationProviders:
      - AuthenticationType: AWS_IAM
      LogConfig:
        CloudWatchLogsRoleArn:
          Fn::GetAtt:
          - AppSyncCwlRole
          - Arn
        ExcludeVerboseContent: false
        FieldLogLevel:
          Fn::FindInMap:
          - AppSyncLogLevelMap
          - Ref: LogLevel
          - FieldLogLevel
      IntrospectionConfig: DISABLED
    Metadata:
      SamResourceId: GraphQLApi
  GraphQLApiLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      LogGroupName:
        Fn::Sub: /aws/appsync/apis/${GraphQLApi.ApiId}
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: GraphQLApiLogGroup
  AppSyncCwlRole:
    DependsOn:
    - IsStacknameLengthOK
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
      - Fn::Sub: arn:${AWS::Partition}:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - sts:AssumeRole
          Principal:
            Service:
            - Fn::Sub: appsync.${AWS::URLSuffix}
    Metadata:
      SamResourceId: AppSyncCwlRole
  WAFIPV4Set:
    Type: AWS::WAFv2::IPSet
    Condition: IsWafEnabled
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-allowed-ips
      Description: IP ranges allowed to access the AppSync API
      Scope: REGIONAL
      IPAddressVersion: IPV4
      Addresses:
        Fn::Split:
        - ', '
        - Ref: WAFAllowedIPv4Ranges
    Metadata:
      SamResourceId: WAFIPV4Set
  WAFLambdaServiceIPSet:
    Type: AWS::WAFv2::IPSet
    Condition: IsWafEnabled
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-lambda-service-ips
      Description: IP ranges used by AWS Lambda service
      Scope: REGIONAL
      IPAddressVersion: IPV4
      Addresses:
      - '0.0.0.0/32'
    Metadata:
      SamResourceId: WAFLambdaServiceIPSet
  IPSetUpdaterFunction:
    Type: AWS::Serverless::Function
    Condition: IsWafEnabled
    Metadata:
      SamResourceId: IPSetUpdaterFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/3274cd33c5af66cbfe5d04f871146939
      Handler: index.lambda_handler
      Runtime: python3.12
      Timeout: 300
      MemorySize: 256
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          IPSET_NAME:
            Fn::Sub: ${AWS::StackName}-lambda-service-ips
      Policies:
      - Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - wafv2:GetIPSet
          - wafv2:UpdateIPSet
          Resource:
            Fn::GetAtt:
            - WAFLambdaServiceIPSet
            - Arn
        - Effect: Allow
          Action:
          - wafv2:ListIPSets
          Resource:
            Fn::Sub: arn:${AWS::Partition}:wafv2:${AWS::Region}:${AWS::AccountId}:regional/ipset/*
      Events:
        Schedule:
          Type: Schedule
          Properties:
            Schedule: rate(1 day)
            Description: Update Lambda IP ranges daily
            Enabled: true
  IPSetUpdaterCustomResource:
    Type: Custom::IPSetUpdater
    Condition: IsWafEnabled
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - IPSetUpdaterFunction
        - Arn
      UpdateTime: '2026-03-27 22:07:17'
    Metadata:
      SamResourceId: IPSetUpdaterCustomResource
  WAFWebACL:
    Type: AWS::WAFv2::WebACL
    Condition: IsWafEnabled
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-appsync-acl
      Description: Web ACL for AppSync API
      Scope: REGIONAL
      DefaultAction:
        Block: {}
      Rules:
      - Name: AllowLambdaServiceIPs
        Priority: 1
        Statement:
          IPSetReferenceStatement:
            Arn:
              Fn::GetAtt:
              - WAFLambdaServiceIPSet
              - Arn
        Action:
          Allow: {}
        VisibilityConfig:
          SampledRequestsEnabled: true
          CloudWatchMetricsEnabled: true
          MetricName: AllowLambdaServiceIPsRule
      - Name: AllowListedIPs
        Priority: 2
        Statement:
          IPSetReferenceStatement:
            Arn:
              Fn::GetAtt:
              - WAFIPV4Set
              - Arn
        Action:
          Allow: {}
        VisibilityConfig:
          SampledRequestsEnabled: true
          CloudWatchMetricsEnabled: true
          MetricName: AllowListedIPsRule
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: APIAccessControl
    Metadata:
      SamResourceId: WAFWebACL
  WAFWebACLAssociation:
    Type: AWS::WAFv2::WebACLAssociation
    Condition: IsWafEnabled
    Properties:
      ResourceArn:
        Fn::GetAtt:
        - GraphQLApi
        - Arn
      WebACLArn:
        Fn::GetAtt:
        - WAFWebACL
        - Arn
    Metadata:
      SamResourceId: WAFWebACLAssociation
  TestFileCopyQueueDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      MessageRetentionPeriod: 1209600
      VisibilityTimeout: 900
    Metadata:
      SamResourceId: TestFileCopyQueueDLQ
  TestFileCopyQueueDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: TestFileCopyQueueDLQ
      PolicyDocument:
        Statement:
        - Sid: DenyInsecureConnections
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - TestFileCopyQueueDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: 'false'
    Metadata:
      SamResourceId: TestFileCopyQueueDLQPolicy
  TestFileCopyQueue:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 900
      RedrivePolicy:
        deadLetterTargetArn:
          Fn::GetAtt:
          - TestFileCopyQueueDLQ
          - Arn
        maxReceiveCount: 3
    Metadata:
      SamResourceId: TestFileCopyQueue
  TestFileCopyQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: TestFileCopyQueue
      PolicyDocument:
        Statement:
        - Sid: DenyInsecureConnections
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - TestFileCopyQueue
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: 'false'
    Metadata:
      SamResourceId: TestFileCopyQueuePolicy
  TestSetFileCopyQueueDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      MessageRetentionPeriod: 1209600
      VisibilityTimeout: 900
    Metadata:
      SamResourceId: TestSetFileCopyQueueDLQ
  TestSetFileCopyQueueDLQPolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: TestSetFileCopyQueueDLQ
      PolicyDocument:
        Statement:
        - Sid: DenyInsecureConnections
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - TestSetFileCopyQueueDLQ
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: 'false'
    Metadata:
      SamResourceId: TestSetFileCopyQueueDLQPolicy
  TestSetFileCopyQueue:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
      VisibilityTimeout: 900
      RedrivePolicy:
        deadLetterTargetArn:
          Fn::GetAtt:
          - TestSetFileCopyQueueDLQ
          - Arn
        maxReceiveCount: 3
    Metadata:
      SamResourceId: TestSetFileCopyQueue
  TestSetFileCopyQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
      Queues:
      - Ref: TestSetFileCopyQueue
      PolicyDocument:
        Statement:
        - Sid: DenyInsecureConnections
          Effect: Deny
          Principal: '*'
          Action: sqs:*
          Resource:
            Fn::GetAtt:
            - TestSetFileCopyQueue
            - Arn
          Condition:
            Bool:
              aws:SecureTransport: 'false'
    Metadata:
      SamResourceId: TestSetFileCopyQueuePolicy
  TestSetFileCopierFunctionDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: TestSetFileCopierFunctionDLQ
  TestSetFileCopierFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: TestSetFileCopierFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W12
          reason: Lambda requires CloudWatch logs permissions
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/0322bb07e86d79125a3bb33b9f0d2baf
      Description: Lambda function to copy test set files from SQS queue
      MemorySize: 1024
      Timeout: 900
      Layers:
      - Ref: IDPCommonBaseLayer
      DeadLetterQueue:
        Type: SQS
        TargetArn:
          Fn::GetAtt:
          - TestSetFileCopierFunctionDLQ
          - Arn
      LoggingConfig:
        LogGroup:
          Ref: TestSetFileCopierFunctionLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          INPUT_BUCKET:
            Ref: InputBucket
          TEST_SET_BUCKET:
            Ref: TestSetBucket
          BASELINE_BUCKET:
            Fn::If:
            - ShouldCreateEvaluationBaselineBucket
            - Ref: EvaluationBaselineBucket
            - Ref: EvaluationBaselineBucketName
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - S3ReadPolicy:
          BucketName:
            Ref: InputBucket
      - S3ReadPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateEvaluationBaselineBucket
            - Ref: EvaluationBaselineBucket
            - Ref: EvaluationBaselineBucketName
      - S3CrudPolicy:
          BucketName:
            Ref: TestSetBucket
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue:
              Fn::GetAtt:
              - TestSetFileCopyQueue
              - Arn
            BatchSize: 1
  TestSetFileCopierFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: TestSetFileCopierFunctionLogGroup
  TestSetZipExtractorFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: TestSetZipExtractorFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Lambda function does not need to be in VPC
        - id: W92
          reason: Lambda function does not need reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/d908c7462b4e841b21adbe86feff51a4
      Handler: index.handler
      Runtime: python3.12
      Timeout: 300
      MemorySize: 1024
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          TRACKING_TABLE:
            Ref: TrackingTable
          TEST_SET_BUCKET:
            Ref: TestSetBucket
      LoggingConfig:
        LogGroup:
          Ref: TestSetZipExtractorFunctionLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  TestSetZipExtractorFunctionInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName:
        Ref: TestSetZipExtractorFunction
      Action: lambda:InvokeFunction
      Principal:
        Fn::Sub: s3.${AWS::URLSuffix}
      SourceArn:
        Fn::GetAtt:
        - TestSetBucket
        - Arn
      SourceAccount:
        Ref: AWS::AccountId
    Metadata:
      SamResourceId: TestSetZipExtractorFunctionInvokePermission
  TestSetZipExtractorFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: TestSetZipExtractorFunctionLogGroup
  TestSetZipExtractorS3Policy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: TestSetZipExtractorS3Access
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Action:
          - s3:GetObject
          - s3:PutObject
          - s3:DeleteObject
          - s3:ListBucket
          Resource:
          - Fn::Sub: ${TestSetBucket.Arn}/*
          - Fn::GetAtt:
            - TestSetBucket
            - Arn
      Roles:
      - Ref: TestSetZipExtractorFunctionRole
    Metadata:
      SamResourceId: TestSetZipExtractorS3Policy
  TestSetBucketNotificationFunction:
    Type: AWS::Serverless::Function
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
      SamResourceId: TestSetBucketNotificationFunction
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      Timeout: 60
      InlineCode: "import boto3\nimport cfnresponse\nimport json\nimport logging\n\
        \nlogger = logging.getLogger()\nlogger.setLevel(logging.INFO)\n\ndef handler(event,\
        \ context):\n    logger.info(json.dumps(event))\n    \n    try:\n        s3_client\
        \ = boto3.client('s3')\n        bucket_name = event['ResourceProperties']['BucketName']\n\
        \        function_arn = event['ResourceProperties']['FunctionArn']\n     \
        \   \n        if event['RequestType'] == 'Create' or event['RequestType']\
        \ == 'Update':\n            # Configure bucket notification\n            notification_config\
        \ = {\n                'LambdaFunctionConfigurations': [\n               \
        \     {\n                        'Id': 'TestSetZipExtractor',\n          \
        \              'LambdaFunctionArn': function_arn,\n                      \
        \  'Events': ['s3:ObjectCreated:*'],\n                        'Filter': {\n\
        \                            'Key': {\n                                'FilterRules':\
        \ [\n                                    {\n                             \
        \           'Name': 'suffix',\n                                        'Value':\
        \ '.zip'\n                                    }\n                        \
        \        ]\n                            }\n                        }\n   \
        \                 }\n                ]\n            }\n            \n    \
        \        s3_client.put_bucket_notification_configuration(\n              \
        \  Bucket=bucket_name,\n                NotificationConfiguration=notification_config\n\
        \            )\n            \n        elif event['RequestType'] == 'Delete':\n\
        \            # Remove bucket notification\n            s3_client.put_bucket_notification_configuration(\n\
        \                Bucket=bucket_name,\n                NotificationConfiguration={}\n\
        \            )\n        \n        cfnresponse.send(event, context, cfnresponse.SUCCESS,\
        \ {})\n        \n    except Exception as e:\n        logger.error(f\"Error:\
        \ {str(e)}\")\n        cfnresponse.send(event, context, cfnresponse.FAILED,\
        \ {}, \n                       reason=f\"Error configuring bucket notification:\
        \ {str(e)}\")\n"
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - s3:PutBucketNotification
          - s3:GetBucketNotification
          Resource:
            Fn::GetAtt:
            - TestSetBucket
            - Arn
  TestSetBucketNotificationConfiguration:
    Type: Custom::S3BucketNotification
    DependsOn:
    - TestSetZipExtractorFunctionInvokePermission
    - TestSetZipExtractorS3Policy
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - TestSetBucketNotificationFunction
        - Arn
      BucketName:
        Ref: TestSetBucket
      FunctionArn:
        Fn::GetAtt:
        - TestSetZipExtractorFunction
        - Arn
    Metadata:
      SamResourceId: TestSetBucketNotificationConfiguration
  TestFileCopierFunctionDLQ:
    Type: AWS::SQS::Queue
    Properties:
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: TestFileCopierFunctionDLQ
  TestFileCopierFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: TestFileCopierFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W12
          reason: Lambda requires CloudWatch logs permissions
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/1ab0d8c6452e5c91c8a6a7f2324953a3
      Description: Lambda function to copy test files from SQS queue
      MemorySize: 1024
      Timeout: 900
      DeadLetterQueue:
        Type: SQS
        TargetArn:
          Fn::GetAtt:
          - TestFileCopierFunctionDLQ
          - Arn
      LoggingConfig:
        LogGroup:
          Ref: TestFileCopierFunctionLogGroup
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          INPUT_BUCKET:
            Ref: InputBucket
          TEST_SET_BUCKET:
            Ref: TestSetBucket
          BASELINE_BUCKET:
            Fn::If:
            - ShouldCreateEvaluationBaselineBucket
            - Ref: EvaluationBaselineBucket
            - Ref: EvaluationBaselineBucketName
          TRACKING_TABLE:
            Ref: TrackingTable
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - S3ReadPolicy:
          BucketName:
            Ref: TestSetBucket
      - S3CrudPolicy:
          BucketName:
            Ref: InputBucket
      - S3CrudPolicy:
          BucketName:
            Fn::If:
            - ShouldCreateEvaluationBaselineBucket
            - Ref: EvaluationBaselineBucket
            - Ref: EvaluationBaselineBucketName
      - Statement:
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue:
              Fn::GetAtt:
              - TestFileCopyQueue
              - Arn
            BatchSize: 1
  TestFileCopierFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: TestFileCopierFunctionLogGroup
  TestResultCacheUpdateQueue:
    Type: AWS::SQS::Queue
    Properties:
      QueueName:
        Fn::Sub: ${AWS::StackName}-test-result-cache-update
      VisibilityTimeout: 900
      MessageRetentionPeriod: 1209600
      KmsMasterKeyId:
        Ref: CustomerManagedEncryptionKey
    Metadata:
      SamResourceId: TestResultCacheUpdateQueue
  DiscoveryProcessorFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: DiscoveryProcessorFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W12
          reason: Lambda requires CloudWatch logs permissions
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/749e91a434a4f971116c3d4a3bc268c7
      Description: Lambda function to process discovery jobs from SQS queue
      MemorySize: 1024
      Timeout: 900
      Layers:
      - Ref: IDPCommonBaseLayer
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          DISCOVERY_BUCKET:
            Ref: DiscoveryBucket
          DISCOVERY_TRACKING_TABLE:
            Ref: DiscoveryTrackingTable
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          BLUEPRINT_OPTIMIZATION_FUNCTION_NAME:
            Ref: BlueprintOptimizationFunction
      LoggingConfig:
        LogGroup:
          Ref: DiscoveryProcessorFunctionLogGroup
      Events:
        SQSEvent:
          Type: SQS
          Properties:
            Queue:
              Fn::GetAtt:
              - DiscoveryQueue
              - Arn
            BatchSize: 1
            FunctionResponseTypes:
            - ReportBatchItemFailures
      Policies:
      - S3ReadPolicy:
          BucketName:
            Ref: DiscoveryBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: DiscoveryTrackingTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConfigurationTable
      - Statement:
        - Effect: Allow
          Action:
          - cloudwatch:PutMetricData
          Resource: '*'
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - bedrock:InvokeModel
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock:*::foundation-model/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/*
        - Effect: Allow
          Action:
          - aws-marketplace:Subscribe
          - aws-marketplace:Unsubscribe
          - aws-marketplace:ViewSubscriptions
          Resource: '*'
        - Effect: Allow
          Action:
          - lambda:InvokeFunction
          Resource:
            Fn::GetAtt:
            - BlueprintOptimizationFunction
            - Arn
  DiscoveryProcessorFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: DiscoveryProcessorFunctionLogGroup
  BlueprintOptimizationFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: BlueprintOptimizationFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
        - id: W12
          reason: Lambda requires CloudWatch logs permissions
        - id: W11
          reason: Role requires * resource access for CloudWatch Metrics and Logs
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      FunctionName:
        Fn::Sub: ${AWS::StackName}-blueprint-optimization
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/2e6ccfa9948a1d38df862650b01fb94d
      Description: Lambda function to optimize BDA blueprints using ground truth data
      MemorySize: 3008
      Timeout: 900
      Layers:
      - Ref: IDPCommonBaseLayer
      Environment:
        Variables:
          LOG_LEVEL:
            Ref: LogLevel
          APPSYNC_API_URL:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
          CONFIGURATION_TABLE_NAME:
            Ref: ConfigurationTable
          STACK_NAME:
            Ref: AWS::StackName
          DISCOVERY_TRACKING_TABLE:
            Ref: DiscoveryTrackingTable
      LoggingConfig:
        LogGroup:
          Ref: BlueprintOptimizationFunctionLogGroup
      Policies:
      - S3CrudPolicy:
          BucketName:
            Ref: DiscoveryBucket
      - DynamoDBCrudPolicy:
          TableName:
            Ref: ConfigurationTable
      - DynamoDBCrudPolicy:
          TableName:
            Ref: DiscoveryTrackingTable
      - Statement:
        - Effect: Allow
          Action:
          - cloudwatch:PutMetricData
          Resource: '*'
        - Effect: Allow
          Action:
          - appsync:GraphQL
          Resource:
          - Fn::Sub: ${GraphQLApi.Arn}/types/Mutation/*
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:ReEncrypt*
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
        - Effect: Allow
          Action:
          - bedrock:InvokeDataAutomationAsync
          - bedrock:CreateDataAutomationProject
          - bedrock:UpdateDataAutomationProject
          - bedrock:GetDataAutomationProject
          - bedrock:GetDataAutomationStatus
          - bedrock:GetBlueprint
          - bedrock:UpdateBlueprint
          - bedrock:CreateBlueprint
          - bedrock:CreateBlueprintVersion
          - bedrock:DeleteBlueprint
          - bedrock:ListBlueprints
          - bedrock:InvokeBlueprintOptimizationAsync
          - bedrock:GetBlueprintOptimizationStatus
          Resource:
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:data-automation-project/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:blueprint/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:aws:blueprint/*
          - Fn::Sub: arn:${AWS::Partition}:bedrock:${AWS::Region}:${AWS::AccountId}:blueprint-optimization-invocation/*
  BlueprintOptimizationFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: BlueprintOptimizationFunctionLogGroup
  CloudFrontOriginAccessIdentity:
    Type: AWS::CloudFront::CloudFrontOriginAccessIdentity
    Condition: UseCloudFrontHosting
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment:
          Fn::Sub: ${AWS::StackName} CloudFront OAI for ${WebUIBucket}
    Metadata:
      SamResourceId: CloudFrontOriginAccessIdentity
  SecurityHeadersPolicy:
    Type: AWS::CloudFront::ResponseHeadersPolicy
    Condition: UseCloudFrontHosting
    Properties:
      ResponseHeadersPolicyConfig:
        Name:
          Fn::Sub: ${AWS::StackName}-security-headers-policy
        Comment: Security headers policy
        SecurityHeadersConfig:
          ContentSecurityPolicy:
            Override: true
            ContentSecurityPolicy: 'script-src ''self'' ''unsafe-inline'' ''unsafe-eval''
              https:; object-src ''self'' blob: data: https:; base-uri ''none''; frame-ancestors
              ''self''; form-action ''self''; img-src ''self'' data: https:; style-src
              ''self'' ''unsafe-inline'' https:; connect-src ''self'' https: wss://*.amazonaws.com
              wss://*.appsync-realtime-api.*.amazonaws.com wss://*.execute-api.*.amazonaws.com
              ws://localhost:*;'
          StrictTransportSecurity:
            Override: true
            AccessControlMaxAgeSec: 31536000
            IncludeSubdomains: true
            Preload: false
          ContentTypeOptions:
            Override: true
          FrameOptions:
            Override: true
            FrameOption: SAMEORIGIN
          ReferrerPolicy:
            Override: true
            ReferrerPolicy: strict-origin-when-cross-origin
    Metadata:
      SamResourceId: SecurityHeadersPolicy
  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Condition: UseCloudFrontHosting
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W70
          reason: Minimum TLS version is determined by CloudFrontDefaultCertificate
            true
      SamResourceId: CloudFrontDistribution
    Properties:
      DistributionConfig:
        Comment:
          Fn::Sub: Web app cloudfront distribution ${AWS::StackName}
        ViewerCertificate:
          CloudFrontDefaultCertificate: true
          MinimumProtocolVersion: TLSv1.2_2021
        Logging:
          Bucket:
            Fn::Sub: ${LoggingBucket}.s3.${AWS::URLSuffix}
          Prefix: cloudfront-logs
          IncludeCookies: true
        CustomErrorResponses:
        - ErrorCachingMinTTL: 300
          ErrorCode: 403
          ResponseCode: 200
          ResponsePagePath: /index.html
        - ErrorCachingMinTTL: 300
          ErrorCode: 404
          ResponseCode: 200
          ResponsePagePath: /index.html
        DefaultCacheBehavior:
          AllowedMethods:
          - GET
          - HEAD
          - OPTIONS
          Compress: true
          ForwardedValues:
            QueryString: false
            Cookies:
              Forward: none
          TargetOriginId: webapp-s3-bucket
          ViewerProtocolPolicy: redirect-to-https
          DefaultTTL: 600
          MinTTL: 300
          MaxTTL: 900
          ResponseHeadersPolicyId:
            Ref: SecurityHeadersPolicy
        DefaultRootObject: index.html
        Enabled: true
        HttpVersion: http2
        IPV6Enabled: true
        Origins:
        - Id: webapp-s3-bucket
          DomainName:
            Fn::GetAtt:
            - WebUIBucket
            - RegionalDomainName
          S3OriginConfig:
            OriginAccessIdentity:
              Fn::Sub: origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity}
        PriceClass:
          Ref: CloudFrontPriceClass
        Restrictions:
          Fn::If:
          - ShouldEnableGeoRestriction
          - GeoRestriction:
              RestrictionType: whitelist
              Locations:
                Fn::Split:
                - ','
                - Ref: CloudFrontAllowedGeos
          - GeoRestriction:
              RestrictionType: none
  ALBHOSTINGSTACK:
    Type: AWS::CloudFormation::Stack
    Condition: UseALBHosting
    Properties:
      TemplateURL: https://s3.amazonaws.com/aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/2eb9030bb7dd5b2694e0c2eac97d3278.template
      Parameters:
        VpcId:
          Ref: ALBVpcId
        SubnetIds:
          Fn::Join:
          - ','
          - Ref: ALBSubnetIds
        CertificateArn:
          Ref: ALBCertificateArn
        ALBScheme:
          Ref: ALBScheme
        ALBAllowedCIDRs:
          Ref: ALBAllowedCIDRs
        WebUIBucketName:
          Ref: WebUIBucket
        StackName:
          Ref: AWS::StackName
        PermissionsBoundaryArn:
          Ref: PermissionsBoundaryArn
        LoggingBucketName:
          Ref: LoggingBucket
    Metadata:
      SamResourceId: ALBHOSTINGSTACK
  UICodeBuildServiceRole:
    Type: AWS::IAM::Role
    Properties:
      Path: /
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Action: sts:AssumeRole
          Effect: Allow
          Principal:
            Service:
              Fn::Sub: codebuild.${AWS::URLSuffix}
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: ecs-service
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Resource:
            - Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1
            - Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/*
            Effect: Allow
            Action:
            - s3:GetObject
            - s3:GetObjectVersion
            - s3:GetBucketAcl
            - s3:GetBucketLocation
            - s3:PutObject
            - s3:ListBucket
          - Resource:
            - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*
            - Fn::Sub: arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*
            Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
          - Effect: Allow
            Action:
            - s3:PutObject
            - s3:DeleteObject
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:s3:::${WebUIBucket}/*
          - Fn::If:
            - UseCloudFrontHosting
            - Effect: Allow
              Action:
              - cloudfront:CreateInvalidation
              Resource:
              - Fn::Sub: arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}
            - Ref: AWS::NoValue
    Metadata:
      SamResourceId: UICodeBuildServiceRole
  UICodeBuildProject:
    Type: AWS::CodeBuild::Project
    Properties:
      Name:
        Fn::Sub: ${AWS::StackName}-webui-build
      Description:
        Fn::Sub: Web UI build for GenAIIDP stack - ${AWS::StackName}
      ServiceRole:
        Ref: UICodeBuildServiceRole
      EncryptionKey: alias/aws/s3
      Artifacts:
        Type: NO_ARTIFACTS
      Source:
        Location:
          Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/src-77d011f5bcd302f1.zip
        Type: S3
        BuildSpec: "version: 0.2\nphases:\n  pre_build:\n    commands:\n      - echo\
          \ ${SOURCE_CODE_LOCATION}\n      - echo `ls -altr`\n      - echo `pwd`\n\
          \      - echo Installing NodeJS\n      - n 22.14.0\n      - npm install\
          \ -g npm@11.1.0\n      - echo Installing Web UI dependencies\n      - npm\
          \ ci\n  build:\n    commands:\n      - echo Build started on `date`\n  \
          \    - cd $CODEBUILD_SRC_DIR\n      - echo Building Web UI\n      - npm\
          \ run build\n      - >\n        printf '{\"RepositoryUri\":\"%s\",\"ProjectName\"\
          :\"%s\",\"ArtifactBucket\":\"%s\"}'\n        $REPOSITORY_URI $PROJECT_NAME\
          \ $ARTIFACT_BUCKET > build.json\n  post_build:\n    commands:\n      - echo\
          \ Build completed on `date`\n      - echo Copying Web UI\n      - find build\
          \ -ls\n      - aws s3 cp --recursive build s3://${WEBAPP_BUCKET}/\n    \
          \  - |\n        if [ \"$WEB_UI_HOSTING\" = \"CloudFront\" ]; then\n    \
          \      echo \"Invalidating CloudFront Distribution\"\n          aws cloudfront\
          \ create-invalidation --distribution-id \"$CLOUDFRONT_DISTRIBUTION_ID\"\
          \ --paths '/*'\n        else\n          echo \"ALB hosting mode - skipping\
          \ CloudFront invalidation\"\n        fi\nartifacts:\n  files:\n    - build.json\n"
      Environment:
        ComputeType: BUILD_GENERAL1_MEDIUM
        Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
        Type: LINUX_CONTAINER
        PrivilegedMode: true
        EnvironmentVariables:
        - Name: AWS_DEFAULT_REGION
          Value:
            Ref: AWS::Region
        - Name: AWS_ACCOUNT_ID
          Value:
            Ref: AWS::AccountId
        - Name: SOURCE_CODE_LOCATION
          Value:
            Fn::Sub: aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4
        - Name: WEBAPP_BUCKET
          Value:
            Ref: WebUIBucket
        - Name: WEB_UI_HOSTING
          Value:
            Ref: WebUIHosting
        - Name: CLOUDFRONT_DISTRIBUTION_ID
          Value:
            Fn::If:
            - UseCloudFrontHosting
            - Ref: CloudFrontDistribution
            - ''
        - Name: VITE_SETTINGS_PARAMETER
          Value:
            Ref: SettingsParameter
        - Name: VITE_USER_POOL_ID
          Value:
            Ref: UserPool
        - Name: VITE_USER_POOL_CLIENT_ID
          Value:
            Ref: UserPoolClient
        - Name: VITE_IDENTITY_POOL_ID
          Value:
            Ref: IdentityPool
        - Name: VITE_APPSYNC_GRAPHQL_URL
          Value:
            Fn::GetAtt:
            - GraphQLApi
            - GraphQLUrl
        - Name: VITE_AWS_REGION
          Value:
            Ref: AWS::Region
        - Name: VITE_SHOULD_HIDE_SIGN_UP
          Value:
            Fn::If:
            - ShouldAllowSignUpEmailDomain
            - 'false'
            - 'true'
        - Name: VITE_CLOUDFRONT_DOMAIN
          Value:
            Fn::If:
            - UseCloudFrontHosting
            - Fn::Sub: https://${CloudFrontDistribution.DomainName}/
            - Fn::Sub: ${ALBHOSTINGSTACK.Outputs.WebUIURL}/
        - Name: VITE_TEST_SET_BUCKET_NAME
          Value:
            Ref: TestSetBucket
        - Name: VITE_INPUT_BUCKET_NAME
          Value:
            Ref: InputBucket
        - Name: VITE_LATENCY_OPTIONS
          Value: '[60, 120, 300, 600, 900, 1800, 3600]'
        - Name: VITE_DEFAULT_MAX_LATENCY
          Value: '60'
        - Name: VITE_DEFAULT_TOKENS_BY_STEP
          Value: '{"OCR":4000,"Classification":2000,"Extraction":8000,"Assessment":4000,"Summarization":4000,"default":4000}'
        - Name: VITE_DEFAULT_MAX_TOKENS_PER_REQUEST
          Value: '4000'
        - Name: VITE_BDA_TOKENS_PER_PAGE
          Value: '2000'
      TimeoutInMinutes: 30
    Metadata:
      SamResourceId: UICodeBuildProject
  StartUICodeBuildExecutionRole:
    Type: AWS::IAM::Role
    Metadata:
      cfn_nag:
        rules_to_suppress:
        - id: W11
          reason: Role requires * resource access for CloudWatch Logs and CloudFormation
            custom resource operations
      SamResourceId: StartUICodeBuildExecutionRole
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - Fn::Sub: lambda.${AWS::URLSuffix}
          Action:
          - sts:AssumeRole
      Path: /
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: root
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - codebuild:StartBuild
            - codebuild:BatchGetBuilds
            Resource:
            - Fn::GetAtt:
              - UICodeBuildProject
              - Arn
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            Resource: '*'
      - PolicyName: CustomResourcePoller
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - events:PutRule
            - events:DeleteRule
            - events:PutTargets
            - events:RemoveTargets
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:events:${AWS::Region}:${AWS::AccountId}:rule/*
          - Effect: Allow
            Action:
            - lambda:AddPermission
            - lambda:RemovePermission
            Resource:
            - Fn::Sub: arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*
  StartUICodeBuild:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: StartUICodeBuild
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access as it only interacts with AWS
            services via APIs
        - id: W92
          reason: Function does not require reserved concurrency as it scales based
            on demand
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Role:
        Fn::GetAtt:
        - StartUICodeBuildExecutionRole
        - Arn
      Runtime: python3.12
      Timeout: 60
      MemorySize: 128
      Handler: index.handler
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/55cb79efb673b97eb15b48062dc24f6d
      Description: This AWS Lambda Function kicks off a code build job.
      LoggingConfig:
        LogGroup:
          Ref: StartUICodeBuildLogGroup
  StartUICodeBuildLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: StartUICodeBuildLogGroup
  CodeBuildRun:
    Type: Custom::CodeBuildRun
    Properties:
      ServiceToken:
        Fn::GetAtt:
        - StartUICodeBuild
        - Arn
      BuildProjectName:
        Ref: UICodeBuildProject
      SettingsParameter:
        Ref: SettingsParameter
      CodeLocation:
        Fn::Sub: arn:${AWS::Partition}:s3:::aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/src-77d011f5bcd302f1.zip
    Metadata:
      SamResourceId: CodeBuildRun
  UserManagementFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: UserManagementFunctionLogGroup
  UserManagementFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: UserManagementFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/4d18a028aa250c8b3a8cb288b4dac2c1
      Description: Lambda function for user management (create, delete, list users)
      MemorySize: 256
      Timeout: 30
      Environment:
        Variables:
          USER_POOL_ID:
            Ref: UserPool
          ADMIN_GROUP:
            Ref: AdminGroup
          AUTHOR_GROUP:
            Ref: AuthorGroup
          REVIEWER_GROUP:
            Ref: ReviewerGroup
          VIEWER_GROUP:
            Ref: ViewerGroup
          USERS_TABLE_NAME:
            Ref: UsersTable
          ALLOWED_SIGNUP_EMAIL_DOMAINS:
            Ref: AllowedSignUpEmailDomain
      LoggingConfig:
        LogGroup:
          Ref: UserManagementFunctionLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: UsersTable
      - Statement:
        - Effect: Allow
          Action:
          - cognito-idp:AdminCreateUser
          - cognito-idp:AdminDeleteUser
          - cognito-idp:AdminAddUserToGroup
          - cognito-idp:AdminRemoveUserFromGroup
          - cognito-idp:AdminListGroupsForUser
          - cognito-idp:ListUsers
          - cognito-idp:ListUsersInGroup
          Resource:
            Fn::GetAtt:
            - UserPool
            - Arn
        - Effect: Allow
          Action:
          - kms:Encrypt
          - kms:Decrypt
          - kms:GenerateDataKey*
          - kms:DescribeKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  UserSyncFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: UserSyncFunctionLogGroup
  UserSyncFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: UserSyncFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/6a5f87de14b7f6f08e7b7ae5d2364bf9
      Description: Lambda function to sync DynamoDB user changes to Cognito
      MemorySize: 256
      Timeout: 30
      Environment:
        Variables:
          USER_POOL_ID:
            Ref: UserPool
          ADMIN_GROUP:
            Ref: AdminGroup
          REVIEWER_GROUP:
            Ref: ReviewerGroup
      LoggingConfig:
        LogGroup:
          Ref: UserSyncFunctionLogGroup
      Policies:
      - Statement:
        - Effect: Allow
          Action:
          - cognito-idp:AdminCreateUser
          - cognito-idp:AdminDeleteUser
          - cognito-idp:AdminAddUserToGroup
          - cognito-idp:AdminRemoveUserFromGroup
          Resource:
            Fn::GetAtt:
            - UserPool
            - Arn
      Events:
        DynamoDBStream:
          Type: DynamoDB
          Properties:
            Stream:
              Fn::GetAtt:
              - UsersTable
              - StreamArn
            StartingPosition: TRIM_HORIZON
            BatchSize: 10
            MaximumBatchingWindowInSeconds: 5
  CompleteSectionReviewFunctionLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      KmsKeyId:
        Fn::GetAtt:
        - CustomerManagedEncryptionKey
        - Arn
      RetentionInDays:
        Ref: LogRetentionDays
    Metadata:
      SamResourceId: CompleteSectionReviewFunctionLogGroup
  CompleteSectionReviewFunction:
    Type: AWS::Serverless::Function
    Metadata:
      SamResourceId: CompleteSectionReviewFunction
      cfn_nag:
        rules_to_suppress:
        - id: W89
          reason: Function does not require VPC access
        - id: W92
          reason: Function does not require reserved concurrency
    Properties:
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Handler: index.handler
      Runtime: python3.12
      CodeUri: s3://aws-ml-blog-us-east-1/artifacts/genai-idp/0.5.4/8998fe8e20fe11b780e2d1a05217ff3d
      Description: Lambda function to mark HITL section review as complete
      MemorySize: 512
      Timeout: 60
      Layers:
      - Ref: IDPCommonBaseLayer
      Environment:
        Variables:
          TRACKING_TABLE_NAME:
            Ref: TrackingTable
          TRACKING_TABLE:
            Ref: TrackingTable
          OUTPUT_BUCKET:
            Ref: OutputBucket
          INPUT_BUCKET:
            Ref: InputBucket
          WORKING_BUCKET:
            Ref: WorkingBucket
          QUEUE_URL:
            Ref: DocumentQueue
      LoggingConfig:
        LogGroup:
          Ref: CompleteSectionReviewFunctionLogGroup
      Policies:
      - DynamoDBCrudPolicy:
          TableName:
            Ref: TrackingTable
      - S3CrudPolicy:
          BucketName:
            Ref: OutputBucket
      - S3CrudPolicy:
          BucketName:
            Ref: WorkingBucket
      - SQSSendMessagePolicy:
          QueueName:
            Fn::GetAtt:
            - DocumentQueue
            - QueueName
      - Statement:
        - Effect: Allow
          Action:
          - kms:Decrypt
          - kms:GenerateDataKey
          Resource:
            Fn::GetAtt:
            - CustomerManagedEncryptionKey
            - Arn
  HITLAppSyncServiceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            Service:
              Fn::Sub: appsync.${AWS::URLSuffix}
          Action: sts:AssumeRole
      PermissionsBoundary:
        Fn::If:
        - HasPermissionsBoundary
        - Ref: PermissionsBoundaryArn
        - Ref: AWS::NoValue
      Policies:
      - PolicyName: HITLLambdaInvoke
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource:
            - Fn::GetAtt:
              - UserManagementFunction
              - Arn
            - Fn::GetAtt:
              - CompleteSectionReviewFunction
              - Arn
            - Fn::GetAtt:
              - CalculateCapacityResolverFunction
              - Arn
    Metadata:
      SamResourceId: HITLAppSyncServiceRole
  UserManagementDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      Name: UserManagementDataSource
      Type: AWS_LAMBDA
      ServiceRoleArn:
        Fn::GetAtt:
        - HITLAppSyncServiceRole
        - Arn
      LambdaConfig:
        LambdaFunctionArn:
          Fn::GetAtt:
          - UserManagementFunction
          - Arn
    Metadata:
      SamResourceId: UserManagementDataSource
  CreateUserResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - UserManagementDataSource
        - Name
      TypeName: Mutation
      FieldName: createUser
    Metadata:
      SamResourceId: CreateUserResolver
  DeleteUserResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - UserManagementDataSource
        - Name
      TypeName: Mutation
      FieldName: deleteUser
    Metadata:
      SamResourceId: DeleteUserResolver
  ListUsersResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - UserManagementDataSource
        - Name
      TypeName: Query
      FieldName: listUsers
    Metadata:
      SamResourceId: ListUsersResolver
  UpdateUserResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - UserManagementDataSource
        - Name
      TypeName: Mutation
      FieldName: updateUser
    Metadata:
      SamResourceId: UpdateUserResolver
  GetMyProfileResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - UserManagementDataSource
        - Name
      TypeName: Query
      FieldName: getMyProfile
    Metadata:
      SamResourceId: GetMyProfileResolver
  CompleteSectionReviewDataSource:
    Type: AWS::AppSync::DataSource
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      Name: CompleteSectionReviewDataSource
      Type: AWS_LAMBDA
      ServiceRoleArn:
        Fn::GetAtt:
        - HITLAppSyncServiceRole
        - Arn
      LambdaConfig:
        LambdaFunctionArn:
          Fn::GetAtt:
          - CompleteSectionReviewFunction
          - Arn
    Metadata:
      SamResourceId: CompleteSectionReviewDataSource
  CompleteSectionReviewResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - CompleteSectionReviewDataSource
        - Name
      TypeName: Mutation
      FieldName: completeSectionReview
    Metadata:
      SamResourceId: CompleteSectionReviewResolver
  SkipAllSectionsReviewResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - CompleteSectionReviewDataSource
        - Name
      TypeName: Mutation
      FieldName: skipAllSectionsReview
    Metadata:
      SamResourceId: SkipAllSectionsReviewResolver
  ClaimReviewResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - CompleteSectionReviewDataSource
        - Name
      TypeName: Mutation
      FieldName: claimReview
    Metadata:
      SamResourceId: ClaimReviewResolver
  ReleaseReviewResolver:
    Type: AWS::AppSync::Resolver
    DependsOn: APPSYNCSTACK
    Properties:
      ApiId:
        Fn::GetAtt:
        - GraphQLApi
        - ApiId
      DataSourceName:
        Fn::GetAtt:
        - CompleteSectionReviewDataSource
        - Name
      TypeName: Mutation
      FieldName: releaseReview
    Metadata:
      SamResourceId: ReleaseReviewResolver
Outputs:
  CustomerManagedEncryptionKeyArn:
    Description: ARN of the customer-managed KMS encryption key used for S3 buckets.
      Needed when deploying Lambda Hook functions that read from the working bucket.
    Value:
      Fn::GetAtt:
      - CustomerManagedEncryptionKey
      - Arn
  ApplicationWebURL:
    Description: Application Web URL
    Value:
      Fn::If:
      - UseCloudFrontHosting
      - Fn::Sub: https://${CloudFrontDistribution.DomainName}/
      - Fn::GetAtt:
        - ALBHOSTINGSTACK
        - Outputs.WebUIURL
  S3InstallBucket:
    Description: Install S3 bucket name
    Value:
      Ref: InputBucket
  S3LoggingBucket:
    Description: Logging S3 bucket name
    Value:
      Ref: LoggingBucket
  S3WebUIBucket:
    Description: WebUI S3 bucket name
    Value:
      Ref: WebUIBucket
  S3InputBucketName:
    Description: Input S3 bucket name
    Value:
      Ref: InputBucket
  S3DiscoveryBucketName:
    Description: Discovery S3 bucket name
    Value:
      Ref: DiscoveryBucket
  S3InputBucketConsoleURL:
    Description: Input S3 bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${InputBucket}
  S3DiscoveryBucketConsoleURL:
    Description: Discovery S3 bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${DiscoveryBucket}
  S3OutputBucketName:
    Description: Output S3 bucket name
    Value:
      Ref: OutputBucket
  S3OutputBucketConsoleURL:
    Description: Output S3 bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${OutputBucket}
  S3ReportingBucketName:
    Description: Reporting S3 bucket name
    Value:
      Fn::If:
      - ShouldCreateReportingBucket
      - Ref: ReportingBucket
      - Ref: ReportingBucketName
  S3ReportingBucketConsoleURL:
    Description: Reporting S3 bucket console URL
    Value:
      Fn::Sub:
      - https://s3.console.aws.amazon.com/s3/buckets/${Bucket}
      - Bucket:
          Fn::If:
          - ShouldCreateReportingBucket
          - Ref: ReportingBucket
          - Ref: ReportingBucketName
  ReportingDatabase:
    Description: AWS Glue database containing evaluation metrics tables (document_evaluations,
      section_evaluations, attribute_evaluations) that can be queried with Amazon
      Athena for analytics and reporting
    Value:
      Ref: ReportingDatabase
  S3EvaluationBaselineBucketName:
    Description: Evaluation Baseline S3 bucket name
    Value:
      Fn::If:
      - ShouldCreateEvaluationBaselineBucket
      - Ref: EvaluationBaselineBucket
      - Ref: EvaluationBaselineBucketName
  S3EvaluationBaselineBucketConsoleURL:
    Description: Evaluation Baseline S3 bucket console URL
    Value:
      Fn::Sub:
      - https://s3.console.aws.amazon.com/s3/buckets/${Bucket}
      - Bucket:
          Fn::If:
          - ShouldCreateEvaluationBaselineBucket
          - Ref: EvaluationBaselineBucket
          - Ref: EvaluationBaselineBucketName
  S3ConfigurationBucketName:
    Description: Configuration S3 bucket name
    Value:
      Ref: ConfigurationBucket
  S3ConfigurationBucketConsoleURL:
    Description: Configuration S3 bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${ConfigurationBucket}
  S3TestSetBucketName:
    Description: Test Set S3 bucket name
    Value:
      Ref: TestSetBucket
  S3TestSetBucketConsoleURL:
    Description: Test Set S3 bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${TestSetBucket}
  StateMachineArn:
    Description: Step Functions State machine ARN
    Value:
      Fn::GetAtt:
      - PATTERNSTACK
      - Outputs.StateMachineArn
  StateMachineConsoleURL:
    Description: Step Functions State machine console URL
    Value:
      Fn::Sub:
      - https://${AWS::Region}.console.aws.amazon.com/states/home?region=${AWS::Region}#/statemachines/view/${StateMachineArn}
      - StateMachineArn:
          Fn::GetAtt:
          - PATTERNSTACK
          - Outputs.StateMachineArn
  CWDashboardConsoleName:
    Description: Name of the merged CloudWatch dashboard
    Value:
      Fn::GetAtt:
      - MergedDashboard
      - DashboardName
  CWDashboardConsoleURL:
    Description: URL of the merged CloudWatch dashboard
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/cloudwatch/home?region=${AWS::Region}#dashboards:name=${MergedDashboard.DashboardName}
  SNSAlertsTopicARN:
    Description: SNS Topic ARN for alerts
    Value:
      Ref: AlertsTopic
  SNSAlertsTopicConsoleURL:
    Description: SNS Topic console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/sns/v3/home?region=${AWS::Region}#/topic/${AlertsTopic}
  DynamoDBTrackingTableConsoleURL:
    Description: DynamoDB table console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/dynamodbv2/home?region=${AWS::Region}#item-explorer?maximize=true&operation=QUERY&table=${TrackingTable}
  DynamoDBConcurrencyTableConsoleURL:
    Description: DynamoDB table console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/dynamodbv2/home?region=${AWS::Region}#item-explorer?maximize=true&operation=QUERY&table=${ConcurrencyTable}
  DynamoDBConfigurationTableConsoleURL:
    Description: DynamoDB table console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/dynamodbv2/home?region=${AWS::Region}#item-explorer?maximize=true&operation=QUERY&table=${ConfigurationTable}
  DynamoDBAgentTableName:
    Description: Name of the DynamoDB table for agent job tracking
    Value:
      Ref: AgentTable
  DynamoDBAgentTableConsoleURL:
    Description: DynamoDB table console URL for agent job tracking
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/dynamodbv2/home?region=${AWS::Region}#item-explorer?maximize=true&operation=QUERY&table=${AgentTable}
  LambdaLookupFunctionName:
    Description: Name of the Lookup function
    Value:
      Ref: LookupFunction
  LambdaLookupFunctionConsoleURL:
    Description: Lambda function console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/lambda/home?region=${AWS::Region}#/functions/${LookupFunction}
  SQSDocumentQueueUrl:
    Description: SQS Queue URL
    Value:
      Ref: DocumentQueue
  SQSDocumentQueueConsoleURL:
    Description: SQS Queue console URL
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/sqs/v2/home?region=${AWS::Region}#/queues/${DocumentQueue}
  WebUITestEnvFile:
    Description: Copy to create .env file for local UI testing
    Value:
      Fn::Sub: 'VITE_USER_POOL_ID=${UserPool}

        VITE_USER_POOL_CLIENT_ID=${UserPoolClient}

        VITE_IDENTITY_POOL_ID=${IdentityPool}

        VITE_APPSYNC_GRAPHQL_URL=${GraphQLApi.GraphQLUrl}

        VITE_AWS_REGION=${AWS::Region}

        VITE_SETTINGS_PARAMETER=${SettingsParameter}

        VITE_TEST_SET_BUCKET_NAME=${TestSetBucket}

        VITE_INPUT_BUCKET_NAME=${InputBucket}

        VITE_LATENCY_OPTIONS=[60, 120, 300, 600, 900, 1800, 3600]

        VITE_DEFAULT_MAX_LATENCY=60

        VITE_DEFAULT_TOKENS_BY_STEP={"OCR":4000,"Classification":2000,"Extraction":8000,"Assessment":4000,"Summarization":4000,"default":4000}

        VITE_DEFAULT_MAX_TOKENS_PER_REQUEST=4000

        VITE_BDA_TOKENS_PER_PAGE=2000'
  ExternalMCPAgentsSecretConsoleURL:
    Description: External MCP Agents secret console URL - configure MCP server credentials
      here (JSON array format)
    Value:
      Fn::Sub: https://${AWS::Region}.console.aws.amazon.com/secretsmanager/secret?name=${AWS::StackName}/external-mcp-agents/credentials&region=${AWS::Region}
  IDPCommonBaseLayerArn:
    Description: ARN of IDP Common Base Layer
    Value:
      Ref: IDPCommonBaseLayer
  IDPCommonReportingLayerArn:
    Description: ARN of IDP Common Reporting Layer
    Value:
      Ref: IDPCommonReportingLayer
  IDPCommonAgentsLayerArn:
    Description: ARN of IDP Common Agents Layer
    Value:
      Ref: IDPCommonAgentsLayer
  MCPServerEndpoint:
    Condition: CreateAgentCoreLambda
    Description: MCP Server Endpoint
    Value:
      Fn::GetAtt:
      - AgentCoreGateway
      - GatewayUrl
  MCPClientId:
    Condition: CreateAgentCoreLambda
    Description: Cognito client ID for user-based authentication with the IDP MCP
      server. Used by external applications like Amazon QuickSight that require user
      password authentication.
    Value:
      Ref: ExternalAppClient
  MCPClientSecret:
    Condition: CreateAgentCoreLambda
    Description: Cognito client secret for user-based authentication with the IDP
      MCP server. Used by external applications like Amazon QuickSight that require
      user password authentication.
    Value:
      Fn::GetAtt:
      - ExternalAppClient
      - ClientSecret
  MCPUserPool:
    Condition: CreateAgentCoreLambda
    Description: MCP User Pool ID
    Value:
      Ref: UserPool
  MCPTokenURL:
    Condition: CreateAgentCoreLambda
    Description: MCP Token URL
    Value:
      Fn::Sub: https://${GetDomain.OutputString}.auth.${AWS::Region}.amazoncognito.com/oauth2/token
  MCPAuthorizationURL:
    Condition: CreateAgentCoreLambda
    Description: MCP Authorization URL
    Value:
      Fn::Sub: https://${GetDomain.OutputString}.auth.${AWS::Region}.amazoncognito.com/oauth2/authorize
  MCPConnectorClientId:
    Condition: CreateAgentCoreLambda
    Description: Cognito client ID used by the MCP Connector for machine-to-machine
      (M2M) authentication with the IDP system
    Value:
      Ref: MCPConnectorClient
  MCPConnectorClientSecret:
    Condition: CreateAgentCoreLambda
    Description: Cognito client secret used by the MCP Connector for machine-to-machine
      (M2M) authentication with the IDP system
    Value:
      Fn::GetAtt:
      - MCPConnectorClient
      - ClientSecret
  MCPContentBucketConsoleURL:
    Description: MCP server content bucket console URL
    Value:
      Fn::Sub: https://s3.console.aws.amazon.com/s3/buckets/${MCPContentBucket}
