

There are more AWS SDK examples available in the [AWS Doc SDK Examples](https://github.com/awsdocs/aws-doc-sdk-examples) GitHub repo.

# Scenarios for Lambda using AWS SDKs
<a name="lambda_code_examples_scenarios"></a>

The following code examples show you how to implement common scenarios in Lambda with AWS SDKs. These scenarios show you how to accomplish specific tasks by calling multiple functions within Lambda or combined with other AWS services. Each scenario includes a link to the complete source code, where you can find instructions on how to set up and run the code. 

Scenarios target an intermediate level of experience to help you understand service actions in context.

**Topics**
+ [Automatically confirm known users with a Lambda function](lambda_example_cross_CognitoAutoConfirmUser_section.md)
+ [Automatically migrate known users with a Lambda function](lambda_example_cross_CognitoAutoMigrateUser_section.md)
+ [Create a REST API to track COVID-19 data](lambda_example_cross_ApiGatewayDataTracker_section.md)
+ [Create a REST API with Lambda proxy integration](lambda_example_api_gateway_GettingStarted_087_section.md)
+ [Create a lending library REST API](lambda_example_cross_AuroraRestLendingLibrary_section.md)
+ [Create a messenger application](lambda_example_cross_StepFunctionsMessenger_section.md)
+ [Create a serverless application to manage photos](lambda_example_cross_PAM_section.md)
+ [Create a websocket chat application](lambda_example_cross_ApiGatewayWebsocketChat_section.md)
+ [Create an application to analyze customer feedback](lambda_example_cross_FSA_section.md)
+ [Creating a CloudWatch dashboard with function name as a variable](lambda_example_cloudwatch_GettingStarted_031_section.md)
+ [Creating your first Lambda function](lambda_example_lambda_GettingStarted_019_section.md)
+ [Invoke a Lambda function from a browser](lambda_example_cross_LambdaForBrowser_section.md)
+ [Transform data with S3 Object Lambda](lambda_example_cross_ServerlessS3DataTransformation_section.md)
+ [Use API Gateway to invoke a Lambda function](lambda_example_cross_LambdaAPIGateway_section.md)
+ [Use Step Functions to invoke Lambda functions](lambda_example_cross_ServerlessWorkflows_section.md)
+ [Use scheduled events to invoke a Lambda function](lambda_example_cross_LambdaScheduledEvents_section.md)
+ [Use the Neptune API to query graph data](lambda_example_cross_Neptune_Query_section.md)
+ [Using property variables in CloudWatch dashboards to monitor multiple Lambda functions](lambda_example_iam_GettingStarted_032_section.md)
+ [Write custom activity data with a Lambda function after Amazon Cognito user authentication](lambda_example_cross_CognitoCustomActivityLog_section.md)

# Automatically confirm known Amazon Cognito users with a Lambda function using an AWS SDK
<a name="lambda_example_cross_CognitoAutoConfirmUser_section"></a>

The following code examples show how to automatically confirm known Amazon Cognito users with a Lambda function.
+ Configure a user pool to call a Lambda function for the `PreSignUp` trigger.
+ Sign up a user with Amazon Cognito.
+ The Lambda function scans a DynamoDB table and automatically confirms known users.
+ Sign in as the new user, then clean up resources.

------
#### [ Go ]

**SDK for Go V2**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/gov2/workflows/user_pools_and_lambda_triggers#code-examples). 
Run an interactive scenario at a command prompt.  

```
import (
	"context"
	"errors"
	"log"
	"strings"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// AutoConfirm separates the steps of this scenario into individual functions so that
// they are simpler to read and understand.
type AutoConfirm struct {
	helper       IScenarioHelper
	questioner   demotools.IQuestioner
	resources    Resources
	cognitoActor *actions.CognitoActions
}

// NewAutoConfirm constructs a new auto confirm runner.
func NewAutoConfirm(sdkConfig aws.Config, questioner demotools.IQuestioner, helper IScenarioHelper) AutoConfirm {
	scenario := AutoConfirm{
		helper:       helper,
		questioner:   questioner,
		resources:    Resources{},
		cognitoActor: &actions.CognitoActions{CognitoClient: cognitoidentityprovider.NewFromConfig(sdkConfig)},
	}
	scenario.resources.init(scenario.cognitoActor, questioner)
	return scenario
}

// AddPreSignUpTrigger adds a Lambda handler as an invocation target for the PreSignUp trigger.
func (runner *AutoConfirm) AddPreSignUpTrigger(ctx context.Context, userPoolId string, functionArn string) {
	log.Printf("Let's add a Lambda function to handle the PreSignUp trigger from Cognito.\n" +
		"This trigger happens when a user signs up, and lets your function take action before the main Cognito\n" +
		"sign up processing occurs.\n")
	err := runner.cognitoActor.UpdateTriggers(
		ctx, userPoolId,
		actions.TriggerInfo{Trigger: actions.PreSignUp, HandlerArn: aws.String(functionArn)})
	if err != nil {
		panic(err)
	}
	log.Printf("Lambda function %v added to user pool %v to handle the PreSignUp trigger.\n",
		functionArn, userPoolId)
}

// SignUpUser signs up a user from the known user table with a password you specify.
func (runner *AutoConfirm) SignUpUser(ctx context.Context, clientId string, usersTable string) (string, string) {
	log.Println("Let's sign up a user to your Cognito user pool. When the user's email matches an email in the\n" +
		"DynamoDB known users table, it is automatically verified and the user is confirmed.")

	knownUsers, err := runner.helper.GetKnownUsers(ctx, usersTable)
	if err != nil {
		panic(err)
	}
	userChoice := runner.questioner.AskChoice("Which user do you want to use?\n", knownUsers.UserNameList())
	user := knownUsers.Users[userChoice]

	var signedUp bool
	var userConfirmed bool
	password := runner.questioner.AskPassword("Enter a password that has at least eight characters, uppercase, lowercase, numbers and symbols.\n"+
		"(the password will not display as you type):", 8)
	for !signedUp {
		log.Printf("Signing up user '%v' with email '%v' to Cognito.\n", user.UserName, user.UserEmail)
		userConfirmed, err = runner.cognitoActor.SignUp(ctx, clientId, user.UserName, password, user.UserEmail)
		if err != nil {
			var invalidPassword *types.InvalidPasswordException
			if errors.As(err, &invalidPassword) {
				password = runner.questioner.AskPassword("Enter another password:", 8)
			} else {
				panic(err)
			}
		} else {
			signedUp = true
		}
	}
	log.Printf("User %v signed up, confirmed = %v.\n", user.UserName, userConfirmed)

	log.Println(strings.Repeat("-", 88))

	return user.UserName, password
}

// SignInUser signs in a user.
func (runner *AutoConfirm) SignInUser(ctx context.Context, clientId string, userName string, password string) string {
	runner.questioner.Ask("Press Enter when you're ready to continue.")
	log.Printf("Let's sign in as %v...\n", userName)
	authResult, err := runner.cognitoActor.SignIn(ctx, clientId, userName, password)
	if err != nil {
		panic(err)
	}
	log.Printf("Successfully signed in. Your access token starts with: %v...\n", (*authResult.AccessToken)[:10])
	log.Println(strings.Repeat("-", 88))
	return *authResult.AccessToken
}

// Run runs the scenario.
func (runner *AutoConfirm) Run(ctx context.Context, stackName string) {
	defer func() {
		if r := recover(); r != nil {
			log.Println("Something went wrong with the demo.")
			runner.resources.Cleanup(ctx)
		}
	}()

	log.Println(strings.Repeat("-", 88))
	log.Printf("Welcome\n")

	log.Println(strings.Repeat("-", 88))

	stackOutputs, err := runner.helper.GetStackOutputs(ctx, stackName)
	if err != nil {
		panic(err)
	}
	runner.resources.userPoolId = stackOutputs["UserPoolId"]
	runner.helper.PopulateUserTable(ctx, stackOutputs["TableName"])

	runner.AddPreSignUpTrigger(ctx, stackOutputs["UserPoolId"], stackOutputs["AutoConfirmFunctionArn"])
	runner.resources.triggers = append(runner.resources.triggers, actions.PreSignUp)
	userName, password := runner.SignUpUser(ctx, stackOutputs["UserPoolClientId"], stackOutputs["TableName"])
	runner.helper.ListRecentLogEvents(ctx, stackOutputs["AutoConfirmFunction"])
	runner.resources.userAccessTokens = append(runner.resources.userAccessTokens,
		runner.SignInUser(ctx, stackOutputs["UserPoolClientId"], userName, password))

	runner.resources.Cleanup(ctx)

	log.Println(strings.Repeat("-", 88))
	log.Println("Thanks for watching!")
	log.Println(strings.Repeat("-", 88))
}
```
Handle the `PreSignUp` trigger with a Lambda function.  

```
import (
	"context"
	"log"
	"os"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

const TABLE_NAME = "TABLE_NAME"

// UserInfo defines structured user data that can be marshalled to a DynamoDB format.
type UserInfo struct {
	UserName  string `dynamodbav:"UserName"`
	UserEmail string `dynamodbav:"UserEmail"`
}

// GetKey marshals the user email value to a DynamoDB key format.
func (user UserInfo) GetKey() map[string]dynamodbtypes.AttributeValue {
	userEmail, err := attributevalue.Marshal(user.UserEmail)
	if err != nil {
		panic(err)
	}
	return map[string]dynamodbtypes.AttributeValue{"UserEmail": userEmail}
}

type handler struct {
	dynamoClient *dynamodb.Client
}

// HandleRequest handles the PreSignUp event by looking up a user in an Amazon DynamoDB table and
// specifying whether they should be confirmed and verified.
func (h *handler) HandleRequest(ctx context.Context, event events.CognitoEventUserPoolsPreSignup) (events.CognitoEventUserPoolsPreSignup, error) {
	log.Printf("Received presignup from %v for user '%v'", event.TriggerSource, event.UserName)
	if event.TriggerSource != "PreSignUp_SignUp" {
		// Other trigger sources, such as PreSignUp_AdminInitiateAuth, ignore the response from this handler.
		return event, nil
	}
	tableName := os.Getenv(TABLE_NAME)
	user := UserInfo{
		UserEmail: event.Request.UserAttributes["email"],
	}
	log.Printf("Looking up email %v in table %v.\n", user.UserEmail, tableName)
	output, err := h.dynamoClient.GetItem(ctx, &dynamodb.GetItemInput{
		Key:       user.GetKey(),
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Error looking up email %v.\n", user.UserEmail)
		return event, err
	}
	if output.Item == nil {
		log.Printf("Email %v not found. Email verification is required.\n", user.UserEmail)
		return event, err
	}

	err = attributevalue.UnmarshalMap(output.Item, &user)
	if err != nil {
		log.Printf("Couldn't unmarshal DynamoDB item. Here's why: %v\n", err)
		return event, err
	}

	if user.UserName != event.UserName {
		log.Printf("UserEmail %v found, but stored UserName '%v' does not match supplied UserName '%v'. Verification is required.\n",
			user.UserEmail, user.UserName, event.UserName)
	} else {
		log.Printf("UserEmail %v found with matching UserName %v. User is confirmed.\n", user.UserEmail, user.UserName)
		event.Response.AutoConfirmUser = true
		event.Response.AutoVerifyEmail = true
	}

	return event, err
}

func main() {
	ctx := context.Background()
	sdkConfig, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		log.Panicln(err)
	}
	h := handler{
		dynamoClient: dynamodb.NewFromConfig(sdkConfig),
	}
	lambda.Start(h.HandleRequest)
}
```
Create a struct that performs common tasks.  

```
import (
	"context"
	"log"
	"strings"
	"time"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// IScenarioHelper defines common functions used by the workflows in this example.
type IScenarioHelper interface {
	Pause(secs int)
	GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error)
	PopulateUserTable(ctx context.Context, tableName string)
	GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error)
	AddKnownUser(ctx context.Context, tableName string, user actions.User)
	ListRecentLogEvents(ctx context.Context, functionName string)
}

// ScenarioHelper contains AWS wrapper structs used by the workflows in this example.
type ScenarioHelper struct {
	questioner  demotools.IQuestioner
	dynamoActor *actions.DynamoActions
	cfnActor    *actions.CloudFormationActions
	cwlActor    *actions.CloudWatchLogsActions
	isTestRun   bool
}

// NewScenarioHelper constructs a new scenario helper.
func NewScenarioHelper(sdkConfig aws.Config, questioner demotools.IQuestioner) ScenarioHelper {
	scenario := ScenarioHelper{
		questioner:  questioner,
		dynamoActor: &actions.DynamoActions{DynamoClient: dynamodb.NewFromConfig(sdkConfig)},
		cfnActor:    &actions.CloudFormationActions{CfnClient: cloudformation.NewFromConfig(sdkConfig)},
		cwlActor:    &actions.CloudWatchLogsActions{CwlClient: cloudwatchlogs.NewFromConfig(sdkConfig)},
	}
	return scenario
}

// Pause waits for the specified number of seconds.
func (helper ScenarioHelper) Pause(secs int) {
	if !helper.isTestRun {
		time.Sleep(time.Duration(secs) * time.Second)
	}
}

// GetStackOutputs gets the outputs from the specified CloudFormation stack in a structured format.
func (helper ScenarioHelper) GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error) {
	return helper.cfnActor.GetOutputs(ctx, stackName), nil
}

// PopulateUserTable fills the known user table with example data.
func (helper ScenarioHelper) PopulateUserTable(ctx context.Context, tableName string) {
	log.Printf("First, let's add some users to the DynamoDB %v table we'll use for this example.\n", tableName)
	err := helper.dynamoActor.PopulateTable(ctx, tableName)
	if err != nil {
		panic(err)
	}
}

// GetKnownUsers gets the users from the known users table in a structured format.
func (helper ScenarioHelper) GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error) {
	knownUsers, err := helper.dynamoActor.Scan(ctx, tableName)
	if err != nil {
		log.Printf("Couldn't get known users from table %v. Here's why: %v\n", tableName, err)
	}
	return knownUsers, err
}

// AddKnownUser adds a user to the known users table.
func (helper ScenarioHelper) AddKnownUser(ctx context.Context, tableName string, user actions.User) {
	log.Printf("Adding user '%v' with email '%v' to the DynamoDB known users table...\n",
		user.UserName, user.UserEmail)
	err := helper.dynamoActor.AddUser(ctx, tableName, user)
	if err != nil {
		panic(err)
	}
}

// ListRecentLogEvents gets the most recent log stream and events for the specified Lambda function and displays them.
func (helper ScenarioHelper) ListRecentLogEvents(ctx context.Context, functionName string) {
	log.Println("Waiting a few seconds to let Lambda write to CloudWatch Logs...")
	helper.Pause(10)
	log.Println("Okay, let's check the logs to find what's happened recently with your Lambda function.")
	logStream, err := helper.cwlActor.GetLatestLogStream(ctx, functionName)
	if err != nil {
		panic(err)
	}
	log.Printf("Getting some recent events from log stream %v\n", *logStream.LogStreamName)
	events, err := helper.cwlActor.GetLogEvents(ctx, functionName, *logStream.LogStreamName, 10)
	if err != nil {
		panic(err)
	}
	for _, event := range events {
		log.Printf("\t%v", *event.Message)
	}
	log.Println(strings.Repeat("-", 88))
}
```
Create a struct that wraps Amazon Cognito actions.  

```
import (
	"context"
	"errors"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
)

type CognitoActions struct {
	CognitoClient *cognitoidentityprovider.Client
}



// Trigger and TriggerInfo define typed data for updating an Amazon Cognito trigger.
type Trigger int

const (
	PreSignUp Trigger = iota
	UserMigration
	PostAuthentication
)

type TriggerInfo struct {
	Trigger    Trigger
	HandlerArn *string
}

// UpdateTriggers adds or removes Lambda triggers for a user pool. When a trigger is specified with a `nil` value,
// it is removed from the user pool.
func (actor CognitoActions) UpdateTriggers(ctx context.Context, userPoolId string, triggers ...TriggerInfo) error {
	output, err := actor.CognitoClient.DescribeUserPool(ctx, &cognitoidentityprovider.DescribeUserPoolInput{
		UserPoolId: aws.String(userPoolId),
	})
	if err != nil {
		log.Printf("Couldn't get info about user pool %v. Here's why: %v\n", userPoolId, err)
		return err
	}
	lambdaConfig := output.UserPool.LambdaConfig
	for _, trigger := range triggers {
		switch trigger.Trigger {
		case PreSignUp:
			lambdaConfig.PreSignUp = trigger.HandlerArn
		case UserMigration:
			lambdaConfig.UserMigration = trigger.HandlerArn
		case PostAuthentication:
			lambdaConfig.PostAuthentication = trigger.HandlerArn
		}
	}
	_, err = actor.CognitoClient.UpdateUserPool(ctx, &cognitoidentityprovider.UpdateUserPoolInput{
		UserPoolId:   aws.String(userPoolId),
		LambdaConfig: lambdaConfig,
	})
	if err != nil {
		log.Printf("Couldn't update user pool %v. Here's why: %v\n", userPoolId, err)
	}
	return err
}



// SignUp signs up a user with Amazon Cognito.
func (actor CognitoActions) SignUp(ctx context.Context, clientId string, userName string, password string, userEmail string) (bool, error) {
	confirmed := false
	output, err := actor.CognitoClient.SignUp(ctx, &cognitoidentityprovider.SignUpInput{
		ClientId: aws.String(clientId),
		Password: aws.String(password),
		Username: aws.String(userName),
		UserAttributes: []types.AttributeType{
			{Name: aws.String("email"), Value: aws.String(userEmail)},
		},
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't sign up user %v. Here's why: %v\n", userName, err)
		}
	} else {
		confirmed = output.UserConfirmed
	}
	return confirmed, err
}



// SignIn signs in a user to Amazon Cognito using a username and password authentication flow.
func (actor CognitoActions) SignIn(ctx context.Context, clientId string, userName string, password string) (*types.AuthenticationResultType, error) {
	var authResult *types.AuthenticationResultType
	output, err := actor.CognitoClient.InitiateAuth(ctx, &cognitoidentityprovider.InitiateAuthInput{
		AuthFlow:       "USER_PASSWORD_AUTH",
		ClientId:       aws.String(clientId),
		AuthParameters: map[string]string{"USERNAME": userName, "PASSWORD": password},
	})
	if err != nil {
		var resetRequired *types.PasswordResetRequiredException
		if errors.As(err, &resetRequired) {
			log.Println(*resetRequired.Message)
		} else {
			log.Printf("Couldn't sign in user %v. Here's why: %v\n", userName, err)
		}
	} else {
		authResult = output.AuthenticationResult
	}
	return authResult, err
}



// ForgotPassword starts a password recovery flow for a user. This flow typically sends a confirmation code
// to the user's configured notification destination, such as email.
func (actor CognitoActions) ForgotPassword(ctx context.Context, clientId string, userName string) (*types.CodeDeliveryDetailsType, error) {
	output, err := actor.CognitoClient.ForgotPassword(ctx, &cognitoidentityprovider.ForgotPasswordInput{
		ClientId: aws.String(clientId),
		Username: aws.String(userName),
	})
	if err != nil {
		log.Printf("Couldn't start password reset for user '%v'. Here;s why: %v\n", userName, err)
	}
	return output.CodeDeliveryDetails, err
}



// ConfirmForgotPassword confirms a user with a confirmation code and a new password.
func (actor CognitoActions) ConfirmForgotPassword(ctx context.Context, clientId string, code string, userName string, password string) error {
	_, err := actor.CognitoClient.ConfirmForgotPassword(ctx, &cognitoidentityprovider.ConfirmForgotPasswordInput{
		ClientId:         aws.String(clientId),
		ConfirmationCode: aws.String(code),
		Password:         aws.String(password),
		Username:         aws.String(userName),
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't confirm user %v. Here's why: %v", userName, err)
		}
	}
	return err
}



// DeleteUser removes a user from the user pool.
func (actor CognitoActions) DeleteUser(ctx context.Context, userAccessToken string) error {
	_, err := actor.CognitoClient.DeleteUser(ctx, &cognitoidentityprovider.DeleteUserInput{
		AccessToken: aws.String(userAccessToken),
	})
	if err != nil {
		log.Printf("Couldn't delete user. Here's why: %v\n", err)
	}
	return err
}



// AdminCreateUser uses administrator credentials to add a user to a user pool. This method leaves the user
// in a state that requires they enter a new password next time they sign in.
func (actor CognitoActions) AdminCreateUser(ctx context.Context, userPoolId string, userName string, userEmail string) error {
	_, err := actor.CognitoClient.AdminCreateUser(ctx, &cognitoidentityprovider.AdminCreateUserInput{
		UserPoolId:     aws.String(userPoolId),
		Username:       aws.String(userName),
		MessageAction:  types.MessageActionTypeSuppress,
		UserAttributes: []types.AttributeType{{Name: aws.String("email"), Value: aws.String(userEmail)}},
	})
	if err != nil {
		var userExists *types.UsernameExistsException
		if errors.As(err, &userExists) {
			log.Printf("User %v already exists in the user pool.", userName)
			err = nil
		} else {
			log.Printf("Couldn't create user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}



// AdminSetUserPassword uses administrator credentials to set a password for a user without requiring a
// temporary password.
func (actor CognitoActions) AdminSetUserPassword(ctx context.Context, userPoolId string, userName string, password string) error {
	_, err := actor.CognitoClient.AdminSetUserPassword(ctx, &cognitoidentityprovider.AdminSetUserPasswordInput{
		Password:   aws.String(password),
		UserPoolId: aws.String(userPoolId),
		Username:   aws.String(userName),
		Permanent:  true,
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't set password for user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}
```
Create a struct that wraps DynamoDB actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

// DynamoActions encapsulates the Amazon Simple Notification Service (Amazon SNS) actions
// used in the examples.
type DynamoActions struct {
	DynamoClient *dynamodb.Client
}

// User defines structured user data.
type User struct {
	UserName  string
	UserEmail string
	LastLogin *LoginInfo `dynamodbav:",omitempty"`
}

// LoginInfo defines structured custom login data.
type LoginInfo struct {
	UserPoolId string
	ClientId   string
	Time       string
}

// UserList defines a list of users.
type UserList struct {
	Users []User
}

// UserNameList returns the usernames contained in a UserList as a list of strings.
func (users *UserList) UserNameList() []string {
	names := make([]string, len(users.Users))
	for i := 0; i < len(users.Users); i++ {
		names[i] = users.Users[i].UserName
	}
	return names
}

// PopulateTable adds a set of test users to the table.
func (actor DynamoActions) PopulateTable(ctx context.Context, tableName string) error {
	var err error
	var item map[string]types.AttributeValue
	var writeReqs []types.WriteRequest
	for i := 1; i < 4; i++ {
		item, err = attributevalue.MarshalMap(User{UserName: fmt.Sprintf("test_user_%v", i), UserEmail: fmt.Sprintf("test_email_%v@example.com", i)})
		if err != nil {
			log.Printf("Couldn't marshall user into DynamoDB format. Here's why: %v\n", err)
			return err
		}
		writeReqs = append(writeReqs, types.WriteRequest{PutRequest: &types.PutRequest{Item: item}})
	}
	_, err = actor.DynamoClient.BatchWriteItem(ctx, &dynamodb.BatchWriteItemInput{
		RequestItems: map[string][]types.WriteRequest{tableName: writeReqs},
	})
	if err != nil {
		log.Printf("Couldn't populate table %v with users. Here's why: %v\n", tableName, err)
	}
	return err
}

// Scan scans the table for all items.
func (actor DynamoActions) Scan(ctx context.Context, tableName string) (UserList, error) {
	var userList UserList
	output, err := actor.DynamoClient.Scan(ctx, &dynamodb.ScanInput{
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't scan table %v for items. Here's why: %v\n", tableName, err)
	} else {
		err = attributevalue.UnmarshalListOfMaps(output.Items, &userList.Users)
		if err != nil {
			log.Printf("Couldn't unmarshal items into users. Here's why: %v\n", err)
		}
	}
	return userList, err
}

// AddUser adds a user item to a table.
func (actor DynamoActions) AddUser(ctx context.Context, tableName string, user User) error {
	userItem, err := attributevalue.MarshalMap(user)
	if err != nil {
		log.Printf("Couldn't marshall user to item. Here's why: %v\n", err)
	}
	_, err = actor.DynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
		Item:      userItem,
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't put item in table %v. Here's why: %v", tableName, err)
	}
	return err
}
```
Create a struct that wraps CloudWatch Logs actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
)

type CloudWatchLogsActions struct {
	CwlClient *cloudwatchlogs.Client
}

// GetLatestLogStream gets the most recent log stream for a Lambda function.
func (actor CloudWatchLogsActions) GetLatestLogStream(ctx context.Context, functionName string) (types.LogStream, error) {
	var logStream types.LogStream
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.DescribeLogStreams(ctx, &cloudwatchlogs.DescribeLogStreamsInput{
		Descending:   aws.Bool(true),
		Limit:        aws.Int32(1),
		LogGroupName: aws.String(logGroupName),
		OrderBy:      types.OrderByLastEventTime,
	})
	if err != nil {
		log.Printf("Couldn't get log streams for log group %v. Here's why: %v\n", logGroupName, err)
	} else {
		logStream = output.LogStreams[0]
	}
	return logStream, err
}

// GetLogEvents gets the most recent eventCount events from the specified log stream.
func (actor CloudWatchLogsActions) GetLogEvents(ctx context.Context, functionName string, logStreamName string, eventCount int32) (
	[]types.OutputLogEvent, error) {
	var events []types.OutputLogEvent
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.GetLogEvents(ctx, &cloudwatchlogs.GetLogEventsInput{
		LogStreamName: aws.String(logStreamName),
		Limit:         aws.Int32(eventCount),
		LogGroupName:  aws.String(logGroupName),
	})
	if err != nil {
		log.Printf("Couldn't get log event for log stream %v. Here's why: %v\n", logStreamName, err)
	} else {
		events = output.Events
	}
	return events, err
}
```
Create a struct that wraps CloudFormation actions.  

```
import (
	"context"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
)

// StackOutputs defines a map of outputs from a specific stack.
type StackOutputs map[string]string

type CloudFormationActions struct {
	CfnClient *cloudformation.Client
}

// GetOutputs gets the outputs from a CloudFormation stack and puts them into a structured format.
func (actor CloudFormationActions) GetOutputs(ctx context.Context, stackName string) StackOutputs {
	output, err := actor.CfnClient.DescribeStacks(ctx, &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackName),
	})
	if err != nil || len(output.Stacks) == 0 {
		log.Panicf("Couldn't find a CloudFormation stack named %v. Here's why: %v\n", stackName, err)
	}
	stackOutputs := StackOutputs{}
	for _, out := range output.Stacks[0].Outputs {
		stackOutputs[*out.OutputKey] = *out.OutputValue
	}
	return stackOutputs
}
```
Clean up resources.  

```
import (
	"context"
	"log"
	"user_pools_and_lambda_triggers/actions"

	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// Resources keeps track of AWS resources created during an example and handles
// cleanup when the example finishes.
type Resources struct {
	userPoolId       string
	userAccessTokens []string
	triggers         []actions.Trigger

	cognitoActor *actions.CognitoActions
	questioner   demotools.IQuestioner
}

func (resources *Resources) init(cognitoActor *actions.CognitoActions, questioner demotools.IQuestioner) {
	resources.userAccessTokens = []string{}
	resources.triggers = []actions.Trigger{}
	resources.cognitoActor = cognitoActor
	resources.questioner = questioner
}

// Cleanup deletes all AWS resources created during an example.
func (resources *Resources) Cleanup(ctx context.Context) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("Something went wrong during cleanup.\n%v\n", r)
			log.Println("Use the AWS Management Console to remove any remaining resources \n" +
				"that were created for this scenario.")
		}
	}()

	wantDelete := resources.questioner.AskBool("Do you want to remove all of the AWS resources that were created "+
		"during this demo (y/n)?", "y")
	if wantDelete {
		for _, accessToken := range resources.userAccessTokens {
			err := resources.cognitoActor.DeleteUser(ctx, accessToken)
			if err != nil {
				log.Println("Couldn't delete user during cleanup.")
				panic(err)
			}
			log.Println("Deleted user.")
		}
		triggerList := make([]actions.TriggerInfo, len(resources.triggers))
		for i := 0; i < len(resources.triggers); i++ {
			triggerList[i] = actions.TriggerInfo{Trigger: resources.triggers[i], HandlerArn: nil}
		}
		err := resources.cognitoActor.UpdateTriggers(ctx, resources.userPoolId, triggerList...)
		if err != nil {
			log.Println("Couldn't update Cognito triggers during cleanup.")
			panic(err)
		}
		log.Println("Removed Cognito triggers from user pool.")
	} else {
		log.Println("Be sure to remove resources when you're done with them to avoid unexpected charges!")
	}
}
```
+ For API details, see the following topics in *AWS SDK for Go API Reference*.
  + [DeleteUser](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.DeleteUser)
  + [InitiateAuth](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.InitiateAuth)
  + [SignUp](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.SignUp)
  + [UpdateUserPool](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.UpdateUserPool)

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/wkflw-pools-triggers#code-examples). 
Configure an interactive "Scenario" run. The JavaScript (v3) examples share a Scenario runner to streamline complex examples. The complete source code is on GitHub.   

```
import { AutoConfirm } from "./scenario-auto-confirm.js";

/**
 * The context is passed to every scenario. Scenario steps
 * will modify the context.
 */
const context = {
  errors: [],
  users: [
    {
      UserName: "test_user_1",
      UserEmail: "test_email_1@example.com",
    },
    {
      UserName: "test_user_2",
      UserEmail: "test_email_2@example.com",
    },
    {
      UserName: "test_user_3",
      UserEmail: "test_email_3@example.com",
    },
  ],
};

/**
 * Three Scenarios are created for the workflow. A Scenario is an orchestration class
 * that simplifies running a series of steps.
 */
export const scenarios = {
  // Demonstrate automatically confirming known users in a database.
  "auto-confirm": AutoConfirm(context),
};

// Call function if run directly
import { fileURLToPath } from "node:url";
import { parseScenarioArgs } from "@aws-doc-sdk-examples/lib/scenario/index.js";

if (process.argv[1] === fileURLToPath(import.meta.url)) {
  parseScenarioArgs(scenarios, {
    name: "Cognito user pools and triggers",
    description:
      "Demonstrate how to use the AWS SDKs to customize Amazon Cognito authentication behavior.",
  });
}
```
This Scenario demonstrates auto-confirming a known user. It orchestrates the example steps.   

```
import { wait } from "@aws-doc-sdk-examples/lib/utils/util-timers.js";
import {
  Scenario,
  ScenarioAction,
  ScenarioInput,
  ScenarioOutput,
} from "@aws-doc-sdk-examples/lib/scenario/scenario.js";

import {
  getStackOutputs,
  logCleanUpReminder,
  promptForStackName,
  promptForStackRegion,
  skipWhenErrors,
} from "./steps-common.js";
import { populateTable } from "./actions/dynamodb-actions.js";
import {
  addPreSignUpHandler,
  deleteUser,
  getUser,
  signIn,
  signUpUser,
} from "./actions/cognito-actions.js";
import {
  getLatestLogStreamForLambda,
  getLogEvents,
} from "./actions/cloudwatch-logs-actions.js";

/**
 * @typedef {{
 *   errors: Error[],
 *   password: string,
 *   users: { UserName: string, UserEmail: string }[],
 *   selectedUser?: string,
 *   stackName?: string,
 *   stackRegion?: string,
 *   token?: string,
 *   confirmDeleteSignedInUser?: boolean,
 *   TableName?: string,
 *   UserPoolClientId?: string,
 *   UserPoolId?: string,
 *   UserPoolArn?: string,
 *   AutoConfirmHandlerArn?: string,
 *   AutoConfirmHandlerName?: string
 * }} State
 */

const greeting = new ScenarioOutput(
  "greeting",
  (/** @type {State} */ state) => `This demo will populate some users into the \
database created as part of the "${state.stackName}" stack. \
Then the AutoConfirmHandler will be linked to the PreSignUp \
trigger from Cognito. Finally, you will choose a user to sign up.`,
  { skipWhen: skipWhenErrors },
);

const logPopulatingUsers = new ScenarioOutput(
  "logPopulatingUsers",
  "Populating the DynamoDB table with some users.",
  { skipWhenErrors: skipWhenErrors },
);

const logPopulatingUsersComplete = new ScenarioOutput(
  "logPopulatingUsersComplete",
  "Done populating users.",
  { skipWhen: skipWhenErrors },
);

const populateUsers = new ScenarioAction(
  "populateUsers",
  async (/** @type {State} */ state) => {
    const [_, err] = await populateTable({
      region: state.stackRegion,
      tableName: state.TableName,
      items: state.users,
    });
    if (err) {
      state.errors.push(err);
    }
  },
  {
    skipWhen: skipWhenErrors,
  },
);

const logSetupSignUpTrigger = new ScenarioOutput(
  "logSetupSignUpTrigger",
  "Setting up the PreSignUp trigger for the Cognito User Pool.",
  { skipWhen: skipWhenErrors },
);

const setupSignUpTrigger = new ScenarioAction(
  "setupSignUpTrigger",
  async (/** @type {State} */ state) => {
    const [_, err] = await addPreSignUpHandler({
      region: state.stackRegion,
      userPoolId: state.UserPoolId,
      handlerArn: state.AutoConfirmHandlerArn,
    });
    if (err) {
      state.errors.push(err);
    }
  },
  {
    skipWhen: skipWhenErrors,
  },
);

const logSetupSignUpTriggerComplete = new ScenarioOutput(
  "logSetupSignUpTriggerComplete",
  (
    /** @type {State} */ state,
  ) => `The lambda function "${state.AutoConfirmHandlerName}" \
has been configured as the PreSignUp trigger handler for the user pool "${state.UserPoolId}".`,
  { skipWhen: skipWhenErrors },
);

const selectUser = new ScenarioInput(
  "selectedUser",
  "Select a user to sign up.",
  {
    type: "select",
    choices: (/** @type {State} */ state) => state.users.map((u) => u.UserName),
    skipWhen: skipWhenErrors,
    default: (/** @type {State} */ state) => state.users[0].UserName,
  },
);

const checkIfUserAlreadyExists = new ScenarioAction(
  "checkIfUserAlreadyExists",
  async (/** @type {State} */ state) => {
    const [user, err] = await getUser({
      region: state.stackRegion,
      userPoolId: state.UserPoolId,
      username: state.selectedUser,
    });

    if (err?.name === "UserNotFoundException") {
      // Do nothing. We're not expecting the user to exist before
      // sign up is complete.
      return;
    }

    if (err) {
      state.errors.push(err);
      return;
    }

    if (user) {
      state.errors.push(
        new Error(
          `The user "${state.selectedUser}" already exists in the user pool "${state.UserPoolId}".`,
        ),
      );
    }
  },
  {
    skipWhen: skipWhenErrors,
  },
);

const createPassword = new ScenarioInput(
  "password",
  "Enter a password that has at least eight characters, uppercase, lowercase, numbers and symbols.",
  { type: "password", skipWhen: skipWhenErrors, default: "Abcd1234!" },
);

const logSignUpExistingUser = new ScenarioOutput(
  "logSignUpExistingUser",
  (/** @type {State} */ state) => `Signing up user "${state.selectedUser}".`,
  { skipWhen: skipWhenErrors },
);

const signUpExistingUser = new ScenarioAction(
  "signUpExistingUser",
  async (/** @type {State} */ state) => {
    const signUp = (password) =>
      signUpUser({
        region: state.stackRegion,
        userPoolClientId: state.UserPoolClientId,
        username: state.selectedUser,
        email: state.users.find((u) => u.UserName === state.selectedUser)
          .UserEmail,
        password,
      });

    let [_, err] = await signUp(state.password);

    while (err?.name === "InvalidPasswordException") {
      console.warn("The password you entered was invalid.");
      await createPassword.handle(state);
      [_, err] = await signUp(state.password);
    }

    if (err) {
      state.errors.push(err);
    }
  },
  { skipWhen: skipWhenErrors },
);

const logSignUpExistingUserComplete = new ScenarioOutput(
  "logSignUpExistingUserComplete",
  (/** @type {State} */ state) =>
    `"${state.selectedUser} was signed up successfully.`,
  { skipWhen: skipWhenErrors },
);

