AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: eth-demo — Lambda + Step Functions demo Parameters: Prefix: Type: String Default: 2026/04/ Description: >- S3 key prefix the function scans for PDFs. Matches the seed script's default. Override with --parameter-overrides Prefix=other/ to target a different prefix without changing the template. Trailing slash required. AllowedPattern: ".+/" ConstraintDescription: "must end with '/' (e.g. 2026/04/)" Globals: Function: Runtime: python3.13 Timeout: 30 MemorySize: 256 Architectures: [arm64] LoggingConfig: LogFormat: JSON Resources: ReportsBucket: Type: AWS::S3::Bucket Properties: BucketName: !Sub eth-demo-reports-${AWS::AccountId} LifecycleConfiguration: Rules: - Id: expire-manifests Status: Enabled Prefix: manifests/ ExpirationInDays: 1 SignPdfsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/eth-demo-sign-pdfs RetentionInDays: 7 DedupTable: Type: AWS::DynamoDB::Table Properties: TableName: !Sub ${AWS::StackName}-sign-pdfs-dedup BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: id AttributeType: S KeySchema: - AttributeName: id KeyType: HASH TimeToLiveSpecification: AttributeName: ttl Enabled: true PdfIndexTable: Type: AWS::DynamoDB::Table Properties: TableName: !Sub ${AWS::StackName}-pdf-index BillingMode: PAY_PER_REQUEST AttributeDefinitions: - AttributeName: key AttributeType: S KeySchema: - AttributeName: key KeyType: HASH ExtractMetadataLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/eth-demo-extract-metadata RetentionInDays: 7 ExtractMetadataFunction: Type: AWS::Serverless::Function Properties: FunctionName: eth-demo-extract-metadata CodeUri: functions/extract_metadata/ Handler: handler.handler Timeout: 60 MemorySize: 512 LoggingConfig: LogFormat: JSON LogGroup: !Ref ExtractMetadataLogGroup Environment: Variables: BUCKET_NAME: !Ref ReportsBucket Policies: - Statement: - Sid: ReadPdf Effect: Allow Action: s3:GetObject Resource: !Sub "${ReportsBucket.Arn}/*" ListPdfsLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: /aws/lambda/eth-demo-list-pdfs RetentionInDays: 7 ListPdfsFunction: Type: AWS::Serverless::Function Properties: FunctionName: eth-demo-list-pdfs CodeUri: functions/list_pdfs/ Handler: handler.handler LoggingConfig: LogFormat: JSON LogGroup: !Ref ListPdfsLogGroup Environment: Variables: BUCKET_NAME: !Ref ReportsBucket PREFIX: !Ref Prefix Policies: - Statement: - Sid: ListReportsBucket Effect: Allow Action: s3:ListBucket Resource: !GetAtt ReportsBucket.Arn PdfIndexStateMachineLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/vendedlogs/states/${AWS::StackName}-pdf-index RetentionInDays: 7 PdfIndexStateMachine: Type: AWS::Serverless::StateMachine Properties: Name: !Sub ${AWS::StackName}-pdf-index DefinitionUri: statemachines/pdf-index/definition.asl.json DefinitionSubstitutions: ListPdfsFunctionArn: !GetAtt ListPdfsFunction.Arn ExtractMetadataFunctionArn: !GetAtt ExtractMetadataFunction.Arn PdfIndexTableName: !Ref PdfIndexTable Logging: Destinations: - CloudWatchLogsLogGroup: LogGroupArn: !GetAtt PdfIndexStateMachineLogGroup.Arn IncludeExecutionData: true Level: ALL Policies: - LambdaInvokePolicy: FunctionName: !Ref ListPdfsFunction - LambdaInvokePolicy: FunctionName: !Ref ExtractMetadataFunction - DynamoDBWritePolicy: TableName: !Ref PdfIndexTable # SFN logging requires log-delivery API perms at account scope; SAM # doesn't auto-add these even with Logging configured. - Statement: - Effect: Allow Action: - logs:CreateLogDelivery - logs:GetLogDelivery - logs:UpdateLogDelivery - logs:DeleteLogDelivery - logs:ListLogDeliveries - logs:PutResourcePolicy - logs:DescribeResourcePolicies - logs:DescribeLogGroups Resource: "*" SignPdfsFunction: Type: AWS::Serverless::Function Properties: FunctionName: eth-demo-sign-pdfs CodeUri: functions/sign_pdfs/ Handler: handler.handler LoggingConfig: LogFormat: JSON LogGroup: !Ref SignPdfsLogGroup Environment: Variables: BUCKET_NAME: !Ref ReportsBucket PREFIX: !Ref Prefix URL_EXPIRY_SECONDS: "900" DEDUP_TABLE: !Ref DedupTable Policies: - Statement: - Sid: ListReportsBucket Effect: Allow Action: s3:ListBucket Resource: !GetAtt ReportsBucket.Arn - Sid: ReadReports Effect: Allow Action: s3:GetObject Resource: !Sub "${ReportsBucket.Arn}/*" - Sid: WriteManifests Effect: Allow Action: s3:PutObject Resource: !Sub "${ReportsBucket.Arn}/manifests/*" - Sid: DedupTableAccess Effect: Allow Action: - dynamodb:GetItem - dynamodb:PutItem Resource: !GetAtt DedupTable.Arn Outputs: ReportsBucketName: Description: S3 bucket for seed PDFs and manifest output Value: !Ref ReportsBucket SignPdfsFunctionArn: Description: ARN of the main Lambda Value: !GetAtt SignPdfsFunction.Arn