Temporary elevated access to AWS accounts via AWS IAM Identity Center (SSO) and Slack.
Note: This is a fork of FivexL/terraform-aws-sso-elevator. Credit to FivexL for creating and maintaining the original module.
AWS IAM Identity Center doesn't support temporary assignment of permission sets. This module enables temporary elevated access to AWS accounts, achieving the principle of least privilege access without permanently assigned permission sets.
Users request access via a Slack form. Requests are approved/denied by designated approvers. Access is automatically revoked when the time expires.
sequenceDiagram
Requester->>Slack: submits form in Slack - CMD+K, search access or /access command
Slack->>AWS Lambda - Access Requester: sends request to access-requester
AWS Lambda - Access Requester->>Slack: sends a message to Slack channel with approve/deny buttons and tags approvers
Approver->>Slack: pressed approve button in Slack message
Slack->>AWS Lambda - Access Requester: Send approved request to access-requester
AWS Lambda - Access Requester->>AWS IAM Identity Center(SSO): creates user-level permission set assignment based on approved request
AWS Lambda - Access Requester->>AWS EventBridge: creates revocation schedule
AWS Lambda - Access Requester->>AWS S3: logs audit record
AWS EventBridge->>AWS Lambda - Access Revoker: sends revocation event when times come
AWS Lambda - Access Revoker->>AWS IAM Identity Center(SSO): revokes user-level permission set assignment
AWS Lambda - Access Revoker->>AWS S3: logs audit record
AWS Lambda - Access Revoker->>Slack: send notification about revocation
data "aws_ssoadmin_instances" "this" {}
data "aws_ssm_parameter" "slack_signing_secret" {
name = "/sso-elevator/slack-signing-secret"
}
data "aws_ssm_parameter" "slack_bot_token" {
name = "/sso-elevator/slack-bot-token"
}
module "aws_sso_elevator" {
source = "github.com/PostHog/terraform-aws-sso-elevator"
slack_signing_secret = data.aws_ssm_parameter.slack_signing_secret.value
slack_bot_token = data.aws_ssm_parameter.slack_bot_token.value
slack_channel_id = "C01234567"
identity_store_id = tolist(data.aws_ssoadmin_instances.this.identity_store_ids)[0]
s3_logging = {
target_bucket = "my-access-logs-bucket"
target_prefix = "sso-elevator/"
}
config = [
{
"ResourceType" : "Account",
"Resource" : "*",
"PermissionSet" : "*",
"Approvers" : ["admin@company.com"],
"AllowSelfApproval" : true,
},
]
}
output "api_endpoint_url" {
value = module.aws_sso_elevator.requester_api_endpoint_url
}- Configuration - Configuration structure, rules, explicit deny, auto-approval
- Group Access - Group assignments mode, attribute-based sync
- Deployment - SSO delegation, build process, Terraform examples, Slack app setup
- Features - Secondary domains, direct messages, API gateway, request expiration
- Cache Implementation - Details on caching mechanism
- Athena Queries - Query audit logs with AWS Athena
- Your Slack user email must match your SSO user ID for requests to work.
- The access-revoker will revoke all user-level Permission Set assignments not created by SSO Elevator. Use group-level assignments for permanent access.
- If using
group_config, SSO Elevator will remove users from those groups if they weren't added by SSO Elevator.
| Name | Version |
|---|---|
| terraform | ~> 1.0 |
| aws | >= 4.64 |
| external | >= 1.0 |
| local | >= 1.0 |
| null | >= 2.0 |
| random | >= 3.0 |
| Name | Version |
|---|---|
| aws | >= 4.64 |
| null | >= 2.0 |
| random | >= 3.0 |
| Name | Source | Version |
|---|---|---|
| access_requester_slack_handler | terraform-aws-modules/lambda/aws | 8.1.2 |
| access_revoker | terraform-aws-modules/lambda/aws | 8.1.2 |
| attribute_syncer | terraform-aws-modules/lambda/aws | 8.1.2 |
| audit_bucket | fivexl/account-baseline/aws//modules/s3_baseline | 2.0.0 |
| config_bucket | fivexl/account-baseline/aws//modules/s3_baseline | 2.0.0 |
| http_api | terraform-aws-modules/apigateway-v2/aws | 5.0.0 |
| slack_handler_alias | terraform-aws-modules/lambda/aws//modules/alias | 8.1.2 |
| sso_elevator_dependencies | terraform-aws-modules/lambda/aws | 8.1.2 |
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| allow_anyone_to_end_session_early | Controls who can click the "End session early" button to revoke access before the scheduled expiration. If false (default), only the requester and approvers listed in the matching statement can end the session. If true, anyone in the Slack channel can end any session early. |
bool |
false |
no |
| api_gateway_name | The name of the API Gateway for SSO Elevator's access-requester Lambda | string |
"sso-elevator-access-requster" |
no |
| api_gateway_throttling_burst_limit | The maximum number of requests that API Gateway allows in a burst. | number |
5 |
no |
| api_gateway_throttling_rate_limit | The maximum number of requests that API Gateway allows per second. | number |
1 |
no |
| approver_renotification_backoff_multiplier | The multiplier applied to the wait time for each subsequent notification sent to the approver. Default is 2, which means the wait time will double for each attempt. | number |
2 |
no |
| approver_renotification_initial_wait_time | The initial wait time before the first re-notification to the approver is sent. This is measured in minutes. If set to 0, no re-notifications will be sent. | number |
15 |
no |
| attribute_sync_enabled | Enable attribute-based group sync feature. When enabled, users will be automatically added to groups based on their Identity Store attributes. | bool |
false |
no |
| attribute_sync_event_rule_name | Name for the EventBridge rule that triggers the attribute syncer. | string |
"sso-elevator-attribute-sync" |
no |
| attribute_sync_lambda_memory | Memory allocation for attribute syncer Lambda (MB). Increase for large user/group sets. | number |
512 |
no |
| attribute_sync_lambda_timeout | Timeout for attribute syncer Lambda (seconds). Increase for large user/group sets. | number |
300 |
no |
| attribute_sync_managed_groups | List of group names to manage via attribute sync. Only these groups will be monitored and modified by the sync process. | list(string) |
[] |
no |
| attribute_sync_manual_assignment_policy | Policy for handling manual assignments (users in managed groups who don't match any rules): 'warn' only logs and notifies, 'remove' automatically removes them. | string |
"remove" |
no |
| attribute_sync_rules | Attribute mapping rules for group sync. Each rule specifies a group name and the attribute conditions that must be met for a user to be added to that group. Example: [ { group_name = "Engineering" attributes = { department = "Engineering" employeeType = "FullTime" } } ] |
list(object({ |
[] |
no |
| attribute_sync_schedule | Schedule expression for attribute sync (e.g., 'rate(1 hour)' or 'cron(0 * * * ? *)'). Determines how often the sync runs. | string |
"rate(1 hour)" |
no |
| attribute_syncer_lambda_name | Name for the attribute syncer Lambda function. | string |
"attribute-syncer" |
no |
| aws_sns_topic_subscription_email | value for the email address to subscribe to the SNS topic | string |
"" |
no |
| cache_enabled | Enable caching of AWS accounts and permission sets in S3. If set to false, caching is disabled but the S3 bucket will still be created for future config storage. | bool |
true |
no |
| config | value for the SSO Elevator config | any |
[] |
no |
| config_bucket_kms_key_arn | ARN of the KMS key to use for config S3 bucket encryption. If not provided, uses AES256 encryption. | string |
null |
no |
| config_bucket_name | Name of the S3 bucket for storing configuration and cache data (accounts, permission sets, and future config files) | string |
"sso-elevator-config" |
no |
| create_api_gateway | If true, module will create & configure API Gateway for the Lambda function | bool |
true |
no |
| ecr_owner_account_id | In what account is the ECR repository located. | string |
"222341826240" |
no |
| ecr_repo_name | The name of the ECR repository. | string |
"aws-sso-elevator" |
no |
| ecr_repo_tag | The tag of the image in the ECR repository. | string |
"4.1.0" |
no |
| event_bridge_check_on_inconsistency_rule_name | value for the event bridge check on inconsistency rule name | string |
null |
no |
| event_bridge_scheduled_revocation_rule_name | value for the event bridge scheduled revocation rule name | string |
null |
no |
| event_brige_check_on_inconsistency_rule_name | DEPRECATED: Use event_bridge_check_on_inconsistency_rule_name instead. This variable contains a typo and will be removed in a future version. | string |
"sso-elevator-check-on-inconsistency" |
no |
| event_brige_scheduled_revocation_rule_name | DEPRECATED: Use event_bridge_scheduled_revocation_rule_name instead. This variable contains a typo and will be removed in a future version. | string |
"sso-elevator-scheduled-revocation" |
no |
| group_config | value for the SSO Elevator group config | any |
[] |
no |
| identity_store_id | The Identity Store ID (e.g., "d-1234567890"). If not provided and sso_instance_arn is also not provided, it will be automatically discovered. Providing this value is RECOMMENDED for API efficiency - it eliminates describe_sso_instance API calls on every Lambda invocation. You can find this value in the AWS IAM Identity Center console or via: aws sso-admin list-instances --query 'Instances[0].IdentityStoreId' --output text |
string |
"" |
no |
| lambda_architecture | The instruction set architecture for Lambda functions. Valid values are 'x86_64' or 'arm64'. Use 'arm64' for better price/performance on Graviton2. | string |
"x86_64" |
no |
| lambda_memory_size | Amount of memory in MB your Lambda Function can use at runtime. Valid value between 128 MB to 10,240 MB (10 GB), in 64 MB increments. | number |
256 |
no |
| lambda_timeout | The amount of time your Lambda Function has to run in seconds. | number |
30 |
no |
| log_level | value for the log level | string |
"INFO" |
no |
| logs_retention_in_days | The number of days you want to retain log events in the log group for both Lambda functions and API Gateway. | number |
365 |
no |
| max_permissions_duration_time | Maximum duration (in hours) for permissions granted by Elevator. Max number - 48 hours. Due to Slack's dropdown limit of 100 items, anything above 48 hours will cause issues when generating half-hour increments and Elevator will not display more then 48 hours in the dropdown. |
number |
24 |
no |
| permission_duration_list_override | An explicit list of duration values to appear in the drop-down menu users use to select how long to request permissions for. Each entry in the list should be formatted as "hh:mm", e.g. "01:30" for an hour and a half. Note that while the number of minutes must be between 0-59, the number of hours can be any number. If this variable is set, the max_permission_duration_time is ignored. |
list(string) |
[] |
no |
| posthog_api_key | PostHog API key for analytics. Leave empty to disable analytics tracking. | string |
"" |
no |
| posthog_host | PostHog host URL for analytics. | string |
"https://us.i.posthog.com" |
no |
| request_expiration_hours | After how many hours should the request expire? If set to 0, the request will never expire. | number |
8 |
no |
| requester_lambda_name | value for the requester lambda name | string |
"access-requester" |
no |
| revoker_lambda_name | value for the revoker lambda name | string |
"access-revoker" |
no |
| revoker_post_update_to_slack | Should revoker send a confirmation of the revocation to Slack? | bool |
true |
no |
| s3_bucket_name_for_audit_entry | The name of the S3 bucket that will be used by the module to store logs about every access request. If s3_name_of_the_existing_bucket is not provided, the module will create a new bucket with this name. |
string |
"sso-elevator-audit-entry" |
no |
| s3_bucket_partition_prefix | The prefix for the S3 audit bucket object partitions. Don't use slashes (/) in the prefix, as it will be added automatically, e.g. "logs" will be transformed to "logs/". If you want to use the root of the bucket, leave this empty. |
string |
"logs" |
no |
| s3_logging | Map containing access bucket logging configuration. If you are not providing s3_name_of_the_existing_bucket variable, then module will create bucket for you. If the module is creating an audit bucket for you, then you must provide a logging configuration via this input variable, with at least the target_bucket key specified. |
map(string) |
{} |
no |
| s3_mfa_delete | Whether to enable MFA delete for the S3 bucket | bool |
false |
no |
| s3_name_of_the_existing_bucket | Name of an existing S3 bucket to use for storing SSO Elevator audit logs. An audit log bucket is mandatory. If you specify this variable, the module will use your existing bucket. Otherwise, if you don't provide this variable, the module will create a new bucket named according to the "s3_bucket_name_for_audit_entry" variable. If the module is creating an audit bucket for you, then you must provide a logging configuration via the s3_logging input variable, with at least the target_bucket key specified. |
string |
"" |
no |
| s3_object_lock | Enable object lock | bool |
false |
no |
| s3_object_lock_configuration | Object lock configuration | any |
{ |
no |
| schedule_expression | recovation schedule expression (will revoke all user-level assignments unknown to the Elevator) | string |
"cron(0 23 * * ? *)" |
no |
| schedule_expression_for_check_on_inconsistency | how often revoker should check for inconsistency (warn if found unknown user-level assignments) | string |
"rate(2 hours)" |
no |
| schedule_group_name | value for the schedule group name | string |
"sso-elevator-scheduled-revocation" |
no |
| schedule_role_name | value for the schedule role name | string |
"sso-elevator-event-bridge-role" |
no |
| secondary_fallback_email_domains | Value example: ["@new.domain", "@second.domain"], every domain name should start with "@". WARNING: This feature is STRONGLY DISCOURAGED because it can introduce security risks and open up potential avenues for abuse. SSO Elevator uses Slack email addresses to find users in AWS SSO. In some cases, the domain of a Slack user's email (e.g., "john.doe@old.domain") differs from the domain defined in AWS SSO (e.g., "john.doe@new.domain"). By setting these fallback domains, SSO Elevator will attempt to replace the original domain from Slack with each secondary domain in order to locate a matching AWS SSO user. Use Cases: - This mechanism should only be used in rare or critical situations where you cannot align Slack and AWS SSO domains. Use Case Example: - Slack email: john.doe@old.domain - AWS SSO email: john.doe@new.domain Without fallback domains, SSO Elevator cannot find the SSO user due to the domain mismatch. By setting secondary_fallback_email_domains = ["@new.domain"], SSO Elevator will swap out "@old.domain" for "@new.domain" (and any other domain in the list) and attempt to locate "john.doe@new.domain" in AWS SSO. Security Risks & Recommendations: - If multiple SSO users share the same local-part (before the "@") across different domains, SSO Elevator may grant permissions to the wrong user. - Disable or remove entries in this variable as soon as you no longer need domain fallback functionality to restore a more secure configuration. IN SUMMARY: Use "secondary_fallback_email_domains" ONLY if absolutely necessary. It is best practice to maintain consistent, verified email domains in Slack and AWS SSO. Remove these fallback entries as soon as you resolve the underlying domain mismatch to minimize security exposure. Notes: - SSO Elevator always prioritizes the primary domain from Slack (the Slack user's email) when searching for a user in AWS SSO. - SSO Elevator adds a large warning message in Slack if it uses a secondary fallback domain to find a user in AWS SSO. - The secondary domain feature works ONLY for the requester, approvers in the configuration must have the same email domain as in Slack. |
list(string) |
[] |
no |
| send_dm_if_user_not_in_channel | If the user is not in the SSO Elevator channel, Elevator will send them a direct message with the request status (waiting for approval, declined, approved, etc.) and the result of the request. Using this feature requires the following Slack app permissions: "channels:read", "groups:read", and "im:write". Please ensure these permissions are enabled in the Slack app configuration. |
bool |
true |
no |
| slack_bot_token | value for the Slack bot token | string |
n/a | yes |
| slack_channel_id | value for the Slack channel ID | string |
n/a | yes |
| slack_handler_provisioned_concurrent_executions | Provisioned concurrent executions for the Slack handler Lambda. Set to a positive number to reduce cold starts. | number |
-1 |
no |
| slack_signing_secret | value for the Slack signing secret | string |
n/a | yes |
| sso_instance_arn | value for the SSO instance ARN | string |
"" |
no |
| tags | A map of tags to assign to resources. | map(string) |
{} |
no |
| use_pre_created_image | If true, the image will be pulled from the ECR repository. If false, the image will be built using Docker from the source code. | bool |
true |
no |
| Name | Description |
|---|---|
| attribute_sync_schedule_rule_arn | The ARN of the EventBridge rule that triggers the attribute syncer. |
| attribute_syncer_lambda_arn | The ARN of the attribute syncer Lambda function. |
| attribute_syncer_lambda_name | The name of the attribute syncer Lambda function. |
| config_s3_bucket_arn | The ARN of the S3 bucket for storing configuration and cache data. |
| config_s3_bucket_name | The name of the S3 bucket for storing configuration and cache data. |
| requester_api_endpoint_url | The full URL to invoke the API. Pass this URL into the Slack App manifest as the Request URL. |
| revoker_lambda_name | The name of the revoker Lambda function. |
| schedule_group_name | The name of the EventBridge Scheduler schedule group. |
| sso_elevator_bucket_id | The name of the SSO elevator bucket. |