AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Innovator Island - Theme park backend # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 5 Resources: InitStateFunction: Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Properties: CodeUri: getInitState/ Handler: app.lambdaHandler MemorySize: 128 Runtime: nodejs16.x Environment: Variables: DDB_TABLE_NAME: !Ref DynamoDBTable Policies: - DynamoDBCrudPolicy: TableName: !Ref DynamoDBTable Events: InitStateAPI: Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api Properties: Path: /InitState Method: get S3UploaderFunction: # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction Type: AWS::Serverless::Function Properties: CodeUri: getUploadURL/ Handler: app.handler MemorySize: 128 Runtime: nodejs16.x Environment: Variables: UploadBucket: !Ref UploadBucket Policies: - S3CrudPolicy: BucketName: !Ref UploadBucket Events: HttpPost: Type: Api Properties: Path: '/Upload' Method: get ########################################## # Application's DynamoDB table # ########################################## DynamoDBTable: Type: AWS::DynamoDB::Table Properties: AttributeDefinitions: - AttributeName: partitionKey AttributeType: S - AttributeName: sortKey AttributeType: S KeySchema: - AttributeName: partitionKey KeyType: HASH - AttributeName: sortKey KeyType: RANGE BillingMode: PROVISIONED ProvisionedThroughput: ReadCapacityUnits: 2 WriteCapacityUnits: 2 ########################################## # S3 Buckets # ########################################## UploadBucket: Type: 'AWS::S3::Bucket' Properties: CorsConfiguration: CorsRules: - AllowedHeaders: - "*" AllowedMethods: - GET - PUT - POST - DELETE - HEAD AllowedOrigins: - "*" ProcessingBucket: Type: 'AWS::S3::Bucket' FinalBucket: Type: 'AWS::S3::Bucket' Properties: PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true ########################################## # Resources for realtime messaging # ########################################## ThemeParkRealtime: Type: "AWS::IoT::Thing" Properties: ThingName: "theme-park-realtime" AttributePayload: Attributes: {} UserPool: Type: "AWS::Cognito::UserPool" Properties: UserPoolName: ThemeParkUserPool MfaConfiguration: "OFF" Schema: - Name: email AttributeDataType: String Mutable: false Required: true # Creates a User Pool Client to be used by the identity pool UserPoolClient: Type: "AWS::Cognito::UserPoolClient" Properties: ClientName: ThemeParkUserPoolClient GenerateSecret: false UserPoolId: !Ref UserPool # Creates a federated Identity pool IdentityPool: Type: "AWS::Cognito::IdentityPool" Properties: IdentityPoolName: ThemeParkIdentityPool AllowUnauthenticatedIdentities: true CognitoIdentityProviders: - ClientId: !Ref UserPoolClient ProviderName: !GetAtt UserPool.ProviderName # Create a role for unauthorized access to AWS resources. CognitoUnAuthorizedRole: 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": unauthenticated Policies: - PolicyName: "CognitoAuthorizedPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "cognito-sync:*" Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ] - Effect: Allow Action: - iot:Connect Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/theme*" ] ] - Effect: Allow Action: - iot:Subscribe Resource: "*" - Effect: Allow Action: - iot:Receive Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ] # Create a role for authorized acces to AWS resources. 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 Policies: - PolicyName: "CognitoAuthorizedPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "cognito-sync:*" Resource: !Join [ "", [ "arn:aws:cognito-sync:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":identitypool/", !Ref IdentityPool] ] - Effect: Allow Action: - iot:Connect Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":client/theme*" ] ] - Effect: Allow Action: - iot:Subscribe Resource: "*" - Effect: Allow Action: - iot:Receive Resource: !Join [ "", [ "arn:aws:iot:", !Ref "AWS::Region", ":", !Ref "AWS::AccountId", ":topic/*" ] ] # Assigns the roles to the Identity Pool IdentityPoolRoleMapping: Type: "AWS::Cognito::IdentityPoolRoleAttachment" Properties: IdentityPoolId: !Ref IdentityPool Roles: authenticated: !GetAtt CognitoAuthorizedRole.Arn unauthenticated: !GetAtt CognitoUnAuthorizedRole.Arn ####################################################### # Resources for Custom Role for user Lambda functions # ####################################################### ThemeParkLambdaRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - lambda.amazonaws.com Policies: - PolicyName: ThemeParkAccess PolicyDocument: Version: 2012-10-17 Statement: - Effect: "Allow" Action: - "s3:*" Resource: - !Sub ${UploadBucket.Arn}/* - !Sub ${ProcessingBucket.Arn}/* - !Sub ${FinalBucket.Arn}/* - Effect: "Allow" Action: - "dynamodb:*" Resource: - !GetAtt DynamoDBTable.Arn - Effect: "Allow" Action: - "iot:*" Resource: - "*" - Effect: "Allow" Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: - "*" Final3BucketPolicy: Type: AWS::S3::BucketPolicy Properties: Bucket: !Ref FinalBucket PolicyDocument: Id: PolicyForCompositeFunction Version: "2012-10-17" Statement: - Sid: AllowCompositeFunction Effect: Allow Principal: Service: lambda.amazonaws.com Action: s3:* Resource: !Sub arn:aws:s3:::${FinalBucket}/* Condition: ArnLike: AWS:SourceArn: !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:theme-park-photos-CompositeFunction - Sid: AllowCloudFrontServicePrincipal Effect: Allow Principal: Service: cloudfront.amazonaws.com Action: s3:GetObject Resource: !Sub arn:aws:s3:::${FinalBucket}/* Condition: StringEquals: AWS:SourceArn: !Sub arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution} WebAppOriginAccessControl: Type: AWS::CloudFront::OriginAccessControl Properties: OriginAccessControlConfig: Name: !Sub "${AWS::StackName}-web-app-origin-access-control" OriginAccessControlOriginType: s3 SigningBehavior: always SigningProtocol: sigv4 CloudFrontDistribution: Type: AWS::CloudFront::Distribution Properties: DistributionConfig: Origins: - DomainName: !Sub ${FinalBucket}.s3.${AWS::Region}.amazonaws.com Id: !Sub ${FinalBucket}.s3.${AWS::Region}.amazonaws.com OriginAccessControlId: !GetAtt WebAppOriginAccessControl.Id S3OriginConfig: {} Enabled: "true" DefaultCacheBehavior: AllowedMethods: - GET - HEAD - OPTIONS TargetOriginId: !Sub ${FinalBucket}.s3.${AWS::Region}.amazonaws.com CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized OriginRequestPolicyId: 88a5eaf4-2fd4-4709-b370-b4c650ea3fcf # CORS-S3Origin ViewerProtocolPolicy: redirect-to-https PriceClass: PriceClass_All Outputs: # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function # Find out more about other implicit resources you can reference within SAM # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api InitStateApi: Description: "API Gateway endpoint URL for Prod stage for Init State function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/InitState/" UploadApi: Description: "API Gateway endpoint URL for upload function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/Upload" IdentityPoolId: Value: !Ref IdentityPool Export: Name: "IdentityPool::Id" IotEndpoint: Value: !Ref ThemeParkRealtime UploadBucketName: Value: !Ref UploadBucket ProcessingBucketName: Value: !Ref ProcessingBucket FinalBucketName: Value: !Ref FinalBucket WebAppDomain: Value: !GetAtt CloudFrontDistribution.DomainName CloudFrontDistributionId: Value: !GetAtt CloudFrontDistribution.Id