• A Premium edition account (P1 or P2)
  • Global administrator access to the account
  • Optional: The Azure CLI (run AZ login to log in)


The following provisioning features are supported:

  • Push Groups and New Users
  • Push Profile or Group Updates
  • Push User Deactivation
  • Reactivate Users

Step By Step Instructions

Step 1. Create User Groups

Access to Hosts is granted at the user groups level. If you don't already have groups set up, you'll want to create a group for each kind of user you wish to provide access to your servers. For example, you might have a group for ssh users, and one for sudo users.

When creating the groups, give them names and accept the defaults on all other settings.

Step 2. Create an OAuth OIDC App

Create an App Registration

In the Azure Portal, under App Registrations, choose + New Registration.

Display name: "Smallstep SSH"

Account type: Accounts in this organizational directory only (Default Directory only - Single tenant)

Redirect URI Platform: Public client/native (mobile & desktop)

Redirect URI:

Or on the command line, with the az app, run:

az ad app create --display-name "Smallstep SSH" --reply-urls

Fetch the client and tenant IDs

Under App Registrations, choose the "All Applications" tab at the top.

Choose the Smallstep SSH app you just created.

Note the Application (client) ID and the Directory (tenant) ID. You will need them in a moment.

Add a Client Secret

  • Now choose "Certificates & Secrets" on the left.
  • Under Client Secrets, make a + New client secret.
    • Name doesn't matter
    • Expires Never
  • Copy the value of the client secret you just created and save it. You will need it in a moment.

In this context, the OAuth client "secret" is not really a secret
Smallstep SSH uses the Authorization Code flow for native OAuth apps. This flow requires the redirect URI hostname be hardcoded as (or localhost) in the client. This constraint obviates the need for a client secret, because the loopback address is inherently resistant to network attacks that the client secret is designed to mitigate in other, non-native app OAuth flows.
This is a secure implementation of OAuth that conforms to the OAuth Best Current Practices for for Native Apps (RFC8252 / IETF BCP212). It is the same approach that Google's gcloud CLI tool uses for Google Cloud Platform authentication: An OAuth client secret is hardcoded into its source code. In the Smallstep world, it's published to clients via the CA's provisioner list API endpoint (/provisioners).

Step 3: Create an OIDC provisioner

On the command line:

Open the admin-tools container and run:

manage-provisioners \
   --ssh \
   --type OIDC \
   --name "azuread" \
   --client-id [client-id] \
   --client-secret [client-secret] \
   --configuration-endpoint[azure-tenant-id]/v2.0/.well-known/openid-configuration \
   --domain [azure-email-domain] \
   --authority [authority-id] \
   --tenant-id [azure-tenant-id] \

Then, log in to the smallstep dashboard UI, go to the Users tab, and select "Azure AD" as your identity provider.

Or, in the Smallstep UI:

  1. Paste in your Application (client) ID and the Client secret from Azure AD.
  2. For the Configuration Endpoint, find your Directory (tenant) ID and insert it into this URL:
  3. You've completed the OIDC portion of the setup.

Step 4. Create a SCIM application

  1. Go to Enterprise Applications.
  2. Choose + Add Application.
  3. Add a Non-Gallery Application.
  4. Call it `smallstep-usersync` or some other name you'll remember.

1. Assign your ssh and sudo groups with the application you just created

In your new application, under Users and Groups on the left:

  • Go to + Add User to create a new assignment.
  • Select your sudo and ssh groups.
  • Click Select on the bottom right.
  • Click Assign at the bottom left.

Your Users and Groups list should look something like this:

2. Create your directory

  • Now go into the Smallstep UI and create a directory.
  • Choose Azure AD as your identity provider.

3. Supply your credentials

Under Provisioning on the left:

  1. Set the provisioning mode to Automatic
  2. Expand Admin Credentials:
    • Supply the Tenant URL and Secret Token:
    • The Tenant URL will be
  3.<Smallstep user directory id>/v2
    • The SCIM secret token will come from the database. Run:
$ kubectl exec -n smallstep -it deploy/admin-tools -- bash
[inside the pod]
$ psql folk
> select token from scim_logins;
  • You can test the SCIM endpoint manually via:
$  curl -H "Authorization: Bearer [secret-token]"[directory-uuid]/v2/Users
  • Choose Test Connection and make sure that it works.

4. Create attribute mappings

Smallstep needs to see the right attributes when you sync your directory with us.

Expand Mappings

Click on Synchronize Azure Active Directory Users to customappsso

Under the Attributes Mappings section:

  1. Delete all attribute mappings except for displayName, mail, givenName, and surname
  2. Edit the userPrincipalNameattribute
    1. Mapping Type: Expression
    2. Expression: ToLower(Replace([userPrincipalName], , "(?<Suffix>@(.)*)", "Suffix", "", , ))
    3. Target Attributes: userName
    4. Match objects using this attribute: Yes
    5. Matching precedence: 1

Alternative User Principal Name mappings

The userName attribute, in this context, determines the name of the POSIX account that will be created when users connect to a host. The expression default returns everything before the @ in the UPN, converted to lowercase. Here are some alternative UPN expressions that have been useful to customers:

Convert to bgates: 
ToLower(Replace(Replace([userPrincipalName], , "(?<=^.{1}).\w+.\.", "", "", , ), , "(@(.)*)","","" , ,))

Convert to billgates:
ToLower(Replace(Replace([userPrincipalName], , "(?<Suffix>@(.)*)", "Suffix", "", , ), ".", , ,"", , ))

Add an active attribute

  • Mapping Type: Expression
  • Expression: Not([IsSoftDeleted])
  • Target attribute: active
  • Match objects using this attribute: No
  • Apply this mapping: Always
  • Ok

Your mappings should look like this when you're finished:

5. Provision

  1. Save your settings and return to Provisioning.
  2. Change Provisioning Status to On.
  3. Click Save
There's a quirk in Microsoft's UI here, and you may see an error when saving after turning provisioning on. If so, wait 60 seconds and try Save again.

Step 6: Sign in to the smallstep UI

  1. Sign in at[TEAM NAME]
  2. Navigate to the LOGS menu. You should see a list of success messages associated with SCIM-SYNC category items.
  3. You should see your directory with users and groups synced.

Don't see your users and groups? Microsoft's SCIM service may add a 40-minute delay after you set it up. Azure Logs also have a significant delay. You can force an update by choosing Restart synchronization in the Provisioning panel. Even then, it may take up to an hour to sync with Smallstep.

Switch your SSH directory id (in folk) to use your Azure directory.

Run this from inside the admin-tools container:

$ psql folk
folk=> update teams set ssh_directory_id = '[directory-id]';

Test your SSH config

step ssh config --team [team-name] --team-url 'https://[smallstep-api-url]/v1/teams/<>/authorities/ssh'
step ssh login

Troubleshooting Tips

  • Initial activation of Azure AD OIDC provisioning in Smallstep SSH requires entering your Directory (tenant) ID, Application (client) ID, and the Client secret into the Smallstep UI. Contact smallstep support with any questions.
  • Note: When users are deactivated in Azure AD, they will be deactivated in Smallstep. Users will not be able to SSH to servers, but their user accounts will remain on smallstep managed hosts. To permanently delete user data on smallstep managed hosts, contact Smallstep Support.
  • If you get "The request lacked necessary authorization to be completed." after successfully logging in to Azure (with step ssh config or step ssh login):
    • This may be because the email field in the OIDC token is empty, and our Smallstep SSH requires it to be populated in order to set the SSH certificate principals.
    • As described by this Stack post, Azure AD UserPrincipalNames are not email addresses. Azure AD users do not have mailfields unless:
      • The user is a Guest in the directory
      • The user is a Microsoft Account
      • The user has Exchange set up (??)
      • The user is synced from an external Active Directory server and has an email there.

And if they don't have mail fields, then the email field of the ID token is empty.

Use az ad user list to see that your AAD user has a value for the mail field.