Skip to content

Commit

Permalink
Adds aws-ts-lambda-slack example (#1709)
Browse files Browse the repository at this point in the history
Co-authored-by: diana esteves <[email protected]>
  • Loading branch information
desteves and diana esteves authored Sep 26, 2024
1 parent 1851dce commit a857962
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 0 deletions.
3 changes: 3 additions & 0 deletions aws-ts-lambda-slack/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/bin/
/node_modules/
Pulumi.dev.yaml
11 changes: 11 additions & 0 deletions aws-ts-lambda-slack/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: ${PROJECT}
description: ${DESCRIPTION}
runtime: nodejs

template:
description: Deploy a Slack webhook Lambda function.
config:
aws:region:
description: AWS Region
default: us-west-2

51 changes: 51 additions & 0 deletions aws-ts-lambda-slack/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# AWS Lambda for Slack Notification

A Pulumi example to:

- Creates an AWS Lambda function to post a message on Slack via a Webhook URL.
- Adds an AWS API Gateway so the Lambda can be invoked externally, e.g, via GitHub Webhooks.
- Uses a Pulumi ESC Environment to dynamically retrieve AWS OIDC Credentials and the Slack URL from AWS Secrets Manager.

Last update: September 2024

## 📋 Pre-requisites

- AWS OIDC configured in an Pulumi ESC Environment
- AWS Secrets Manager with a Slack Webhook URL secret
- A properly configured Slack Webhook URL
- [Pulumi CLI](https://www.pulumi.com/docs/get-started/install/)
- [Pulumi Cloud account](https://app.pulumi.com/signup)
- [npm](https://www.npmjs.com/get-npm)

## 👩‍🏫 Get started

This Pulumi example is written as a template. It is meant to be copied via `pulumi new`

```bash
# login to your Pulumi Cloud if you haven't already
$ pulumi login

# create a new dir and cd to it
$ mkdir my-slack-demo
$ cd my-slack-demo

# start your pulumi project
$ pulumi new https://github.com/pulumi/examples/aws-ts-lambda-slack
```

```bash
# Add your Pulumi ESC Environment
$ pulumi config env add YOUR_ESC_ENV --yes --non-interactive
$ pulumi up
# select 'yes' to confirm the expected changes
# 🎉 Ta-Da!
```

## 🧹 Clean up

To clean up your infrastructure, run:

```bash
$ pulumi destroy
# select 'yes' to confirm the expected changes
```
96 changes: 96 additions & 0 deletions aws-ts-lambda-slack/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

// Copyright 2024, Pulumi Corporation. All rights reserved.

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();
const slackWebhookUrl = config.requireSecret("slackWebhookUrl");

// Create an IAM role for the Lambda function
const lambdaRole = new aws.iam.Role("lambdaRole", {
assumeRolePolicy: {
Version: "2012-10-17",
Statement: [{
Action: "sts:AssumeRole",
Principal: {
Service: "lambda.amazonaws.com",
},
Effect: "Allow",
}],
},
});

// Attach a policy to the role to allow Lambda to log to CloudWatch
const rpa = new aws.iam.RolePolicyAttachment("lambdaRolePolicy", {
role: lambdaRole.name,
policyArn: aws.iam.ManagedPolicies.AWSLambdaBasicExecutionRole,
});

// Create the Lambda function
const lambdaFunction = new aws.lambda.Function("myLambda", {
// https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported
runtime: "nodejs20.x",
role: lambdaRole.arn,
handler: "index.handler",
code: new pulumi.asset.AssetArchive({
".": new pulumi.asset.FileArchive("./lambda"),
}),
environment: {
variables: {
"SLACK_WEBHOOK_URL": slackWebhookUrl,
},
},
memorySize: 128,
timeout: 30,
tags: {
"Environment": "dev",
},
});

// Export the Lambda function name
// export const lambdaFunctionName = lambdaFunction.name;

// Create an API Gateway
const api = new aws.apigateway.RestApi("myApi", {
description: "API Gateway for Lambda function",
});


// Create a root resource
const rootResource = api.rootResourceId;

// Create a method for the root resource
const rootMethod = new aws.apigateway.Method("rootMethod", {
restApi: api.id,
resourceId: rootResource,
httpMethod: "ANY",
authorization: "NONE",
});

// Integrate the Lambda function with the root method
const rootIntegration = new aws.apigateway.Integration("rootIntegration", {
restApi: api.id,
resourceId: rootResource,
httpMethod: rootMethod.httpMethod,
integrationHttpMethod: "POST",
type: "AWS_PROXY",
uri: lambdaFunction.invokeArn,
});

// Grant API Gateway permission to invoke the Lambda function
const lambdaPermission = new aws.lambda.Permission("apiGatewayPermission", {
action: "lambda:InvokeFunction",
function: lambdaFunction.arn,
principal: "apigateway.amazonaws.com",
sourceArn: pulumi.interpolate`${api.executionArn}/*/*`,
});

// Deploy the API
const deployment = new aws.apigateway.Deployment("myDeployment", {
restApi: api.id,
stageName: "dev",
}, { dependsOn: [rootIntegration] });

// Export the URL of the API
export const url = pulumi.interpolate`${deployment.invokeUrl}`;
53 changes: 53 additions & 0 deletions aws-ts-lambda-slack/lambda/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const https = require('https');

exports.handler = async (event) => {
// Get the Slack webhook URL from environment variables
const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;

// Define the message payload
const slackMessage = JSON.stringify({
text: event.body
});

// Slack Webhook URL parsed
const url = new URL(slackWebhookUrl);

const options = {
hostname: url.hostname,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': slackMessage.length
}
};

// Create a promise to post the message
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let response = '';

res.on('data', (chunk) => {
response += chunk;
});

res.on('end', () => {
resolve({
statusCode: res.statusCode,
body: response
});
});
});

req.on('error', (error) => {
reject({
statusCode: 500,
body: JSON.stringify(error)
});
});

// Send the Slack message
req.write(slackMessage);
req.end();
});
};
12 changes: 12 additions & 0 deletions aws-ts-lambda-slack/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "aws-ts-lambda-slack",
"main": "index.ts",
"devDependencies": {
"@types/node": "^22",
"typescript": "^5.6.2"
},
"dependencies": {
"@pulumi/aws": "^6.52.0",
"@pulumi/pulumi": "^3.133.0"
}
}
18 changes: 18 additions & 0 deletions aws-ts-lambda-slack/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2020",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}

0 comments on commit a857962

Please sign in to comment.