const logLambdaLogs = new ScenarioAction(
  "logLambdaLogs",
  async (/** @type {State} */ state) => {
    console.log(
      "Waiting a few seconds to let Lambda write to CloudWatch Logs...\n",
    );
    await wait(10);

    const [logStream, logStreamErr] = await getLatestLogStreamForLambda({
      functionName: state.AutoConfirmHandlerName,
      region: state.stackRegion,
    });
    if (logStreamErr) {
      state.errors.push(logStreamErr);
      return;
    }

    console.log(
      `Getting some recent events from log stream "${logStream.logStreamName}"`,
    );
    const [logEvents, logEventsErr] = await getLogEvents({
      functionName: state.AutoConfirmHandlerName,
      region: state.stackRegion,
      eventCount: 10,
      logStreamName: logStream.logStreamName,
    });
    if (logEventsErr) {
      state.errors.push(logEventsErr);
      return;
    }

    console.log(logEvents.map((ev) => `\t${ev.message}`).join(""));
  },
  { skipWhen: skipWhenErrors },
);

const logSignInUser = new ScenarioOutput(
  "logSignInUser",
  (/** @type {State} */ state) => `Let's sign in as ${state.selectedUser}`,
  { skipWhen: skipWhenErrors },
);

const signInUser = new ScenarioAction(
  "signInUser",
  async (/** @type {State} */ state) => {
    const [response, err] = await signIn({
      region: state.stackRegion,
      clientId: state.UserPoolClientId,
      username: state.selectedUser,
      password: state.password,
    });

    if (err?.name === "PasswordResetRequiredException") {
      state.errors.push(new Error("Please reset your password."));
      return;
    }

    if (err) {
      state.errors.push(err);
      return;
    }

    state.token = response?.AuthenticationResult?.AccessToken;
  },
  { skipWhen: skipWhenErrors },
);

const logSignInUserComplete = new ScenarioOutput(
  "logSignInUserComplete",
  (/** @type {State} */ state) =>
    `Successfully signed in. Your access token starts with: ${state.token.slice(0, 11)}`,
  { skipWhen: skipWhenErrors },
);

const confirmDeleteSignedInUser = new ScenarioInput(
  "confirmDeleteSignedInUser",
  "Do you want to delete the currently signed in user?",
  { type: "confirm", skipWhen: skipWhenErrors },
);

const deleteSignedInUser = new ScenarioAction(
  "deleteSignedInUser",
  async (/** @type {State} */ state) => {
    const [_, err] = await deleteUser({
      region: state.stackRegion,
      accessToken: state.token,
    });

    if (err) {
      state.errors.push(err);
    }
  },
  {
    skipWhen: (/** @type {State} */ state) =>
      skipWhenErrors(state) || !state.confirmDeleteSignedInUser,
  },
);

const logErrors = new ScenarioOutput(
  "logErrors",
  (/** @type {State}*/ state) => {
    const errorList = state.errors
      .map((err) => ` - ${err.name}: ${err.message}`)
      .join("\n");
    return `Scenario errors found:\n${errorList}`;
  },
  {
    // Don't log errors when there aren't any!
    skipWhen: (/** @type {State} */ state) => state.errors.length === 0,
  },
);

export const AutoConfirm = (context) =>
  new Scenario(
    "AutoConfirm",
    [
      promptForStackName,
      promptForStackRegion,
      getStackOutputs,
      greeting,
      logPopulatingUsers,
      populateUsers,
      logPopulatingUsersComplete,
      logSetupSignUpTrigger,
      setupSignUpTrigger,
      logSetupSignUpTriggerComplete,
      selectUser,
      checkIfUserAlreadyExists,
      createPassword,
      logSignUpExistingUser,
      signUpExistingUser,
      logSignUpExistingUserComplete,
      logLambdaLogs,
      logSignInUser,
      signInUser,
      logSignInUserComplete,
      confirmDeleteSignedInUser,
      deleteSignedInUser,
      logCleanUpReminder,
      logErrors,
    ],
    context,
  );
```
These are steps that are shared with other Scenarios.  

```
import {
  ScenarioAction,
  ScenarioInput,
  ScenarioOutput,
} from "@aws-doc-sdk-examples/lib/scenario/scenario.js";
import { getCfnOutputs } from "@aws-doc-sdk-examples/lib/sdk/cfn-outputs.js";

export const skipWhenErrors = (state) => state.errors.length > 0;

export const getStackOutputs = new ScenarioAction(
  "getStackOutputs",
  async (state) => {
    if (!state.stackName || !state.stackRegion) {
      state.errors.push(
        new Error(
          "No stack name or region provided. The stack name and \
region are required to fetch CFN outputs relevant to this example.",
        ),
      );
      return;
    }

    const outputs = await getCfnOutputs(state.stackName, state.stackRegion);
    Object.assign(state, outputs);
  },
);

export const promptForStackName = new ScenarioInput(
  "stackName",
  "Enter the name of the stack you deployed earlier.",
  { type: "input", default: "PoolsAndTriggersStack" },
);

export const promptForStackRegion = new ScenarioInput(
  "stackRegion",
  "Enter the region of the stack you deployed earlier.",
  { type: "input", default: "us-east-1" },
);

export const logCleanUpReminder = new ScenarioOutput(
  "logCleanUpReminder",
  "All done. Remember to run 'cdk destroy' to teardown the stack.",
  { skipWhen: skipWhenErrors },
);
```
A handler for the `PreSignUp` trigger with a Lambda function.  

```
import type { PreSignUpTriggerEvent, Handler } from "aws-lambda";
import type { UserRepository } from "./user-repository";
import { DynamoDBUserRepository } from "./user-repository";

export class PreSignUpHandler {
  private userRepository: UserRepository;

  constructor(userRepository: UserRepository) {
    this.userRepository = userRepository;
  }

  private isPreSignUpTriggerSource(event: PreSignUpTriggerEvent): boolean {
    return event.triggerSource === "PreSignUp_SignUp";
  }

  private getEventUserEmail(event: PreSignUpTriggerEvent): string {
    return event.request.userAttributes.email;
  }

  async handlePreSignUpTriggerEvent(
    event: PreSignUpTriggerEvent,
  ): Promise<PreSignUpTriggerEvent> {
    console.log(
      `Received presignup from ${event.triggerSource} for user '${event.userName}'`,
    );

    if (!this.isPreSignUpTriggerSource(event)) {
      return event;
    }

    const eventEmail = this.getEventUserEmail(event);
    console.log(`Looking up email ${eventEmail}.`);
    const storedUserInfo =
      await this.userRepository.getUserInfoByEmail(eventEmail);

    if (!storedUserInfo) {
      console.log(
        `Email ${eventEmail} not found. Email verification is required.`,
      );
      return event;
    }

    if (storedUserInfo.UserName !== event.userName) {
      console.log(
        `UserEmail ${eventEmail} found, but stored UserName '${storedUserInfo.UserName}' does not match supplied UserName '${event.userName}'. Verification is required.`,
      );
    } else {
      console.log(
        `UserEmail ${eventEmail} found with matching UserName ${storedUserInfo.UserName}. User is confirmed.`,
      );
      event.response.autoConfirmUser = true;
      event.response.autoVerifyEmail = true;
    }
    return event;
  }
}

