AWSTemplateFormatVersion: 2010-09-09
Parameters:
  StorageGatewayID:
    Type: String
    Description: >-
      Enter the Storage Gateway ID thats needs to be monitored. For example,
      sgw-1234E67A
    AllowedPattern: ^sgw-.*
  EmailAddress:
    Type: String
    Description: >-
      Provide email address to be notified of storage gateway status when cloudwatch alarm is set off. This email address should be verified.
    AllowedPattern: ^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$
Resources:
  SGWStateCheckerLambdaIAMRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action: 'sts:AssumeRole'
            Principal:
              Service: lambda.amazonaws.com
      Description: This IAM Role is used by Lambda for SGW-State-Checker
      Path: /
      RoleName: !Sub 'SGWStateCheckerRole${AWS::StackName}'
      Tags:
        - Key: Name
          Value: !Sub 'SGWStateCheckerRole${AWS::StackName}'
  SGWStateCheckerLambdaIAMPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: !Sub 'LambdaPermissionSGWStateChecker-${AWS::StackName}'
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - 'logs:CreateLogGroup'
            Resource: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*'
          - Effect: Allow
            Action:
              - 'logs:CreateLogStream'
              - 'logs:PutLogEvents'
            Resource: !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/SGWStateChecker*
          - Effect: Allow
            Action:
              - 'storagegateway:DescribeGatewayInformation'
            Resource: !Sub 'arn:aws:storagegateway:${AWS::Region}:${AWS::AccountId}:gateway/*'
          - Effect: Allow
            Action:
              - 'cloudwatch:PutMetricData'
            Condition:
              StringEquals:
                'cloudwatch:namespace': StorageGatewayCustom
            Resource: '*'
      Roles:
        - !Ref SGWStateCheckerLambdaIAMRole
  SGWStateCheckerLambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
      FunctionName: !Sub 'SGWStateChecker-${AWS::StackName}'
      Code:
        ZipFile: |
          import boto3
          import os
       
          sgw_client = boto3.client('storagegateway')
          cloudwatch_client = boto3.client('cloudwatch')

          def lambda_handler(event, context):
            gateway_id = event['gateway_id']
            account_number = event['account_number']
            region = os.environ['AWS_REGION']
            gateway_arn = f"arn:aws:storagegateway:{region}:{account_number}:gateway/{gateway_id}"
            gateway_state = get_gateway_state(gateway_arn)
            metric_value = 1.0 if get_gateway_state(gateway_arn) == 'RUNNING' else 0.0
            cw_dimensions = [{'Name': 'GatewayState', 'Value': 'RUNNING'}, {'Name': 'GatewayID', 'Value': gateway_id}]
            cloudwatch_client.put_metric_data(
              Namespace='StorageGatewayCustom',
              MetricData=[{'MetricName': 'GatewayInfo', 'Value': metric_value, 'Dimensions': cw_dimensions}]
            )
          def get_gateway_state(gateway_arn):
            try:
              sgw_info = sgw_client.describe_gateway_information(GatewayARN=gateway_arn)
              return sgw_info['GatewayState']
            except sgw_client.exceptions.InvalidGatewayRequestException as err:
              if err.response['message'] == "The specified gateway is not connected.":
                return 'OFFLINE'
              raise err
      Handler: index.lambda_handler
      Runtime: python3.12
      Timeout: '10'
      Description: This Lambda function for SGWStateChecker
      ReservedConcurrentExecutions: 1
      Role: !GetAtt 
        - SGWStateCheckerLambdaIAMRole
        - Arn
  SGWStateCheckerEventBridgeRule:
    Type: 'AWS::Events::Rule'
    Properties:
      Description: >-
        This Eventbridge rule is used for SGW state checked - used for
        monitoring the status of Storage Gateway
      Name: !Sub 'SGWStateCheckerEvent-${AWS::StackName}'
      ScheduleExpression: rate(1 minute)
      State: ENABLED
      Targets:
        - Arn: !GetAtt
            - SGWStateCheckerLambdaFunction
            - Arn
          Id: SGWStateCheckerLambdaFunction
          Input: !Sub  
            - |
              {
                "gateway_id": "${GatewayID}",
                "account_number": "${AWS::AccountId}"
              }
            - GatewayID: !Ref StorageGatewayID
  SGWStateCheckerLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn: 
      - SGWStateCheckerEventBridgeRule
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName: !Ref SGWStateCheckerLambdaFunction
      Principal: events.amazonaws.com
      SourceAccount: !Ref 'AWS::AccountId'
      SourceArn: !GetAtt SGWStateCheckerEventBridgeRule.Arn
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      FifoTopic: False
      DisplayName: SGW-State-Checker-SNS
      TopicName: SGW-State-Checker-SNS
  SNSSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Endpoint: !Ref EmailAddress
      Protocol: email-json
      TopicArn: !Ref SNSTopic
  SNSAccessPolicy:
    Type: AWS::SNS::TopicPolicy
    DependsOn: 
      - SNSTopic
    Properties: 
      PolicyDocument:
        Version: "2012-10-17" 
        Statement:
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "events.amazonaws.com"
                - "cloudwatch.amazonaws.com"
            Action: 
              - "sns:Publish"
            Resource: 
              - !Ref SNSTopic
      Topics: 
        - !Ref SNSTopic
  SGWStateCheckerCloudwatchAlarm:
    Type: 'AWS::CloudWatch::Alarm'
    DependsOn: 
      - SGWStateCheckerLambdaFunction
    Properties:
      ActionsEnabled: true
      AlarmDescription: >-
        This Cloudwatch alarm is used for setting off SNS notification if SGW
        goes to offline status
      AlarmName: SGWStatusCheckerAlarm
      Namespace: StorageGatewayCustom
      MetricName: GatewayInfo
      Dimensions:
        - Name: GatewayState
          Value: RUNNING
        - Name: GatewayID
          Value: !Ref StorageGatewayID
      Statistic: Average
      Period: '60'
      EvaluationPeriods: 5
      ComparisonOperator: LessThanThreshold
      DatapointsToAlarm: 5
      Threshold: 1
      TreatMissingData: missing
      AlarmActions:
        - !Ref SNSTopic
      OKActions:
        - !Ref SNSTopic
Outputs:
  SGWStateCheckerLambdaIAMRole:
    Description: Lambda Role created by Cloudformation for SGWStateChecker
    Value: !Ref SGWStateCheckerLambdaIAMRole
  SGWStateCheckerEventBridgeRule:
    Description: EventBridge Rule created for SGWStateChecker
    Value: !Ref SGWStateCheckerEventBridgeRule