# (c) 2020 Amazon Web Services, Inc. or its affiliates. All Rights Reserved. This AWS Content is provided subject to the terms of the AWS Customer
# Agreement available at https://aws.amazon.com/agreement/ or other written agreement between Customer and Amazon Web Services, Inc.

AWSTemplateFormatVersion: '2010-09-09'

Description:
  This template deploys VPC and the required roles and to run the EMR cluster.

Parameters:
  VPCName:
    Description: The VPC name will be prefixed to VPC resources
    Type: String
    Default: EMVPC

  VpcCIDR:
    Description: Please enter the IP range (CIDR notation) for this VPC
    Type: String
    Default: 10.192.0.0/16

  PublicSubnet1CIDR:
    Description: IP range (CIDR notation) for the public subnet
    Type: String
    Default: 10.192.10.0/24

  PrivateSubnet1CIDR:
    Description: IP range (CIDR notation) for the private subnet
    Type: String
    Default: 10.192.20.0/24

Resources:
    VPC:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: !Ref VpcCIDR
        EnableDnsSupport: true
        EnableDnsHostnames: true
        Tags:
          - Key: Name
            Value: !Ref VPCName
      
    InternetGateway:
      Type: AWS::EC2::InternetGateway
      Properties:
        Tags:
          - Key: Name
            Value: !Ref VPCName

    InternetGatewayAttachment:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
        InternetGatewayId: !Ref InternetGateway
        VpcId: !Ref VPC

    PublicSubnet1:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 0, !GetAZs '' ]
        CidrBlock: !Ref PublicSubnet1CIDR
        MapPublicIpOnLaunch: true
        Tags:
          - Key: Name
            Value: !Sub ${VPCName} Public Subnet 
    
    PrivateSubnet1:
      Type: AWS::EC2::Subnet
      Properties:
        VpcId: !Ref VPC
        AvailabilityZone: !Select [ 0, !GetAZs '' ]
        CidrBlock: !Ref PrivateSubnet1CIDR
        MapPublicIpOnLaunch: false
        Tags:
          - Key: Name
            Value: !Sub ${VPCName} Private Subnet 
    
    NatGatewayEIP:
      Type: AWS::EC2::EIP
      DependsOn: InternetGatewayAttachment
      Properties:
        Domain: vpc

    NatGateway:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId: !GetAtt NatGatewayEIP.AllocationId
        SubnetId: !Ref PublicSubnet1
    
    PublicRouteTable:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref VPC
        Tags:
          - Key: Name
            Value: !Sub ${VPCName} Public Route Table

    DefaultPublicRoute:
      Type: AWS::EC2::Route
      DependsOn: InternetGatewayAttachment
      Properties:
        RouteTableId: !Ref PublicRouteTable
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId: !Ref InternetGateway

    PublicSubnet1RouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref PublicRouteTable
        SubnetId: !Ref PublicSubnet1

    PrivateRouteTable1:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId: !Ref VPC
        Tags:
          - Key: Name
            Value: !Sub ${VPCName} Private Routes (AZ1)

    DefaultPrivateRoute1:
      Type: AWS::EC2::Route
      Properties:
        RouteTableId: !Ref PrivateRouteTable1
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId: !Ref NatGateway

    PrivateSubnet1RouteTableAssociation:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId: !Ref PrivateRouteTable1
        SubnetId: !Ref PrivateSubnet1

    SecurityGroup:
      Type: AWS::EC2::SecurityGroup
      Properties:
        GroupDescription: "Security group with no ingress rule"
        VpcId: !Ref VPC

    SecurityGroupIngress:
      Type: 'AWS::EC2::SecurityGroupIngress'
      Properties:
        GroupId: !GetAtt SecurityGroup.GroupId
        IpProtocol: '-1'
        SourceSecurityGroupId: !Ref SecurityGroup    

    SecurityGroupEgress:
      Type: 'AWS::EC2::SecurityGroupEgress'
      Properties:
        GroupId: !GetAtt SecurityGroup.GroupId
        IpProtocol: '-1'
        CidrIp: 0.0.0.0/0

    EMRAssetS3BucketEncryptionKey:
      Type: 'AWS::KMS::Key'
      Properties:
        Description: EMR Assets S3 bucket encryption KMS key
        EnableKeyRotation: true
        PendingWindowInDays: 20
        KeyPolicy:
          Version: 2012-10-17
          Statement:
            - Sid: Enable IAM User Permissions
              Effect: Allow
              Principal:
                AWS: !Join
                  - ''
                  - - 'arn:'
                    - !Ref 'AWS::Partition'
                    - ':iam::'
                    - !Ref 'AWS::AccountId'
                    - ':root'
              Action: 'kms:*'
              Resource: '*'
            - Sid: Allow service permissions
              Effect: Allow
              Principal:
                Service:
                  - kms.amazonaws.com
                  - lambda.amazonaws.com
                  - s3.amazonaws.com
                  - ec2.amazonaws.com
                  - elasticmapreduce.amazonaws.com
              Action:
                - 'kms:Decrypt'
                - 'kms:DescribeKey'
                - 'kms:Encrypt'
                - 'kms:GenerateDataKey*'
                - 'kms:ReEncrypt*'
              Resource: '*'

    EMRAssetsS3Bucket:
        Type: 'AWS::S3::Bucket'
        Properties:
          BucketName: !Sub 'spark-transient-emr-lambda-${AWS::Region}-${AWS::AccountId}'
          BucketEncryption:
            ServerSideEncryptionConfiguration:
              - ServerSideEncryptionByDefault:
                    SSEAlgorithm: 'aws:kms'
                    KMSMasterKeyID: !GetAtt EMRAssetS3BucketEncryptionKey.Arn
                BucketKeyEnabled: true
        DeletionPolicy: Delete

    EMRAssetS3BucketBucketPolicy:
      Type: 'AWS::S3::BucketPolicy'
      Properties:
        Bucket: !Ref EMRAssetsS3Bucket
        PolicyDocument:
          Statement:
            - Action: s3:*
              Condition:
                Bool:
                  'aws:SecureTransport': 'false'
              Effect: Deny
              Principal:
                AWS: '*'
              Resource:
                - !GetAtt
                  - EMRAssetsS3Bucket
                  - Arn
                - !Join
                  - ''
                  - - !GetAtt
                      - EMRAssetsS3Bucket
                      - Arn
                    - /*
          Version: 2012-10-17
    
    LambdaEMRExecutionRole: 
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Statement:
            - Action:
              - sts:AssumeRole
              Effect: Allow
              Principal:
                Service:
                - lambda.amazonaws.com
          Version: 2012-10-17
        Policies:
          - PolicyName : allow-encryption-operations
            PolicyDocument:
              Statement:
                - Action:
                    - kms:Decrypt
                    - kms:Encrypt
                    - kms:GenerateDataKey
                  Effect: Allow
                  Resource: !GetAtt
                    - EMRAssetS3BucketEncryptionKey
                    - Arn
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/AWSLambdaExecute
          - arn:aws:iam::aws:policy/AmazonElasticMapReduceFullAccess
          - arn:aws:iam::aws:policy/AmazonAthenaFullAccess
          - arn:aws:iam::aws:policy/AmazonS3FullAccess

    EMRRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Version: 2008-10-17
          Statement:
            - Sid: ''
              Effect: Allow
              Principal:
                Service: elasticmapreduce.amazonaws.com
              Action: 'sts:AssumeRole'
        Policies:
          - PolicyName : allow-encryption-operations
            PolicyDocument:
              Statement:
                - Action:
                    - kms:Decrypt
                    - kms:Encrypt
                    - kms:GenerateDataKey
                  Effect: Allow
                  Resource: !GetAtt
                    - EMRAssetS3BucketEncryptionKey
                    - Arn
        Path: /
        ManagedPolicyArns:
          - 'arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceRole'
    
    EMREC2Role:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Version: 2008-10-17
          Statement:
            - Sid: ''
              Effect: Allow
              Principal:
                Service: ec2.amazonaws.com
              Action: 'sts:AssumeRole'
        Policies:
          - PolicyName : allow-encryption-operations
            PolicyDocument:
              Statement:
                - Action:
                    - kms:Decrypt
                    - kms:Encrypt
                    - kms:GenerateDataKey
                  Effect: Allow
                  Resource: !GetAtt
                    - EMRAssetS3BucketEncryptionKey
                    - Arn
        Path: /
        ManagedPolicyArns:
          - 'arn:aws:iam::aws:policy/service-role/AmazonElasticMapReduceforEC2Role'
      
    EMREC2InstanceProfile:
      Type: AWS::IAM::InstanceProfile
      Properties:
        Path: /
        Roles:
          - !Ref EMREC2Role

Outputs:
  LambdaExecutionRole: 
    Description: Lambda role
    Value: !Ref LambdaEMRExecutionRole

  ServiceRole: 
    Description: EMR role name
    Value: !Ref EMRRole

  JobFlowRole: 
    Description: EC2 role name
    Value: !Ref EMREC2InstanceProfile

  Ec2SubnetId: 
    Description: Private subnet ID for EMR
    Value: !Ref PrivateSubnet1

  S3Bucket:
    Description: S3 bucket for EMR
    Value: !Ref EMRAssetsS3Bucket