const createPreSignUpHandler = (): PreSignUpHandler => {
  const tableName = process.env.TABLE_NAME;
  if (!tableName) {
    throw new Error("TABLE_NAME environment variable is not set");
  }

  const userRepository = new DynamoDBUserRepository(tableName);
  return new PreSignUpHandler(userRepository);
};

export const handler: Handler = async (event: PreSignUpTriggerEvent) => {
  const preSignUpHandler = createPreSignUpHandler();
  return preSignUpHandler.handlePreSignUpTriggerEvent(event);
};
```
Module of CloudWatch Logs actions.  

```
import {
  CloudWatchLogsClient,
  GetLogEventsCommand,
  OrderBy,
  paginateDescribeLogStreams,
} from "@aws-sdk/client-cloudwatch-logs";

/**
 * Get the latest log stream for a Lambda function.
 * @param {{ functionName: string, region: string }} config
 * @returns {Promise<[import("@aws-sdk/client-cloudwatch-logs").LogStream | null, unknown]>}
 */
export const getLatestLogStreamForLambda = async ({ functionName, region }) => {
  try {
    const logGroupName = `/aws/lambda/${functionName}`;
    const cwlClient = new CloudWatchLogsClient({ region });
    const paginator = paginateDescribeLogStreams(
      { client: cwlClient },
      {
        descending: true,
        limit: 1,
        orderBy: OrderBy.LastEventTime,
        logGroupName,
      },
    );

    for await (const page of paginator) {
      return [page.logStreams[0], null];
    }
  } catch (err) {
    return [null, err];
  }
};

/**
 * Get the log events for a Lambda function's log stream.
 * @param {{
 *   functionName: string,
 *   logStreamName: string,
 *   eventCount: number,
 *   region: string
 * }} config
 * @returns {Promise<[import("@aws-sdk/client-cloudwatch-logs").OutputLogEvent[] | null, unknown]>}
 */
export const getLogEvents = async ({
  functionName,
  logStreamName,
  eventCount,
  region,
}) => {
  try {
    const cwlClient = new CloudWatchLogsClient({ region });
    const logGroupName = `/aws/lambda/${functionName}`;
    const response = await cwlClient.send(
      new GetLogEventsCommand({
        logStreamName: logStreamName,
        limit: eventCount,
        logGroupName: logGroupName,
      }),
    );

    return [response.events, null];
  } catch (err) {
    return [null, err];
  }
};
```
Module of Amazon Cognito actions.  

```
import {
  AdminGetUserCommand,
  CognitoIdentityProviderClient,
  DeleteUserCommand,
  InitiateAuthCommand,
  SignUpCommand,
  UpdateUserPoolCommand,
} from "@aws-sdk/client-cognito-identity-provider";

/**
 * Connect a Lambda function to the PreSignUp trigger for a Cognito user pool
 * @param {{ region: string, userPoolId: string, handlerArn: string }} config
 * @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").UpdateUserPoolCommandOutput | null, unknown]>}
 */
