A Terraform Workaround for Cognito’s Inbound Federation Trigger
Attach Cognito’s inbound federation Lambda trigger with Terraform and the AWS CLI while native provider support is pending.
Ally Piechowski · · 3 min read
Attach Cognito’s inbound federation trigger through Terraform and the AWS CLI while native support is pending. As of October 20, 2026, my provider PR is still open.
Later pool updates can remove the trigger, even with ignore_changes (issue #49707).
Set it up
Point the example at your existing pool and tested Lambda; reuse your invocation permission if you have one.
Fill cognito_pool_update with all updateable pool settings except UserPoolId and LambdaConfig. Omitted settings reset to defaults. Review the full request; don’t send an empty or partial object or reuse raw describe-user-pool output. Build existing_lambda_config from every existing trigger and supporting setting, including KMSKeyID, using Terraform references.
Show the full Terraform example
Download inbound-federation.tf
# Adapt the references to your existing pool, tested Lambda functions, and KMS key.
# Declare hashicorp/null in your required providers.
# Preserve every existing LambdaConfig entry and all updateable pool settings.
variable "aws_region" {
type = string
}
variable "cognito_pool_update" {
description = "Reviewed complete UpdateUserPool settings, excluding UserPoolId and LambdaConfig."
type = any
}
locals {
existing_lambda_config = {
PreSignUp = aws_lambda_function.presignup.arn
PostConfirmation = aws_lambda_function.post_confirmation.arn
PostAuthentication = aws_lambda_function.post_authentication.arn
CustomEmailSender = {
LambdaArn = aws_lambda_function.email_sender.arn
LambdaVersion = "V1_0"
}
KMSKeyID = aws_kms_key.cognito_email.arn
}
pool_update_json = jsonencode(merge(var.cognito_pool_update, {
UserPoolId = aws_cognito_user_pool.main.id
LambdaConfig = merge(local.existing_lambda_config, {
InboundFederation = {
LambdaArn = aws_lambda_function.inbound_federation.arn
LambdaVersion = "V1_0"
}
})
}))
}
resource "aws_lambda_permission" "inbound_federation" {
statement_id = "AllowCognitoInboundFederation"
action = "lambda:InvokeFunction"
function_name = aws_lambda_function.inbound_federation.function_name
principal = "cognito-idp.amazonaws.com"
source_arn = aws_cognito_user_pool.main.arn
source_account = data.aws_caller_identity.current.account_id
}
data "aws_caller_identity" "current" {}
resource "null_resource" "inbound_federation" {
triggers = {
pool_id = aws_cognito_user_pool.main.id
request_hash = sha256(local.pool_update_json)
region = var.aws_region
script_revision = "1"
}
provisioner "local-exec" {
interpreter = ["/bin/sh", "-c"]
environment = {
COGNITO_UPDATE_JSON = local.pool_update_json
COGNITO_REGION = var.aws_region
}
command = <<-EOT
set -eu
umask 077
request=$(mktemp)
trap 'rm -f "$request"' 0
printf '%s' "$COGNITO_UPDATE_JSON" > "$request"
aws cognito-idp update-user-pool \
--region "$COGNITO_REGION" \
--cli-input-json "file://$request" \
--no-cli-pager
EOT
}
depends_on = [aws_lambda_permission.inbound_federation]
}
Use an AWS CLI version that recognizes InboundFederation, with cognito-idp:UpdateUserPool and cognito-idp:DescribeUserPool permissions. It doesn’t inherit the provider’s profile or assumed role. Set AWS_PROFILE if needed, and match the provider’s account and region.
For custom email or SMS senders, the CLI identity also needs kms:CreateGrant on the sender’s KMS key.
Merge this into the pool’s existing lifecycle block:
lifecycle {
ignore_changes = [lambda_config]
}
This hides changes to every trigger in the block. Update triggers through the example’s full payload. Keep it current and avoid concurrent pool updates.
Apply and check
Start with a nonproduction pool. Set AWS_REGION and USER_POOL_ID to its region and ID. Review the plan before applying:
terraform init
terraform apply
aws cognito-idp describe-user-pool --region "$AWS_REGION" \
--user-pool-id "$USER_POOL_ID" \
--query 'UserPool.LambdaConfig' --no-cli-pager
After pool updates, verify the settings, new and returning federated sign-ins, and existing authentication flows.
The payload hash doesn’t detect drift. To reattach, refresh and review the full request, then run terraform apply -replace=null_resource.inbound_federation. Bump script_revision after shell changes.
Once a released provider supports the trigger, replace the null_resource and ignore_changes with its native block. Keep the invocation permission, check for no pool replacement, and repeat verification.
Hand it to your agent
Read https://piechowski.io/post/terraform-cognito-inbound-federation-workaround/ and inspect this repo. Check https://github.com/hashicorp/terraform-provider-aws/pull/47954 and released hashicorp/aws provider docs and schema against the project's constraints and lockfile. A newer Terraform CLI or merged PR doesn't prove native support.
Use native inbound_federation if a compatible released provider supports it. Otherwise, follow the workaround, preserving pool settings and triggers, invocation permissions, and matching CLI account and region. Document why it's temporary, the PR link, drift and reapply limits, and removal steps once support ships. Validate and show the plan; don't apply infrastructure.