{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "A stack that sets up a reliable export of the CloudWatch Logs to S3 bucket. You will be billed for the AWS resources used if you create a stack from this template. This template requires setting the \"Create IAM resources\" parameter to True.",
    "Parameters": {
        "CreateTemplateURLPrefix": {
            "Type": "String",
            "Description": "Prefix of the Bucket URL that contains '/templates/create-cwl-s3-export.template' CloudFormation template, which actually sets up a reliable export of the CloudWatch Logs to S3 bucket. Bucket name must be in the following format: [bucket_prefix].[region]",
            "Default": "https://s3.amazonaws.com/alertlogic-public-repo"
        },
        "CloudWatchLogGroup": {
            "Description": "The name of the CloudWatch Logs Group to export to S3 bucket.",
            "Type": "String",
            "ConstraintDescription": "Must be an existing CloudWatch Log Group."
        },
        "KinesisShards": {
            "Description": "Number of shards to create for the Kinesis subscription stream.",
            "Type": "Number",
            "Default": "1",
            "MinValue": "1"
        },
        "LogFormat": {
            "Description": "Choose the format that best describes the type of logs in the selected log group",
            "Type": "String",
            "Default": "AWS VPC Flow Logs",
            "AllowedValues": [
                "AWS VPC Flow Logs",
                "AWS Lambda",
                "AWS IoT",
                "Custom"
            ]
        },
        "SubscriptionFilterPattern": {
            "Description": "Specify the CloudWatch subscription filter to be used to filter data sent to the S3.",
            "Type": "String",
            "Default": ""
        },
        "S3BucketName": {
            "Description": "S3 bucket to archive CloudWatch logs into.",
            "Type": "String",
            "MinLength": "5"
        },
        "S3LogFolderName": {
            "Description": "Add a name of the folder to place logs into. If you leave this parameter empty, the name of the stack will be used.",
            "Type": "String",
            "Default": ""
        },
        "LambdaS3BucketNamePrefix": {
            "Description": "The prefix of the S3 bucket which contains Lambda package to be used for archiving CloudWath Logs to S3. Note that the name of the bucket must contain a region name suffix. The following is the valid backet name syntax: <bucket_name_prefix>.us-east-1.",
            "Type": "String",
            "Default": "alertlogic-public-repo",
            "MinLength": "5"
        },
        "LambdaPackageName": {
            "Description": "Object name containing Lambda package to be used for archiving CloudWath Logs to S3.",
            "Type": "String",
            "Default": "cloudwatch-logs-s3-export-0.0.14.zip",
            "MinLength": "5"
        },
        "AlertLogicAWSAccountId": {
            "Description": "Alert Logic's AWS Account ID used for collecting data. A third-party role will be created to give read-only access to the 'S3BucketName'. Don't change this value",
            "Type": "String",
            "Default": "239734009475",
            "AllowedValues": [
                "239734009475"
            ]
        },
        "AccessKeyId": {
            "Description": "Alertlogic Access Key Id obtained from AIMS",
            "Type": "String"
        },
        "SecretKey": {
            "Description": "Alertlogic Secret Key returned from AIMS for the Access Key Id",
            "Type": "String",
            "NoEcho": true
        },
        "AlApiEndpoint": {
            "Description": "Alert Logic API endpoint",
            "Type": "String",
            "Default": "api.global-services.global.alertlogic.com",
            "AllowedValues": [
                "api.global-services.global.alertlogic.com",
                "api.global-integration.product.dev.alertlogic.com"
            ]
        },
        "AlertLogicAccountId": {
            "Description": "Alert Logic's Account ID. Leave this field empty if you want send data to your account. Use child Account ID if you want to send to a child account.",
            "Type": "String",
            "Default": ""
        }
    },
    "Conditions": {
        "EmptyPrefix": {
            "Fn::Equals": [
                {
                    "Ref": "S3LogFolderName"
                },
                ""
            ]
        }
    },
    "Metadata": {
        "AWS::CloudFormation::Interface": {
            "ParameterGroups": [
                {
                    "Label": {
                        "default": "Alert Logic Log Manager Configuration"
                    },
                    "Parameters": [
                        "AlertLogicAccountId",
                        "AlertLogicAWSAccountId",
                        "AccessKeyId",
                        "SecretKey",
                        "AlApiEndpoint"
                    ]
                },
                {
                    "Label": {
                        "default": "Data Collection Configuration"
                    },
                    "Parameters": [
                        "CloudWatchLogGroup",
                        "LogFormat",
                        "S3BucketName",
                        "S3LogFolderName",
                        "SubscriptionFilterPattern"
                    ]
                },
                {
                    "Label": {
                        "default": "Lambda Configuration"
                    },
                    "Parameters": [
                        "LambdaS3BucketNamePrefix",
                        "LambdaPackageName",
                        "KinesisShards"
                    ]
                }
            ],
            "ParameterLabels": {
                "AlertLogicAccountId": {
                    "default": "Alert Logic account id:"
                },
                "AlertLogicAWSAccountId": {
                    "default": "Alert Logic AWS account id to grant access to your data:"
                },
                "AccessKeyId": {
                    "default": "Alertlogic Access Key Id obtained from AIMS"
                },
                "SecretKey": {
                    "default": "Alertlogic Secret Key returned from AIMS for the Access Key Id"
                },
                "AlApiEndpoint": {
                    "default": "Alert Logic API endpoint"
                },
                "LambdaS3BucketNamePrefix": {
                    "default": "Lambda package S3 bucket prefix:"
                },
                "LambdaPackageName": {
                    "default": "Lambda package name:"
                },
                "KinesisShards": {
                    "default": "Kinesis shards count:"
                },
                "CloudWatchLogGroup": {
                    "default": "CloudWatch log group name:"
                },
                "LogFormat": {
                    "default": "Log Format:"
                },
                "S3BucketName": {
                    "default": "S3 bucket name:"
                },
                "S3LogFolderName": {
                    "default": "Logs folder name:"
                },
                "SubscriptionFilterPattern": {
                    "default": "CloudWatch subscription filter:"
                }
            }
        }
    },
    "Resources": {
        "LambdaGetCloudWatchLogGroupInfoRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": "lambda.amazonaws.com"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                }
            }
        },
        "LambdaGetCloudWatchLogGroupInfoExecutionPolicy": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": "LambdaGetCloudWatchLogGroupInfoExecutionPolicy",
                "Roles": [
                    {
                        "Ref": "LambdaGetCloudWatchLogGroupInfoRole"
                    }
                ],
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "logs:DescribeLogStreams",
                                "logs:CreateLogGroup",
                                "logs:CreateLogStream",
                                "logs:PutLogEvents",
                                "logs:DescribeSubscriptionFilters"
                            ],
                            "Resource": "arn:aws:logs:*:*:*"
                        }
                    ]
                }
            },
            "DependsOn": [
                "LambdaGetCloudWatchLogGroupInfoRole"
            ]
        },
        "GetCloudWatchLogGroupInfo": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Handler": "index.handler",
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaGetCloudWatchLogGroupInfoRole",
                        "Arn"
                    ]
                },
                "Description": "Function to get subscription filters information.",
                "Code": {
                    "ZipFile": {
                        "Fn::Join": [
                            "",
                            [
                                "'use strict';\n",
                                "\n",
                                "const { CloudWatchLogsClient, DescribeSubscriptionFiltersCommand } = require('@aws-sdk/client-cloudwatch-logs');\n",
                                "const response = require('./cfn-response');\n",
                                "\n",
                                "function hasKinesisArn(subscriber) {\n",
                                "    const awsResourceType = subscriber.destinationArn.split(':')[2];\n",
                                "    return awsResourceType === 'kinesis';\n",
                                "}\n",
                                "\n",
                                "function filterKinesisSubscribers(subscribers) {\n",
                                "    const initialValue = [];\n",
                                "    return subscribers.reduce(\n",
                                "        (accumulator, subscriber) => {\n",
                                "            if (hasKinesisArn(subscriber)) {\n",
                                "                accumulator.push(subscriber);\n",
                                "            }\n",
                                "            return accumulator;\n",
                                "        },\n",
                                "        initialValue\n",
                                "    );\n",
                                "}\n",
                                "\n",
                                "function resolveSubscriber(logGroupData) {\n",
                                "    const subscribers = logGroupData.subscriptionFilters;\n",
                                "    const kinesisSubscribers = filterKinesisSubscribers(subscribers);\n",
                                "    var responseData = { destinationArn: '' };\n",
                                "    if (kinesisSubscribers.length === 0 && subscribers.length >= 2) {\n",
                                "        responseData = {\n",
                                "            Error:\n",
                                "                'Only kinesis subscribers are supported. '\n",
                                "                + 'Cannot create a new subscription filter for this log group'\n",
                                "                + ' because it already has the maximum allowed number of subscription filters.'\n",
                                "        };\n",
                                "    } else if (kinesisSubscribers.length > 0) {\n",
                                "        responseData = kinesisSubscribers[0];\n",
                                "    }\n",
                                "    return responseData;\n",
                                "}\n",
                                "\n",
                                "exports.handler = async function (event, context) {\n",
                                "    console.log('REQUEST RECEIVED:\\n', JSON.stringify(event));\n",
                                "    if (event.RequestType !== 'Create') {\n",
                                "        return response.send(event, context, response.SUCCESS);\n",
                                "    }\n",
                                "    const awsRegion = event.ResourceProperties.Region;\n",
                                "    const logGroup = event.ResourceProperties.LogGroup;\n",
                                "    const client = new CloudWatchLogsClient({ region: awsRegion });\n",
                                "    const params = { logGroupName: logGroup };\n",
                                "    try {\n",
                                "        const command = new DescribeSubscriptionFiltersCommand(params);\n",
                                "        const data = await client.send(command);\n",
                                "        const responseData = resolveSubscriber(data);\n",
                                "        const responseStatus = responseData.Error ? response.FAILED : response.SUCCESS;\n",
                                "        return response.send(event, context, responseStatus, responseData);\n",
                                "    } catch (err) {\n",
                                "        const responseData = { Error: 'DescribeSubscriptionFilters call failed' };\n",
                                "        console.log(responseData.Error + ':\\n', err);\n",
                                "        return response.send(event, context, response.FAILED, responseData);\n",
                                "    }\n",
                                "};\n"
                            ]
                        ]
                    }
                },
                "Runtime": "nodejs20.x",
                "Timeout": "180"
            },
            "DependsOn": [
                "LambdaGetCloudWatchLogGroupInfoExecutionPolicy"
            ]
        },
        "CloudWatchLogGroupInfo": {
            "Type": "Custom::CloudWatchLogGroupInfo",
            "Properties": {
                "ServiceToken": {
                    "Fn::GetAtt": [
                        "GetCloudWatchLogGroupInfo",
                        "Arn"
                    ]
                },
                "Region": {
                    "Ref": "AWS::Region"
                },
                "LogGroup": {
                    "Ref": "CloudWatchLogGroup"
                }
            }
        },
        "StackCreateCloudWatchLogExport": {
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "Parameters": {
                    "CloudWatchLogGroup": {
                        "Ref": "CloudWatchLogGroup"
                    },
                    "KinesisShards": {
                        "Ref": "KinesisShards"
                    },
                    "LogFormat": {
                        "Ref": "LogFormat"
                    },
                    "SubscriptionFilterPattern": {
                        "Ref": "SubscriptionFilterPattern"
                    },
                    "S3BucketName": {
                        "Ref": "S3BucketName"
                    },
                    "S3LogFolderName": {
                        "Ref": "S3LogFolderName"
                    },
                    "LambdaS3BucketNamePrefix": {
                        "Ref": "LambdaS3BucketNamePrefix"
                    },
                    "LambdaPackageName": {
                        "Ref": "LambdaPackageName"
                    },
                    "AlertLogicAWSAccountId": {
                        "Ref": "AlertLogicAWSAccountId"
                    },
                    "AccessKeyId": {
                        "Ref": "AccessKeyId"
                    },
                    "SecretKey": {
                        "Ref": "SecretKey"
                    },
                    "AlApiEndpoint": {
                        "Ref": "AlApiEndpoint"
                    },
                    "AlertLogicAccountId": {
                        "Ref": "AlertLogicAccountId"
                    },
                    "DestinationArn": {
                        "Fn::GetAtt": [
                            "CloudWatchLogGroupInfo",
                            "destinationArn"
                        ]
                    }
                },
                "TemplateURL": {
                    "Fn::Join": [
                        "",
                        [
                            {
                                "Ref": "CreateTemplateURLPrefix"
                            },
                            ".",
                            {
                                "Ref": "AWS::Region"
                            },
                            "/templates/create-cwl-s3-export.template"
                        ]
                    ]
                },
                "TimeoutInMinutes": "10"
            }
        }
    },
    "Outputs": {
        "DestinationArn": {
            "Value": {
                "Fn::GetAtt": [
                    "CloudWatchLogGroupInfo",
                    "destinationArn"
                ]
            },
            "Description": "CloudWatch Log Group Subscription Destination ARN"
        }
    }
}