package main import ( "encoding/json" "fmt" "log" "os" "github.com/aws/aws-cdk-go/awscdk/v2" "github.com/aws/aws-cdk-go/awscdk/v2/awsdynamodb" "github.com/aws/aws-cdk-go/awscdk/v2/awslogs" "github.com/aws/aws-cdk-go/awscdk/v2/awslogsdestinations" "github.com/aws/aws-cdk-go/awscdklambdagoalpha/v2" "github.com/aws/constructs-go/constructs/v10" "github.com/aws/jsii-runtime-go" ) type CloudwatchLogAlertsStackProps struct { awscdk.StackProps SlackWebhook string LambdaFunctionNames []string } func NewCloudwatchLogAlertsStack(scope constructs.Construct, id string, props *CloudwatchLogAlertsStackProps) awscdk.Stack { var sprops awscdk.StackProps if props != nil { sprops = props.StackProps } stack := awscdk.NewStack(scope, &id, &sprops) table := awsdynamodb.NewTableV2(stack, jsii.String("CloudwatchLogAlertsTable"), &awsdynamodb.TablePropsV2{ PartitionKey: &awsdynamodb.Attribute{ Name: jsii.String("fingerprint"), Type: awsdynamodb.AttributeType_STRING, }, TableClass: awsdynamodb.TableClass_STANDARD, Billing: awsdynamodb.Billing_OnDemand(), Encryption: awsdynamodb.TableEncryptionV2_DynamoOwnedKey(), PointInTimeRecovery: jsii.Bool(true), TimeToLiveAttribute: jsii.String("expires"), }) lambda_fn := awscdklambdagoalpha.NewGoFunction(stack, jsii.String("CloudwatchLogAlertsLambda"), &awscdklambdagoalpha.GoFunctionProps{ Description: jsii.String("CloudWatch Log Alerts"), Environment: &map[string]*string{ "SLACK_WEBHOOK": jsii.String(props.SlackWebhook), "DDB_TABLE": table.TableName(), }, LogRetention: awslogs.RetentionDays_ONE_MONTH, MemorySize: jsii.Number(512), Timeout: awscdk.Duration_Seconds(jsii.Number(5)), Entry: jsii.String("./lambda-fn/main.go"), }, ) table.GrantWriteData(lambda_fn) for _, functionName := range props.LambdaFunctionNames { awslogs.NewSubscriptionFilter( stack, jsii.String(fmt.Sprintf("SubscriptionFilter_%s", functionName)), &awslogs.SubscriptionFilterProps{ LogGroup: awslogs.LogGroup_FromLogGroupName( stack, jsii.String(fmt.Sprintf("LogGroup_%s", functionName)), jsii.String(fmt.Sprintf("/aws/lambda/%s", functionName)), ), Destination: awslogsdestinations.NewLambdaDestination( lambda_fn, nil, ), FilterPattern: awslogs.FilterPattern_StringValue( jsii.String("$.level"), jsii.String("="), jsii.String("error"), ), }, ) } return stack } func main() { defer jsii.Close() var props CloudwatchLogAlertsStackProps f, err := os.Open("properties.json") if err != nil { log.Fatalf("Error opening properties.json: %v", err) } defer f.Close() if err := json.NewDecoder(f).Decode(&props); err != nil { log.Fatalf("Error parsing properties.json: %v", err) } props.StackProps = awscdk.StackProps{ Env: env(), } app := awscdk.NewApp(nil) NewCloudwatchLogAlertsStack(app, "CloudwatchLogAlertsStack", &props) app.Synth(nil) } // env determines the AWS environment (account+region) in which our stack is to // be deployed. For more information see: https://docs.aws.amazon.com/cdk/latest/guide/environments.html func env() *awscdk.Environment { // If unspecified, this stack will be "environment-agnostic". // Account/Region-dependent features and context lookups will not work, but a // single synthesized template can be deployed anywhere. //--------------------------------------------------------------------------- //return nil // Uncomment if you know exactly what account and region you want to deploy // the stack to. This is the recommendation for production stacks. //--------------------------------------------------------------------------- // return &awscdk.Environment{ // Account: jsii.String("111111111111"), // Region: jsii.String("eu-west-1"), // } // Uncomment to specialize this stack for the AWS Account and Region that are // implied by the current CLI configuration. This is recommended for dev // stacks. //--------------------------------------------------------------------------- return &awscdk.Environment{ Account: jsii.String(os.Getenv("CDK_DEFAULT_ACCOUNT")), Region: jsii.String(os.Getenv("CDK_DEFAULT_REGION")), } }