export const addPreSignUpHandler = async ({
  region,
  userPoolId,
  handlerArn,
}) => {
  try {
    const cognitoClient = new CognitoIdentityProviderClient({
      region,
    });

    const command = new UpdateUserPoolCommand({
      UserPoolId: userPoolId,
      LambdaConfig: {
        PreSignUp: handlerArn,
      },
    });

    const response = await cognitoClient.send(command);
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};

/**
 * Attempt to register a user to a user pool with a given username and password.
 * @param {{
 *   region: string,
 *   userPoolClientId: string,
 *   username: string,
 *   email: string,
 *   password: string
 * }} config
 * @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").SignUpCommandOutput | null, unknown]>}
 */
export const signUpUser = async ({
  region,
  userPoolClientId,
  username,
  email,
  password,
}) => {
  try {
    const cognitoClient = new CognitoIdentityProviderClient({
      region,
    });

    const response = await cognitoClient.send(
      new SignUpCommand({
        ClientId: userPoolClientId,
        Username: username,
        Password: password,
        UserAttributes: [{ Name: "email", Value: email }],
      }),
    );
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};

/**
 * Sign in a user to Amazon Cognito using a username and password authentication flow.
 * @param {{ region: string, clientId: string, username: string, password: string }} config
 * @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").InitiateAuthCommandOutput | null, unknown]>}
 */
export const signIn = async ({ region, clientId, username, password }) => {
  try {
    const cognitoClient = new CognitoIdentityProviderClient({ region });
    const response = await cognitoClient.send(
      new InitiateAuthCommand({
        AuthFlow: "USER_PASSWORD_AUTH",
        ClientId: clientId,
        AuthParameters: { USERNAME: username, PASSWORD: password },
      }),
    );
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};

/**
 * Retrieve an existing user from a user pool.
 * @param {{ region: string, userPoolId: string, username: string }} config
 * @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").AdminGetUserCommandOutput | null, unknown]>}
 */
export const getUser = async ({ region, userPoolId, username }) => {
  try {
    const cognitoClient = new CognitoIdentityProviderClient({ region });
    const response = await cognitoClient.send(
      new AdminGetUserCommand({
        UserPoolId: userPoolId,
        Username: username,
      }),
    );
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};

/**
 * Delete the signed-in user. Useful for allowing a user to delete their
 * own profile.
 * @param {{ region: string, accessToken: string }} config
 * @returns {Promise<[import("@aws-sdk/client-cognito-identity-provider").DeleteUserCommandOutput | null, unknown]>}
 */
export const deleteUser = async ({ region, accessToken }) => {
  try {
    const client = new CognitoIdentityProviderClient({ region });
    const response = await client.send(
      new DeleteUserCommand({ AccessToken: accessToken }),
    );
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};
```
Module of DynamoDB actions.  

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
  BatchWriteCommand,
  DynamoDBDocumentClient,
} from "@aws-sdk/lib-dynamodb";

/**
 * Populate a DynamoDB table with provide items.
 * @param {{ region: string, tableName: string, items: Record<string, unknown>[] }} config
 * @returns {Promise<[import("@aws-sdk/lib-dynamodb").BatchWriteCommandOutput | null, unknown]>}
 */
export const populateTable = async ({ region, tableName, items }) => {
  try {
    const ddbClient = new DynamoDBClient({ region });
    const docClient = DynamoDBDocumentClient.from(ddbClient);
    const response = await docClient.send(
      new BatchWriteCommand({
        RequestItems: {
          [tableName]: items.map((item) => ({
            PutRequest: {
              Item: item,
            },
          })),
        },
      }),
    );
    return [response, null];
  } catch (err) {
    return [null, err];
  }
};
```
+ For API details, see the following topics in *AWS SDK for JavaScript API Reference*.
  + [DeleteUser](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/command/DeleteUserCommand)
  + [InitiateAuth](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/command/InitiateAuthCommand)
  + [SignUp](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/command/SignUpCommand)
  + [UpdateUserPool](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/cognito-identity-provider/command/UpdateUserPoolCommand)

------

# Automatically migrate known Amazon Cognito users with a Lambda function using an AWS SDK
<a name="lambda_example_cross_CognitoAutoMigrateUser_section"></a>

The following code example shows how to automatically migrate known Amazon Cognito users with a Lambda function.
+ Configure a user pool to call a Lambda function for the `MigrateUser` trigger.
+ Sign in to Amazon Cognito with a username and email that is not in the user pool.
+ The Lambda function scans a DynamoDB table and automatically migrates known users to the user pool.
+ Perform the forgot password flow to reset the password for the migrated user.
+ Sign in as the new user, then clean up resources.

------
#### [ Go ]

**SDK for Go V2**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/gov2/workflows/user_pools_and_lambda_triggers#code-examples). 
Run an interactive scenario at a command prompt.  

```
import (
	"context"
	"errors"
	"fmt"
	"log"
	"strings"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// MigrateUser separates the steps of this scenario into individual functions so that
// they are simpler to read and understand.
type MigrateUser struct {
	helper       IScenarioHelper
	questioner   demotools.IQuestioner
	resources    Resources
	cognitoActor *actions.CognitoActions
}

// NewMigrateUser constructs a new migrate user runner.
func NewMigrateUser(sdkConfig aws.Config, questioner demotools.IQuestioner, helper IScenarioHelper) MigrateUser {
	scenario := MigrateUser{
		helper:       helper,
		questioner:   questioner,
		resources:    Resources{},
		cognitoActor: &actions.CognitoActions{CognitoClient: cognitoidentityprovider.NewFromConfig(sdkConfig)},
	}
	scenario.resources.init(scenario.cognitoActor, questioner)
	return scenario
}

// AddMigrateUserTrigger adds a Lambda handler as an invocation target for the MigrateUser trigger.
func (runner *MigrateUser) AddMigrateUserTrigger(ctx context.Context, userPoolId string, functionArn string) {
	log.Printf("Let's add a Lambda function to handle the MigrateUser trigger from Cognito.\n" +
		"This trigger happens when an unknown user signs in, and lets your function take action before Cognito\n" +
		"rejects the user.\n\n")
	err := runner.cognitoActor.UpdateTriggers(
		ctx, userPoolId,
		actions.TriggerInfo{Trigger: actions.UserMigration, HandlerArn: aws.String(functionArn)})
	if err != nil {
		panic(err)
	}
	log.Printf("Lambda function %v added to user pool %v to handle the MigrateUser trigger.\n",
		functionArn, userPoolId)

	log.Println(strings.Repeat("-", 88))
}

// SignInUser adds a new user to the known users table and signs that user in to Amazon Cognito.
func (runner *MigrateUser) SignInUser(ctx context.Context, usersTable string, clientId string) (bool, actions.User) {
	log.Println("Let's sign in a user to your Cognito user pool. When the username and email matches an entry in the\n" +
		"DynamoDB known users table, the email is automatically verified and the user is migrated to the Cognito user pool.")

	user := actions.User{}
	user.UserName = runner.questioner.Ask("\nEnter a username:")
	user.UserEmail = runner.questioner.Ask("\nEnter an email that you own. This email will be used to confirm user migration\n" +
		"during this example:")

	runner.helper.AddKnownUser(ctx, usersTable, user)

	var err error
	var resetRequired *types.PasswordResetRequiredException
	var authResult *types.AuthenticationResultType
	signedIn := false
	for !signedIn && resetRequired == nil {
		log.Printf("Signing in to Cognito as user '%v'. The expected result is a PasswordResetRequiredException.\n\n", user.UserName)
		authResult, err = runner.cognitoActor.SignIn(ctx, clientId, user.UserName, "_")
		if err != nil {
			if errors.As(err, &resetRequired) {
				log.Printf("\nUser '%v' is not in the Cognito user pool but was found in the DynamoDB known users table.\n"+
					"User migration is started and a password reset is required.", user.UserName)
			} else {
				panic(err)
			}
		} else {
			log.Printf("User '%v' successfully signed in. This is unexpected and probably means you have not\n"+
				"cleaned up a previous run of this scenario, so the user exist in the Cognito user pool.\n"+
				"You can continue this example and select to clean up resources, or manually remove\n"+
				"the user from your user pool and try again.", user.UserName)
			runner.resources.userAccessTokens = append(runner.resources.userAccessTokens, *authResult.AccessToken)
			signedIn = true
		}
	}

	log.Println(strings.Repeat("-", 88))
	return resetRequired != nil, user
}

// ResetPassword starts a password recovery flow.
func (runner *MigrateUser) ResetPassword(ctx context.Context, clientId string, user actions.User) {
	wantCode := runner.questioner.AskBool(fmt.Sprintf("In order to migrate the user to Cognito, you must be able to receive a confirmation\n"+
		"code by email at %v. Do you want to send a code (y/n)?", user.UserEmail), "y")
	if !wantCode {
		log.Println("To complete this example and successfully migrate a user to Cognito, you must enter an email\n" +
			"you own that can receive a confirmation code.")
		return
	}
	codeDelivery, err := runner.cognitoActor.ForgotPassword(ctx, clientId, user.UserName)
	if err != nil {
		panic(err)
	}
	log.Printf("\nA confirmation code has been sent to %v.", *codeDelivery.Destination)
	code := runner.questioner.Ask("Check your email and enter it here:")

	confirmed := false
	password := runner.questioner.AskPassword("\nEnter a password that has at least eight characters, uppercase, lowercase, numbers and symbols.\n"+
		"(the password will not display as you type):", 8)
	for !confirmed {
		log.Printf("\nConfirming password reset for user '%v'.\n", user.UserName)
		err = runner.cognitoActor.ConfirmForgotPassword(ctx, clientId, code, user.UserName, password)
		if err != nil {
			var invalidPassword *types.InvalidPasswordException
			if errors.As(err, &invalidPassword) {
				password = runner.questioner.AskPassword("\nEnter another password:", 8)
			} else {
				panic(err)
			}
		} else {
			confirmed = true
		}
	}
	log.Printf("User '%v' successfully confirmed and migrated.\n", user.UserName)
	log.Println("Signing in with your username and password...")
	authResult, err := runner.cognitoActor.SignIn(ctx, clientId, user.UserName, password)
	if err != nil {
		panic(err)
	}
	log.Printf("Successfully signed in. Your access token starts with: %v...\n", (*authResult.AccessToken)[:10])
	runner.resources.userAccessTokens = append(runner.resources.userAccessTokens, *authResult.AccessToken)

	log.Println(strings.Repeat("-", 88))
}

// Run runs the scenario.
func (runner *MigrateUser) Run(ctx context.Context, stackName string) {
	defer func() {
		if r := recover(); r != nil {
			log.Println("Something went wrong with the demo.")
			runner.resources.Cleanup(ctx)
		}
	}()

	log.Println(strings.Repeat("-", 88))
	log.Printf("Welcome\n")

	log.Println(strings.Repeat("-", 88))

	stackOutputs, err := runner.helper.GetStackOutputs(ctx, stackName)
	if err != nil {
		panic(err)
	}
	runner.resources.userPoolId = stackOutputs["UserPoolId"]

	runner.AddMigrateUserTrigger(ctx, stackOutputs["UserPoolId"], stackOutputs["MigrateUserFunctionArn"])
	runner.resources.triggers = append(runner.resources.triggers, actions.UserMigration)
	resetNeeded, user := runner.SignInUser(ctx, stackOutputs["TableName"], stackOutputs["UserPoolClientId"])
	if resetNeeded {
		runner.helper.ListRecentLogEvents(ctx, stackOutputs["MigrateUserFunction"])
		runner.ResetPassword(ctx, stackOutputs["UserPoolClientId"], user)
	}

	runner.resources.Cleanup(ctx)

	log.Println(strings.Repeat("-", 88))
	log.Println("Thanks for watching!")
	log.Println(strings.Repeat("-", 88))
}
```
Handle the `MigrateUser` trigger with a Lambda function.  

```
import (
	"context"
	"log"
	"os"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
)

const TABLE_NAME = "TABLE_NAME"

// UserInfo defines structured user data that can be marshalled to a DynamoDB format.
type UserInfo struct {
	UserName  string `dynamodbav:"UserName"`
	UserEmail string `dynamodbav:"UserEmail"`
}

type handler struct {
	dynamoClient *dynamodb.Client
}

// HandleRequest handles the MigrateUser event by looking up a user in an Amazon DynamoDB table and
// specifying whether they should be migrated to the user pool.
func (h *handler) HandleRequest(ctx context.Context, event events.CognitoEventUserPoolsMigrateUser) (events.CognitoEventUserPoolsMigrateUser, error) {
	log.Printf("Received migrate trigger from %v for user '%v'", event.TriggerSource, event.UserName)
	if event.TriggerSource != "UserMigration_Authentication" {
		return event, nil
	}
	tableName := os.Getenv(TABLE_NAME)
	user := UserInfo{
		UserName: event.UserName,
	}
	log.Printf("Looking up user '%v' in table %v.\n", user.UserName, tableName)
	filterEx := expression.Name("UserName").Equal(expression.Value(user.UserName))
	expr, err := expression.NewBuilder().WithFilter(filterEx).Build()
	if err != nil {
		log.Printf("Error building expression to query for user '%v'.\n", user.UserName)
		return event, err
	}
	output, err := h.dynamoClient.Scan(ctx, &dynamodb.ScanInput{
		TableName:                 aws.String(tableName),
		FilterExpression:          expr.Filter(),
		ExpressionAttributeNames:  expr.Names(),
		ExpressionAttributeValues: expr.Values(),
	})
	if err != nil {
		log.Printf("Error looking up user '%v'.\n", user.UserName)
		return event, err
	}
	if len(output.Items) == 0 {
		log.Printf("User '%v' not found, not migrating user.\n", user.UserName)
		return event, err
	}

	var users []UserInfo
	err = attributevalue.UnmarshalListOfMaps(output.Items, &users)
	if err != nil {
		log.Printf("Couldn't unmarshal DynamoDB items. Here's why: %v\n", err)
		return event, err
	}

	user = users[0]
	log.Printf("UserName '%v' found with email %v. User is migrated and must reset password.\n", user.UserName, user.UserEmail)
	event.CognitoEventUserPoolsMigrateUserResponse.UserAttributes = map[string]string{
		"email":          user.UserEmail,
		"email_verified": "true", // email_verified is required for the forgot password flow.
	}
	event.CognitoEventUserPoolsMigrateUserResponse.FinalUserStatus = "RESET_REQUIRED"
	event.CognitoEventUserPoolsMigrateUserResponse.MessageAction = "SUPPRESS"

	return event, err
}

func main() {
	ctx := context.Background()
	sdkConfig, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		log.Panicln(err)
	}
	h := handler{
		dynamoClient: dynamodb.NewFromConfig(sdkConfig),
	}
	lambda.Start(h.HandleRequest)
}
```
Create a struct that performs common tasks.  

```
import (
	"context"
	"log"
	"strings"
	"time"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// IScenarioHelper defines common functions used by the workflows in this example.
type IScenarioHelper interface {
	Pause(secs int)
	GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error)
	PopulateUserTable(ctx context.Context, tableName string)
	GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error)
	AddKnownUser(ctx context.Context, tableName string, user actions.User)
	ListRecentLogEvents(ctx context.Context, functionName string)
}

// ScenarioHelper contains AWS wrapper structs used by the workflows in this example.
type ScenarioHelper struct {
	questioner  demotools.IQuestioner
	dynamoActor *actions.DynamoActions
	cfnActor    *actions.CloudFormationActions
	cwlActor    *actions.CloudWatchLogsActions
	isTestRun   bool
}

// NewScenarioHelper constructs a new scenario helper.
func NewScenarioHelper(sdkConfig aws.Config, questioner demotools.IQuestioner) ScenarioHelper {
	scenario := ScenarioHelper{
		questioner:  questioner,
		dynamoActor: &actions.DynamoActions{DynamoClient: dynamodb.NewFromConfig(sdkConfig)},
		cfnActor:    &actions.CloudFormationActions{CfnClient: cloudformation.NewFromConfig(sdkConfig)},
		cwlActor:    &actions.CloudWatchLogsActions{CwlClient: cloudwatchlogs.NewFromConfig(sdkConfig)},
	}
	return scenario
}

// Pause waits for the specified number of seconds.
func (helper ScenarioHelper) Pause(secs int) {
	if !helper.isTestRun {
		time.Sleep(time.Duration(secs) * time.Second)
	}
}

// GetStackOutputs gets the outputs from the specified CloudFormation stack in a structured format.
func (helper ScenarioHelper) GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error) {
	return helper.cfnActor.GetOutputs(ctx, stackName), nil
}

// PopulateUserTable fills the known user table with example data.
func (helper ScenarioHelper) PopulateUserTable(ctx context.Context, tableName string) {
	log.Printf("First, let's add some users to the DynamoDB %v table we'll use for this example.\n", tableName)
	err := helper.dynamoActor.PopulateTable(ctx, tableName)
	if err != nil {
		panic(err)
	}
}

// GetKnownUsers gets the users from the known users table in a structured format.
func (helper ScenarioHelper) GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error) {
	knownUsers, err := helper.dynamoActor.Scan(ctx, tableName)
	if err != nil {
		log.Printf("Couldn't get known users from table %v. Here's why: %v\n", tableName, err)
	}
	return knownUsers, err
}

// AddKnownUser adds a user to the known users table.
func (helper ScenarioHelper) AddKnownUser(ctx context.Context, tableName string, user actions.User) {
	log.Printf("Adding user '%v' with email '%v' to the DynamoDB known users table...\n",
		user.UserName, user.UserEmail)
	err := helper.dynamoActor.AddUser(ctx, tableName, user)
	if err != nil {
		panic(err)
	}
}

// ListRecentLogEvents gets the most recent log stream and events for the specified Lambda function and displays them.
func (helper ScenarioHelper) ListRecentLogEvents(ctx context.Context, functionName string) {
	log.Println("Waiting a few seconds to let Lambda write to CloudWatch Logs...")
	helper.Pause(10)
	log.Println("Okay, let's check the logs to find what's happened recently with your Lambda function.")
	logStream, err := helper.cwlActor.GetLatestLogStream(ctx, functionName)
	if err != nil {
		panic(err)
	}
	log.Printf("Getting some recent events from log stream %v\n", *logStream.LogStreamName)
	events, err := helper.cwlActor.GetLogEvents(ctx, functionName, *logStream.LogStreamName, 10)
	if err != nil {
		panic(err)
	}
	for _, event := range events {
		log.Printf("\t%v", *event.Message)
	}
	log.Println(strings.Repeat("-", 88))
}
```
Create a struct that wraps Amazon Cognito actions.  

```
import (
	"context"
	"errors"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
)

type CognitoActions struct {
	CognitoClient *cognitoidentityprovider.Client
}



// Trigger and TriggerInfo define typed data for updating an Amazon Cognito trigger.
type Trigger int

const (
	PreSignUp Trigger = iota
	UserMigration
	PostAuthentication
)

type TriggerInfo struct {
	Trigger    Trigger
	HandlerArn *string
}

// UpdateTriggers adds or removes Lambda triggers for a user pool. When a trigger is specified with a `nil` value,
// it is removed from the user pool.
func (actor CognitoActions) UpdateTriggers(ctx context.Context, userPoolId string, triggers ...TriggerInfo) error {
	output, err := actor.CognitoClient.DescribeUserPool(ctx, &cognitoidentityprovider.DescribeUserPoolInput{
		UserPoolId: aws.String(userPoolId),
	})
	if err != nil {
		log.Printf("Couldn't get info about user pool %v. Here's why: %v\n", userPoolId, err)
		return err
	}
	lambdaConfig := output.UserPool.LambdaConfig
	for _, trigger := range triggers {
		switch trigger.Trigger {
		case PreSignUp:
			lambdaConfig.PreSignUp = trigger.HandlerArn
		case UserMigration:
			lambdaConfig.UserMigration = trigger.HandlerArn
		case PostAuthentication:
			lambdaConfig.PostAuthentication = trigger.HandlerArn
		}
	}
	_, err = actor.CognitoClient.UpdateUserPool(ctx, &cognitoidentityprovider.UpdateUserPoolInput{
		UserPoolId:   aws.String(userPoolId),
		LambdaConfig: lambdaConfig,
	})
	if err != nil {
		log.Printf("Couldn't update user pool %v. Here's why: %v\n", userPoolId, err)
	}
	return err
}



// SignUp signs up a user with Amazon Cognito.
func (actor CognitoActions) SignUp(ctx context.Context, clientId string, userName string, password string, userEmail string) (bool, error) {
	confirmed := false
	output, err := actor.CognitoClient.SignUp(ctx, &cognitoidentityprovider.SignUpInput{
		ClientId: aws.String(clientId),
		Password: aws.String(password),
		Username: aws.String(userName),
		UserAttributes: []types.AttributeType{
			{Name: aws.String("email"), Value: aws.String(userEmail)},
		},
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't sign up user %v. Here's why: %v\n", userName, err)
		}
	} else {
		confirmed = output.UserConfirmed
	}
	return confirmed, err
}



// SignIn signs in a user to Amazon Cognito using a username and password authentication flow.
func (actor CognitoActions) SignIn(ctx context.Context, clientId string, userName string, password string) (*types.AuthenticationResultType, error) {
	var authResult *types.AuthenticationResultType
	output, err := actor.CognitoClient.InitiateAuth(ctx, &cognitoidentityprovider.InitiateAuthInput{
		AuthFlow:       "USER_PASSWORD_AUTH",
		ClientId:       aws.String(clientId),
		AuthParameters: map[string]string{"USERNAME": userName, "PASSWORD": password},
	})
	if err != nil {
		var resetRequired *types.PasswordResetRequiredException
		if errors.As(err, &resetRequired) {
			log.Println(*resetRequired.Message)
		} else {
			log.Printf("Couldn't sign in user %v. Here's why: %v\n", userName, err)
		}
	} else {
		authResult = output.AuthenticationResult
	}
	return authResult, err
}



// ForgotPassword starts a password recovery flow for a user. This flow typically sends a confirmation code
// to the user's configured notification destination, such as email.
func (actor CognitoActions) ForgotPassword(ctx context.Context, clientId string, userName string) (*types.CodeDeliveryDetailsType, error) {
	output, err := actor.CognitoClient.ForgotPassword(ctx, &cognitoidentityprovider.ForgotPasswordInput{
		ClientId: aws.String(clientId),
		Username: aws.String(userName),
	})
	if err != nil {
		log.Printf("Couldn't start password reset for user '%v'. Here;s why: %v\n", userName, err)
	}
	return output.CodeDeliveryDetails, err
}



// ConfirmForgotPassword confirms a user with a confirmation code and a new password.
func (actor CognitoActions) ConfirmForgotPassword(ctx context.Context, clientId string, code string, userName string, password string) error {
	_, err := actor.CognitoClient.ConfirmForgotPassword(ctx, &cognitoidentityprovider.ConfirmForgotPasswordInput{
		ClientId:         aws.String(clientId),
		ConfirmationCode: aws.String(code),
		Password:         aws.String(password),
		Username:         aws.String(userName),
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't confirm user %v. Here's why: %v", userName, err)
		}
	}
	return err
}



// DeleteUser removes a user from the user pool.
func (actor CognitoActions) DeleteUser(ctx context.Context, userAccessToken string) error {
	_, err := actor.CognitoClient.DeleteUser(ctx, &cognitoidentityprovider.DeleteUserInput{
		AccessToken: aws.String(userAccessToken),
	})
	if err != nil {
		log.Printf("Couldn't delete user. Here's why: %v\n", err)
	}
	return err
}



// AdminCreateUser uses administrator credentials to add a user to a user pool. This method leaves the user
// in a state that requires they enter a new password next time they sign in.
func (actor CognitoActions) AdminCreateUser(ctx context.Context, userPoolId string, userName string, userEmail string) error {
	_, err := actor.CognitoClient.AdminCreateUser(ctx, &cognitoidentityprovider.AdminCreateUserInput{
		UserPoolId:     aws.String(userPoolId),
		Username:       aws.String(userName),
		MessageAction:  types.MessageActionTypeSuppress,
		UserAttributes: []types.AttributeType{{Name: aws.String("email"), Value: aws.String(userEmail)}},
	})
	if err != nil {
		var userExists *types.UsernameExistsException
		if errors.As(err, &userExists) {
			log.Printf("User %v already exists in the user pool.", userName)
			err = nil
		} else {
			log.Printf("Couldn't create user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}



// AdminSetUserPassword uses administrator credentials to set a password for a user without requiring a
// temporary password.
func (actor CognitoActions) AdminSetUserPassword(ctx context.Context, userPoolId string, userName string, password string) error {
	_, err := actor.CognitoClient.AdminSetUserPassword(ctx, &cognitoidentityprovider.AdminSetUserPasswordInput{
		Password:   aws.String(password),
		UserPoolId: aws.String(userPoolId),
		Username:   aws.String(userName),
		Permanent:  true,
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't set password for user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}
```
Create a struct that wraps DynamoDB actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

// DynamoActions encapsulates the Amazon Simple Notification Service (Amazon SNS) actions
// used in the examples.
type DynamoActions struct {
	DynamoClient *dynamodb.Client
}

// User defines structured user data.
type User struct {
	UserName  string
	UserEmail string
	LastLogin *LoginInfo `dynamodbav:",omitempty"`
}

// LoginInfo defines structured custom login data.
type LoginInfo struct {
	UserPoolId string
	ClientId   string
	Time       string
}

// UserList defines a list of users.
type UserList struct {
	Users []User
}

// UserNameList returns the usernames contained in a UserList as a list of strings.
func (users *UserList) UserNameList() []string {
	names := make([]string, len(users.Users))
	for i := 0; i < len(users.Users); i++ {
		names[i] = users.Users[i].UserName
	}
	return names
}

// PopulateTable adds a set of test users to the table.
func (actor DynamoActions) PopulateTable(ctx context.Context, tableName string) error {
	var err error
	var item map[string]types.AttributeValue
	var writeReqs []types.WriteRequest
	for i := 1; i < 4; i++ {
		item, err = attributevalue.MarshalMap(User{UserName: fmt.Sprintf("test_user_%v", i), UserEmail: fmt.Sprintf("test_email_%v@example.com", i)})
		if err != nil {
			log.Printf("Couldn't marshall user into DynamoDB format. Here's why: %v\n", err)
			return err
		}
		writeReqs = append(writeReqs, types.WriteRequest{PutRequest: &types.PutRequest{Item: item}})
	}
	_, err = actor.DynamoClient.BatchWriteItem(ctx, &dynamodb.BatchWriteItemInput{
		RequestItems: map[string][]types.WriteRequest{tableName: writeReqs},
	})
	if err != nil {
		log.Printf("Couldn't populate table %v with users. Here's why: %v\n", tableName, err)
	}
	return err
}

// Scan scans the table for all items.
func (actor DynamoActions) Scan(ctx context.Context, tableName string) (UserList, error) {
	var userList UserList
	output, err := actor.DynamoClient.Scan(ctx, &dynamodb.ScanInput{
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't scan table %v for items. Here's why: %v\n", tableName, err)
	} else {
		err = attributevalue.UnmarshalListOfMaps(output.Items, &userList.Users)
		if err != nil {
			log.Printf("Couldn't unmarshal items into users. Here's why: %v\n", err)
		}
	}
	return userList, err
}

// AddUser adds a user item to a table.
func (actor DynamoActions) AddUser(ctx context.Context, tableName string, user User) error {
	userItem, err := attributevalue.MarshalMap(user)
	if err != nil {
		log.Printf("Couldn't marshall user to item. Here's why: %v\n", err)
	}
	_, err = actor.DynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
		Item:      userItem,
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't put item in table %v. Here's why: %v", tableName, err)
	}
	return err
}
```
Create a struct that wraps CloudWatch Logs actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
)

type CloudWatchLogsActions struct {
	CwlClient *cloudwatchlogs.Client
}

// GetLatestLogStream gets the most recent log stream for a Lambda function.
func (actor CloudWatchLogsActions) GetLatestLogStream(ctx context.Context, functionName string) (types.LogStream, error) {
	var logStream types.LogStream
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.DescribeLogStreams(ctx, &cloudwatchlogs.DescribeLogStreamsInput{
		Descending:   aws.Bool(true),
		Limit:        aws.Int32(1),
		LogGroupName: aws.String(logGroupName),
		OrderBy:      types.OrderByLastEventTime,
	})
	if err != nil {
		log.Printf("Couldn't get log streams for log group %v. Here's why: %v\n", logGroupName, err)
	} else {
		logStream = output.LogStreams[0]
	}
	return logStream, err
}

// GetLogEvents gets the most recent eventCount events from the specified log stream.
func (actor CloudWatchLogsActions) GetLogEvents(ctx context.Context, functionName string, logStreamName string, eventCount int32) (
	[]types.OutputLogEvent, error) {
	var events []types.OutputLogEvent
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.GetLogEvents(ctx, &cloudwatchlogs.GetLogEventsInput{
		LogStreamName: aws.String(logStreamName),
		Limit:         aws.Int32(eventCount),
		LogGroupName:  aws.String(logGroupName),
	})
	if err != nil {
		log.Printf("Couldn't get log event for log stream %v. Here's why: %v\n", logStreamName, err)
	} else {
		events = output.Events
	}
	return events, err
}
```
Create a struct that wraps CloudFormation actions.  

```
import (
	"context"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
)

// StackOutputs defines a map of outputs from a specific stack.
type StackOutputs map[string]string

type CloudFormationActions struct {
	CfnClient *cloudformation.Client
}

// GetOutputs gets the outputs from a CloudFormation stack and puts them into a structured format.
func (actor CloudFormationActions) GetOutputs(ctx context.Context, stackName string) StackOutputs {
	output, err := actor.CfnClient.DescribeStacks(ctx, &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackName),
	})
	if err != nil || len(output.Stacks) == 0 {
		log.Panicf("Couldn't find a CloudFormation stack named %v. Here's why: %v\n", stackName, err)
	}
	stackOutputs := StackOutputs{}
	for _, out := range output.Stacks[0].Outputs {
		stackOutputs[*out.OutputKey] = *out.OutputValue
	}
	return stackOutputs
}
```
Clean up resources.  

```
import (
	"context"
	"log"
	"user_pools_and_lambda_triggers/actions"

	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// Resources keeps track of AWS resources created during an example and handles
// cleanup when the example finishes.
type Resources struct {
	userPoolId       string
	userAccessTokens []string
	triggers         []actions.Trigger

	cognitoActor *actions.CognitoActions
	questioner   demotools.IQuestioner
}

func (resources *Resources) init(cognitoActor *actions.CognitoActions, questioner demotools.IQuestioner) {
	resources.userAccessTokens = []string{}
	resources.triggers = []actions.Trigger{}
	resources.cognitoActor = cognitoActor
	resources.questioner = questioner
}

// Cleanup deletes all AWS resources created during an example.
func (resources *Resources) Cleanup(ctx context.Context) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("Something went wrong during cleanup.\n%v\n", r)
			log.Println("Use the AWS Management Console to remove any remaining resources \n" +
				"that were created for this scenario.")
		}
	}()

	wantDelete := resources.questioner.AskBool("Do you want to remove all of the AWS resources that were created "+
		"during this demo (y/n)?", "y")
	if wantDelete {
		for _, accessToken := range resources.userAccessTokens {
			err := resources.cognitoActor.DeleteUser(ctx, accessToken)
			if err != nil {
				log.Println("Couldn't delete user during cleanup.")
				panic(err)
			}
			log.Println("Deleted user.")
		}
		triggerList := make([]actions.TriggerInfo, len(resources.triggers))
		for i := 0; i < len(resources.triggers); i++ {
			triggerList[i] = actions.TriggerInfo{Trigger: resources.triggers[i], HandlerArn: nil}
		}
		err := resources.cognitoActor.UpdateTriggers(ctx, resources.userPoolId, triggerList...)
		if err != nil {
			log.Println("Couldn't update Cognito triggers during cleanup.")
			panic(err)
		}
		log.Println("Removed Cognito triggers from user pool.")
	} else {
		log.Println("Be sure to remove resources when you're done with them to avoid unexpected charges!")
	}
}
```
+ For API details, see the following topics in *AWS SDK for Go API Reference*.
  + [ConfirmForgotPassword](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.ConfirmForgotPassword)
  + [DeleteUser](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.DeleteUser)
  + [ForgotPassword](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.ForgotPassword)
  + [InitiateAuth](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.InitiateAuth)
  + [SignUp](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.SignUp)
  + [UpdateUserPool](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.UpdateUserPool)

------

# Create an API Gateway REST API to track COVID-19 data
<a name="lambda_example_cross_ApiGatewayDataTracker_section"></a>

The following code example shows how to create a REST API that simulates a system to track daily cases of COVID-19 in the United States, using fictional data.

------
#### [ Python ]

**SDK for Python (Boto3)**  
 Shows how to use AWS Chalice with the AWS SDK for Python (Boto3) to create a serverless REST API that uses Amazon API Gateway, AWS Lambda, and Amazon DynamoDB. The REST API simulates a system that tracks daily cases of COVID-19 in the United States, using fictional data. Learn how to:   
+ Use AWS Chalice to define routes in Lambda functions that are called to handle REST requests that come through API Gateway.
+ Use Lambda functions to retrieve and store data in a DynamoDB table to serve REST requests.
+ Define table structure and security role resources in an AWS CloudFormation template.
+ Use AWS Chalice and CloudFormation to package and deploy all necessary resources.
+ Use CloudFormation to clean up all created resources.
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/cross_service/apigateway_covid-19_tracker).   

**Services used in this example**
+ API Gateway
+ CloudFormation
+ DynamoDB
+ Lambda

------

# Create a REST API with Lambda proxy integration
<a name="lambda_example_api_gateway_GettingStarted_087_section"></a>

The following code example shows how to:
+ Create an IAM role for Lambda execution
+ Create and deploy a Lambda function
+ Create a REST API
+ Configure Lambda proxy integration
+ Deploy and test the API
+ Clean up resources

------
#### [ Bash ]

**AWS CLI with Bash script**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [Sample developer tutorials](https://github.com/aws-samples/sample-developer-tutorials/tree/main/tuts/087-apigateway-lambda-integration) repository. 

```
#!/bin/bash

# Simple API Gateway Lambda Integration Script
# This script creates a REST API with Lambda proxy integration

# Generate random identifiers
FUNCTION_NAME="GetStartedLambdaProxyIntegration-$(openssl rand -hex 4)"
ROLE_NAME="GetStartedLambdaBasicExecutionRole-$(openssl rand -hex 4)"
API_NAME="LambdaProxyAPI-$(openssl rand -hex 4)"

# Get AWS account info
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=$(aws configure get region || echo "us-east-1")

echo "Creating Lambda function code..."

# Create Lambda function code
cat > lambda_function.py << 'EOF'
import json

def lambda_handler(event, context):
    print(event)
    
    greeter = 'World'
    
    try:
        if (event['queryStringParameters']) and (event['queryStringParameters']['greeter']) and (
                event['queryStringParameters']['greeter'] is not None):
            greeter = event['queryStringParameters']['greeter']
    except KeyError:
        print('No greeter')
    
    try:
        if (event['multiValueHeaders']) and (event['multiValueHeaders']['greeter']) and (
                event['multiValueHeaders']['greeter'] is not None):
            greeter = " and ".join(event['multiValueHeaders']['greeter'])
    except KeyError:
        print('No greeter')
    
    try:
        if (event['headers']) and (event['headers']['greeter']) and (
                event['headers']['greeter'] is not None):
            greeter = event['headers']['greeter']
    except KeyError:
        print('No greeter')
    
    if (event['body']) and (event['body'] is not None):
        body = json.loads(event['body'])
        try:
            if (body['greeter']) and (body['greeter'] is not None):
                greeter = body['greeter']
        except KeyError:
            print('No greeter')
    
    res = {
        "statusCode": 200,
        "headers": {
            "Content-Type": "*/*"
        },
        "body": "Hello, " + greeter + "!"
    }
    
    return res
EOF

# Create deployment package
zip function.zip lambda_function.py

echo "Creating IAM role..."

# Create IAM trust policy
cat > trust-policy.json << 'EOF'
{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# Create IAM role
aws iam create-role \
    --role-name "$ROLE_NAME" \
    --assume-role-policy-document file://trust-policy.json

# Attach execution policy
aws iam attach-role-policy \
    --role-name "$ROLE_NAME" \
    --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"

# Wait for role propagation
sleep 15

echo "Creating Lambda function..."

# Create Lambda function
aws lambda create-function \
    --function-name "$FUNCTION_NAME" \
    --runtime python3.9 \
    --role "arn:aws:iam::$ACCOUNT_ID:role/$ROLE_NAME" \
    --handler lambda_function.lambda_handler \
    --zip-file fileb://function.zip

echo "Creating API Gateway..."

# Create REST API
aws apigateway create-rest-api \
    --name "$API_NAME" \
    --endpoint-configuration types=REGIONAL

# Get API ID
API_ID=$(aws apigateway get-rest-apis --query "items[?name=='$API_NAME'].id" --output text)

# Get root resource ID
ROOT_RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query 'items[?path==`/`].id' --output text)

# Create helloworld resource
aws apigateway create-resource \
    --rest-api-id "$API_ID" \
    --parent-id "$ROOT_RESOURCE_ID" \
    --path-part helloworld

# Get resource ID
RESOURCE_ID=$(aws apigateway get-resources --rest-api-id "$API_ID" --query "items[?pathPart=='helloworld'].id" --output text)

# Create ANY method
aws apigateway put-method \
    --rest-api-id "$API_ID" \
    --resource-id "$RESOURCE_ID" \
    --http-method ANY \
    --authorization-type NONE

# Set up Lambda proxy integration
LAMBDA_URI="arn:aws:apigateway:$REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:$REGION:$ACCOUNT_ID:function:$FUNCTION_NAME/invocations"

aws apigateway put-integration \
    --rest-api-id "$API_ID" \
    --resource-id "$RESOURCE_ID" \
    --http-method ANY \
    --type AWS_PROXY \
    --integration-http-method POST \
    --uri "$LAMBDA_URI"

# Grant API Gateway permission to invoke Lambda
SOURCE_ARN="arn:aws:execute-api:$REGION:$ACCOUNT_ID:$API_ID/*/*"

aws lambda add-permission \
    --function-name "$FUNCTION_NAME" \
    --statement-id "apigateway-invoke-$(openssl rand -hex 4)" \
    --action lambda:InvokeFunction \
    --principal apigateway.amazonaws.com \
    --source-arn "$SOURCE_ARN"

# Deploy API
aws apigateway create-deployment \
    --rest-api-id "$API_ID" \
    --stage-name test

echo "Testing API..."

# Test the API
INVOKE_URL="https://$API_ID.execute-api.$REGION.amazonaws.com/test/helloworld"

echo "API URL: $INVOKE_URL"

# Test with query parameter
echo "Testing with query parameter:"
curl -X GET "$INVOKE_URL?greeter=John"
echo ""

# Test with header
echo "Testing with header:"
curl -X GET "$INVOKE_URL" \
    -H 'content-type: application/json' \
    -H 'greeter: John'
echo ""

# Test with body
echo "Testing with POST body:"
curl -X POST "$INVOKE_URL" \
    -H 'content-type: application/json' \
    -d '{ "greeter": "John" }'
echo ""

echo "Tutorial completed! API is available at: $INVOKE_URL"

# Cleanup
echo "Cleaning up resources..."

# Delete API
aws apigateway delete-rest-api --rest-api-id "$API_ID"

# Delete Lambda function
aws lambda delete-function --function-name "$FUNCTION_NAME"

# Detach policy and delete role
aws iam detach-role-policy \
    --role-name "$ROLE_NAME" \
    --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"

aws iam delete-role --role-name "$ROLE_NAME"

# Clean up local files
rm -f lambda_function.py function.zip trust-policy.json

echo "Cleanup completed!"
```
+ For API details, see the following topics in *AWS CLI Command Reference*.
  + [AddPermission](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/AddPermission)
  + [AttachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/AttachRolePolicy)
  + [CreateDeployment](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/CreateDeployment)
  + [CreateFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/CreateFunction)
  + [CreateResource](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/CreateResource)
  + [CreateRestApi](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/CreateRestApi)
  + [CreateRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/CreateRole)
  + [DeleteFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/DeleteFunction)
  + [DeleteRestApi](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/DeleteRestApi)
  + [DeleteRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DeleteRole)
  + [DetachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DetachRolePolicy)
  + [GetCallerIdentity](https://docs.aws.amazon.com/goto/aws-cli/sts-2011-06-15/GetCallerIdentity)
  + [GetResources](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/GetResources)
  + [GetRestApis](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/GetRestApis)
  + [PutIntegration](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/PutIntegration)
  + [PutMethod](https://docs.aws.amazon.com/goto/aws-cli/apigateway-2015-07-09/PutMethod)

------

# Create a lending library REST API
<a name="lambda_example_cross_AuroraRestLendingLibrary_section"></a>

The following code example shows how to create a lending library where patrons can borrow and return books by using a REST API backed by an Amazon Aurora database.

------
#### [ Python ]

**SDK for Python (Boto3)**  
 Shows how to use the AWS SDK for Python (Boto3) with the Amazon Relational Database Service (Amazon RDS) API and AWS Chalice to create a REST API backed by an Amazon Aurora database. The web service is fully serverless and represents a simple lending library where patrons can borrow and return books. Learn how to:   
+ Create and manage a serverless Aurora database cluster.
+ Use AWS Secrets Manager to manage database credentials.
+ Implement a data storage layer that uses Amazon RDS to move data into and out of the database.
+ Use AWS Chalice to deploy a serverless REST API to Amazon API Gateway and AWS Lambda.
+ Use the Requests package to send requests to the web service.
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/cross_service/aurora_rest_lending_library).   

**Services used in this example**
+ API Gateway
+ Aurora
+ Lambda
+ Secrets Manager

------

# Create a messenger application with Step Functions
<a name="lambda_example_cross_StepFunctionsMessenger_section"></a>

The following code example shows how to create an AWS Step Functions messenger application that retrieves message records from a database table.

------
#### [ Python ]

**SDK for Python (Boto3)**  
 Shows how to use the AWS SDK for Python (Boto3) with AWS Step Functions to create a messenger application that retrieves message records from an Amazon DynamoDB table and sends them with Amazon Simple Queue Service (Amazon SQS). The state machine integrates with an AWS Lambda function to scan the database for unsent messages.   
+ Create a state machine that retrieves and updates message records from an Amazon DynamoDB table.
+ Update the state machine definition to also send messages to Amazon Simple Queue Service (Amazon SQS).
+ Start and stop state machine runs.
+ Connect to Lambda, DynamoDB, and Amazon SQS from a state machine by using service integrations.
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/cross_service/stepfunctions_messenger).   

**Services used in this example**
+ DynamoDB
+ Lambda
+ Amazon SQS
+ Step Functions

------

# Create a photo asset management application that lets users manage photos using labels
<a name="lambda_example_cross_PAM_section"></a>

The following code examples show how to create a serverless application that lets users manage photos using labels.

------
#### [ .NET ]

**SDK for .NET**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/PhotoAssetManager).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ C\$1\$1 ]

**SDK for C\$1\$1**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/cpp/example_code/cross-service/photo_asset_manager).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ Java ]

**SDK for Java 2.x**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/pam_source_files).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/photo-asset-manager).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ Kotlin ]

