AWSTemplateFormatVersion: '2010-09-09'

Description: >
  Script to create S3 bucket, DNS (Route53) and Cloudfront distribution.

###############################################################################
Parameters:
###############################################################################

  DomainName:
    Type: String
    Description: The domain name.
    Default: 'ordbok.aws.uib.no'
    AllowedPattern: (?!-)[a-zA-Z0-9-.]{1,63}(?<!-)
    ConstraintDescription: must be a valid DNS zone name
    
  DomainPrefix:
    Type: String
    Default: beta
    
  PriceClass:
    Type: String
    Description: The CloudFront distribution price class
    Default: 'PriceClass_100'
    AllowedValues:
      - 'PriceClass_100'
      #- 'PriceClass_200'
      #- 'PriceClass_All'

  SecretArn:
    Type: String
    
  CertificateArn:
    Type: String
    Default: ''

    
Conditions:
  UseDomain: !Not [!Or [!Equals [!Ref DomainName, ''], !Equals [!Ref CertificateArn, '']]]
  
###############################################################################
Resources:
###############################################################################

  DNS:
    Type: "AWS::Route53::RecordSet"
    Condition: UseDomain
    Properties:
      HostedZoneConfig:
        Comment: !Join ['', ['Hosted zone for ', !Ref 'DomainName']]
      HostedZoneName: !Join ['.', [!Ref DomainName, '']]
      Name: !Join ['.', [!Ref DomainPrefix, !Ref DomainName, '']]
      Type: A
      AliasTarget:
        HostedZoneId: xxxxxxxxxxx
        DNSName: !GetAtt Distribution.DomainName
      HostedZoneTags:
      - Key: Application
        Value: beta.ordbok.uib.no

        
  WebBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Sub "${AWS::StackName}.aws.uib.no"
      VersioningConfiguration:
        Status: Enabled
      AccessControl: !If
        - UseDomain
        - !Ref "AWS::NoValue"
        - "PublicRead"
      WebsiteConfiguration: !If
      - UseDomain
      - !Ref "AWS::NoValue"
      - IndexDocument: index.html
      Tags:
        - Key: Application
          Value: !Ref DomainName
  

  BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      PolicyDocument:
        Id: MyPolicy
        Version: 2012-10-17
        Statement:
          - Sid: PublicReadForGetBucketObjects
            Effect: Allow
            Principal: !If
              - UseDomain
              - CanonicalUser: !GetAtt CloudFrontOriginIdentity.S3CanonicalUserId
              - "*"
            Action: !If
              - UseDomain
              - 's3:*'
              - 's3:GetObject'
            Resource: !Join
              - ''
              - - 'arn:aws:s3:::'
                - !Ref WebBucket
                - /*
      Bucket: !Ref WebBucket
      
      
  ItaIpSet:
    Type: "AWS::WAF::IPSet"
    Condition: UseDomain
    Properties:
      IPSetDescriptors:
      - Type: "IPV4"
        Value: "129.177.0.0/16"
      Name: "allowed IPs"
  
  
  ITARule:
    Type: "AWS::WAF::Rule"
    Condition: UseDomain
    Properties:
      MetricName: "ITARule"
      Name: "ITARule"
      Predicates:
      - Type: "IPMatch"
        Negated: false
        DataId: !Ref ItaIpSet
        
        
  ACL:
    Type: "AWS::WAF::WebACL"
    Condition: UseDomain
    Properties:
      DefaultAction:
        Type: "BLOCK"
      Name: "intern ITA"
      MetricName: "WebACL"
      Rules:
        - Action:
            Type: "ALLOW"
          Priority: 1
          RuleId: !Ref ITARule
          
          
  Distribution:
    Type: "AWS::CloudFront::Distribution"
    Condition: UseDomain
    Properties:
      DistributionConfig:
        Enabled: true
        HttpVersion: http2
        PriceClass: !Ref PriceClass
        Enabled: 'true'
        DefaultRootObject: index.html
        Origins:
        - DomainName: !Sub "${WebBucket}.s3-${AWS::Region}.amazonaws.com"
          Id: s3ProductionBucket
          S3OriginConfig:
            OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${CloudFrontOriginIdentity}"
        Aliases:
        - !Join ['.', [!Ref DomainPrefix, !Ref DomainName]]
        CustomErrorResponses:
          - ErrorCachingMinTTL: 300
            ErrorCode: 403
            ResponseCode: 200
            ResponsePagePath: /index.html
          - ErrorCachingMinTTL: 300
            ErrorCode: 404
            ResponseCode: 200
            ResponsePagePath: /index.html
        DefaultCacheBehavior:
          AllowedMethods:
          - GET
          - HEAD
          Compress: true
          TargetOriginId: s3ProductionBucket
          ForwardedValues:
            QueryString: 'false'
            Cookies:
              Forward: none
          ViewerProtocolPolicy: redirect-to-https
        ViewerCertificate:
          AcmCertificateArn: !Ref CertificateArn
          MinimumProtocolVersion: TLSv1.1_2016
          SslSupportMethod: sni-only
        WebACLId: !If
          - UseDomain
          - !Ref ACL
          - !Ref "AWS::NoValue"

  CloudFrontOriginIdentity:
    Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity"
    Properties:
      CloudFrontOriginAccessIdentityConfig:
        Comment: "origin identity"
       
  XRayPolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      ManagedPolicyName: !Sub "${AWS::StackName}-XRayPolicy"
      PolicyDocument:
        Version: 2012-10-17
        Statement:
        - Action:
          - 'xray:PutTelemetryRecords'
          - 'xray:PutTraceSegments'
          Effect: Allow
          Resource: '*'
          
          
  S3LambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
        - Effect: "Allow"
          Principal:
            Service:
            - "lambda.amazonaws.com"
          Action:
          - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonS3FullAccess"
        - !Ref XRayPolicy
      Policies:
        - PolicyName: !Sub "${AWS::StackName}-SecretsPolicy"
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Action: 'secretsmanager:GetSecretValue'
                Effect: Allow
                Resource: !Ref SecretArn
      RoleName: !Sub "${AWS::StackName}-S3LambdaRole"

