Skip to content

Cognito User Pool Module

This module creates and configures an AWS Cognito User Pool with support for external identity providers (Entra ID, Google), custom domains, MFA, and customizable email templates.

Features

  • Cognito User Pool with admin-only user creation
  • Multi-factor authentication (MFA) support with software tokens
  • External identity provider integration:
  • Microsoft Entra ID (Azure AD) via OIDC
  • Google OAuth
  • Pre-token generation Lambda for external IDP group passthrough
  • Custom domain support with Route53 DNS management
  • Customizable email templates for invitations and verification
  • Account recovery via email and phone

Usage

Basic Example

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "my-app-federated"
}

With MFA Enabled

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "secure-user-pool"
  mfa_configuration = "ON"  # Required for all users
}

With Entra ID (Azure AD) Integration

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "enterprise-federated"

  entra_provider = {
    client_id = "c7ab4d19-581b-4c69-b09b-2f066b7281d2"
    tenant_id = "e184a7ee-f2d8-48a3-9f0b-388319478763"
    # client_secret_parameter defaults to "/cognito/external/idp/entra/CLIENT_SECRET"
  }
}

With Google Integration

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "consumer-federated"

  google_provider = {
    client_id = "660314451535-e75j9m339mjmb8u37402v6oabvbuaouk.apps.googleusercontent.com"
    # client_secret_parameter defaults to "/cognito/external/idp/google/CLIENT_SECRET"
  }
}

With Custom Domain

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "branded-user-pool"

  custom_domain = {
    domain           = "auth.example.com"
    certificate_arn  = data.terraform_remote_state.acm_global.outputs.acm_certificate_arn
    hosted_zone_name = "example.com"  # or use hosted_zone_id directly
  }
}

With Custom Email Templates

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "my-app-federated"

  invite_message_template = {
    email_subject = "MyApp – Your Account Has Been Created"
    email_message = <<-HTML
      <html>
      <body>
        <h2>Welcome to MyApp</h2>
        <p>Your credentials:</p>
        <ul>
          <li>Username: {username}</li>
          <li>Temporary password: {####}</li>
        </ul>
        <p>Sign in at <a href="https://app.example.com">https://app.example.com</a></p>
      </body>
      </html>
    HTML
    sms_message   = "Your MyApp username is {username} and temporary password is {####}"
  }

  verification_message_template = {
    email_subject = "MyApp – Verify Your Email"
    email_message = <<-HTML
      <html>
      <body>
        <h2>MyApp</h2>
        <p>Your verification code: {####}</p>
      </body>
      </html>
    HTML
  }
}

Full Production Example

module "cognito_pool" {
  source = "./modules/cognito_pool"

  create            = true
  cognito_pool_name = "production-federated"
  mfa_configuration = "ON"

  # Multiple identity providers
  entra_provider = {
    client_id = "c7ab4d19-581b-4c69-b09b-2f066b7281d2"
    tenant_id = "e184a7ee-f2d8-48a3-9f0b-388319478763"
  }

  google_provider = {
    client_id = "660314451535-e75j9m339mjmb8u37402v6oabvbuaouk.apps.googleusercontent.com"
  }

  # Custom branded domain
  custom_domain = {
    domain           = "auth.app.example.com"
    certificate_arn  = data.terraform_remote_state.acm_global.outputs.acm_certificate_arn
    hosted_zone_name = "app.example.com"
  }

  # Custom email templates (optional - defaults use cognito_pool_name)
  invite_message_template = {
    email_subject = "MyApp – Your Account Has Been Created"
    email_message = <<-HTML
      <html>
      <body>
        <h2>Welcome to MyApp</h2>
        <p>Username: {username}</p>
        <p>Temporary password: {####}</p>
        <p>Sign in at <a href="https://app.example.com">https://app.example.com</a></p>
      </body>
      </html>
    HTML
    sms_message   = "Your MyApp username is {username} and temporary password is {####}"
  }

  verification_message_template = {
    email_subject = "MyApp – Verify Your Email"
    email_message = <<-HTML
      <html>
      <body>
        <h2>MyApp</h2>
        <p>Your verification code: {####}</p>
      </body>
      </html>
    HTML
  }
}

# App client with all identity providers
module "cognito_app" {
  source = "./modules/cognito_app"

  create               = true
  client_app_name      = "my-web-app"
  cognito_user_pool_id = module.cognito_pool.aws_cognito_pool_id

  callback_urls = [
    "https://app.example.com/oauth/openid/callback",
    "http://localhost:8000/callback"
  ]

  explicit_auth_flows          = ["ALLOW_REFRESH_TOKEN_AUTH"]
  allowed_oauth_flows          = ["code"]
  allowed_oauth_scopes         = ["openid", "email", "profile"]
  supported_identity_providers = concat(["COGNITO"], module.cognito_pool.identity_provider_names)
}

Identity Provider Configuration

Microsoft Entra ID (Azure AD)

The module automatically configures: - OIDC issuer URL derived from tenant ID - Standard scopes: openid profile email - Attribute mapping for email, name, username, and groups - Custom entra_groups attribute for group passthrough

Prerequisites: 1. Store the Entra client secret in SSM Parameter Store (default: /cognito/external/idp/entra/CLIENT_SECRET) 2. Configure your Entra app registration with the Cognito callback URL: https://<cognito-domain>/oauth2/idpresponse

Entra App Registration Settings: - Redirect URI: https://auth.example.com/oauth2/idpresponse - Token configuration: Add groups claim to ID token

Google

The module automatically configures: - Google OAuth provider - Standard scopes: email profile openid - Attribute mapping for email, name, given_name, family_name, picture, and username

Prerequisites: 1. Store the Google client secret in SSM Parameter Store (default: /cognito/external/idp/google/CLIENT_SECRET) 2. Configure your Google OAuth consent screen and credentials 3. Add authorized redirect URI: https://<cognito-domain>/oauth2/idpresponse

Pre-Token Generation Lambda

When external identity providers are configured, the module automatically deploys a Lambda function that: - Intercepts the pre-token generation event (V3_0) - Extracts group claims from external IDPs (Entra groups via custom:entra_groups) - Passes through external groups to the cognito:groups claim - Merges external groups with any existing Cognito groups

This allows your application to use external IDP groups for access control without manually syncing groups to Cognito.

MFA Configuration

Value Description
OFF MFA disabled (default)
OPTIONAL Users can enable MFA in their account settings
ON MFA required for all users

When MFA is enabled (OPTIONAL or ON), software token (TOTP) authentication is automatically configured.

Custom Domain Requirements

  • ACM certificate must be in us-east-1 region (CloudFront requirement)
  • Either hosted_zone_id or hosted_zone_name must be provided
  • The module creates the Route53 A record automatically pointing to CloudFront
  • Uses Managed Login Version 2 for modern login experience

Outputs

The module provides several outputs for integration with the cognito_app module:

Output Description
aws_cognito_pool_id User pool ID for app client configuration
aws_cognito_pool_arn User pool ARN for IAM policies
aws_cognito_domain Cognito domain for OAuth endpoints
identity_provider_names List of configured IDP names for supported_identity_providers
domain_cloudfront_distribution CloudFront endpoint for custom domain DNS
pre_token_lambda_arn ARN of the pre-token Lambda (if created)

Modules

Name Source Version
pre_token_lambda terraform-aws-modules/lambda/aws ~> 7.0

Inputs

Name Description Type Default Required
attribute_mapping Attribute mapping of IDP attributes to Cognito Pool attributes map(string) {} no
cognito_pool_name Cognito pool name string n/a yes
create Whether to create the Cognito user pool bool n/a yes
custom_domain Custom domain for Cognito hosted UI. Requires an ACM certificate in us-east-1.
object({
domain = string
certificate_arn = string
hosted_zone_id = optional(string)
hosted_zone_name = optional(string)
})
null no
entra_provider Entra (Azure AD) OIDC identity provider configuration. All Microsoft endpoints are derived from tenant_id.
object({
client_id = string
client_secret_parameter = optional(string, "/cognito/external/idp/entra/CLIENT_SECRET")
tenant_id = string
})
null no
google_provider Google identity provider configuration
object({
client_id = string
client_secret_parameter = optional(string, "/cognito/external/idp/google/CLIENT_SECRET")
})
null no
invite_message_template Custom invite message template for new users. If not provided, a minimal default using cognito_pool_name is used.
object({
email_subject = string
email_message = string
sms_message = string
})
null no
mfa_configuration MFA configuration for Cognito-native users: OFF (disabled), OPTIONAL (user can enable), or ON (required for all users) string "OFF" no
verification_message_template Custom verification message template. If not provided, a minimal default using cognito_pool_name is used.
object({
email_subject = string
email_message = string
})
null no

Outputs

Name Description
aws_cognito_domain Cognito domain, add as authorized JS origins and as authorized redirect url to IDP followed by /oauth2/idpresponse
aws_cognito_pool_arn Cognito user pool arn
aws_cognito_pool_id Cognito user pool id
domain_cloudfront_distribution CloudFront distribution endpoint for custom domain DNS setup (CNAME target)
domain_cloudfront_distribution_zone_id CloudFront distribution hosted zone ID for custom domain DNS setup (Route53 alias)
identity_provider_names List of configured identity provider names for use in cognito_app supported_identity_providers
pre_token_lambda_arn ARN of the pre-token generation Lambda function (empty if not created)