**SDK for Kotlin**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/kotlin/usecases/creating_pam).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ PHP ]

**SDK for PHP**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/php/applications/photo_asset_manager).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------
#### [ Rust ]

**SDK for Rust**  
 Shows how to develop a photo asset management application that detects labels in images using Amazon Rekognition and stores them for later retrieval.   
For complete source code and instructions on how to set up and run, see the full example on [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1/cross_service/photo_asset_management).  
For a deep dive into the origin of this example see the post on [AWS Community](https://community.aws/posts/cloud-journeys/01-serverless-image-recognition-app).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon Rekognition
+ Amazon S3
+ Amazon SNS

------

# Create a websocket chat application with API Gateway
<a name="lambda_example_cross_ApiGatewayWebsocketChat_section"></a>

The following code example shows how to create a chat application that is served by a websocket API built on Amazon API Gateway.

------
#### [ Python ]

**SDK for Python (Boto3)**  
 Shows how to use the AWS SDK for Python (Boto3) with Amazon API Gateway V2 to create a websocket API that integrates with AWS Lambda and Amazon DynamoDB.   
+ Create a websocket API served by API Gateway.
+ Define a Lambda handler that stores connections in DynamoDB and posts messages to other chat participants.
+ Connect to the websocket chat application and send messages with the Websockets package.
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/cross_service/apigateway_websocket_chat).   

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda

------

# Create an application that analyzes customer feedback and synthesizes audio
<a name="lambda_example_cross_FSA_section"></a>

The following code examples show how to create an application that analyzes customer comment cards, translates them from their original language, determines their sentiment, and generates an audio file from the translated text.

------
#### [ .NET ]

**SDK for .NET**  
 This example application analyzes and stores customer feedback cards. Specifically, it fulfills the need of a fictitious hotel in New York City. The hotel receives feedback from guests in various languages in the form of physical comment cards. That feedback is uploaded into the app through a web client. After an image of a comment card is uploaded, the following steps occur:   
+ Text is extracted from the image using Amazon Textract.
+ Amazon Comprehend determines the sentiment of the extracted text and its language.
+ The extracted text is translated to English using Amazon Translate.
+ Amazon Polly synthesizes an audio file from the extracted text.
 The full app can be deployed with the AWS CDK. For source code and deployment instructions, see the project in [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/FeedbackSentimentAnalyzer).   

**Services used in this example**
+ Amazon Comprehend
+ Lambda
+ Amazon Polly
+ Amazon Textract
+ Amazon Translate

------
#### [ Java ]

**SDK for Java 2.x**  
 This example application analyzes and stores customer feedback cards. Specifically, it fulfills the need of a fictitious hotel in New York City. The hotel receives feedback from guests in various languages in the form of physical comment cards. That feedback is uploaded into the app through a web client. After an image of a comment card is uploaded, the following steps occur:   
+ Text is extracted from the image using Amazon Textract.
+ Amazon Comprehend determines the sentiment of the extracted text and its language.
+ The extracted text is translated to English using Amazon Translate.
+ Amazon Polly synthesizes an audio file from the extracted text.
 The full app can be deployed with the AWS CDK. For source code and deployment instructions, see the project in [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_fsa_app).   

**Services used in this example**
+ Amazon Comprehend
+ Lambda
+ Amazon Polly
+ Amazon Textract
+ Amazon Translate

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 This example application analyzes and stores customer feedback cards. Specifically, it fulfills the need of a fictitious hotel in New York City. The hotel receives feedback from guests in various languages in the form of physical comment cards. That feedback is uploaded into the app through a web client. After an image of a comment card is uploaded, the following steps occur:   
+ Text is extracted from the image using Amazon Textract.
+ Amazon Comprehend determines the sentiment of the extracted text and its language.
+ The extracted text is translated to English using Amazon Translate.
+ Amazon Polly synthesizes an audio file from the extracted text.
 The full app can be deployed with the AWS CDK. For source code and deployment instructions, see the project in [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/feedback-sentiment-analyzer). The following excerpts show how the AWS SDK for JavaScript is used inside of Lambda functions.   

```
import {
  ComprehendClient,
  DetectDominantLanguageCommand,
  DetectSentimentCommand,
} from "@aws-sdk/client-comprehend";

/**
 * Determine the language and sentiment of the extracted text.
 *
 * @param {{ source_text: string}} extractTextOutput
 */
export const handler = async (extractTextOutput) => {
  const comprehendClient = new ComprehendClient({});

  const detectDominantLanguageCommand = new DetectDominantLanguageCommand({
    Text: extractTextOutput.source_text,
  });

  // The source language is required for sentiment analysis and
  // translation in the next step.
  const { Languages } = await comprehendClient.send(
    detectDominantLanguageCommand,
  );

  const languageCode = Languages[0].LanguageCode;

  const detectSentimentCommand = new DetectSentimentCommand({
    Text: extractTextOutput.source_text,
    LanguageCode: languageCode,
  });

  const { Sentiment } = await comprehendClient.send(detectSentimentCommand);

  return {
    sentiment: Sentiment,
    language_code: languageCode,
  };
};
```

```
import {
  DetectDocumentTextCommand,
  TextractClient,
} from "@aws-sdk/client-textract";

/**
 * Fetch the S3 object from the event and analyze it using Amazon Textract.
 *
 * @param {import("@types/aws-lambda").EventBridgeEvent<"Object Created">} eventBridgeS3Event
 */
export const handler = async (eventBridgeS3Event) => {
  const textractClient = new TextractClient();

  const detectDocumentTextCommand = new DetectDocumentTextCommand({
    Document: {
      S3Object: {
        Bucket: eventBridgeS3Event.bucket,
        Name: eventBridgeS3Event.object,
      },
    },
  });

  // Textract returns a list of blocks. A block can be a line, a page, word, etc.
  // Each block also contains geometry of the detected text.
  // For more information on the Block type, see https://docs.aws.amazon.com/textract/latest/dg/API_Block.html.
  const { Blocks } = await textractClient.send(detectDocumentTextCommand);

  // For the purpose of this example, we are only interested in words.
  const extractedWords = Blocks.filter((b) => b.BlockType === "WORD").map(
    (b) => b.Text,
  );

  return extractedWords.join(" ");
};
```

```
import { PollyClient, SynthesizeSpeechCommand } from "@aws-sdk/client-polly";
import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";

/**
 * Synthesize an audio file from text.
 *
 * @param {{ bucket: string, translated_text: string, object: string}} sourceDestinationConfig
 */
export const handler = async (sourceDestinationConfig) => {
  const pollyClient = new PollyClient({});

  const synthesizeSpeechCommand = new SynthesizeSpeechCommand({
    Engine: "neural",
    Text: sourceDestinationConfig.translated_text,
    VoiceId: "Ruth",
    OutputFormat: "mp3",
  });

  const { AudioStream } = await pollyClient.send(synthesizeSpeechCommand);

  const audioKey = `${sourceDestinationConfig.object}.mp3`;

  // Store the audio file in S3.
  const s3Client = new S3Client();
  const upload = new Upload({
    client: s3Client,
    params: {
      Bucket: sourceDestinationConfig.bucket,
      Key: audioKey,
      Body: AudioStream,
      ContentType: "audio/mp3",
    },
  });

  await upload.done();
  return audioKey;
};
```

```
import {
  TranslateClient,
  TranslateTextCommand,
} from "@aws-sdk/client-translate";

/**
 * Translate the extracted text to English.
 *
 * @param {{ extracted_text: string, source_language_code: string}} textAndSourceLanguage
 */
export const handler = async (textAndSourceLanguage) => {
  const translateClient = new TranslateClient({});

  const translateCommand = new TranslateTextCommand({
    SourceLanguageCode: textAndSourceLanguage.source_language_code,
    TargetLanguageCode: "en",
    Text: textAndSourceLanguage.extracted_text,
  });

  const { TranslatedText } = await translateClient.send(translateCommand);

  return { translated_text: TranslatedText };
};
```

**Services used in this example**
+ Amazon Comprehend
+ Lambda
+ Amazon Polly
+ Amazon Textract
+ Amazon Translate

------
#### [ Ruby ]

**SDK for Ruby**  
 This example application analyzes and stores customer feedback cards. Specifically, it fulfills the need of a fictitious hotel in New York City. The hotel receives feedback from guests in various languages in the form of physical comment cards. That feedback is uploaded into the app through a web client. After an image of a comment card is uploaded, the following steps occur:   
+ Text is extracted from the image using Amazon Textract.
+ Amazon Comprehend determines the sentiment of the extracted text and its language.
+ The extracted text is translated to English using Amazon Translate.
+ Amazon Polly synthesizes an audio file from the extracted text.
 The full app can be deployed with the AWS CDK. For source code and deployment instructions, see the project in [ GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/ruby/cross_service_examples/feedback_sentiment_analyzer).   

**Services used in this example**
+ Amazon Comprehend
+ Lambda
+ Amazon Polly
+ Amazon Textract
+ Amazon Translate

------

# Creating a CloudWatch dashboard with function name as a variable
<a name="lambda_example_cloudwatch_GettingStarted_031_section"></a>

The following code example shows how to:
+ Create a CloudWatch dashboard
+ Add Lambda metrics widgets with a function name variable
+ Verify the dashboard
+ Clean up resources

------
#### [ Bash ]

