MAIN MENU
Devolutions Blog

Announcements, updates, and insights from Devolutions.

Devolutions PowerShell Universal Azure Entra ID password reset illustration for the blog.

Building an Azure Entra ID Password Reset Form Using Microsoft Graph and PowerShell Universal

Register an Entra ID app, save OIDC tokens in PowerShell Universal 5.5, search users with Get-MgUser, then reset a password with Update-MgUser from MgResetPassword.ps1.

In this post, we’ll walk through creating a password reset form for Azure Entra ID using Microsoft Graph and PowerShell Universal. We’ll use the Microsoft.Graph.Authentication and Microsoft.Graph.Users modules to connect to Graph as the current user and reset another user’s password.

Azure Entra ID configuration

The first step is to set up an Azure Entra ID app registration for PowerShell Universal. This will allow users to log in using OpenID Connect and their Entra ID credentials. We’ll also configure the API permissions to provide access to Microsoft Graph.

In the app registration, set the callback path to point to the PowerShell Universal server. HTTPS is required; the port can be whatever PowerShell Universal is listening on. Include a sign-in callback path. You will configure the same value in PowerShell Universal.

Entra ID app registration redirect URI pointing at the PowerShell Universal sign-in callback.
App registration callback URL for PowerShell Universal

Next, ensure the following token types are selected.

Entra ID token configuration with ID tokens and access tokens enabled.
ID and access token types on the app registration

Now set up the permissions that you will delegate to the application and the user logging in. These permissions are required to look up users and reset their accounts. The Directory.AccessAsUser.All permission is required to set another user’s password. It cannot be granted to an application and needs to be provided via delegation. Users in the Global Administrator group have this permission.

Entra ID API permissions including Directory.AccessAsUser.All for delegated password reset.
Delegated Graph permissions for user lookup and password reset

Finally, generate a new client secret for use in PowerShell Universal.

Entra ID certificates and secrets page showing a newly created client secret.
Client secret for the PowerShell Universal app registration

Configure authentication in PowerShell Universal

Now that the app registration is in Entra ID, configure PowerShell Universal to authenticate as the user and provide an access token to Microsoft Graph. Click Security > Authentication and add OpenID Connect to the list of methods. In the properties, connect the app registration to PowerShell Universal.

You will need to specify the following values. These are sample values; yours will be different according to your tenant:

  • Callback Path: https://localhost:5001/signin-oidc. This is the same URL you provided in your app registration.
  • Client ID: 21a267e2-893d-4e0d-a85e-ff21757f082b. The ID of the app registration, on the Overview page.
  • Client Secret: xyz123. The client secret you generated in the app registration.
  • Authority: https://login.microsoftonline.com/3eca8ff3-f62f-431b-88f5-b40183116004. The Microsoft Entra ID login service. The GUID is your tenant ID.
  • Resource: https://graph.microsoft.com. The resource the access token should provide access to.
  • Save Tokens: true. PowerShell Universal should save the access token so the app can use it.
  • Response Type: id_token token. Entra ID should return tokens so you can delegate access later.
  • Scope: openid profile groups offline_access. The access tokens should include group membership and profile info.

Once you have completed configuration, you will be able to log in to PowerShell Universal with OpenID Connect. To validate this, log out of your account and click the login with OpenID Connect button.

Install Microsoft Graph modules

You will need to install the following Graph modules to use this example. The versions used here are included below.

  • Microsoft.Graph.Authentication 2.26.1
  • Microsoft.Graph.Users 2.26.1

You can do so by navigating to Platform > Modules > Galleries within the admin console. We used the following PowerShell Universal version for this demo.

  • PowerShell Universal 5.5.0

Password reset app

Now that authentication is configured, we can create the password reset app. First, store some variables to access Microsoft Graph via the app registration. Navigate to Platform > Variables and create the following variables.

  • PSUMgTenantId: 3eca8ff3-f62f-431b-88f5-b40183116004. The same tenant ID as in the OIDC configuration.
  • PSUMgClientSecret: a PS Credential whose user name is the app registration ID (21a267e2-893d-4e0d-a85e-ff21757f082b) and whose password is the client secret.

Once these variables have been created, navigate to Apps > Apps and create a new app. Click Edit Code to define the app’s contents.

# Connect as the application. Use the process context scope to avoid affecting other apps.
# We are passing in the tenant ID and client secret.
# We are connecting when the app is starting to avoid connecting every time the page is loaded.
Connect-MgGraph -TenantId $PSUMgTenantId -ClientSecretCredential $Secret:PSUMgClientSecret -ContextScope Process -NoWelcome

