IDC User & Group Management

After setting up IDC authentication for your OpenSearch UI application, you need to assign users and groups so they can log in. This guide covers how to manage user and group assignments programmatically using the AWS CLI and SDKs.

How IDC application assignment works

When you create an OpenSearch UI application with IDC enabled, it registers an IAM Identity Center application under the hood. Users must be assigned to this IDC application before they can log in. There are two levels of access:

  • Application assignment — controls who can log in to the OpenSearch UI (managed via sso-admin APIs)
  • Application admin — controls who can edit/delete the app and manage workspaces (managed via opensearch update-application)

Prerequisites

You need three identifiers before managing users:

IdentifierHow to get it
IDC Application ARNFrom aws opensearch get-application
Identity Store IDFrom aws sso-admin list-instances
User/Group IDFrom aws identitystore list-users or list-groups

Get the IDC Application ARN

aws opensearch get-application \
  --id <APP_ID> \
  --region <REGION> \
  --query 'iamIdentityCenterOptions.iamIdentityCenterApplicationArn' \
  --output text

Get the Identity Store ID

aws sso-admin list-instances \
  --region <REGION> \
  --query 'Instances[0].IdentityStoreId' \
  --output text

Managing users

Find a user by email

aws identitystore list-users \
  --identity-store-id <IDENTITY_STORE_ID> \
  --filters '[{"AttributePath":"UserName","AttributeValue":"user@example.com"}]' \
  --region <REGION> \
  --query 'Users[0].UserId' \
  --output text

Add a user to the application

aws sso-admin create-application-assignment \
  --application-arn <IDC_APP_ARN> \
  --principal-id <USER_ID> \
  --principal-type USER \
  --region <REGION>

Remove a user from the application

aws sso-admin delete-application-assignment \
  --application-arn <IDC_APP_ARN> \
  --principal-id <USER_ID> \
  --principal-type USER \
  --region <REGION>

List all assigned users

aws sso-admin list-application-assignments \
  --application-arn <IDC_APP_ARN> \
  --region <REGION>

Managing groups

Find a group by name

aws identitystore list-groups \
  --identity-store-id <IDENTITY_STORE_ID> \
  --filters '[{"AttributePath":"DisplayName","AttributeValue":"MyGroup"}]' \
  --region <REGION> \
  --query 'Groups[0].GroupId' \
  --output text

Add a group to the application

aws sso-admin create-application-assignment \
  --application-arn <IDC_APP_ARN> \
  --principal-id <GROUP_ID> \
  --principal-type GROUP \
  --region <REGION>

Remove a group from the application

aws sso-admin delete-application-assignment \
  --application-arn <IDC_APP_ARN> \
  --principal-id <GROUP_ID> \
  --principal-type GROUP \
  --region <REGION>

Managing application admins

Application admins can edit/delete the OpenSearch UI application and manage workspaces. This is separate from the IDC application assignment above.

Add an IDC user as admin

aws opensearch update-application \
  --id <APP_ID> \
  --app-configs '{
    "key": "opensearchDashboards.dashboardAdmin.users",
    "value": "[\"<IDC_USER_ID>\"]"
  }' \
  --region <REGION>

Add an IDC group as admin

aws opensearch update-application \
  --id <APP_ID> \
  --app-configs '{
    "key": "opensearchDashboards.dashboardAdmin.groups",
    "value": "[\"<IDC_GROUP_ID>\"]"
  }' \
  --region <REGION>

Python SDK example

Assign a single user by email

Look up a user by their email address and assign them to the OpenSearch UI application.

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
# Step 1: Get IDC application ARN
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
 
# Step 2: Get identity store ID
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
# Step 3: Find user by email
users = identity_store.list_users(
    IdentityStoreId=identity_store_id,
    Filters=[{"AttributePath": "UserName", "AttributeValue": "user@example.com"}],
)
user_id = users["Users"][0]["UserId"]
 
# Step 4: Assign user to the application
sso_admin.create_application_assignment(
    ApplicationArn=idc_app_arn,
    PrincipalId=user_id,
    PrincipalType="USER",
)
print(f"Assigned {user_id} to {idc_app_arn}")

Assign a group by name

Look up a group by its display name and assign the entire group. All members of the group get access automatically.

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
group_name = "Analytics Team"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
# Get IDC application ARN
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
 