**AWS CLI with Bash script**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [Sample developer tutorials](https://github.com/aws-samples/sample-developer-tutorials/tree/main/tuts/031-cloudwatch-dynamicdash) repository. 

```
#!/bin/bash

# Script to create a CloudWatch dashboard with Lambda function name as a variable
# This script creates a CloudWatch dashboard that allows you to switch between different Lambda functions

# Set up logging
LOG_FILE="cloudwatch-dashboard-script.log"
exec > >(tee -a "$LOG_FILE") 2>&1

echo "$(date): Starting CloudWatch dashboard creation script"

# Function to handle errors
handle_error() {
    echo "ERROR: $1"
    echo "Resources created:"
    echo "- CloudWatch Dashboard: LambdaMetricsDashboard"
    echo ""
    echo "==========================================="
    echo "CLEANUP CONFIRMATION"
    echo "==========================================="
    echo "An error occurred. Do you want to clean up the created resources? (y/n): "
    read -r CLEANUP_CHOICE
    
    if [[ "${CLEANUP_CHOICE,,}" == "y" ]]; then
        echo "Cleaning up resources..."
        aws cloudwatch delete-dashboards --dashboard-names LambdaMetricsDashboard
        echo "Cleanup complete."
    else
        echo "Resources were not cleaned up. You can manually delete them later."
    fi
    exit 1
}

# Check if AWS CLI is installed and configured
echo "Checking AWS CLI configuration..."
aws sts get-caller-identity > /dev/null 2>&1
if [ $? -ne 0 ]; then
    handle_error "AWS CLI is not properly configured. Please configure it with 'aws configure' and try again."
fi

# Get the current region
REGION=$(aws configure get region)
if [ -z "$REGION" ]; then
    REGION="us-east-1"
    echo "No region found in AWS config, defaulting to $REGION"
fi
echo "Using region: $REGION"

# Check if there are any Lambda functions in the account
echo "Checking for Lambda functions..."
LAMBDA_FUNCTIONS=$(aws lambda list-functions --query "Functions[*].FunctionName" --output text)
if [ -z "$LAMBDA_FUNCTIONS" ]; then
    echo "No Lambda functions found in your account. Creating a simple test function..."
    
    # Create a temporary directory for Lambda function code
    TEMP_DIR=$(mktemp -d)
    
    # Create a simple Lambda function
    cat > "$TEMP_DIR/index.js" << EOF
exports.handler = async (event) => {
    console.log('Event:', JSON.stringify(event, null, 2));
    return {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
};
EOF
    
    # Zip the function code
    cd "$TEMP_DIR" || handle_error "Failed to change to temporary directory"
    zip -q function.zip index.js
    
    # Create a role for the Lambda function
    ROLE_NAME="LambdaDashboardTestRole"
    ROLE_ARN=$(aws iam create-role \
        --role-name "$ROLE_NAME" \
        --assume-role-policy-document '{"Version":"2012-10-17",		 	 	 "Statement":[{"Effect":"Allow","Principal":{"Service":"lambda.amazonaws.com"},"Action":"sts:AssumeRole"}]}' \
        --query "Role.Arn" \
        --output text)
    
    if [ $? -ne 0 ]; then
        handle_error "Failed to create IAM role for Lambda function"
    fi
    
    echo "Waiting for role to be available..."
    sleep 10
    
    # Attach basic Lambda execution policy
    aws iam attach-role-policy \
        --role-name "$ROLE_NAME" \
        --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    
    if [ $? -ne 0 ]; then
        aws iam delete-role --role-name "$ROLE_NAME"
        handle_error "Failed to attach policy to IAM role"
    fi
    
    # Create the Lambda function
    FUNCTION_NAME="DashboardTestFunction"
    aws lambda create-function \
        --function-name "$FUNCTION_NAME" \
        --runtime nodejs18.x \
        --role "$ROLE_ARN" \
        --handler index.handler \
        --zip-file fileb://function.zip
    
    if [ $? -ne 0 ]; then
        aws iam detach-role-policy \
            --role-name "$ROLE_NAME" \
            --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        aws iam delete-role --role-name "$ROLE_NAME"
        handle_error "Failed to create Lambda function"
    fi
    
    # Invoke the function to generate some metrics
    echo "Invoking Lambda function to generate metrics..."
    for i in {1..5}; do
        aws lambda invoke --function-name "$FUNCTION_NAME" --payload '{}' /dev/null > /dev/null
        sleep 1
    done
    
    # Clean up temporary directory
    cd - > /dev/null
    rm -rf "$TEMP_DIR"
    
    # Set the function name for the dashboard
    DEFAULT_FUNCTION="$FUNCTION_NAME"
else
    # Use the first Lambda function as default
    DEFAULT_FUNCTION=$(echo "$LAMBDA_FUNCTIONS" | awk '{print $1}')
    echo "Found Lambda functions. Using $DEFAULT_FUNCTION as default."
fi

# Create a dashboard with Lambda metrics and a function name variable
echo "Creating CloudWatch dashboard with Lambda function name variable..."

# Create a JSON file for the dashboard body
cat > dashboard-body.json << EOF
{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "metrics": [
          [ "AWS/Lambda", "Invocations", "FunctionName", "\${FunctionName}" ],
          [ ".", "Errors", ".", "." ],
          [ ".", "Throttles", ".", "." ]
        ],
        "view": "timeSeries",
        "stacked": false,
        "region": "$REGION",
        "title": "Lambda Function Metrics for \${FunctionName}",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 0,
      "y": 6,
      "width": 12,
      "height": 6,
      "properties": {
        "metrics": [
          [ "AWS/Lambda", "Duration", "FunctionName", "\${FunctionName}", { "stat": "Average" } ]
        ],
        "view": "timeSeries",
        "stacked": false,
        "region": "$REGION",
        "title": "Duration for \${FunctionName}",
        "period": 300
      }
    },
    {
      "type": "metric",
      "x": 12,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "metrics": [
          [ "AWS/Lambda", "ConcurrentExecutions", "FunctionName", "\${FunctionName}" ]
        ],
        "view": "timeSeries",
        "stacked": false,
        "region": "$REGION",
        "title": "Concurrent Executions for \${FunctionName}",
        "period": 300
      }
    }
  ],
  "periodOverride": "auto",
  "variables": [
    {
      "type": "property",
      "id": "FunctionName",
      "property": "FunctionName",
      "label": "Lambda Function",
      "inputType": "select",
      "values": [
        {
          "value": "$DEFAULT_FUNCTION",
          "label": "$DEFAULT_FUNCTION"
        }
      ]
    }
  ]
}
EOF

# Create the dashboard using the JSON file
DASHBOARD_RESULT=$(aws cloudwatch put-dashboard --dashboard-name LambdaMetricsDashboard --dashboard-body file://dashboard-body.json)
DASHBOARD_EXIT_CODE=$?

# Check if there was a fatal error
if [ $DASHBOARD_EXIT_CODE -ne 0 ]; then
    # If we created resources, clean them up
    if [ -n "${FUNCTION_NAME:-}" ]; then
        aws lambda delete-function --function-name "$FUNCTION_NAME"
        aws iam detach-role-policy \
            --role-name "$ROLE_NAME" \
            --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        aws iam delete-role --role-name "$ROLE_NAME"
    fi
    handle_error "Failed to create CloudWatch dashboard."
fi

# Display any validation messages but continue
if [[ "$DASHBOARD_RESULT" == *"DashboardValidationMessages"* ]]; then
    echo "Dashboard created with validation messages:"
    echo "$DASHBOARD_RESULT"
    echo "These validation messages are warnings and the dashboard should still function."
else
    echo "Dashboard created successfully!"
fi

# Verify the dashboard was created
echo "Verifying dashboard creation..."
DASHBOARD_INFO=$(aws cloudwatch get-dashboard --dashboard-name LambdaMetricsDashboard)
DASHBOARD_INFO_EXIT_CODE=$?

if [ $DASHBOARD_INFO_EXIT_CODE -ne 0 ]; then
    # If we created resources, clean them up
    if [ -n "${FUNCTION_NAME:-}" ]; then
        aws lambda delete-function --function-name "$FUNCTION_NAME"
        aws iam detach-role-policy \
            --role-name "$ROLE_NAME" \
            --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        aws iam delete-role --role-name "$ROLE_NAME"
    fi
    handle_error "Failed to verify dashboard creation."
fi

echo "Dashboard verification successful!"
echo "Dashboard details:"
echo "$DASHBOARD_INFO"

# List all dashboards to confirm
echo "Listing all dashboards:"
DASHBOARDS=$(aws cloudwatch list-dashboards)
DASHBOARDS_EXIT_CODE=$?

if [ $DASHBOARDS_EXIT_CODE -ne 0 ]; then
    # If we created resources, clean them up
    if [ -n "${FUNCTION_NAME:-}" ]; then
        aws lambda delete-function --function-name "$FUNCTION_NAME"
        aws iam detach-role-policy \
            --role-name "$ROLE_NAME" \
            --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        aws iam delete-role --role-name "$ROLE_NAME"
    fi
    handle_error "Failed to list dashboards."
fi
echo "$DASHBOARDS"

# Show instructions for accessing the dashboard
echo ""
echo "Dashboard created successfully! To access it:"
echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/"
echo "2. In the navigation pane, choose Dashboards"
echo "3. Select LambdaMetricsDashboard"
echo "4. You should see a dropdown menu labeled 'Lambda Function' at the top of the dashboard"
echo "5. Use this dropdown to select different Lambda functions and see their metrics"
echo ""

# Create a list of resources for cleanup
RESOURCES=("- CloudWatch Dashboard: LambdaMetricsDashboard")
if [ -n "${FUNCTION_NAME:-}" ]; then
    RESOURCES+=("- Lambda Function: $FUNCTION_NAME")
    RESOURCES+=("- IAM Role: $ROLE_NAME")
fi

# Prompt for cleanup
echo "==========================================="
echo "CLEANUP CONFIRMATION"
echo "==========================================="
echo "Resources created:"
for resource in "${RESOURCES[@]}"; do
    echo "$resource"
done
echo ""
echo "Do you want to clean up all created resources? (y/n): "
read -r CLEANUP_CHOICE

if [[ "${CLEANUP_CHOICE,,}" == "y" ]]; then
    echo "Cleaning up resources..."
    
    # Delete the dashboard
    aws cloudwatch delete-dashboards --dashboard-names LambdaMetricsDashboard
    if [ $? -ne 0 ]; then
        echo "WARNING: Failed to delete dashboard. You may need to delete it manually."
    else
        echo "Dashboard deleted successfully."
    fi
    
    # If we created a Lambda function, delete it and its role
    if [ -n "${FUNCTION_NAME:-}" ]; then
        echo "Deleting Lambda function..."
        aws lambda delete-function --function-name "$FUNCTION_NAME"
        if [ $? -ne 0 ]; then
            echo "WARNING: Failed to delete Lambda function. You may need to delete it manually."
        else
            echo "Lambda function deleted successfully."
        fi
        
        echo "Detaching role policy..."
        aws iam detach-role-policy \
            --role-name "$ROLE_NAME" \
            --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        if [ $? -ne 0 ]; then
            echo "WARNING: Failed to detach role policy. You may need to detach it manually."
        else
            echo "Role policy detached successfully."
        fi
        
        echo "Deleting IAM role..."
        aws iam delete-role --role-name "$ROLE_NAME"
        if [ $? -ne 0 ]; then
            echo "WARNING: Failed to delete IAM role. You may need to delete it manually."
        else
            echo "IAM role deleted successfully."
        fi
    fi
    
    # Clean up the JSON file
    rm -f dashboard-body.json
    
    echo "Cleanup complete."
else
    echo "Resources were not cleaned up. You can manually delete them later with:"
    echo "aws cloudwatch delete-dashboards --dashboard-names LambdaMetricsDashboard"
    if [ -n "${FUNCTION_NAME:-}" ]; then
        echo "aws lambda delete-function --function-name $FUNCTION_NAME"
        echo "aws iam detach-role-policy --role-name $ROLE_NAME --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        echo "aws iam delete-role --role-name $ROLE_NAME"
    fi
fi

echo "Script completed successfully!"
```
+ For API details, see the following topics in *AWS CLI Command Reference*.
  + [AttachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/AttachRolePolicy)
  + [CreateFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/CreateFunction)
  + [CreateRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/CreateRole)
  + [DeleteDashboards](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/DeleteDashboards)
  + [DeleteFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/DeleteFunction)
  + [DeleteRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DeleteRole)
  + [DetachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DetachRolePolicy)
  + [GetCallerIdentity](https://docs.aws.amazon.com/goto/aws-cli/sts-2011-06-15/GetCallerIdentity)
  + [GetDashboard](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/GetDashboard)
  + [Invoke](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/Invoke)
  + [ListDashboards](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/ListDashboards)
  + [ListFunctions](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/ListFunctions)
  + [PutDashboard](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/PutDashboard)

------

# Creating your first Lambda function
<a name="lambda_example_lambda_GettingStarted_019_section"></a>

The following code example shows how to:
+ Create an IAM role for Lambda
+ Create function code
+ Create a Lambda function
+ Test your Lambda function
+ Clean up resources

------
#### [ Bash ]

**AWS CLI with Bash script**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [Sample developer tutorials](https://github.com/aws-samples/sample-developer-tutorials/tree/main/tuts/019-lambda-gettingstarted) repository. 

```
#!/bin/bash
# AWS Lambda - Create Your First Function
# This script creates a Lambda function, invokes it with a test event,
# views CloudWatch logs, and cleans up all resources.
#
# Source: https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html
#
# Resources created:
#   - IAM role (Lambda execution role with basic logging permissions)
#   - Lambda function (Python 3.13 or Node.js 22.x runtime)
#   - CloudWatch log group (created automatically by Lambda on invocation)

set -eE

###############################################################################
# Setup
###############################################################################

UNIQUE_ID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 8 | head -n 1)
FUNCTION_NAME="my-lambda-function-${UNIQUE_ID}"
ROLE_NAME="lambda-execution-role-${UNIQUE_ID}"
LOG_GROUP_NAME="/aws/lambda/${FUNCTION_NAME}"

TEMP_DIR=$(mktemp -d)
LOG_FILE="${TEMP_DIR}/lambda-gettingstarted.log"

exec > >(tee -a "$LOG_FILE") 2>&1

declare -a CREATED_RESOURCES

###############################################################################
# Helper functions
###############################################################################

cleanup_resources() {
    # Disable error trap to prevent recursion during cleanup
    trap - ERR
    set +eE

    echo ""
    echo "Cleaning up resources..."
    echo ""

    for ((i=${#CREATED_RESOURCES[@]}-1; i>=0; i--)); do
        local RESOURCE="${CREATED_RESOURCES[$i]}"
        local TYPE="${RESOURCE%%:*}"
        local NAME="${RESOURCE#*:}"

        case "$TYPE" in
            log-group)
                echo "Deleting CloudWatch log group: ${NAME}"
                aws logs delete-log-group \
                    --log-group-name "$NAME" 2>&1 || echo "  WARNING: Could not delete log group ${NAME}."
                ;;
            lambda-function)
                echo "Deleting Lambda function: ${NAME}"
                aws lambda delete-function \
                    --function-name "$NAME" 2>&1 || echo "  WARNING: Could not delete Lambda function ${NAME}."
                echo "  Waiting for function deletion to complete..."
                local DELETE_WAIT=0
                while aws lambda get-function --function-name "$NAME" > /dev/null 2>&1; do
                    sleep 2
                    DELETE_WAIT=$((DELETE_WAIT + 2))
                    if [ "$DELETE_WAIT" -ge 60 ]; then
                        echo "  WARNING: Timed out waiting for function deletion."
                        break
                    fi
                done
                ;;
            iam-role-policy)
                local ROLE_PART="${NAME%%|*}"
                local POLICY_PART="${NAME#*|}"
                echo "Detaching policy from role: ${ROLE_PART}"
                aws iam detach-role-policy \
                    --role-name "$ROLE_PART" \
                    --policy-arn "$POLICY_PART" 2>&1 || echo "  WARNING: Could not detach policy from role ${ROLE_PART}."
                ;;
            iam-role)
                echo "Deleting IAM role: ${NAME}"
                aws iam delete-role \
                    --role-name "$NAME" 2>&1 || echo "  WARNING: Could not delete IAM role ${NAME}."
                ;;
        esac
    done

    if [ -d "$TEMP_DIR" ]; then
        rm -rf "$TEMP_DIR"
    fi

    echo ""
    echo "Cleanup complete."
}

handle_error() {
    echo ""
    echo "==========================================="
    echo "ERROR: Script failed at $1"
    echo "==========================================="
    echo ""
    if [ ${#CREATED_RESOURCES[@]} -gt 0 ]; then
        echo "Attempting to clean up ${#CREATED_RESOURCES[@]} resource(s)..."
        cleanup_resources
    fi
    exit 1
}

trap 'handle_error "line $LINENO"' ERR

wait_for_resource() {
    local DESCRIPTION="$1"
    local COMMAND="$2"
    local TARGET_VALUE="$3"
    local TIMEOUT=300
    local ELAPSED=0
    local INTERVAL=5

    echo "Waiting for ${DESCRIPTION}..."
    while true; do
        local RESULT
        RESULT=$(eval "$COMMAND" 2>&1) || true
        if echo "$RESULT" | grep -q "$TARGET_VALUE"; then
            echo "  ${DESCRIPTION} is ready."
            return 0
        fi
        if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
            echo "ERROR: Timed out waiting for ${DESCRIPTION} after ${TIMEOUT} seconds."
            return 1
        fi
        sleep "$INTERVAL"
        ELAPSED=$((ELAPSED + INTERVAL))
    done
}

###############################################################################
# Region pre-check
###############################################################################

CONFIGURED_REGION=$(aws configure get region 2>/dev/null || true)
if [ -z "$CONFIGURED_REGION" ] && [ -z "$AWS_DEFAULT_REGION" ] && [ -z "$AWS_REGION" ]; then
    echo "ERROR: No AWS region configured."
    echo "Run 'aws configure set region <region>' or export AWS_DEFAULT_REGION."
    exit 1
fi

###############################################################################
# Runtime selection
###############################################################################

echo ""
echo "==========================================="
echo "AWS Lambda - Create Your First Function"
echo "==========================================="
echo ""
echo "Select a runtime for your Lambda function:"
echo "  1) Python 3.13"
echo "  2) Node.js 22.x"
echo ""
echo "Enter your choice (1 or 2): "
read -r RUNTIME_CHOICE

case "$RUNTIME_CHOICE" in
    1)
        RUNTIME="python3.13"
        HANDLER="lambda_function.lambda_handler"
        CODE_FILE="lambda_function.py"
        cat > "${TEMP_DIR}/${CODE_FILE}" << 'PYTHON_EOF'
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
    length = event['length']
    width = event['width']
    area = calculate_area(length, width)
    print(f'The area is {area}')
    logger.info(f'CloudWatch logs group: {context.log_group_name}')
    return json.dumps({'area': area})
def calculate_area(length, width):
    return length * width
PYTHON_EOF
        echo "Selected runtime: Python 3.13"
        ;;
    2)
        RUNTIME="nodejs22.x"
        HANDLER="index.handler"
        CODE_FILE="index.mjs"
        cat > "${TEMP_DIR}/${CODE_FILE}" << 'NODEJS_EOF'
export const handler = async (event, context) => {
  const area = event.length * event.width;
  console.log(`The area is ${area}`);
  console.log('CloudWatch log group: ', context.logGroupName);
  return JSON.stringify({area});
};
NODEJS_EOF
        echo "Selected runtime: Node.js 22.x"
        ;;
    *)
        echo "ERROR: Invalid choice. Please enter 1 or 2."
        exit 1
        ;;
esac

###############################################################################
# Step 1: Create IAM execution role
###############################################################################

echo ""
echo "==========================================="
echo "Step 1: Create IAM execution role"
echo "==========================================="
echo ""

TRUST_POLICY='{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}'

echo "Creating IAM role: ${ROLE_NAME}"
ROLE_OUTPUT=$(aws iam create-role \
    --role-name "$ROLE_NAME" \
    --assume-role-policy-document "$TRUST_POLICY" \
    --query 'Role.Arn' \
    --output text 2>&1)
echo "$ROLE_OUTPUT"
ROLE_ARN="$ROLE_OUTPUT"
CREATED_RESOURCES+=("iam-role:${ROLE_NAME}")
echo "Role ARN: ${ROLE_ARN}"

echo ""
echo "Attaching AWSLambdaBasicExecutionRole policy..."
aws iam attach-role-policy \
    --role-name "$ROLE_NAME" \
    --policy-arn "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" 2>&1
CREATED_RESOURCES+=("iam-role-policy:${ROLE_NAME}|arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole")
echo "Policy attached."

# IAM roles can take a few seconds to propagate
echo "Waiting for IAM role to propagate..."
sleep 10

###############################################################################
# Step 2: Create Lambda function
###############################################################################

echo ""
echo "==========================================="
echo "Step 2: Create Lambda function"
echo "==========================================="
echo ""

echo "Creating deployment package..."
ORIGINAL_DIR=$(pwd)
cd "$TEMP_DIR"
zip -j function.zip "$CODE_FILE" > /dev/null 2>&1
cd "$ORIGINAL_DIR"

echo "Creating Lambda function: ${FUNCTION_NAME}"
echo "  Runtime: ${RUNTIME}"
echo "  Handler: ${HANDLER}"
echo ""

CREATE_OUTPUT=$(aws lambda create-function \
    --function-name "$FUNCTION_NAME" \
    --runtime "$RUNTIME" \
    --role "$ROLE_ARN" \
    --handler "$HANDLER" \
    --architectures x86_64 \
    --zip-file "fileb://${TEMP_DIR}/function.zip" \
    --query '[FunctionName, FunctionArn, Runtime, State]' \
    --output text 2>&1)
echo "$CREATE_OUTPUT"
CREATED_RESOURCES+=("lambda-function:${FUNCTION_NAME}")

wait_for_resource "Lambda function to become Active" \
    "aws lambda get-function-configuration --function-name ${FUNCTION_NAME} --query State --output text" \
    "Active"

###############################################################################
# Step 3: Invoke the function
###############################################################################

echo ""
echo "==========================================="
echo "Step 3: Invoke the function"
echo "==========================================="
echo ""

TEST_EVENT='{"length": 6, "width": 7}'
echo "Invoking function with test event: ${TEST_EVENT}"
echo ""

echo "$TEST_EVENT" > "${TEMP_DIR}/test-event.json"

INVOKE_OUTPUT=$(aws lambda invoke \
    --function-name "$FUNCTION_NAME" \
    --payload "fileb://${TEMP_DIR}/test-event.json" \
    --cli-read-timeout 30 \
    "${TEMP_DIR}/response.json" 2>&1)
echo "$INVOKE_OUTPUT"

RESPONSE=$(cat "${TEMP_DIR}/response.json")
echo ""
echo "Function response: ${RESPONSE}"
echo ""

if echo "$INVOKE_OUTPUT" | grep -qi "functionerror"; then
    echo "WARNING: Function returned an error."
fi

###############################################################################
# Step 4: View CloudWatch logs
###############################################################################

echo ""
echo "==========================================="
echo "Step 4: View CloudWatch Logs"
echo "==========================================="
echo ""

echo "Log group: ${LOG_GROUP_NAME}"
echo ""

echo "Waiting for CloudWatch logs to be available..."

LOG_STREAMS=""
for i in $(seq 1 6); do
    LOG_STREAMS=$(aws logs describe-log-streams \
        --log-group-name "$LOG_GROUP_NAME" \
        --order-by LastEventTime \
        --descending \
        --query 'logStreams[0].logStreamName' \
        --output text 2>/dev/null) || true
    if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then
        break
    fi
    LOG_STREAMS=""
    sleep 5
done

if [ -n "$LOG_STREAMS" ] && [ "$LOG_STREAMS" != "None" ]; then
    echo "Latest log stream: ${LOG_STREAMS}"
    echo ""
    echo "--- Log events ---"
    LOG_EVENTS=$(aws logs get-log-events \
        --log-group-name "$LOG_GROUP_NAME" \
        --log-stream-name "$LOG_STREAMS" \
        --query 'events[].message' \
        --output text 2>&1) || true
    echo "$LOG_EVENTS"
    echo "--- End of log events ---"
else
    echo "No log streams found yet. Logs may take a moment to appear."
    echo "You can view them in the CloudWatch console:"
    echo "  Log group: ${LOG_GROUP_NAME}"
fi

CREATED_RESOURCES+=("log-group:${LOG_GROUP_NAME}")

###############################################################################
# Summary and cleanup
###############################################################################

echo ""
echo "==========================================="
echo "SUMMARY"
echo "==========================================="
echo ""
echo "Resources created:"
echo "  IAM role:          ${ROLE_NAME}"
echo "  Lambda function:   ${FUNCTION_NAME}"
echo "  CloudWatch logs:   ${LOG_GROUP_NAME}"
echo ""
echo "==========================================="
echo "CLEANUP CONFIRMATION"
echo "==========================================="
echo ""
echo "Do you want to clean up all created resources? (y/n): "
read -r CLEANUP_CHOICE

if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then
    cleanup_resources
else
    echo ""
    echo "Resources were NOT deleted. To clean up manually, run:"
    echo ""
    echo "  # Delete the Lambda function"
    echo "  aws lambda delete-function --function-name ${FUNCTION_NAME}"
    echo ""
    echo "  # Delete the CloudWatch log group"
    echo "  aws logs delete-log-group --log-group-name ${LOG_GROUP_NAME}"
    echo ""
    echo "  # Detach the policy and delete the IAM role"
    echo "  aws iam detach-role-policy --role-name ${ROLE_NAME} --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    echo "  aws iam delete-role --role-name ${ROLE_NAME}"
    echo ""

    if [ -d "$TEMP_DIR" ]; then
        rm -rf "$TEMP_DIR"
    fi
fi

echo ""
echo "Done."
```
+ For API details, see the following topics in *AWS CLI Command Reference*.
  + [AttachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/AttachRolePolicy)
  + [CreateFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/CreateFunction)
  + [CreateRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/CreateRole)
  + [DeleteFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/DeleteFunction)
  + [DeleteLogGroup](https://docs.aws.amazon.com/goto/aws-cli/logs-2014-03-28/DeleteLogGroup)
  + [DeleteRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DeleteRole)
  + [DescribeLogStreams](https://docs.aws.amazon.com/goto/aws-cli/logs-2014-03-28/DescribeLogStreams)
  + [DetachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DetachRolePolicy)
  + [GetFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/GetFunction)
  + [GetFunctionConfiguration](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/GetFunctionConfiguration)
  + [GetLogEvents](https://docs.aws.amazon.com/goto/aws-cli/logs-2014-03-28/GetLogEvents)
  + [Invoke](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/Invoke)

------

# Invoke a Lambda function from a browser
<a name="lambda_example_cross_LambdaForBrowser_section"></a>

The following code example shows how to invoke an AWS Lambda function from a browser.

------
#### [ JavaScript ]

**SDK for JavaScript (v2)**  
 You can create a browser-based application that uses an AWS Lambda function to update an Amazon DynamoDB table with user selections.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascript/example_code/lambda/lambda-for-browser).   

**Services used in this example**
+ DynamoDB
+ Lambda

**SDK for JavaScript (v3)**  
 You can create a browser-based application that uses an AWS Lambda function to update an Amazon DynamoDB table with user selections. This app uses AWS SDK for JavaScript v3.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/lambda-for-browser).   

**Services used in this example**
+ DynamoDB
+ Lambda

------

# Transform data for your application with S3 Object Lambda
<a name="lambda_example_cross_ServerlessS3DataTransformation_section"></a>

The following code example shows how to transform data for your application with S3 Object Lambda.

------
#### [ .NET ]

**SDK for .NET**  
 Shows how to add custom code to standard S3 GET requests to modify the requested object retrieved from S3 so that the object suit the needs of the requesting client or application.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/cross-service/S3ObjectLambdaFunction).   

**Services used in this example**
+ Lambda
+ Amazon S3

------

# Use API Gateway to invoke a Lambda function
<a name="lambda_example_cross_LambdaAPIGateway_section"></a>

The following code examples show how to create an AWS Lambda function invoked by Amazon API Gateway.

------
#### [ Java ]

**SDK for Java 2.x**  
 Shows how to create an AWS Lambda function by using the Lambda Java runtime API. This example invokes different AWS services to perform a specific use case. This example demonstrates how to create a Lambda function invoked by Amazon API Gateway that scans an Amazon DynamoDB table for work anniversaries and uses Amazon Simple Notification Service (Amazon SNS) to send a text message to your employees that congratulates them at their one year anniversary date.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_lambda_apigateway).   

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon SNS

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 Shows how to create an AWS Lambda function by using the Lambda JavaScript runtime API. This example invokes different AWS services to perform a specific use case. This example demonstrates how to create a Lambda function invoked by Amazon API Gateway that scans an Amazon DynamoDB table for work anniversaries and uses Amazon Simple Notification Service (Amazon SNS) to send a text message to your employees that congratulates them at their one year anniversary date.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/lambda-api-gateway).   
This example is also available in the [AWS SDK for JavaScript v3 developer guide](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/api-gateway-invoking-lambda-example.html).  

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon SNS

------
#### [ Python ]

**SDK for Python (Boto3)**  
 This example shows how to create and use an Amazon API Gateway REST API that targets an AWS Lambda function. The Lambda handler demonstrates how to route based on HTTP methods; how to get data from the query string, header, and body; and how to return a JSON response.   
+ Deploy a Lambda function.
+ Create an API Gateway REST API.
+ Create a REST resource that targets the Lambda function.
+ Grant permission to let API Gateway invoke the Lambda function.
+ Use the Requests package to send requests to the REST API.
+ Clean up all resources created during the demo.
 This example is best viewed on GitHub. For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/example_code/lambda#readme).   

**Services used in this example**
+ API Gateway
+ DynamoDB
+ Lambda
+ Amazon SNS

------

# Use Step Functions to invoke Lambda functions
<a name="lambda_example_cross_ServerlessWorkflows_section"></a>

The following code example shows how to create an AWS Step Functions state machine that invokes AWS Lambda functions in sequence.

------
#### [ Java ]

**SDK for Java 2.x**  
 Shows how to create an AWS serverless workflow by using AWS Step Functions and the AWS SDK for Java 2.x. Each workflow step is implemented using an AWS Lambda function.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_workflows_stepfunctions).   

**Services used in this example**
+ DynamoDB
+ Lambda
+ Amazon SES
+ Step Functions

------

# Use scheduled events to invoke a Lambda function
<a name="lambda_example_cross_LambdaScheduledEvents_section"></a>

The following code examples show how to create an AWS Lambda function invoked by an Amazon EventBridge scheduled event.

------
#### [ Java ]

**SDK for Java 2.x**  
 Shows how to create an Amazon EventBridge scheduled event that invokes an AWS Lambda function. Configure EventBridge to use a cron expression to schedule when the Lambda function is invoked. In this example, you create a Lambda function by using the Lambda Java runtime API. This example invokes different AWS services to perform a specific use case. This example demonstrates how to create an app that sends a mobile text message to your employees that congratulates them at the one year anniversary date.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_scheduled_events).   

**Services used in this example**
+ CloudWatch Logs
+ DynamoDB
+ EventBridge
+ Lambda
+ Amazon SNS

