Create AWS Cognito User Pools

The aws:cognito/userPool:UserPool resource, part of the Pulumi AWS provider, defines a Cognito user pool that manages user registration, authentication, and account recovery. This guide focuses on three capabilities: basic pool creation, multi-factor authentication setup, and account recovery configuration.

User pools with SMS-based features require IAM roles with SNS permissions; most other features use AWS defaults unless explicitly configured. The examples are intentionally small. Combine them with your own email configuration, password policies, and Lambda triggers.

Create a user pool with minimal configuration

Most deployments start by creating a user pool with just a name, letting AWS apply defaults for authentication and verification.

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

const pool = new aws.cognito.UserPool("pool", {name: "mypool"});
import pulumi
import pulumi_aws as aws

pool = aws.cognito.UserPool("pool", name="mypool")
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cognito.NewUserPool(ctx, "pool", &cognito.UserPoolArgs{
			Name: pulumi.String("mypool"),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var pool = new Aws.Cognito.UserPool("pool", new()
    {
        Name = "mypool",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var pool = new UserPool("pool", UserPoolArgs.builder()
            .name("mypool")
            .build());

    }
}
resources:
  pool:
    type: aws:cognito:UserPool
    properties:
      name: mypool

The name property sets a unique identifier for the pool. Without additional configuration, the pool uses AWS defaults: MFA is disabled, passwords require basic complexity, and email verification is not configured. This minimal setup establishes the foundation for adding authentication features.

Enable SMS and authenticator app MFA

Applications requiring stronger security enable multi-factor authentication, allowing users to verify with SMS codes or authenticator apps.

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

const example = new aws.cognito.UserPool("example", {
    mfaConfiguration: "ON",
    smsAuthenticationMessage: "Your code is {####}",
    smsConfiguration: {
        externalId: "example",
        snsCallerArn: exampleAwsIamRole.arn,
        snsRegion: "us-east-1",
    },
    softwareTokenMfaConfiguration: {
        enabled: true,
    },
});
import pulumi
import pulumi_aws as aws

example = aws.cognito.UserPool("example",
    mfa_configuration="ON",
    sms_authentication_message="Your code is {####}",
    sms_configuration={
        "external_id": "example",
        "sns_caller_arn": example_aws_iam_role["arn"],
        "sns_region": "us-east-1",
    },
    software_token_mfa_configuration={
        "enabled": True,
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cognito.NewUserPool(ctx, "example", &cognito.UserPoolArgs{
			MfaConfiguration:         pulumi.String("ON"),
			SmsAuthenticationMessage: pulumi.String("Your code is {####}"),
			SmsConfiguration: &cognito.UserPoolSmsConfigurationArgs{
				ExternalId:   pulumi.String("example"),
				SnsCallerArn: pulumi.Any(exampleAwsIamRole.Arn),
				SnsRegion:    pulumi.String("us-east-1"),
			},
			SoftwareTokenMfaConfiguration: &cognito.UserPoolSoftwareTokenMfaConfigurationArgs{
				Enabled: pulumi.Bool(true),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Cognito.UserPool("example", new()
    {
        MfaConfiguration = "ON",
        SmsAuthenticationMessage = "Your code is {####}",
        SmsConfiguration = new Aws.Cognito.Inputs.UserPoolSmsConfigurationArgs
        {
            ExternalId = "example",
            SnsCallerArn = exampleAwsIamRole.Arn,
            SnsRegion = "us-east-1",
        },
        SoftwareTokenMfaConfiguration = new Aws.Cognito.Inputs.UserPoolSoftwareTokenMfaConfigurationArgs
        {
            Enabled = true,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.inputs.UserPoolSmsConfigurationArgs;
import com.pulumi.aws.cognito.inputs.UserPoolSoftwareTokenMfaConfigurationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new UserPool("example", UserPoolArgs.builder()
            .mfaConfiguration("ON")
            .smsAuthenticationMessage("Your code is {####}")
            .smsConfiguration(UserPoolSmsConfigurationArgs.builder()
                .externalId("example")
                .snsCallerArn(exampleAwsIamRole.arn())
                .snsRegion("us-east-1")
                .build())
            .softwareTokenMfaConfiguration(UserPoolSoftwareTokenMfaConfigurationArgs.builder()
                .enabled(true)
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:cognito:UserPool
    properties:
      mfaConfiguration: ON
      smsAuthenticationMessage: Your code is {####}
      smsConfiguration:
        externalId: example
        snsCallerArn: ${exampleAwsIamRole.arn}
        snsRegion: us-east-1
      softwareTokenMfaConfiguration:
        enabled: true

Setting mfaConfiguration to “ON” requires all users to provide a second factor at sign-in. The smsConfiguration block defines the IAM role that sends SMS messages via SNS, while softwareTokenMfaConfiguration enables authenticator apps like Google Authenticator. The smsAuthenticationMessage template includes the {####} placeholder that Cognito replaces with the verification code.

Configure password recovery methods

User pools need recovery mechanisms so users can reset forgotten passwords through verified contact methods.

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

const test = new aws.cognito.UserPool("test", {
    name: "mypool",
    accountRecoverySetting: {
        recoveryMechanisms: [
            {
                name: "verified_email",
                priority: 1,
            },
            {
                name: "verified_phone_number",
                priority: 2,
            },
        ],
    },
});
import pulumi
import pulumi_aws as aws

test = aws.cognito.UserPool("test",
    name="mypool",
    account_recovery_setting={
        "recovery_mechanisms": [
            {
                "name": "verified_email",
                "priority": 1,
            },
            {
                "name": "verified_phone_number",
                "priority": 2,
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cognito"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := cognito.NewUserPool(ctx, "test", &cognito.UserPoolArgs{
			Name: pulumi.String("mypool"),
			AccountRecoverySetting: &cognito.UserPoolAccountRecoverySettingArgs{
				RecoveryMechanisms: cognito.UserPoolAccountRecoverySettingRecoveryMechanismArray{
					&cognito.UserPoolAccountRecoverySettingRecoveryMechanismArgs{
						Name:     pulumi.String("verified_email"),
						Priority: pulumi.Int(1),
					},
					&cognito.UserPoolAccountRecoverySettingRecoveryMechanismArgs{
						Name:     pulumi.String("verified_phone_number"),
						Priority: pulumi.Int(2),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.Cognito.UserPool("test", new()
    {
        Name = "mypool",
        AccountRecoverySetting = new Aws.Cognito.Inputs.UserPoolAccountRecoverySettingArgs
        {
            RecoveryMechanisms = new[]
            {
                new Aws.Cognito.Inputs.UserPoolAccountRecoverySettingRecoveryMechanismArgs
                {
                    Name = "verified_email",
                    Priority = 1,
                },
                new Aws.Cognito.Inputs.UserPoolAccountRecoverySettingRecoveryMechanismArgs
                {
                    Name = "verified_phone_number",
                    Priority = 2,
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cognito.UserPool;
import com.pulumi.aws.cognito.UserPoolArgs;
import com.pulumi.aws.cognito.inputs.UserPoolAccountRecoverySettingArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var test = new UserPool("test", UserPoolArgs.builder()
            .name("mypool")
            .accountRecoverySetting(UserPoolAccountRecoverySettingArgs.builder()
                .recoveryMechanisms(                
                    UserPoolAccountRecoverySettingRecoveryMechanismArgs.builder()
                        .name("verified_email")
                        .priority(1)
                        .build(),
                    UserPoolAccountRecoverySettingRecoveryMechanismArgs.builder()
                        .name("verified_phone_number")
                        .priority(2)
                        .build())
                .build())
            .build());

    }
}
resources:
  test:
    type: aws:cognito:UserPool
    properties:
      name: mypool
      accountRecoverySetting:
        recoveryMechanisms:
          - name: verified_email
            priority: 1
          - name: verified_phone_number
            priority: 2

The accountRecoverySetting block defines which verified attributes users can use for password recovery. The priority field determines the order: verified email is tried first, then verified phone number. Users must have at least one verified recovery method to reset their password.

Beyond these examples

These snippets focus on specific user pool features: basic pool creation, multi-factor authentication (SMS and software tokens), and account recovery configuration. They’re intentionally minimal rather than full authentication systems.

The examples may reference pre-existing infrastructure such as IAM roles with SNS permissions for SMS delivery. They focus on configuring the user pool rather than provisioning surrounding infrastructure.

To keep things focused, common user pool patterns are omitted, including:

  • Email configuration (emailConfiguration for SES integration)
  • Password policies (passwordPolicy for complexity requirements)
  • Custom attributes and schemas
  • Lambda triggers for authentication workflows
  • Advanced security features (userPoolAddOns)
  • Username and alias configuration

These omissions are intentional: the goal is to illustrate how each user pool feature is wired, not provide drop-in authentication modules. See the Cognito User Pool resource reference for all available configuration options.

Let's create AWS Cognito User Pools

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Multi-Factor Authentication (MFA)
What MFA methods do I need to configure for ON or OPTIONAL mode?
When mfaConfiguration is set to ON or OPTIONAL, you must configure at least one MFA method: emailMfaConfiguration, smsConfiguration, or softwareTokenMfaConfiguration.
How do I enable SMS and software token MFA?
Set mfaConfiguration to ON, configure smsConfiguration with externalId, snsCallerArn, and snsRegion, and set softwareTokenMfaConfiguration.enabled to true.
What are the requirements for email MFA?
Email MFA requires at least 2 accountRecoverySetting entries and an emailConfiguration block. It only works when mfaConfiguration is ON or OPTIONAL.
When is SMS MFA activated?
SMS MFA is activated only when mfaConfiguration is set to ON or OPTIONAL along with a configured smsConfiguration block.
Immutable Properties & Schema
What properties can't be changed after creating the user pool?
aliasAttributes and usernameAttributes are immutable. Schema attributes can be added but never modified or removed (maximum 50 attributes).
Why can't I remove SMS configuration from my user pool?
Cognito API restrictions prevent removing SMS configuration once applied. The resource ignores removal attempts by disabling drift detection. Use pulumi taint to force recreation if you need to remove it.
Can I modify schema attributes after creation?
No, schema attributes can only be added after creation, not modified or removed. Plan your schema carefully before creating the user pool.
Username & Alias Configuration
What's the difference between aliasAttributes and usernameAttributes?
aliasAttributes allows users to sign in with phone_number, email, or preferred_username as aliases. usernameAttributes lets users sign up with email or phone as their username. These properties conflict and cannot be used together.
Verification Messages
Which verification message properties conflict with each other?
emailVerificationMessage conflicts with verificationMessageTemplate.emailMessage, emailVerificationSubject conflicts with verificationMessageTemplate.emailSubject, and smsVerificationMessage conflicts with verificationMessageTemplate.smsMessage.
What placeholder must SMS authentication messages include?
The smsAuthenticationMessage must contain the {####} placeholder, which gets replaced with the authentication code.
Account Recovery & Protection
How do I configure account recovery mechanisms?
Use accountRecoverySetting with a recoveryMechanisms array. Specify name (verified_email or verified_phone_number) and priority for each mechanism.
How does deletion protection work?
Set deletionProtection to ACTIVE to prevent accidental deletion (default is INACTIVE). You must deactivate it before you can delete the user pool.

Using a different cloud?

Explore security guides for other cloud providers: