AWSTemplateFormatVersion: 2010-09-09
Parameters:
  SelectedVolumeMetrics:
    Type: CommaDelimitedList
    Default: DataReadOperationTime,DataWriteOperationTime
    AllowedValues:
      - DataReadBytes
      - DataWriteBytes
      - DataReadOperations
      - DataWriteOperations
      - MetadataOperations
      - DataReadOperationTime
      - DataWriteOperationTime
      - MetadataOperationTime
      - CapacityPoolReadBytes
      - CapacityPoolReadOperations
      - CapacityPoolWriteBytes
      - CapacityPoolWriteOperations
      - StorageCapacity
      - StorageUsed
      - StorageCapacityUtilization
      - FilesUsed
      - FilesCapacity
      - ''
    Description: Select the volume metrics you want to monitor for your FSx NetApp ONTAP volumes (comma-separated list).
  
  SelectedVolumeMetricsThresholds:
    Type: CommaDelimitedList
    Default: 30,30
    Description: Select the volume metric thresholds you want to use for monitoring, in the same order as the metrics selected above. 
  
  SelectedVolumeMetricsOperator:
    Type: CommaDelimitedList
    Default: GreaterThanThreshold,GreaterThanThreshold
    AllowedValues:
      - GreaterThanOrEqualToThreshold
      - GreaterThanThreshold
      - LessThanThreshold
      - LessThanOrEqualToThreshold
      - LessThanLowerOrGreaterThanUpperThreshold
      - LessThanLowerThreshold
      - GreaterThanUpperThreshold
      - ''
    Description: Select the volume metric operator you want to use for monitoring, in the same order as the metrics selected above.
  
  SelectedVolumeMetricsPeriod:
    Type: CommaDelimitedList
    Default: 60,60
    Description: Select the volume metric period you want to use for monitoring, in the same order as the metrics selected above.

  SelectedVolumeMetricsStatistic:
    Type: CommaDelimitedList
    Default: Average,Average
    Description: Select the volume metric statistic you want to use for monitoring, in the same order as the metrics selected above.

  SelectedFileSystemMetrics:
    Type: CommaDelimitedList
    Default: StorageUsed,CPUUtilization
    AllowedValues:
      - NetworkThroughputUtilization
      - NetworkSentBytes
      - NetworkReceivedBytes
      - DataReadBytes
      - DataWriteBytes
      - DataReadOperations
      - DataWriteOperations
      - MetadataOperations
      - DataReadOperationTime
      - DataWriteOperationTime
      - CapacityPoolReadBytes
      - CapacityPoolReadOperations
      - CapacityPoolWriteBytes
      - CapacityPoolWriteOperations
      - CPUUtilization
      - FileServerDiskThroughputUtilization
      - FileServerDiskThroughputBalance
      - FileServerDiskIopsBalance
      - FileServerDiskIopsUtilization
      - FileServerCacheHitRatio
      - DiskReadBytes
      - DiskWriteBytes
      - DiskIopsUtilization
      - DiskReadOperations
      - DiskWriteOperations
      - StorageEfficiencySavings
      - StorageCapacityUtilization
      - StorageUsed
      - LogicalDataStored
      - ''
    Description: Select the file system metrics you want to monitor for your FSx NetApp ONTAP file systems (comma-separated list).

  SelectedFileSystemMetricsThresholds:
    Type: CommaDelimitedList
    Default: 100000000,80
    Description: Select the file system metric thresholds you want to use for monitoring, in the same order as the metrics selected above. 

  SelectedFileSystemMetricsOperator:
    Type: CommaDelimitedList
    Default: GreaterThanThreshold,GreaterThanThreshold
    AllowedValues:
      - GreaterThanOrEqualToThreshold
      - GreaterThanThreshold
      - LessThanThreshold
      - LessThanOrEqualToThreshold
      - LessThanLowerOrGreaterThanUpperThreshold
      - LessThanLowerThreshold
      - GreaterThanUpperThreshold
      - ''
    Description: Select the file system metric operator you want to use for monitoring, in the same order as the metrics selected above. 
  
  SelectedFileSystemMetricsPeriod:
    Type: CommaDelimitedList
    Default: 60,60
    Description: Select the file system metric period you want to use for monitoring, in the same order as the metrics selected above.

  SelectedFileSystemMetricsStatistic:
    Type: CommaDelimitedList
    Default: Average,Average
    Description: Select the file system metric statistic you want to use for monitoring, in the same order as the metrics selected above.