------
#### [ JavaScript ]

**SDK for JavaScript (v3)**  
 Shows how to create an Amazon EventBridge scheduled event that invokes an AWS Lambda function. Configure EventBridge to use a cron expression to schedule when the Lambda function is invoked. In this example, you create a Lambda function by using the Lambda JavaScript runtime API. This example invokes different AWS services to perform a specific use case. This example demonstrates how to create an app that sends a mobile text message to your employees that congratulates them at the one year anniversary date.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3/example_code/cross-services/lambda-scheduled-events).   
This example is also available in the [AWS SDK for JavaScript v3 developer guide](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/scheduled-events-invoking-lambda-example.html).  

**Services used in this example**
+ CloudWatch Logs
+ DynamoDB
+ EventBridge
+ Lambda
+ Amazon SNS

------
#### [ Python ]

**SDK for Python (Boto3)**  
 This example shows how to register an AWS Lambda function as the target of a scheduled Amazon EventBridge event. The Lambda handler writes a friendly message and the full event data to Amazon CloudWatch Logs for later retrieval.   
+ Deploys a Lambda function.
+ Creates an EventBridge scheduled event and makes the Lambda function the target.
+ Grants permission to let EventBridge invoke the Lambda function.
+ Prints the latest data from CloudWatch Logs to show the result of the scheduled invocations.
+ Cleans up all resources created during the demo.
 This example is best viewed on GitHub. For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python/example_code/lambda#readme).   

**Services used in this example**
+ CloudWatch Logs
+ DynamoDB
+ EventBridge
+ Lambda
+ Amazon SNS

------

# Use the Amazon Neptune API to develop a Lambda function that queries graph data
<a name="lambda_example_cross_Neptune_Query_section"></a>

The following code example shows how to use the Neptune API to query graph data.

------
#### [ Java ]

**SDK for Java 2.x**  
 Shows how to use Amazon Neptune Java API to create a Lambda function that queries graph data within the VPC.   
 For complete source code and instructions on how to set up and run, see the full example on [GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_neptune_lambda).   

**Services used in this example**
+ Lambda
+ Neptune

------

# Using property variables in CloudWatch dashboards to monitor multiple Lambda functions
<a name="lambda_example_iam_GettingStarted_032_section"></a>

The following code example shows how to:
+ Create Lambda functions for monitoring
+ Create a CloudWatch dashboard
+ Add a property variable to the dashboard
+ Clean up resources

------
#### [ Bash ]

