AWSTemplateFormatVersion: 2010-09-09

# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Description: Configure custom snapshot schedule for your Amazon FSx for OpenZFS file system volumes.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: FSx Custom Snapshot Scheduler Parameters
      Parameters:
        - ResourceID
        - CronSchedule
        - RetainDays
        - SnapshotName
    - Label:
        default: Snapshot Notifications
      Parameters:
        - Email
        - SuccessNotification
        
    ParameterLabels:
      FileSystemIds:
        default: "Filesystem ID or Volume ID"
      CronSchedule:
        default: "CRON schedule pattern for snapshots (default is every 6 hours)"
      RetainDays:
        default: "Snapshot retention (days)"
      SnapshotName:
        default: "Name for snapshots"
      SuccessNotification:
        default: "Snapshot Notification (Yes/No)"
      Email: 
        default: "Email address"

Parameters:
  # The FSx for OpenZFS file system IDs for which you want to manage a custom snapshot schedule
  ResourceID:
    Description: Amazon FSx for OpenZFS file system ID or Volume ID
    Type: String
    AllowedPattern: "^fs(vol-|-)[A-Za-z0-9]+"
  # Schedule for creating snapshots (and purging expired)
  CronSchedule:
    Description: CRON schedule for snapshots (default every 6 hours)
    Type: String
    Default: "0 0/6 * * ? *"

  # Number of days of snapshots you want to retain
  RetainDays:
    Description: Number of days to retain custom-scheduled snapshots
    Type: Number
    Default: 7
  # Email for notifications
  Email:
    Description: Email for snapshot notifications
    Type: String
  # Value of snapshot Name (shows in Name column in snapshots list in console)
  SnapshotName:
    Description: The name of snapshots (shown in FSx console)
    Type: String
    Default: "User-scheduled_snapshot"
    AllowedPattern: "^[a-zA-Z0-9_:.-]{1,179}$"
  # If customer wants notification for successful snapshots
  SuccessNotification:
    Description: Do you want to be notified for successful snapshots? *for failure, you will always be notified
    Type: String
    AllowedValues:
      - "Yes"
      - "No"
    Default: "Yes"

Resources:
  SnsKey:
    Type: 'AWS::KMS::Key'
    Properties:
      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:
            Service: !Sub 'cloudwatch.amazonaws.com'
          Action:
          - 'kms:Decrypt'
          - 'kms:GenerateDataKey*'
          Resource: '*'

        - Effect: Allow
          Principal: 
            Service: !Sub 'lambda.amazonaws.com'
          Action:
          - 'kms:Decrypt'
          - 'kms:GenerateDataKey*'
          Resource: '*'

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

  #
  # Serverless resources
  # [SnapshotManager]
  #
  SnapshotManager:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}
      Description: FSx Scheduled Snapshot Manager creates snapshots and deletes expired snapshots
      Environment:
        Variables:
          snapshot_retention_days: !Sub ${RetainDays}
          topic_arn: !Sub ${SNSTopic}
          snapshot_name: !Sub ${SnapshotName}
      Handler: lambda_function.lambda_handler
      Role: !Sub ${SnapshotManagerRole.Arn}
      Code:
         S3Bucket: !Sub solution-references-${AWS::Region}
         S3Key: 'fsx/snapshot/OpenZfsSnapshotManager.zip'
      Runtime: python3.9
      Timeout: 300

  #
  # Cloudwatch events/rule resources
  # [FSxSnapshotStartEvent, SnapshotStartEventLambdaPermission]
  #
  FSxSnapshotStartEvent:
    Type: AWS::Events::Rule
    Properties:
      Description: Schedule to run FSx user-initiated snapshot and purge of snapshots older than retention period
      ScheduleExpression: !Sub 'cron(${CronSchedule})'
      State: ENABLED
      Targets:
        - Arn: !Sub ${SnapshotManager.Arn}
          Id: SnapshotManagerEvent1
          Input: !Sub '{"start-snapshot":"true","purge-snapshots":"true","resource-id":"${ResourceID}","notify_on_success":"${SuccessNotification}"}'

  SnapshotStartEventLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Sub ${SnapshotManager}
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com

  #
  # Notification resources
  # [SNSTopic]
  #
  SNSTopic: 
    DependsOn: SnsKey
    Type: AWS::SNS::Topic
    Properties: 
      DisplayName: !Sub ${AWS::StackName}-custom-snapshot-notification
      Subscription:
      - Endpoint: !Ref Email
        Protocol: "email"
      TopicName: !Sub ${AWS::StackName}-custom-snapshot-notification
      KmsMasterKeyId: !Ref SnsKey

  #
  # IAM resources
  # [SnapshotManagerRole]
  #
  SnapshotManagerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub SnapshotManager-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 SnapshotManager-Policy-${AWS::StackName}-${AWS::Region}
        PolicyDocument:
          Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action:
            - logs:CreateLogGroup
            - logs:CreateLogStream
            - logs:PutLogEvents
            Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*"
          - Effect: Allow
            Action:
            - fsx:CreateSnapshot
            - fsx:DescribeSnapshots
            - fsx:DescribeVolumes
            - fsx:DeleteSnapshot
            - fsx:TagResource
            Resource: "*"
          - Effect: Allow
            Action:
            - sns:Publish
            Resource: !Sub arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${AWS::StackName}-custom-snapshot-notification