Resources:
  InvokeLambda:
    Type: AWS::CloudFormation::CustomResource
    Version: "1.0"
    Properties:
      ServiceToken: !GetAtt LambdaFunction.Arn
      FunctionName: FSx-Storage-Monitoring-Solution
    DependsOn: LambdaIAMPolicy
  SNSTopic:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName: FSx-Monitoring-Solution
      KmsMasterKeyId: !Ref KMSKey
  LambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      Code:
        ZipFile: |
          import sys
          import botocore
          import boto3
          import json
          import math
          import os
          import cfnresponse

          def lambda_handler(event, context):
              requestType = event['RequestType']
              if requestType == "Create":
                  # Get the AWS region from the environment variable
                  aws_region = os.environ['AWS_REGION']

                  # Get environment variables, or set them to empty strings if not provided
                  SelectedFileSystemMetrics = os.environ.get("SelectedFileSystemMetrics", "")
                  SelectedVolumeMetrics = os.environ.get("SelectedVolumeMetrics", "")
                  SelectedFileSystemMetricsThresholds = os.environ.get("SelectedFileSystemMetricsThresholds", "").split(',')
                  SelectedVolumeMetricsThresholds = os.environ.get("SelectedVolumeMetricsThresholds", "").split(',')
                  SelectedFileSystemMetricsOperator = os.environ.get("SelectedFileSystemMetricsOperator", "").split(',')
                  SelectedVolumeMetricsOperator = os.environ.get("SelectedVolumeMetricsOperator", "").split(',')
                  SelectedFileSystemMetricsPeriod = os.environ.get("SelectedFileSystemMetricsPeriod", "").split(',')
                  SelectedFileSystemMetricsStatistic = os.environ.get("SelectedFileSystemMetricsStatistic", "").split(',')
                  SelectedVolumeMetricsPeriod = os.environ.get("SelectedVolumeMetricsPeriod", "").split(',')
                  SelectedVolumeMetricsStatistic = os.environ.get("SelectedVolumeMetricsStatistic", "").split(',')
                  

                  # Construct the SNS topic ARN for FSx Monitoring Solution
                  prefix = 'arn:aws:sns:'
                  suffix = ':FSx-Monitoring-Solution'
                  account_id = context.invoked_function_arn.split(":")[4]
                  topic_arn = prefix + aws_region + ':' + account_id + suffix

                  # Create clients for CloudWatch and FSx
                  cloudwatch_client = boto3.client('cloudwatch')
                  fsx_client = boto3.client('fsx')

                  # Get the list of FSx volumes
                  response = fsx_client.describe_volumes()
                  volumes = response.get('Volumes', [])

                  # Get the list of FSx file systems
                  response = fsx_client.describe_file_systems()
                  file_systems = response.get('FileSystems', [])

                  # Send CloudFormation Success
                  cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData = {})


                  # Iterate over each FSx volume
                  for volume in volumes:
                      file_system_id = volume.get('FileSystemId')
                      volume_id = volume.get('VolumeId')
                      volume_type = volume.get('VolumeType')

                      # If the volume type is ONTAP, set up the CloudWatch alarms for the selected volume metrics
                      if volume_type == 'ONTAP' and SelectedVolumeMetrics:
                          evaluation_periods = 1
                          counter = int(-1)

                          for selected_metric in SelectedVolumeMetrics.split(','):
                              # Add one to counter
                              counter = counter + 1

                              # Create the CloudWatch alarm for the selected volume metric
                              response = cloudwatch_client.put_metric_alarm(
                                  AlarmName=f'{selected_metric} Alarm for {volume_id}',
                                  AlarmActions=[topic_arn],
                                  OKActions=[topic_arn],
                                  AlarmDescription=f'This CloudWatch alarm monitors {selected_metric} for the volume {volume_id}.',
                                  EvaluationPeriods=evaluation_periods,
                                  Dimensions = [
                                                  {'Name': 'VolumeId', 'Value': volume_id},
                                                  {'Name': 'FileSystemId', 'Value': file_system_id}
                                  ],
                                  MetricName=selected_metric,
                                  Namespace = 'AWS/FSx',
                                  ComparisonOperator=SelectedVolumeMetricsOperator[counter],
                                  Threshold=float(SelectedVolumeMetricsThresholds[counter]),
                                  Statistic=SelectedVolumeMetricsStatistic[counter],
                                  Period=int(SelectedVolumeMetricsPeriod[counter])
                              )

                  # Iterate over each FSx file system
                  for file_system in file_systems:
                      file_system_id = file_system.get('FileSystemId')
                      file_system_type = file_system.get('FileSystemType')

                      # Set up the CloudWatch alarms for the selected file system metrics
                      if file_system_type == 'ONTAP' and SelectedFileSystemMetrics:
                          evaluation_periods = 1
                          counter = int(-1)

                          for selected_metric in SelectedFileSystemMetrics.split(','):
                              # Add one to counter
                              counter = counter + 1

                              # Create the CloudWatch alarm for the selected file system metric
                              response = cloudwatch_client.put_metric_alarm(
                                  AlarmName=f'{selected_metric} Alarm for {file_system_id}',
                                  AlarmActions=[topic_arn],
                                  OKActions=[topic_arn],
                                  AlarmDescription=f'This CloudWatch alarm monitors {selected_metric} for the file system {file_system_id}.',
                                  EvaluationPeriods=evaluation_periods,
                                  Dimensions=[
                                      {
                                          'Name': 'FileSystemId',
                                          'Value': file_system_id
                                      },
                                  ],
                                  MetricName=selected_metric,
                                  Namespace = 'AWS/FSx',
                                  ComparisonOperator=SelectedFileSystemMetricsOperator[counter],
                                  Threshold=float(SelectedFileSystemMetricsThresholds[counter]),
                                  Period=int(SelectedFileSystemMetricsPeriod[counter]),
                                  Statistic=SelectedFileSystemMetricsStatistic[counter]
                              )
              else:
                  response=cfnresponse.send(event, context, "SUCCESS", responseData={})
                  return(response)

      Role: !GetAtt LambdaExecutionRole.Arn
      Handler: index.lambda_handler
      FunctionName: FSx-Storage-Monitoring-Solution
      Runtime: python3.12
      Timeout: 900
      Environment:
        Variables:
          SelectedVolumeMetrics: !Join [',', !Ref 'SelectedVolumeMetrics']
          SelectedFileSystemMetrics: !Join [',', !Ref 'SelectedFileSystemMetrics']
          SelectedVolumeMetricsThresholds: !Join [',', !Ref 'SelectedVolumeMetricsThresholds']
          SelectedFileSystemMetricsThresholds: !Join [',', !Ref 'SelectedFileSystemMetricsThresholds']
          SelectedVolumeMetricsOperator: !Join [',', !Ref 'SelectedVolumeMetricsOperator']
          SelectedFileSystemMetricsOperator: !Join [',', !Ref 'SelectedFileSystemMetricsOperator']
          SelectedFileSystemMetricsPeriod: !Join [',', !Ref 'SelectedFileSystemMetricsPeriod']
          SelectedVolumeMetricsPeriod: !Join [',', !Ref 'SelectedVolumeMetricsPeriod']
          SelectedFileSystemMetricsStatistic: !Join [',', !Ref 'SelectedFileSystemMetricsStatistic']
          SelectedVolumeMetricsStatistic: !Join [',', !Ref 'SelectedVolumeMetricsStatistic']
    DependsOn:
      - SNSTopic
  LambdaExecutionRole:
    Type: 'AWS::IAM::Role'
    Properties:
      RoleName: !Join [ '', [ 'LambdaExecutionRoleFSxMonitoringNetAppONTAP', !Ref 'AWS::StackName' ] ]
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: 'sts:AssumeRole'
  LambdaIAMPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: !Join [ '', [ 'LambdaPolicyFSxMonitoringNetAppONTAP', !Ref 'AWS::StackName' ] ]
      Roles:
        - !Join [ '', [ 'LambdaExecutionRoleFSxMonitoringNetAppONTAP', !Ref 'AWS::StackName' ] ]
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          Effect: Allow
          Action:
            - 'logs:CreateLogGroup'
            - 'logs:CreateLogStream'
            - 'logs:PutLogEvents'
            - 'fsx:DescribeVolumes'
            - 'cloudwatch:PutMetricAlarm'
            - 'fsx:DescribeFileSystems'
          Resource: '*'
    DependsOn:
      - LambdaExecutionRole
  KMSKey: 
    Type: AWS::KMS::Key
    Properties:
      Description: 'This KMS Key will be used for an FSx Monitoring Solution using CloudWatch and SNS.'
      EnableKeyRotation: true
      KeyPolicy:
        Version: 2012-10-17
        Id: key-policy-for-fsx-monitoring-solution
        Statement:
          - Sid: Allow Root User Permissions to Key
            Effect: Allow
            Principal: 
              AWS: !Join [ '', ['arn:aws:iam::', !Ref "AWS::AccountId", ':root']]
            Action:
              - 'kms:*'
            Resource: '*'
          - Sid: Allow CloudWatch Access
            Effect: Allow
            Principal:
              Service: cloudwatch.amazonaws.com
            Action:
            - kms:GenerateDataKey*
            - kms:Decrypt
            Resource: '*'
            