# Get identity store ID
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
# Find group by name
groups = identity_store.list_groups(
    IdentityStoreId=identity_store_id,
    Filters=[{"AttributePath": "DisplayName", "AttributeValue": group_name}],
)
group_id = groups["Groups"][0]["GroupId"]
 
# Assign group to the application
sso_admin.create_application_assignment(
    ApplicationArn=idc_app_arn,
    PrincipalId=group_id,
    PrincipalType="GROUP",
)
print(f"Assigned group '{group_name}' ({group_id}) to {idc_app_arn}")

Bulk assign multiple users from a list

Assign a list of users by email in a single script. Skips users that are already assigned or not found.

import boto3
from botocore.exceptions import ClientError
 
region = "us-east-1"
app_id = "your-app-id"
emails = [
    "alice@example.com",
    "bob@example.com",
    "carol@example.com",
]
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
for email in emails:
    # Find user
    users = identity_store.list_users(
        IdentityStoreId=identity_store_id,
        Filters=[{"AttributePath": "UserName", "AttributeValue": email}],
    )
    if not users["Users"]:
        print(f"SKIP {email} — not found in identity store")
        continue
 
    user_id = users["Users"][0]["UserId"]
    try:
        sso_admin.create_application_assignment(
            ApplicationArn=idc_app_arn,
            PrincipalId=user_id,
            PrincipalType="USER",
        )
        print(f"OK   {email} ({user_id})")
    except ClientError as e:
        if e.response["Error"]["Code"] == "ConflictException":
            print(f"SKIP {email} — already assigned")
        else:
            print(f"FAIL {email}{e}")

Bulk assign multiple groups by name

Search for groups by name and assign them all. Useful for onboarding an organization with multiple teams.

import boto3
from botocore.exceptions import ClientError
 
region = "us-east-1"
app_id = "your-app-id"
group_names = [
    "Analytics Team",
    "Security Team",
    "Platform Engineers",
]
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
for name in group_names:
    groups = identity_store.list_groups(
        IdentityStoreId=identity_store_id,
        Filters=[{"AttributePath": "DisplayName", "AttributeValue": name}],
    )
    if not groups["Groups"]:
        print(f"SKIP '{name}' — not found in identity store")
        continue
 
    group_id = groups["Groups"][0]["GroupId"]
    try:
        sso_admin.create_application_assignment(
            ApplicationArn=idc_app_arn,
            PrincipalId=group_id,
            PrincipalType="GROUP",
        )
        print(f"OK   '{name}' ({group_id})")
    except ClientError as e:
        if e.response["Error"]["Code"] == "ConflictException":
            print(f"SKIP '{name}' — already assigned")
        else:
            print(f"FAIL '{name}' — {e}")

List all current assignments

See who currently has access to your application — useful before making changes.

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
paginator = sso_admin.get_paginator("list_application_assignments")
for page in paginator.paginate(ApplicationArn=idc_app_arn):
    for a in page["ApplicationAssignments"]:
        principal_type = a["PrincipalType"]
        principal_id = a["PrincipalId"]
        # Resolve name
        if principal_type == "USER":
            user = identity_store.describe_user(
                IdentityStoreId=identity_store_id, UserId=principal_id
            )
            print(f"USER  {user['UserName']}  ({principal_id})")
        else:
            group = identity_store.describe_group(
                IdentityStoreId=identity_store_id, GroupId=principal_id
            )
            print(f"GROUP {group['DisplayName']}  ({principal_id})")

Remove a user by email

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
email = "alice@example.com"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
users = identity_store.list_users(
    IdentityStoreId=identity_store_id,
    Filters=[{"AttributePath": "UserName", "AttributeValue": email}],
)
user_id = users["Users"][0]["UserId"]
 
sso_admin.delete_application_assignment(
    ApplicationArn=idc_app_arn,
    PrincipalId=user_id,
    PrincipalType="USER",
)
print(f"Removed {email} ({user_id})")

Remove a group by name

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
group_name = "Analytics Team"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
groups = identity_store.list_groups(
    IdentityStoreId=identity_store_id,
    Filters=[{"AttributePath": "DisplayName", "AttributeValue": group_name}],
)
group_id = groups["Groups"][0]["GroupId"]
 
sso_admin.delete_application_assignment(
    ApplicationArn=idc_app_arn,
    PrincipalId=group_id,
    PrincipalType="GROUP",
)
print(f"Removed group '{group_name}' ({group_id})")

Remove all assignments

Revoke access for all users and groups. Useful when decommissioning or resetting an application.

import boto3
 
region = "us-east-1"
app_id = "your-app-id"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
 
paginator = sso_admin.get_paginator("list_application_assignments")
for page in paginator.paginate(ApplicationArn=idc_app_arn):
    for a in page["ApplicationAssignments"]:
        sso_admin.delete_application_assignment(
            ApplicationArn=idc_app_arn,
            PrincipalId=a["PrincipalId"],
            PrincipalType=a["PrincipalType"],
        )
        print(f"Removed {a['PrincipalType']} {a['PrincipalId']}")
 
print("All assignments removed")

Sync from a CSV file

Import users and groups from a CSV file. The CSV should have columns type (USER or GROUP) and name (email or group name).

type,name
USER,alice@example.com
USER,bob@example.com
GROUP,Analytics Team
GROUP,Security Team
import boto3, csv
from botocore.exceptions import ClientError
 
region = "us-east-1"
app_id = "your-app-id"
csv_file = "assignments.csv"
 
opensearch = boto3.client("opensearch", region_name=region)
sso_admin = boto3.client("sso-admin", region_name=region)
identity_store = boto3.client("identitystore", region_name=region)
 
app = opensearch.get_application(id=app_id)
idc_app_arn = app["iamIdentityCenterOptions"]["iamIdentityCenterApplicationArn"]
instances = sso_admin.list_instances()
identity_store_id = instances["Instances"][0]["IdentityStoreId"]
 
with open(csv_file) as f:
    for row in csv.DictReader(f):
        principal_type = row["type"].strip().upper()
        name = row["name"].strip()
 
        # Resolve principal ID
        if principal_type == "USER":
            result = identity_store.list_users(
                IdentityStoreId=identity_store_id,
                Filters=[{"AttributePath": "UserName", "AttributeValue": name}],
            )
            if not result["Users"]:
                print(f"SKIP USER '{name}' — not found")
                continue
            principal_id = result["Users"][0]["UserId"]
        else:
            result = identity_store.list_groups(
                IdentityStoreId=identity_store_id,
                Filters=[{"AttributePath": "DisplayName", "AttributeValue": name}],
            )
            if not result["Groups"]:
                print(f"SKIP GROUP '{name}' — not found")
                continue
            principal_id = result["Groups"][0]["GroupId"]
 
        try:
            sso_admin.create_application_assignment(
                ApplicationArn=idc_app_arn,
                PrincipalId=principal_id,
                PrincipalType=principal_type,
            )
            print(f"OK   {principal_type} '{name}'")
        except ClientError as e:
            if e.response["Error"]["Code"] == "ConflictException":
                print(f"SKIP {principal_type} '{name}' — already assigned")
            else:
                print(f"FAIL {principal_type} '{name}' — {e}")

Create IDC users and groups programmatically

If the user doesn't exist in your IDC identity store yet, you can create them:

Create a user

aws identitystore create-user \
  --identity-store-id <IDENTITY_STORE_ID> \
  --user-name "newuser@example.com" \
  --name '{"GivenName":"Jane","FamilyName":"Doe"}' \
  --display-name "Jane Doe" \
  --emails '[{"Value":"newuser@example.com","Type":"Work","Primary":true}]' \
  --region <REGION>

Create a group

aws identitystore create-group \
  --identity-store-id <IDENTITY_STORE_ID> \
  --display-name "Analytics Team" \
  --region <REGION>

Add a user to a group

aws identitystore create-group-membership \
  --identity-store-id <IDENTITY_STORE_ID> \
  --group-id <GROUP_ID> \
  --member-id '{"UserId":"<USER_ID>"}' \
  --region <REGION>

Troubleshooting

IssueCauseFix
User can't log in after assignmentIDC assignment propagation delayWait 1–2 minutes and retry
AccessDeniedException on create-application-assignmentMissing sso:CreateApplicationAssignment permissionAdd the IDC permissions to your IAM policy
User not found in list-usersUser not created in IDC identity storeCreate the user first, or check you're querying the correct identity store
ConflictException on assignmentUser already assignedUse list-application-assignments to verify