New-UDApp -Content {

    New-UDForm -Children {
        New-UDAutocomplete -OnLoadOptions {
            Get-MgUser -ConsistencyLevel eventual -Count userCount -Search "`"DisplayName:$Body`"" | ForEach-Object {
                New-UDAutocompleteOption -Name $_.DisplayName -Value $_.Id
            }
        } -Label "User" -Id 'UserId'
        New-UDTextbox -Label 'Password' -Type password -Id 'Password'
        New-UDTextbox -Label 'Confirm Password' -Type password -Id 'ConfirmPassword'
    } -OnValidate {
        if ([string]::IsNullOrEmpty($EventData.UserId)) {
            New-UDValidationResult -ValidationError "User is required"
            return
        }

        if ([string]::IsNullOrEmpty($EventData.Password) -or [string]::IsNullOrEmpty($EventData.ConfirmPassword)) {
            New-UDValidationResult -ValidationError "Password is required"
            return
        }

        if ($EventData.Password -cne $EventData.ConfirmPassword) {
            New-UDValidationResult -ValidationError "Passwords do not match"
            return
        }

        $MgUser = Get-MgUser -UserId $EventData.UserId
        if ($MgUser -eq $null) {
            New-UDValidationResult -ValidationError "Unable to locate user."
            return
        }

        New-UDValidationResult -Valid
    } -OnSubmit {

        if ($AccessToken -eq $null) {
            throw "Access Token is required. Ensure you have Save Tokens enabled in your OpenID Connect authentication method."
        }

        Set-PSUCache -Key "MgAccessToken_$User" -Value $AccessToken

        $PasswordKey = (New-Guid).ToString()
        Set-PSUCache -Key $PasswordKey -Value $EventData.Password -AbsoluteExpiration (Get-Date).AddMinutes(5)

        Invoke-PSUScript -Name 'MgResetPassword.ps1' -Parameters @{
            ResetUser = $EventData.UserId
            PasswordKey = $PasswordKey
        } -Wait -Integrated | Out-Null

        Show-UDSnackbar -Message "Password reset!" -Variant success
    }
}

The app connects to Graph as the application when it starts, using Connect-MgGraph and the process context scope so other apps are not affected. The form searches users with Get-MgUser, validates that the passwords match, then caches the signed-in user’s access token and the new password. Caching avoids storing the access token as plain text in the job history and uses the temporary cache so the values do not persist across restarts.

The form then calls MgResetPassword.ps1 with Invoke-PSUScript. A script isolates the Microsoft Graph connection so it is tied only to this user (several people can use the app at the same time) and gives you automatic job history of which operator reset which account.

The resulting app looks like this.

PowerShell Universal app form with user autocomplete and password reset fields.
Entra ID password reset form in a Universal app

Reset script

Now that the user interface is defined, set up the reset script that performs the actual operation. Click Automation > Scripts and create a new script. Name the script MgResetPassword.ps1. Click the Execution tab and set the Environment to PowerShell 7. Once created, click Edit Code and enter the following.

param($ResetUser, $PasswordKey)

try {
    $Password = Get-PSUCache -Key $PasswordKey
    if (-not $Password) {
        throw "Target password not found."
    }

    $AccessToken = Get-PSUCache -Key "MgAccessToken_$($UAJob.Identity.Name)"
    if (-not $AccessToken) {
        throw "Access token not found for $($UAJob.Identity.Name)"
    }

    Connect-MgGraph -AccessToken ($AccessToken | ConvertTo-SecureString -AsPlainText -Force) -NoWelcome

    $User = Get-MgUser -UserId $ResetUser

    if ($User -eq $null) {
        throw "Failed to look up user: $ResetUser"
    }

    $password = @{
        Password = $Password
        ForceChangePasswordNextSignIn = $false
    }
    Update-MgUser -UserId $User.Id -PasswordProfile $password
} finally {
    Remove-PSUCache -Key $PasswordKey
}

The script reads the password and the current operator’s access token from the cache, connects to Graph with that token, looks the target user up, then calls Update-MgUser with a password profile. It then removes the password from the cache.

With the script created, try the app. You will need to log in as a user with the proper Entra ID permissions to perform these operations. After clicking submit on the form, you will see a success notification and a new job in the job history.

PowerShell Universal success snackbar and job history after an Entra ID password reset.
Successful password reset and job history

Ready to build? Download PowerShell Universal.