AWSTemplateFormatVersion: 2010-09-09

Description: Creates SNS Topic and Lambda Function to monitor FSx Health

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: SNS Subscription Endpoint
      Parameters:
        - EmailAddress

Parameters:
  EmailAddress:
    Description: The email address for alarm notification.
    Type: String
    AllowedPattern: ^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$

  CronSchedule:
    Type: String
    Description: The cron schedule (default - every 5 minutes)
    Default: "cron(0/5 * * * ? *)"


Resources:

  snsKey:
    Type: 'AWS::KMS::Key'
    DependsOn: LambdaExecutionRole
    Properties:
      EnableKeyRotation: true
      KeyPolicy:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal:
            AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
          Action: 'kms:*'
          Resource: '*'

        - Effect: Allow
          Principal:
            Service: !Sub 'sns.amazonaws.com'
          Action:
          - 'kms:Encrypt'
          - 'kms:Decrypt'
          - 'kms:ReEncrypt*'
          - 'kms:GenerateDataKey*'
          - 'kms:CreateGrant'
          - 'kms:ListGrants'
          - 'kms:DescribeKey'
          Resource: '*'

        - Effect: Allow
          Principal: 
            AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:role/${LambdaExecutionRole}'
          Action:
          - 'kms:Decrypt'
          - 'kms:GenerateDataKey*'
          Resource: '*'

  SNSTopic: 
    Type: AWS::SNS::Topic
    Properties: 
      DisplayName: !Join [ "-", ["fsx-health-sns-", !Ref "AWS::StackName" ]] 
      Subscription:
      - Endpoint: !Ref EmailAddress
        Protocol: "email"
      TopicName: !Join [ "-", ["fsx-health-sns-", !Ref "AWS::StackName" ]]
      KmsMasterKeyId: !Ref snsKey

  
  SNSTopicPolicy:
    Type: 'AWS::SNS::TopicPolicy'
    Properties:
      Topics:
        - !Ref SNSTopic
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action: 'sns:Publish'
            Resource: !Ref SNSTopic
            Principal:
              AWS: '*'
            Condition:
              ArnLike:
                AWS:SourceArn: !GetAtt CheckFileSystemStatus.Arn

  CheckFileSystemStatus:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: python3.8
      Timeout: 5
      Handler: index.lambda_handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Environment: 
        Variables: 
          LambdaSNSTopic: !Sub ${SNSTopic}

      Code:
        ZipFile: |
          import json
          import boto3
          import os

          def lambda_handler(event, context):
            LAMBDASNSTOPIC = os.environ['LambdaSNSTopic']

            fsx_windows = boto3.client('fsx')
            filesystems = fsx_windows.describe_file_systems()
            for filesystem in filesystems.get('FileSystems'):
                status = filesystem.get('Lifecycle')
                filesystem_id = filesystem.get('FileSystemId')
                sns_client = boto3.client('sns')
                if status != 'AVAILABLE':
                    print("The file system: {} needs attention.".format(filesystem_id))
                    sns_client.publish(TopicArn=LAMBDASNSTOPIC,Message="File System: " + filesystem_id + " needs attention. The status is: " + status, Subject = "FSx Health Warning!")
                else:
                    print("The file system: {} is in a healthy state, and is reachable and available for use.".format(filesystem_id))

            
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub FSx-Health-Lambda-Role-${AWS::StackName}-${AWS::Region}
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: Allow
          Principal: {Service: [lambda.amazonaws.com]}
          Action: ['sts:AssumeRole']
      Path: /
      Policies:
      - PolicyName: !Sub Lambda-Resource-Policy-${AWS::StackName}-${AWS::Region}
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            Resource: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*"
          - Effect: Allow
            Action:
            - fsx:DescribeFileSystems
            Resource: '*'
          - Effect: Allow
            Action:
            - sns:Publish
            Resource: "*"


  SNSEventRule: 
    Type: AWS::Events::Rule
    Properties: 
      Description: "ScheduledRule to invoke Lambda"
      ScheduleExpression: !Ref CronSchedule
      State: "ENABLED"
      Targets: 
        - 
          Arn: 
            Fn::GetAtt: 
              - "CheckFileSystemStatus"
              - "Arn"
          Id: "TargetFunctionV1"

  PermissionForEventsToInvokeLambda: 
    Type: AWS::Lambda::Permission
    Properties: 
      FunctionName: !Ref "CheckFileSystemStatus"
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: 
        Fn::GetAtt: 
          - "SNSEventRule"
          - "Arn"