**AWS CLI with Bash script**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [Sample developer tutorials](https://github.com/aws-samples/sample-developer-tutorials/tree/main/tuts/032-cloudwatch-streams) repository. 

```
#!/bin/bash

# CloudWatch Dashboard with Lambda Function Variable Script
# This script creates a CloudWatch dashboard with a property variable for Lambda function names

# Set up logging
LOG_FILE="cloudwatch-dashboard-script-v4.log"
echo "Starting script execution at $(date)" > "$LOG_FILE"

# Function to log commands and their output
log_cmd() {
    echo "$(date): Running command: $1" >> "$LOG_FILE"
    eval "$1" 2>&1 | tee -a "$LOG_FILE"
    return ${PIPESTATUS[0]}
}

# Function to check for errors in command output
check_error() {
    local cmd_output="$1"
    local cmd_status="$2"
    local error_msg="$3"
    
    if [ $cmd_status -ne 0 ] || echo "$cmd_output" | grep -i "error" > /dev/null; then
        echo "ERROR: $error_msg" | tee -a "$LOG_FILE"
        echo "Command output: $cmd_output" | tee -a "$LOG_FILE"
        cleanup_resources
        exit 1
    fi
}

# Function to clean up resources
cleanup_resources() {
    echo "" | tee -a "$LOG_FILE"
    echo "==========================================" | tee -a "$LOG_FILE"
    echo "CLEANUP PROCESS" | tee -a "$LOG_FILE"
    echo "==========================================" | tee -a "$LOG_FILE"
    
    if [ -n "$DASHBOARD_NAME" ]; then
        echo "Deleting CloudWatch dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE"
        log_cmd "aws cloudwatch delete-dashboards --dashboard-names \"$DASHBOARD_NAME\""
    fi
    
    if [ -n "$LAMBDA_FUNCTION1" ]; then
        echo "Deleting Lambda function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE"
        log_cmd "aws lambda delete-function --function-name \"$LAMBDA_FUNCTION1\""
    fi
    
    if [ -n "$LAMBDA_FUNCTION2" ]; then
        echo "Deleting Lambda function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE"
        log_cmd "aws lambda delete-function --function-name \"$LAMBDA_FUNCTION2\""
    fi
    
    if [ -n "$ROLE_NAME" ]; then
        echo "Detaching policy from role: $ROLE_NAME" | tee -a "$LOG_FILE"
        log_cmd "aws iam detach-role-policy --role-name \"$ROLE_NAME\" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
        
        echo "Deleting IAM role: $ROLE_NAME" | tee -a "$LOG_FILE"
        log_cmd "aws iam delete-role --role-name \"$ROLE_NAME\""
    fi
    
    echo "Cleanup completed." | tee -a "$LOG_FILE"
}

# Function to prompt for cleanup confirmation
confirm_cleanup() {
    echo "" | tee -a "$LOG_FILE"
    echo "==========================================" | tee -a "$LOG_FILE"
    echo "CLEANUP CONFIRMATION" | tee -a "$LOG_FILE"
    echo "==========================================" | tee -a "$LOG_FILE"
    echo "The following resources were created:" | tee -a "$LOG_FILE"
    echo "- CloudWatch Dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE"
    
    if [ -n "$LAMBDA_FUNCTION1" ]; then
        echo "- Lambda Function: $LAMBDA_FUNCTION1" | tee -a "$LOG_FILE"
    fi
    
    if [ -n "$LAMBDA_FUNCTION2" ]; then
        echo "- Lambda Function: $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE"
    fi
    
    if [ -n "$ROLE_NAME" ]; then
        echo "- IAM Role: $ROLE_NAME" | tee -a "$LOG_FILE"
    fi
    
    echo "" | tee -a "$LOG_FILE"
    echo "Do you want to clean up all created resources? (y/n): " | tee -a "$LOG_FILE"
    read -r CLEANUP_CHOICE
    
    if [[ "$CLEANUP_CHOICE" =~ ^[Yy]$ ]]; then
        cleanup_resources
    else
        echo "Resources were not cleaned up. You can manually delete them later." | tee -a "$LOG_FILE"
    fi
}

# Get AWS region
AWS_REGION=$(aws configure get region)
if [ -z "$AWS_REGION" ]; then
    AWS_REGION="us-east-1"
    echo "No region found in AWS config, defaulting to $AWS_REGION" | tee -a "$LOG_FILE"
else
    echo "Using AWS region: $AWS_REGION" | tee -a "$LOG_FILE"
fi

# Generate unique identifiers
RANDOM_ID=$(openssl rand -hex 6)
DASHBOARD_NAME="LambdaMetricsDashboard-${RANDOM_ID}"
LAMBDA_FUNCTION1="TestFunction1-${RANDOM_ID}"
LAMBDA_FUNCTION2="TestFunction2-${RANDOM_ID}"
ROLE_NAME="LambdaExecutionRole-${RANDOM_ID}"

echo "Using random identifier: $RANDOM_ID" | tee -a "$LOG_FILE"
echo "Dashboard name: $DASHBOARD_NAME" | tee -a "$LOG_FILE"
echo "Lambda function names: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE"
echo "IAM role name: $ROLE_NAME" | tee -a "$LOG_FILE"

# Create IAM role for Lambda functions
echo "Creating IAM role for Lambda..." | tee -a "$LOG_FILE"
TRUST_POLICY='{
  "Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "lambda.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}'

echo "$TRUST_POLICY" > trust-policy.json

ROLE_OUTPUT=$(log_cmd "aws iam create-role --role-name \"$ROLE_NAME\" --assume-role-policy-document file://trust-policy.json --output json")
check_error "$ROLE_OUTPUT" $? "Failed to create IAM role"

ROLE_ARN=$(echo "$ROLE_OUTPUT" | grep -o '"Arn": "[^"]*' | cut -d'"' -f4)
echo "Role ARN: $ROLE_ARN" | tee -a "$LOG_FILE"

# Attach Lambda basic execution policy to the role
echo "Attaching Lambda execution policy to role..." | tee -a "$LOG_FILE"
POLICY_OUTPUT=$(log_cmd "aws iam attach-role-policy --role-name \"$ROLE_NAME\" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole")
check_error "$POLICY_OUTPUT" $? "Failed to attach policy to role"

# Wait for role to propagate
echo "Waiting for IAM role to propagate..." | tee -a "$LOG_FILE"
sleep 10

# Create simple Python Lambda function code
echo "Creating Lambda function code..." | tee -a "$LOG_FILE"
cat > lambda_function.py << 'EOF'
def handler(event, context):
    print("Lambda function executed successfully")
    return {
        'statusCode': 200,
        'body': 'Success'
    }
EOF

# Zip the Lambda function code
log_cmd "zip -j lambda_function.zip lambda_function.py"

# Create first Lambda function
echo "Creating first Lambda function: $LAMBDA_FUNCTION1..." | tee -a "$LOG_FILE"
LAMBDA1_OUTPUT=$(log_cmd "aws lambda create-function --function-name \"$LAMBDA_FUNCTION1\" --runtime python3.9 --role \"$ROLE_ARN\" --handler lambda_function.handler --zip-file fileb://lambda_function.zip")
check_error "$LAMBDA1_OUTPUT" $? "Failed to create first Lambda function"

# Create second Lambda function
echo "Creating second Lambda function: $LAMBDA_FUNCTION2..." | tee -a "$LOG_FILE"
LAMBDA2_OUTPUT=$(log_cmd "aws lambda create-function --function-name \"$LAMBDA_FUNCTION2\" --runtime python3.9 --role \"$ROLE_ARN\" --handler lambda_function.handler --zip-file fileb://lambda_function.zip")
check_error "$LAMBDA2_OUTPUT" $? "Failed to create second Lambda function"

# Invoke Lambda functions to generate some metrics
echo "Invoking Lambda functions to generate metrics..." | tee -a "$LOG_FILE"
log_cmd "aws lambda invoke --function-name \"$LAMBDA_FUNCTION1\" --payload '{}' /dev/null"
log_cmd "aws lambda invoke --function-name \"$LAMBDA_FUNCTION2\" --payload '{}' /dev/null"

# Create CloudWatch dashboard with property variable
echo "Creating CloudWatch dashboard with property variable..." | tee -a "$LOG_FILE"

# Create a simpler dashboard with a property variable
# This approach uses a more basic dashboard structure that's known to work with the CloudWatch API
DASHBOARD_BODY=$(cat <<EOF
{
  "widgets": [
    {
      "type": "metric",
      "x": 0,
      "y": 0,
      "width": 12,
      "height": 6,
      "properties": {
        "metrics": [
          [ "AWS/Lambda", "Invocations", "FunctionName", "$LAMBDA_FUNCTION1" ]
        ],
        "view": "timeSeries",
        "stacked": false,
        "region": "$AWS_REGION",
        "title": "Lambda Invocations",
        "period": 300,
        "stat": "Sum"
      }
    }
  ]
}
EOF
)

# First create a basic dashboard without variables
echo "Creating initial dashboard without variables..." | tee -a "$LOG_FILE"
DASHBOARD_OUTPUT=$(log_cmd "aws cloudwatch put-dashboard --dashboard-name \"$DASHBOARD_NAME\" --dashboard-body '$DASHBOARD_BODY'")
check_error "$DASHBOARD_OUTPUT" $? "Failed to create initial CloudWatch dashboard"

# Now let's try to add a property variable using the console instructions
echo "To complete the tutorial, please follow these steps in the CloudWatch console:" | tee -a "$LOG_FILE"
echo "1. Open the CloudWatch console at https://console.aws.amazon.com/cloudwatch/" | tee -a "$LOG_FILE"
echo "2. Navigate to Dashboards and select your dashboard: $DASHBOARD_NAME" | tee -a "$LOG_FILE"
echo "3. Choose Actions > Variables > Create a variable" | tee -a "$LOG_FILE"
echo "4. Choose Property variable" | tee -a "$LOG_FILE"
echo "5. For Property that the variable changes, choose FunctionName" | tee -a "$LOG_FILE"
echo "6. For Input type, choose Select menu (dropdown)" | tee -a "$LOG_FILE"
echo "7. Choose Use the results of a metric search" | tee -a "$LOG_FILE"
echo "8. Choose Pre-built queries > Lambda > Errors" | tee -a "$LOG_FILE"
echo "9. Choose By Function Name and then choose Search" | tee -a "$LOG_FILE"
echo "10. (Optional) Configure any secondary settings as desired" | tee -a "$LOG_FILE"
echo "11. Choose Add variable" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"
echo "The dashboard has been created and can be accessed at:" | tee -a "$LOG_FILE"
echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE"

# Verify dashboard creation
echo "Verifying dashboard creation..." | tee -a "$LOG_FILE"
VERIFY_OUTPUT=$(log_cmd "aws cloudwatch get-dashboard --dashboard-name \"$DASHBOARD_NAME\"")
check_error "$VERIFY_OUTPUT" $? "Failed to verify dashboard creation"

echo "" | tee -a "$LOG_FILE"
echo "==========================================" | tee -a "$LOG_FILE"
echo "DASHBOARD CREATED SUCCESSFULLY" | tee -a "$LOG_FILE"
echo "==========================================" | tee -a "$LOG_FILE"
echo "Dashboard Name: $DASHBOARD_NAME" | tee -a "$LOG_FILE"
echo "Lambda Functions: $LAMBDA_FUNCTION1, $LAMBDA_FUNCTION2" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"
echo "You can view your dashboard in the CloudWatch console:" | tee -a "$LOG_FILE"
echo "https://console.aws.amazon.com/cloudwatch/home#dashboards:name=$DASHBOARD_NAME" | tee -a "$LOG_FILE"
echo "" | tee -a "$LOG_FILE"

# Prompt for cleanup
confirm_cleanup

echo "Script completed successfully." | tee -a "$LOG_FILE"
exit 0
```
+ For API details, see the following topics in *AWS CLI Command Reference*.
  + [AttachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/AttachRolePolicy)
  + [CreateFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/CreateFunction)
  + [CreateRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/CreateRole)
  + [DeleteDashboards](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/DeleteDashboards)
  + [DeleteFunction](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/DeleteFunction)
  + [DeleteRole](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DeleteRole)
  + [DetachRolePolicy](https://docs.aws.amazon.com/goto/aws-cli/iam-2010-05-08/DetachRolePolicy)
  + [GetDashboard](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/GetDashboard)
  + [Invoke](https://docs.aws.amazon.com/goto/aws-cli/lambda-2015-03-31/Invoke)
  + [PutDashboard](https://docs.aws.amazon.com/goto/aws-cli/monitoring-2010-08-01/PutDashboard)

------

# Write custom activity data with a Lambda function after Amazon Cognito user authentication using an AWS SDK
<a name="lambda_example_cross_CognitoCustomActivityLog_section"></a>

The following code example shows how to write custom activity data with a Lambda function after Amazon Cognito user authentication.
+ Use administrator functions to add a user to a user pool.
+ Configure a user pool to call a Lambda function for the `PostAuthentication` trigger.
+ Sign the new user in to Amazon Cognito.
+ The Lambda function writes custom information to CloudWatch Logs and to an DynamoDB table.
+ Get and display custom data from the DynamoDB table, then clean up resources.

------
#### [ Go ]

**SDK for Go V2**  
 There's more on GitHub. Find the complete example and learn how to set up and run in the [AWS Code Examples Repository](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/gov2/workflows/user_pools_and_lambda_triggers#code-examples). 
Run an interactive scenario at a command prompt.  

```
import (
	"context"
	"errors"
	"log"
	"strings"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// ActivityLog separates the steps of this scenario into individual functions so that
// they are simpler to read and understand.
type ActivityLog struct {
	helper       IScenarioHelper
	questioner   demotools.IQuestioner
	resources    Resources
	cognitoActor *actions.CognitoActions
}

// NewActivityLog constructs a new activity log runner.
func NewActivityLog(sdkConfig aws.Config, questioner demotools.IQuestioner, helper IScenarioHelper) ActivityLog {
	scenario := ActivityLog{
		helper:       helper,
		questioner:   questioner,
		resources:    Resources{},
		cognitoActor: &actions.CognitoActions{CognitoClient: cognitoidentityprovider.NewFromConfig(sdkConfig)},
	}
	scenario.resources.init(scenario.cognitoActor, questioner)
	return scenario
}

// AddUserToPool selects a user from the known users table and uses administrator credentials to add the user to the user pool.
func (runner *ActivityLog) AddUserToPool(ctx context.Context, userPoolId string, tableName string) (string, string) {
	log.Println("To facilitate this example, let's add a user to the user pool using administrator privileges.")
	users, err := runner.helper.GetKnownUsers(ctx, tableName)
	if err != nil {
		panic(err)
	}
	user := users.Users[0]
	log.Printf("Adding known user %v to the user pool.\n", user.UserName)
	err = runner.cognitoActor.AdminCreateUser(ctx, userPoolId, user.UserName, user.UserEmail)
	if err != nil {
		panic(err)
	}
	pwSet := false
	password := runner.questioner.AskPassword("\nEnter a password that has at least eight characters, uppercase, lowercase, numbers and symbols.\n"+
		"(the password will not display as you type):", 8)
	for !pwSet {
		log.Printf("\nSetting password for user '%v'.\n", user.UserName)
		err = runner.cognitoActor.AdminSetUserPassword(ctx, userPoolId, user.UserName, password)
		if err != nil {
			var invalidPassword *types.InvalidPasswordException
			if errors.As(err, &invalidPassword) {
				password = runner.questioner.AskPassword("\nEnter another password:", 8)
			} else {
				panic(err)
			}
		} else {
			pwSet = true
		}
	}

	log.Println(strings.Repeat("-", 88))

	return user.UserName, password
}

// AddActivityLogTrigger adds a Lambda handler as an invocation target for the PostAuthentication trigger.
func (runner *ActivityLog) AddActivityLogTrigger(ctx context.Context, userPoolId string, activityLogArn string) {
	log.Println("Let's add a Lambda function to handle the PostAuthentication trigger from Cognito.\n" +
		"This trigger happens after a user is authenticated, and lets your function take action, such as logging\n" +
		"the outcome.")
	err := runner.cognitoActor.UpdateTriggers(
		ctx, userPoolId,
		actions.TriggerInfo{Trigger: actions.PostAuthentication, HandlerArn: aws.String(activityLogArn)})
	if err != nil {
		panic(err)
	}
	runner.resources.triggers = append(runner.resources.triggers, actions.PostAuthentication)
	log.Printf("Lambda function %v added to user pool %v to handle PostAuthentication Cognito trigger.\n",
		activityLogArn, userPoolId)

	log.Println(strings.Repeat("-", 88))
}

// SignInUser signs in as the specified user.
func (runner *ActivityLog) SignInUser(ctx context.Context, clientId string, userName string, password string) {
	log.Printf("Now we'll sign in user %v and check the results in the logs and the DynamoDB table.", userName)
	runner.questioner.Ask("Press Enter when you're ready.")
	authResult, err := runner.cognitoActor.SignIn(ctx, clientId, userName, password)
	if err != nil {
		panic(err)
	}
	log.Println("Sign in successful.",
		"The PostAuthentication Lambda handler writes custom information to CloudWatch Logs.")

	runner.resources.userAccessTokens = append(runner.resources.userAccessTokens, *authResult.AccessToken)
}

// GetKnownUserLastLogin gets the login info for a user from the Amazon DynamoDB table and displays it.
func (runner *ActivityLog) GetKnownUserLastLogin(ctx context.Context, tableName string, userName string) {
	log.Println("The PostAuthentication handler also writes login data to the DynamoDB table.")
	runner.questioner.Ask("Press Enter when you're ready to continue.")
	users, err := runner.helper.GetKnownUsers(ctx, tableName)
	if err != nil {
		panic(err)
	}
	for _, user := range users.Users {
		if user.UserName == userName {
			log.Println("The last login info for the user in the known users table is:")
			log.Printf("\t%+v", *user.LastLogin)
		}
	}
	log.Println(strings.Repeat("-", 88))
}

// Run runs the scenario.
func (runner *ActivityLog) Run(ctx context.Context, stackName string) {
	defer func() {
		if r := recover(); r != nil {
			log.Println("Something went wrong with the demo.")
			runner.resources.Cleanup(ctx)
		}
	}()

	log.Println(strings.Repeat("-", 88))
	log.Printf("Welcome\n")

	log.Println(strings.Repeat("-", 88))

	stackOutputs, err := runner.helper.GetStackOutputs(ctx, stackName)
	if err != nil {
		panic(err)
	}
	runner.resources.userPoolId = stackOutputs["UserPoolId"]
	runner.helper.PopulateUserTable(ctx, stackOutputs["TableName"])
	userName, password := runner.AddUserToPool(ctx, stackOutputs["UserPoolId"], stackOutputs["TableName"])

	runner.AddActivityLogTrigger(ctx, stackOutputs["UserPoolId"], stackOutputs["ActivityLogFunctionArn"])
	runner.SignInUser(ctx, stackOutputs["UserPoolClientId"], userName, password)
	runner.helper.ListRecentLogEvents(ctx, stackOutputs["ActivityLogFunction"])
	runner.GetKnownUserLastLogin(ctx, stackOutputs["TableName"], userName)

	runner.resources.Cleanup(ctx)

	log.Println(strings.Repeat("-", 88))
	log.Println("Thanks for watching!")
	log.Println(strings.Repeat("-", 88))
}
```
Handle the `PostAuthentication` trigger with a Lambda function.  

```
import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/aws/aws-lambda-go/events"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

const TABLE_NAME = "TABLE_NAME"

// LoginInfo defines structured login data that can be marshalled to a DynamoDB format.
type LoginInfo struct {
	UserPoolId string `dynamodbav:"UserPoolId"`
	ClientId   string `dynamodbav:"ClientId"`
	Time       string `dynamodbav:"Time"`
}

// UserInfo defines structured user data that can be marshalled to a DynamoDB format.
type UserInfo struct {
	UserName  string    `dynamodbav:"UserName"`
	UserEmail string    `dynamodbav:"UserEmail"`
	LastLogin LoginInfo `dynamodbav:"LastLogin"`
}

// GetKey marshals the user email value to a DynamoDB key format.
func (user UserInfo) GetKey() map[string]dynamodbtypes.AttributeValue {
	userEmail, err := attributevalue.Marshal(user.UserEmail)
	if err != nil {
		panic(err)
	}
	return map[string]dynamodbtypes.AttributeValue{"UserEmail": userEmail}
}

type handler struct {
	dynamoClient *dynamodb.Client
}

// HandleRequest handles the PostAuthentication event by writing custom data to the logs and
// to an Amazon DynamoDB table.
func (h *handler) HandleRequest(ctx context.Context, event events.CognitoEventUserPoolsPostAuthentication) (events.CognitoEventUserPoolsPostAuthentication, error) {
	log.Printf("Received post authentication trigger from %v for user '%v'", event.TriggerSource, event.UserName)
	tableName := os.Getenv(TABLE_NAME)
	user := UserInfo{
		UserName:  event.UserName,
		UserEmail: event.Request.UserAttributes["email"],
		LastLogin: LoginInfo{
			UserPoolId: event.UserPoolID,
			ClientId:   event.CallerContext.ClientID,
			Time:       time.Now().Format(time.UnixDate),
		},
	}
	// Write to CloudWatch Logs.
	fmt.Printf("%#v", user)

	// Also write to an external system. This examples uses DynamoDB to demonstrate.
	userMap, err := attributevalue.MarshalMap(user)
	if err != nil {
		log.Printf("Couldn't marshal to DynamoDB map. Here's why: %v\n", err)
	} else if len(userMap) == 0 {
		log.Printf("User info marshaled to an empty map.")
	} else {
		_, err := h.dynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
			Item:      userMap,
			TableName: aws.String(tableName),
		})
		if err != nil {
			log.Printf("Couldn't write to DynamoDB. Here's why: %v\n", err)
		} else {
			log.Printf("Wrote user info to DynamoDB table %v.\n", tableName)
		}
	}

	return event, nil
}

func main() {
	ctx := context.Background()
	sdkConfig, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		log.Panicln(err)
	}
	h := handler{
		dynamoClient: dynamodb.NewFromConfig(sdkConfig),
	}
	lambda.Start(h.HandleRequest)
}
```
Create a struct that performs common tasks.  

```
import (
	"context"
	"log"
	"strings"
	"time"
	"user_pools_and_lambda_triggers/actions"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// IScenarioHelper defines common functions used by the workflows in this example.
type IScenarioHelper interface {
	Pause(secs int)
	GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error)
	PopulateUserTable(ctx context.Context, tableName string)
	GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error)
	AddKnownUser(ctx context.Context, tableName string, user actions.User)
	ListRecentLogEvents(ctx context.Context, functionName string)
}

// ScenarioHelper contains AWS wrapper structs used by the workflows in this example.
type ScenarioHelper struct {
	questioner  demotools.IQuestioner
	dynamoActor *actions.DynamoActions
	cfnActor    *actions.CloudFormationActions
	cwlActor    *actions.CloudWatchLogsActions
	isTestRun   bool
}

// NewScenarioHelper constructs a new scenario helper.
func NewScenarioHelper(sdkConfig aws.Config, questioner demotools.IQuestioner) ScenarioHelper {
	scenario := ScenarioHelper{
		questioner:  questioner,
		dynamoActor: &actions.DynamoActions{DynamoClient: dynamodb.NewFromConfig(sdkConfig)},
		cfnActor:    &actions.CloudFormationActions{CfnClient: cloudformation.NewFromConfig(sdkConfig)},
		cwlActor:    &actions.CloudWatchLogsActions{CwlClient: cloudwatchlogs.NewFromConfig(sdkConfig)},
	}
	return scenario
}

// Pause waits for the specified number of seconds.
func (helper ScenarioHelper) Pause(secs int) {
	if !helper.isTestRun {
		time.Sleep(time.Duration(secs) * time.Second)
	}
}

// GetStackOutputs gets the outputs from the specified CloudFormation stack in a structured format.
func (helper ScenarioHelper) GetStackOutputs(ctx context.Context, stackName string) (actions.StackOutputs, error) {
	return helper.cfnActor.GetOutputs(ctx, stackName), nil
}

// PopulateUserTable fills the known user table with example data.
func (helper ScenarioHelper) PopulateUserTable(ctx context.Context, tableName string) {
	log.Printf("First, let's add some users to the DynamoDB %v table we'll use for this example.\n", tableName)
	err := helper.dynamoActor.PopulateTable(ctx, tableName)
	if err != nil {
		panic(err)
	}
}

// GetKnownUsers gets the users from the known users table in a structured format.
func (helper ScenarioHelper) GetKnownUsers(ctx context.Context, tableName string) (actions.UserList, error) {
	knownUsers, err := helper.dynamoActor.Scan(ctx, tableName)
	if err != nil {
		log.Printf("Couldn't get known users from table %v. Here's why: %v\n", tableName, err)
	}
	return knownUsers, err
}

// AddKnownUser adds a user to the known users table.
func (helper ScenarioHelper) AddKnownUser(ctx context.Context, tableName string, user actions.User) {
	log.Printf("Adding user '%v' with email '%v' to the DynamoDB known users table...\n",
		user.UserName, user.UserEmail)
	err := helper.dynamoActor.AddUser(ctx, tableName, user)
	if err != nil {
		panic(err)
	}
}

// ListRecentLogEvents gets the most recent log stream and events for the specified Lambda function and displays them.
func (helper ScenarioHelper) ListRecentLogEvents(ctx context.Context, functionName string) {
	log.Println("Waiting a few seconds to let Lambda write to CloudWatch Logs...")
	helper.Pause(10)
	log.Println("Okay, let's check the logs to find what's happened recently with your Lambda function.")
	logStream, err := helper.cwlActor.GetLatestLogStream(ctx, functionName)
	if err != nil {
		panic(err)
	}
	log.Printf("Getting some recent events from log stream %v\n", *logStream.LogStreamName)
	events, err := helper.cwlActor.GetLogEvents(ctx, functionName, *logStream.LogStreamName, 10)
	if err != nil {
		panic(err)
	}
	for _, event := range events {
		log.Printf("\t%v", *event.Message)
	}
	log.Println(strings.Repeat("-", 88))
}
```
Create a struct that wraps Amazon Cognito actions.  

```
import (
	"context"
	"errors"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
	"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider/types"
)

type CognitoActions struct {
	CognitoClient *cognitoidentityprovider.Client
}



// Trigger and TriggerInfo define typed data for updating an Amazon Cognito trigger.
type Trigger int

const (
	PreSignUp Trigger = iota
	UserMigration
	PostAuthentication
)

type TriggerInfo struct {
	Trigger    Trigger
	HandlerArn *string
}

// UpdateTriggers adds or removes Lambda triggers for a user pool. When a trigger is specified with a `nil` value,
// it is removed from the user pool.
func (actor CognitoActions) UpdateTriggers(ctx context.Context, userPoolId string, triggers ...TriggerInfo) error {
	output, err := actor.CognitoClient.DescribeUserPool(ctx, &cognitoidentityprovider.DescribeUserPoolInput{
		UserPoolId: aws.String(userPoolId),
	})
	if err != nil {
		log.Printf("Couldn't get info about user pool %v. Here's why: %v\n", userPoolId, err)
		return err
	}
	lambdaConfig := output.UserPool.LambdaConfig
	for _, trigger := range triggers {
		switch trigger.Trigger {
		case PreSignUp:
			lambdaConfig.PreSignUp = trigger.HandlerArn
		case UserMigration:
			lambdaConfig.UserMigration = trigger.HandlerArn
		case PostAuthentication:
			lambdaConfig.PostAuthentication = trigger.HandlerArn
		}
	}
	_, err = actor.CognitoClient.UpdateUserPool(ctx, &cognitoidentityprovider.UpdateUserPoolInput{
		UserPoolId:   aws.String(userPoolId),
		LambdaConfig: lambdaConfig,
	})
	if err != nil {
		log.Printf("Couldn't update user pool %v. Here's why: %v\n", userPoolId, err)
	}
	return err
}



// SignUp signs up a user with Amazon Cognito.
func (actor CognitoActions) SignUp(ctx context.Context, clientId string, userName string, password string, userEmail string) (bool, error) {
	confirmed := false
	output, err := actor.CognitoClient.SignUp(ctx, &cognitoidentityprovider.SignUpInput{
		ClientId: aws.String(clientId),
		Password: aws.String(password),
		Username: aws.String(userName),
		UserAttributes: []types.AttributeType{
			{Name: aws.String("email"), Value: aws.String(userEmail)},
		},
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't sign up user %v. Here's why: %v\n", userName, err)
		}
	} else {
		confirmed = output.UserConfirmed
	}
	return confirmed, err
}



// SignIn signs in a user to Amazon Cognito using a username and password authentication flow.
func (actor CognitoActions) SignIn(ctx context.Context, clientId string, userName string, password string) (*types.AuthenticationResultType, error) {
	var authResult *types.AuthenticationResultType
	output, err := actor.CognitoClient.InitiateAuth(ctx, &cognitoidentityprovider.InitiateAuthInput{
		AuthFlow:       "USER_PASSWORD_AUTH",
		ClientId:       aws.String(clientId),
		AuthParameters: map[string]string{"USERNAME": userName, "PASSWORD": password},
	})
	if err != nil {
		var resetRequired *types.PasswordResetRequiredException
		if errors.As(err, &resetRequired) {
			log.Println(*resetRequired.Message)
		} else {
			log.Printf("Couldn't sign in user %v. Here's why: %v\n", userName, err)
		}
	} else {
		authResult = output.AuthenticationResult
	}
	return authResult, err
}



// ForgotPassword starts a password recovery flow for a user. This flow typically sends a confirmation code
// to the user's configured notification destination, such as email.
func (actor CognitoActions) ForgotPassword(ctx context.Context, clientId string, userName string) (*types.CodeDeliveryDetailsType, error) {
	output, err := actor.CognitoClient.ForgotPassword(ctx, &cognitoidentityprovider.ForgotPasswordInput{
		ClientId: aws.String(clientId),
		Username: aws.String(userName),
	})
	if err != nil {
		log.Printf("Couldn't start password reset for user '%v'. Here;s why: %v\n", userName, err)
	}
	return output.CodeDeliveryDetails, err
}



// ConfirmForgotPassword confirms a user with a confirmation code and a new password.
func (actor CognitoActions) ConfirmForgotPassword(ctx context.Context, clientId string, code string, userName string, password string) error {
	_, err := actor.CognitoClient.ConfirmForgotPassword(ctx, &cognitoidentityprovider.ConfirmForgotPasswordInput{
		ClientId:         aws.String(clientId),
		ConfirmationCode: aws.String(code),
		Password:         aws.String(password),
		Username:         aws.String(userName),
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't confirm user %v. Here's why: %v", userName, err)
		}
	}
	return err
}



// DeleteUser removes a user from the user pool.
func (actor CognitoActions) DeleteUser(ctx context.Context, userAccessToken string) error {
	_, err := actor.CognitoClient.DeleteUser(ctx, &cognitoidentityprovider.DeleteUserInput{
		AccessToken: aws.String(userAccessToken),
	})
	if err != nil {
		log.Printf("Couldn't delete user. Here's why: %v\n", err)
	}
	return err
}



// AdminCreateUser uses administrator credentials to add a user to a user pool. This method leaves the user
// in a state that requires they enter a new password next time they sign in.
func (actor CognitoActions) AdminCreateUser(ctx context.Context, userPoolId string, userName string, userEmail string) error {
	_, err := actor.CognitoClient.AdminCreateUser(ctx, &cognitoidentityprovider.AdminCreateUserInput{
		UserPoolId:     aws.String(userPoolId),
		Username:       aws.String(userName),
		MessageAction:  types.MessageActionTypeSuppress,
		UserAttributes: []types.AttributeType{{Name: aws.String("email"), Value: aws.String(userEmail)}},
	})
	if err != nil {
		var userExists *types.UsernameExistsException
		if errors.As(err, &userExists) {
			log.Printf("User %v already exists in the user pool.", userName)
			err = nil
		} else {
			log.Printf("Couldn't create user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}



// AdminSetUserPassword uses administrator credentials to set a password for a user without requiring a
// temporary password.
func (actor CognitoActions) AdminSetUserPassword(ctx context.Context, userPoolId string, userName string, password string) error {
	_, err := actor.CognitoClient.AdminSetUserPassword(ctx, &cognitoidentityprovider.AdminSetUserPasswordInput{
		Password:   aws.String(password),
		UserPoolId: aws.String(userPoolId),
		Username:   aws.String(userName),
		Permanent:  true,
	})
	if err != nil {
		var invalidPassword *types.InvalidPasswordException
		if errors.As(err, &invalidPassword) {
			log.Println(*invalidPassword.Message)
		} else {
			log.Printf("Couldn't set password for user %v. Here's why: %v\n", userName, err)
		}
	}
	return err
}
```
Create a struct that wraps DynamoDB actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb"
	"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
)

// DynamoActions encapsulates the Amazon Simple Notification Service (Amazon SNS) actions
// used in the examples.
type DynamoActions struct {
	DynamoClient *dynamodb.Client
}

// User defines structured user data.
type User struct {
	UserName  string
	UserEmail string
	LastLogin *LoginInfo `dynamodbav:",omitempty"`
}

// LoginInfo defines structured custom login data.
type LoginInfo struct {
	UserPoolId string
	ClientId   string
	Time       string
}

// UserList defines a list of users.
type UserList struct {
	Users []User
}

// UserNameList returns the usernames contained in a UserList as a list of strings.
func (users *UserList) UserNameList() []string {
	names := make([]string, len(users.Users))
	for i := 0; i < len(users.Users); i++ {
		names[i] = users.Users[i].UserName
	}
	return names
}

// PopulateTable adds a set of test users to the table.
func (actor DynamoActions) PopulateTable(ctx context.Context, tableName string) error {
	var err error
	var item map[string]types.AttributeValue
	var writeReqs []types.WriteRequest
	for i := 1; i < 4; i++ {
		item, err = attributevalue.MarshalMap(User{UserName: fmt.Sprintf("test_user_%v", i), UserEmail: fmt.Sprintf("test_email_%v@example.com", i)})
		if err != nil {
			log.Printf("Couldn't marshall user into DynamoDB format. Here's why: %v\n", err)
			return err
		}
		writeReqs = append(writeReqs, types.WriteRequest{PutRequest: &types.PutRequest{Item: item}})
	}
	_, err = actor.DynamoClient.BatchWriteItem(ctx, &dynamodb.BatchWriteItemInput{
		RequestItems: map[string][]types.WriteRequest{tableName: writeReqs},
	})
	if err != nil {
		log.Printf("Couldn't populate table %v with users. Here's why: %v\n", tableName, err)
	}
	return err
}

// Scan scans the table for all items.
func (actor DynamoActions) Scan(ctx context.Context, tableName string) (UserList, error) {
	var userList UserList
	output, err := actor.DynamoClient.Scan(ctx, &dynamodb.ScanInput{
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't scan table %v for items. Here's why: %v\n", tableName, err)
	} else {
		err = attributevalue.UnmarshalListOfMaps(output.Items, &userList.Users)
		if err != nil {
			log.Printf("Couldn't unmarshal items into users. Here's why: %v\n", err)
		}
	}
	return userList, err
}

// AddUser adds a user item to a table.
func (actor DynamoActions) AddUser(ctx context.Context, tableName string, user User) error {
	userItem, err := attributevalue.MarshalMap(user)
	if err != nil {
		log.Printf("Couldn't marshall user to item. Here's why: %v\n", err)
	}
	_, err = actor.DynamoClient.PutItem(ctx, &dynamodb.PutItemInput{
		Item:      userItem,
		TableName: aws.String(tableName),
	})
	if err != nil {
		log.Printf("Couldn't put item in table %v. Here's why: %v", tableName, err)
	}
	return err
}
```
Create a struct that wraps CloudWatch Logs actions.  

```
import (
	"context"
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
	"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
)

type CloudWatchLogsActions struct {
	CwlClient *cloudwatchlogs.Client
}

// GetLatestLogStream gets the most recent log stream for a Lambda function.
func (actor CloudWatchLogsActions) GetLatestLogStream(ctx context.Context, functionName string) (types.LogStream, error) {
	var logStream types.LogStream
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.DescribeLogStreams(ctx, &cloudwatchlogs.DescribeLogStreamsInput{
		Descending:   aws.Bool(true),
		Limit:        aws.Int32(1),
		LogGroupName: aws.String(logGroupName),
		OrderBy:      types.OrderByLastEventTime,
	})
	if err != nil {
		log.Printf("Couldn't get log streams for log group %v. Here's why: %v\n", logGroupName, err)
	} else {
		logStream = output.LogStreams[0]
	}
	return logStream, err
}

// GetLogEvents gets the most recent eventCount events from the specified log stream.
func (actor CloudWatchLogsActions) GetLogEvents(ctx context.Context, functionName string, logStreamName string, eventCount int32) (
	[]types.OutputLogEvent, error) {
	var events []types.OutputLogEvent
	logGroupName := fmt.Sprintf("/aws/lambda/%s", functionName)
	output, err := actor.CwlClient.GetLogEvents(ctx, &cloudwatchlogs.GetLogEventsInput{
		LogStreamName: aws.String(logStreamName),
		Limit:         aws.Int32(eventCount),
		LogGroupName:  aws.String(logGroupName),
	})
	if err != nil {
		log.Printf("Couldn't get log event for log stream %v. Here's why: %v\n", logStreamName, err)
	} else {
		events = output.Events
	}
	return events, err
}
```
Create a struct that wraps CloudFormation actions.  

```
import (
	"context"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
)

// StackOutputs defines a map of outputs from a specific stack.
type StackOutputs map[string]string

type CloudFormationActions struct {
	CfnClient *cloudformation.Client
}

// GetOutputs gets the outputs from a CloudFormation stack and puts them into a structured format.
func (actor CloudFormationActions) GetOutputs(ctx context.Context, stackName string) StackOutputs {
	output, err := actor.CfnClient.DescribeStacks(ctx, &cloudformation.DescribeStacksInput{
		StackName: aws.String(stackName),
	})
	if err != nil || len(output.Stacks) == 0 {
		log.Panicf("Couldn't find a CloudFormation stack named %v. Here's why: %v\n", stackName, err)
	}
	stackOutputs := StackOutputs{}
	for _, out := range output.Stacks[0].Outputs {
		stackOutputs[*out.OutputKey] = *out.OutputValue
	}
	return stackOutputs
}
```
Clean up resources.  

```
import (
	"context"
	"log"
	"user_pools_and_lambda_triggers/actions"

	"github.com/awsdocs/aws-doc-sdk-examples/gov2/demotools"
)

// Resources keeps track of AWS resources created during an example and handles
// cleanup when the example finishes.
type Resources struct {
	userPoolId       string
	userAccessTokens []string
	triggers         []actions.Trigger

	cognitoActor *actions.CognitoActions
	questioner   demotools.IQuestioner
}

func (resources *Resources) init(cognitoActor *actions.CognitoActions, questioner demotools.IQuestioner) {
	resources.userAccessTokens = []string{}
	resources.triggers = []actions.Trigger{}
	resources.cognitoActor = cognitoActor
	resources.questioner = questioner
}

// Cleanup deletes all AWS resources created during an example.
func (resources *Resources) Cleanup(ctx context.Context) {
	defer func() {
		if r := recover(); r != nil {
			log.Printf("Something went wrong during cleanup.\n%v\n", r)
			log.Println("Use the AWS Management Console to remove any remaining resources \n" +
				"that were created for this scenario.")
		}
	}()

	wantDelete := resources.questioner.AskBool("Do you want to remove all of the AWS resources that were created "+
		"during this demo (y/n)?", "y")
	if wantDelete {
		for _, accessToken := range resources.userAccessTokens {
			err := resources.cognitoActor.DeleteUser(ctx, accessToken)
			if err != nil {
				log.Println("Couldn't delete user during cleanup.")
				panic(err)
			}
			log.Println("Deleted user.")
		}
		triggerList := make([]actions.TriggerInfo, len(resources.triggers))
		for i := 0; i < len(resources.triggers); i++ {
			triggerList[i] = actions.TriggerInfo{Trigger: resources.triggers[i], HandlerArn: nil}
		}
		err := resources.cognitoActor.UpdateTriggers(ctx, resources.userPoolId, triggerList...)
		if err != nil {
			log.Println("Couldn't update Cognito triggers during cleanup.")
			panic(err)
		}
		log.Println("Removed Cognito triggers from user pool.")
	} else {
		log.Println("Be sure to remove resources when you're done with them to avoid unexpected charges!")
	}
}
```
+ For API details, see the following topics in *AWS SDK for Go API Reference*.
  + [AdminCreateUser](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.AdminCreateUser)
  + [AdminSetUserPassword](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.AdminSetUserPassword)
  + [DeleteUser](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.DeleteUser)
  + [InitiateAuth](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.InitiateAuth)
  + [UpdateUserPool](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider#Client.UpdateUserPool)

------