AWSTemplateFormatVersion: '2010-09-09'
Description: >
  CloudFront Log Analytics Dashboard - Deploys a serverless, modular analytics
  dashboard for CloudFront distribution logs with optional WAF and MediaInfo support.

Metadata:
  ExternalDependencyDisclosure: >
    This application downloads the following artifacts from the seller's S3 bucket
    at deployment time. No customer data is collected or transmitted externally.
    1) mediainfo.zip - Lambda function code for video metadata extraction.
    2) mediainfo_lib.zip - Native pymediainfo library Lambda layer.
    3) athena-dynamodb-connector.jar - Athena federated query connector for DynamoDB.
    4) dashboard.json - Grafana dashboard configuration (downloaded via EC2 UserData).
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "Data Sources"
        Parameters:
          - CloudFrontLogsBucketName
          - CloudFrontLogsKeyPrefix
          - CloudFrontOriginBucketName
          - WafLogsBucketName
          - WafLogsKeyPrefix
      - Label:
          default: "Grafana Configuration"
        Parameters:
          - AmiId
          - InstanceType
          - GrafanaAdminPassword
          - CertificateArn
      - Label:
          default: "Network Configuration"
        Parameters:
          - VpcCidr
          - PublicSubnetACidr
          - PublicSubnetBCidr
          - AllowedCidrBlock
      - Label:
          default: "Feature Toggles"
        Parameters:
          - EnableMediaInfo
          - DevMode
      - Label:
          default: "Advanced Configuration"
        Parameters:
          - MaxVideoWorkers
          - VideoWorkerInstanceType
          - ScheduleExpression
          - AlertEmail
      - Label:
          default: "AWS Marketplace Configuration"
        Parameters:
          - MPS3BucketName
          - MPS3BucketRegion
          - MPS3KeyPrefix
      - Label:
          default: "Resource tags (optional)"
        Parameters:
          - TagProject
          - TagOwner
    ParameterLabels:
      AmiId:
        default: "Grafana AMI ID"
      CloudFrontOriginBucketName:
        default: "CloudFront Origin S3 Bucket"
      CloudFrontLogsBucketName:
        default: "CloudFront Logs S3 Bucket"
      CloudFrontLogsKeyPrefix:
        default: "CloudFront Logs Key Prefix"
      WafLogsBucketName:
        default: "WAF Logs S3 Bucket (optional)"
      WafLogsKeyPrefix:
        default: "WAF Logs Key Prefix (optional)"
      EnableMediaInfo:
        default: "Enable Video Metadata Extraction"
      CertificateArn:
        default: "ACM Certificate ARN (optional, for HTTPS)"
      DevMode:
        default: "Development Mode (destroys data on delete)"
      VpcCidr:
        default: "VPC CIDR Block"
      PublicSubnetACidr:
        default: "Public Subnet A CIDR"
      PublicSubnetBCidr:
        default: "Public Subnet B CIDR"
      InstanceType:
        default: "Grafana EC2 Instance Type"
      AllowedCidrBlock:
        default: "Allowed CIDR for Dashboard Access"
      GrafanaAdminPassword:
        default: "Grafana Admin Password"
      MaxVideoWorkers:
        default: "Max Video Processing Workers"
      VideoWorkerInstanceType:
        default: "Video Worker Instance Type"
      ScheduleExpression:
        default: "Pipeline Schedule Expression"
      AlertEmail:
        default: "Alert Email Address (optional)"
      MPS3BucketName:
        default: "Nested Template S3 Bucket Name"
      MPS3BucketRegion:
        default: "Nested Template S3 Bucket Region"
      MPS3KeyPrefix:
        default: "Nested Template S3 Key Prefix"
      TagProject:
        default: "Project tag (optional)"
      TagOwner:
        default: "Owner tag (optional)"

Parameters:
  AmiId:
    Description: The ID of the Grafana AMI (region-specific, provided by the Marketplace subscription)
    Type: AWS::EC2::Image::Id
  CloudFrontOriginBucketName:
    Description: Name of the S3 bucket where the CloudFront origin content is stored
    Type: String
    AllowedPattern: '^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$'
    ConstraintDescription: Must be a valid S3 bucket name (3-63 characters, lowercase, numbers, hyphens, periods)
  CloudFrontLogsBucketName:
    Description: Name of the S3 bucket where CloudFront standard logs are delivered
    Type: String
    AllowedPattern: '^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$'
    ConstraintDescription: Must be a valid S3 bucket name
  CloudFrontLogsKeyPrefix:
    Description: "The S3 key prefix for CloudFront logs (e.g. AWSLogs/123456789012/CloudFront/)"
    Type: String
    AllowedPattern: '^[a-zA-Z0-9/_.-]*$'
    ConstraintDescription: Must be a valid S3 key prefix
  WafLogsBucketName:
    Description: "(Optional) Name of the S3 bucket where WAF logs are stored. Leave empty to disable WAF analytics."
    Type: String
    Default: ""
  WafLogsKeyPrefix:
    Description: "(Optional) The S3 key prefix for WAF logs. Only required if WAF logs bucket is provided."
    Type: String
    Default: ""
  EnableMediaInfo:
    Description: Enable MediaInfo video metadata extraction for media-specific dashboard panels
    Type: String
    Default: "false"
    AllowedValues:
      - "true"
      - "false"
  CertificateArn:
    Description: Optional ACM certificate ARN for HTTPS on the Grafana ALB. Leave empty for HTTP only.
    Type: String
    Default: ""
  GrafanaAdminPassword:
    Description: "Password for the Grafana admin user. Must be at least 8 characters."
    Type: String
    NoEcho: true
    MinLength: 8
    ConstraintDescription: Must be at least 8 characters
  DevMode:
    Description: "Enable dev mode for easy stack teardown. WARNING: All resources will be destroyed on stack deletion. Do not enable in production."
    Type: String
    Default: "false"
    AllowedValues:
      - "true"
      - "false"
  VpcCidr:
    Type: String
    Default: '10.100.0.0/16'
    Description: CIDR block for the Grafana VPC
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR block
  PublicSubnetACidr:
    Type: String
    Default: '10.100.0.0/24'
    Description: CIDR block for public subnet A
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR block
  PublicSubnetBCidr:
    Type: String
    Default: '10.100.1.0/24'
    Description: CIDR block for public subnet B
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR block
  InstanceType:
    Type: String
    Default: t3.medium
    Description: EC2 instance type for the Grafana server
    AllowedValues:
      - t3.medium
      - t3.large
      - t3.xlarge
      - m5.large
      - m5.xlarge
  MaxVideoWorkers:
    Description: "Maximum number of video processing EC2 instances in the Auto Scaling Group (only used when MediaInfo is enabled)"
    Type: Number
    Default: 2
    MinValue: 1
    MaxValue: 10
  VideoWorkerInstanceType:
    Description: "EC2 instance type for video processing workers (only used when MediaInfo is enabled). Uses Spot instances for cost savings."
    Type: String
    Default: t3.small
    AllowedValues:
      - t3.small
      - t3.medium
      - t3.large
  ScheduleExpression:
    Description: "Schedule for the daily data processing pipeline (e.g., rate(1 day), rate(12 hours), or a cron expression)"
    Type: String
    Default: "rate(1 day)"
  AllowedCidrBlock:
    Type: String
    Description: "Please set CIDR to x.x.x.x/32 to allow one specific IP address access, 0.0.0.0/0 to allow all IP addresses access, or another CIDR range."
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}/\d{1,2}$'
    ConstraintDescription: Must be a valid CIDR block
  AlertEmail:
    Description: "(Optional) Email address to receive alerts when ETL pipeline jobs fail. Leave empty to disable."
    Type: String
    Default: ""
  MPS3BucketName:
    Type: String
    Description: >-
      S3 bucket name for the nested CloudFormation templates.
      Provided automatically by AWS Marketplace.
    Default: 'a-project-marketplace-assets-394125495069'
  MPS3BucketRegion:
    Type: String
    Description: >-
      AWS region of the S3 bucket hosting nested templates.
      Provided automatically by AWS Marketplace.
    Default: 'us-east-1'
  MPS3KeyPrefix:
    Type: String
    Description: >-
      S3 key prefix for nested CloudFormation templates.
      Provided automatically by AWS Marketplace.
    Default: ''
  TagProject:
    Type: String
    Description: >-
      Optional. Value for the Project tag on nested stacks (cost allocation, etc.).
      Leave empty to omit. Use only letters, numbers, spaces, and _.:/=+-@ — ampersands,
      commas, pipes, etc. are rejected so tagging stays valid for S3, IAM, and CloudFormation.
    Default: ''
    MaxLength: 128
    AllowedPattern: '^([a-zA-Z0-9 _.:/=+@-]{0,128})$'
    ConstraintDescription: >-
      Letters, numbers, spaces, and _.:/=+-@ only; leave empty to omit (no &, commas, etc.).
  TagOwner:
    Type: String
    Description: >-
      Optional. Value for the Owner tag on nested stacks. Leave empty to omit.
      Same character rules as TagProject (AWS tag-safe across S3, IAM, CloudFormation).
    Default: ''
    MaxLength: 128
    AllowedPattern: '^([a-zA-Z0-9 _.:/=+@-]{0,128})$'
    ConstraintDescription: >-
      Letters, numbers, spaces, and _.:/=+-@ only; leave empty to omit (no &, commas, etc.).

Conditions:
  HasTagProject: !Not [!Equals [!Ref TagProject, ""]]
  HasTagOwner: !Not [!Equals [!Ref TagOwner, ""]]

Resources:
  S3ObjectsTransformer:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: !Sub 'https://${MPS3BucketName}.s3.${MPS3BucketRegion}.${AWS::URLSuffix}/${MPS3KeyPrefix}s3-object-transformer.yaml'
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName} - S3 object transformer stack'
        - !If
          - HasTagProject
          - Key: Project
            Value: !Ref TagProject
          - !Ref AWS::NoValue
        - !If
          - HasTagOwner
          - Key: Owner
            Value: !Ref TagOwner
          - !Ref AWS::NoValue
  Infrastructure:
    Type: AWS::CloudFormation::Stack
    DependsOn: S3ObjectsTransformer
    Properties:
      TemplateURL: !Sub 'https://${MPS3BucketName}.s3.${MPS3BucketRegion}.${AWS::URLSuffix}/${MPS3KeyPrefix}cloudfront-infrastructure.yaml'
      Tags:
        - Key: Name
          Value: !Sub '${AWS::StackName} - CloudFront analytics stack'
        - !If
          - HasTagProject
          - Key: Project
            Value: !Ref TagProject
          - !Ref AWS::NoValue
        - !If
          - HasTagOwner
          - Key: Owner
            Value: !Ref TagOwner
          - !Ref AWS::NoValue
      Parameters:
        AmiId: !Ref AmiId
        CloudFrontOriginBucketName: !Ref CloudFrontOriginBucketName
        CloudFrontLogsBucketName: !Ref CloudFrontLogsBucketName
        CloudFrontLogsKeyPrefix: !Ref CloudFrontLogsKeyPrefix
        WafLogsBucketName: !Ref WafLogsBucketName
        WafLogsKeyPrefix: !Ref WafLogsKeyPrefix
        StackName: !Ref AWS::StackName
        EnableMediaInfo: !Ref EnableMediaInfo
        CertificateArn: !Ref CertificateArn
        GrafanaAdminPassword: !Ref GrafanaAdminPassword
        DevMode: !Ref DevMode
        VpcCidr: !Ref VpcCidr
        PublicSubnetACidr: !Ref PublicSubnetACidr
        PublicSubnetBCidr: !Ref PublicSubnetBCidr
        InstanceType: !Ref InstanceType
        AllowedCidrBlock: !Ref AllowedCidrBlock
        ResourceFunctionArn: !GetAtt S3ObjectsTransformer.Outputs.ResourceFunctionArn
        MaxVideoWorkers: !Ref MaxVideoWorkers
        VideoWorkerInstanceType: !Ref VideoWorkerInstanceType
        ScheduleExpression: !Ref ScheduleExpression
        AlertEmail: !Ref AlertEmail

Outputs:
  GrafanaDashboardUrl:
    Description: URL to access the Grafana dashboard
    Value: !GetAtt Infrastructure.Outputs.GrafanaDashboardUrl
  StackRegion:
    Description: AWS Region where the stack is deployed
    Value: !Ref AWS::Region
