Terraform AWS Cognito User Pool: Module Patterns and Native Resource Configuration

Amazon Cognito User Pools provide a secure user directory that scales to hundreds of millions of users. As a fully managed service, User Pools are easy to set up without any worries about standing up server infrastructure. Terraform brings repeatable, versioned configuration to Cognito, with two dominant community module patterns and native resource definitions that expose the full Cognito API.

Introduction

Terraform for Cognito sits at the intersection of identity security, OAuth configuration, and immutable infrastructure constraints. Cognito settings are largely immutable after creation. Terraform will try to destroy and recreate the pool, which means losing all users. Plan these carefully. That makes getting the initial setup right particularly important, and it's why Terraform is so valuable here.

This article covers module-based workflows using lgallard/terraform-aws-cognito-user-pool and mineiros-io/terraform-aws-cognito-user-pool, plus native resource patterns for custom attributes, log delivery, resource servers, Lambda triggers, and hosted UI configuration.

Module Approaches for Cognito User Pools

lgallard Cognito User Pool Module

The lgallard module creates Amazon Cognito User Pools, configure its attributes and resources such as app clients, domain, resource servers. You can use this module to create a Cognito User Pool using the default values or use the detailed definition to set every aspect of the Cognito User Pool.

Examples include the simple example using default values, the simpleextended version which adds app clients, domain, resource servers resources, the complete version with a detailed example, or the withbranding example that demonstrates managed login branding capabilities.

Simple default usage:

hcl module "aws_cognito_user_pool_simple" { source = "lgallard/cognito-user-pool/aws" user_pool_name = "mypool" # Recommended: Enable schema ignore changes for new deployments # This prevents perpetual diffs if you plan to use custom schemas ignore_schema_changes = true tags = { Owner = "infra" Environment = "production" Terraform = true } }

Conditional creation for older Terraform forms is supported:

hcl module "aws_cognito_user_pool_conditional_creation" { source = "lgallard/cognito-user-pool/aws" user_pool_name = "conditional_user_pool" enabled = false ignore_schema_changes = true tags = { Owner = "infra" Environment = "production" Terraform = true } }

For Terraform 0.14 and later you can use count inside module blocks, or use the input variable enabled as described above.

Log Delivery Configuration

You can configure Cognito user pool log delivery for notification errors and threat-protection user activity logs. Notification logs use userNotification with ERROR and CloudWatch Logs.

```hcl
resource "awscloudwatchloggroup" "cognitousernotificationerrors" {
name = "/aws/vendedlogs/cognito/mypool/user-notification-errors"
retentionindays = 30
}

module "awscognitouserpoolwithlogdelivery" {
source = "lgallard/cognito-user-pool/aws"
userpoolname = "mypool"
ignoreschemachanges = true
logdeliveryconfiguration = {
logconfigurations = [
{
event
source = "userNotification"
loglevel = "ERROR"
cloud
watchlogsconfiguration = {
loggrouparn = awscloudwatchloggroup.cognitousernotificationerrors.arn
}
}
]
}
}
```

See examples/log_delivery for a complete example.

Mineiros Cognito User Pool Module

This module supports Terraform v1.x, v0.15, v0.14, v0.13 as well as v0.12.20 and above and is compatible with the Terraform AWS provider v3.50 and above.

Module features include Getting Started, Module Argument Reference, Module Outputs, External Documentation, Module Versioning, About Mineiros, Reporting Issues, Contributing, Makefile Targets, License.

In contrast to the plain cognitouserpool resource this module has a more secure level of default settings. While all settings can be customized as needed, best practices are pre-configured.

Default Security Settings:
Per default, only administrators are allowed to create user profiles by setting allowadmincreateuseronly to true. This module comes with a strong default password policy.

Standard Cognito Features:
- Create a Cognito User Pool with pre-configured best practices
- Create Cognito User Pool Clients
- Create a Cognito User Pool Domain
- Create Cognito User Pool Resource Servers as associated scopes

Features not yet implemented:
cognitousergroup

Most basic usage just setting required arguments:

hcl module "terraform-aws-cognito-user-pool" { source = "mineiros-io/cognito-user-pool/aws" version = "~> 0.9.0" name = "application-userpool" }

Advanced usage as found in examples/complete/main.tf setting all required and optional arguments to their default values.

Module argument reference includes module_enabled:

module_enabled: (Optional bool) Specifies whether resources in the module will be created. Default is true.

Module Comparison

Capability lgallard Module mineiros-io Module
Terraform versions Supports modern usage with enabled flag Terraform v1.x, v0.15, v0.14, v0.13, v0.12.20+
AWS Provider Implicit via module Compatible with Terraform AWS provider v3.50 and above
Default security posture Configurable via ignoreschemachanges Pre-configured best practices, allowadmincreateuseronly true by default
Log delivery logdeliveryconfiguration with event_source userNotification ERROR Not detailed in reference
Conditional creation enabled = false module_enabled optional bool default true
Branding with_branding example Not detailed in reference
Resource servers simple_extended example Create Cognito User Pool Resource Servers as associated scopes

Native Resource Configuration

Using the AWS provider directly gives full control over Cognito's immutable settings. Cognito with Terraform is a great combination because so many Cognito settings are immutable after creation.

Basic User Pool with Email Sign-In

```hcl
resource "awscognitouserpool" "main" {
name = "myapp-users"
username
attributes = ["email"]
autoverifiedattributes = ["email"]

passwordpolicy {
minimum
length = 12
requirelowercase = true
require
uppercase = true
requirenumbers = true
require
symbols = false
temporarypasswordvalidity_days = 7
}

accountrecoverysetting {
recoverymechanism {
name = "verified
email"
priority = 1
}
}

schema {
name = "email"
attributedatatype = "String"
required = true
mutable = true
stringattributeconstraints {
minlength = 5
max
length = 256
}
}

schema {
name = "name"
attributedatatype = "String"
required = true
mutable = true
stringattributeconstraints {
minlength = 1
max
length = 256
}
}

tags = {
Environment = "production"
ManagedBy = "terraform"
}
}
```

Important: username_attributes and schema settings with required = true cannot be changed after the user pool is created.

Custom Attributes

You can add custom attributes for application-specific data:

```hcl
resource "awscognitouserpool" "withcustomattrs" {
name = "myapp-users-v2"
username
attributes = ["email"]

schema {
name = "company"
attributedatatype = "String"
mutable = true
stringattributeconstraints {
minlength = 1
max
length = 256
}
}

schema {
name = "role"
attributedatatype = "String"
mutable = true
stringattributeconstraints {
minlength = 1
max
length = 50
}
}

schema {
name = "accountid"
attribute
datatype = "Number"
mutable = false
number
attributeconstraints {
min
value = 1
max_value = 999999999
}
}
}
```

Custom attributes are accessed as custom:company, custom:role, etc. in tokens and API calls. Also note: custom attributes can't be removed or changed once created. You can only add new ones.

Resource Server and Custom OAuth Scopes

If your API needs custom OAuth scopes, define a resource server:

```hcl
resource "awscognitoresourceserver" "api" {
identifier = "api"
name = "My API"
user
poolid = awscognitouserpool.main.id

scope {
scopename = "read"
scope
description = "Read access"
}

scope {
scopename = "write"
scope
description = "Write access"
}
}
```

After creating the resource server, use scopes like api/read and api/write in your app clients.

Lambda Triggers

Common use cases for triggers:
- Pre sign-up: Block certain email domains, auto-verify emails
- Post confirmation: Create a user record in your database, send a welcome email
- Pre token generation: Add custom claims based on user attributes
- Custom message: Localize or brand verification emails

Outputs export the values your application needs:

```hcl
output "userpoolid" {
value = awscognitouser_pool.main.id
}

output "userpoolarn" {
value = awscognitouser_pool.main.arn
}

output "clientid" {
value = aws
cognitouserpool_client.web.id
}

output "hosteduidomain" {
value = "https://${awscognitouserpooldomain.main.domain}.auth.${var.aws_region}.amazoncognito.com"
}
```

Email Configuration

By default, Cognito sends emails using a shared address with strict rate limits.

Immutable Planning and Safe Upgrades

In this guide, we'll build a Cognito user pool from scratch, add app clients, configure the hosted UI, and wire up Lambda triggers for custom logic.

Cognito has a lot of configuration options, and many of them can't be changed after creation. That makes getting the initial setup right particularly important, and it's why Terraform is so valuable here.

Key immutable considerations:
- username_attributes cannot be changed
- schema attributes with required = true cannot be changed
- Custom attributes cannot be removed or changed once created
- Password policy changes are limited after creation

Use ignoreschemachanges = true to prevent perpetual diffs if you plan to use custom schemas.

Module Outputs and Example Patterns

The lgallard module supports app clients, domain, resource servers resources in the simple_extended version. The complete version with a detailed example shows full attribute configuration.

The mineiros module provides module argument reference and module outputs with pre-configured best practices.

Practical Terraform Patterns

TL;DR: This is a simple article on how to use Terraform to setup and maintain an AWS Cognito user pool. If you are in a hurry and you already know Terraform the example below should be enough for you.

A simple article on how to use Terraform to setup and maintain an AWS Cognito user pool.

For conditional resources in earlier form versions such as 0.11, 0.12 and 0.13 you can set the input variable enabled to false.

Pattern Use Case
ignoreschemachanges = true Prevent perpetual diffs for custom schemas
enabled = false Skip creation in older Terraform versions
module_enabled Toggle creation in mineiros module
logdeliveryconfiguration Ship userNotification ERROR to CloudWatch
allowadmincreateuseronly true Default secure setting in mineiros module

Conclusion

Terraform AWS Cognito User Pool management requires careful handling of immutable settings and a clear choice between module convenience and native resource control. The lgallard module offers flexible examples for default values, extended resources, log delivery, and branding with an ignoreschemachanges safeguard for custom schemas. The mineiros-io module brings version support for Terraform v1.x down to v0.12.20 and AWS provider v3.50+, with pre-configured best practices such as allowadmincreateuseronly true and a strong default password policy.

Native resource definitions remain essential for custom attributes, resource servers with scopes like api/read and api/write, Lambda triggers for pre sign-up, post confirmation, pre token generation, and custom message workflows, and precise password policy and account recovery settings. Outputs such as userpoolid, userpoolarn, clientid, and hostedui_domain complete the integration.

Plan immutable fields at creation time: username_attributes, required schema, custom attributes, and password policy constraints. Use module toggles and log delivery for operational safety. This combination delivers repeatable, secure Cognito deployments at scale.

Sources

  1. lgallard/terraform-aws-cognito-user-pool
  2. mineiros-io/terraform-aws-cognito-user-pool
  3. epiphyte.io/cognito-user-pool-with-terraform
  4. oneuptime.com/create-cognito-user-pools-with-terraform

Related Posts