Advanced installation: Create Entra ID application

Traditional Nerdio Manager deployments should follow the steps and instructions provided in our Installation Guide. See Nerdio Manager Installation Guide for details. The advanced install methods outlined here should only be used for specific situations as warranted.

Warning

This is an advanced, custom, install of Nerdio Manager recommended only for special circumstances. Please use this with the advice and guidance of Nerdio Support. Please contact us with any questions at nme.support@getnerdio.com.

Note

Updates for Nerdio Manager require running Cloud Shell or PowerShell scripts. See Update the Nerdio Manager Application for details.

Note

The PowerShell scripts in this article use the Microsoft Graph PowerShell SDK, which is preinstalled in Azure Cloud Shell. To run them anywhere else, first install the required modules: Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Users -Scope CurrentUser. Each script signs in with Connect-MgGraph and requests the permissions it needs. Sign in as the administrator described for that section.

Advanced Install steps for manually creating Nerdio Manager application in Entra ID:

  • Azure Marketplace deployment (completed by subscription Owner).

  • Entra ID application registration and setup (completed by Global Administrator or Privileged Role Administrator combined with Cloud Application Administrator).

  • Azure resource configuration (completed by subscription Owner).

To complete the Entra ID application steps in a single run, see Run all steps in one script.

Create the application and a service principal

The following procedure creates the application and a service principal.

To create the application and a service principal:
  1. In the Azure portal, navigate to Microsoft Entra ID > App Registrations > New registration.

  2. Enter the following information:

    • Name: nerdio-nmw-app (optionally rename the default application)

    • Account type: 'Accounts in this organizational directory only (Single tenant)'

    • Redirect URL: Web, https://<app_service_name>.azurewebsites.net/

  3. Save the Application ID. (To be provided for Nerdio Manager deployment.)

  4. Navigate to Microsoft Entra ID > App Registrations > All Applications > nerdio-nmw-app (or custom app name) > Authentication.

  5. Enter the following information:

    • Redirect URLs > Add URL: https://<app_service_name>.azurewebsites.net/signin-oidc

    • Front-channel logout URL: https://<app_service_name>.azurewebsites.net/signout-oidc

    • Implicit grant and hybrid flows: Leave Access tokens and ID tokens unselected.

  6. Navigate to Microsoft Entra ID > Enterprise Applications > All Applications > nerdio-nmw-app (or custom app name)

  7. Save the Object ID. (To be provided for Nerdio Manager deployment.)

  8. Alternatively, the following PowerShell script completes these changes:

    $webAppName = "" # REQUIRED: Name of app service provisioned in marketplace deployment
    $appName = "nerdio-nmw-app" # Optionally rename the default application
    Connect-MgGraph -Scopes "Application.ReadWrite.All"
    $webAppUrl = 'https://' + $webAppName + '.azurewebsites.net/'
    $loginUrl = $webAppUrl + 'signin-oidc'
    $logoutUrl = $webAppUrl + 'signout-oidc'
    $application = New-MgApplication -DisplayName $appName -SignInAudience "AzureADMyOrg" -Web @{
        HomePageUrl           = $webAppUrl
        LogoutUrl             = $logoutUrl
        RedirectUris          = @($webAppUrl, $loginUrl)
        ImplicitGrantSettings = @{ EnableAccessTokenIssuance = $false; EnableIdTokenIssuance = $false }
    }
    $servicePrincipal = New-MgServicePrincipal -AppId $application.AppId -AppRoleAssignmentRequired:$true -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
    Write-Host "Application ID to be saved in to app service: $($application.AppId)"
    Write-Host "Service Principal ID to be provided to Nerdio Manager deployment: $($servicePrincipal.Id)"
    

Configure application roles and assign AVD admin

The following procedure configures the application roles and assigns the AVD admin.

Note

The app roles require a specific ID to be assigned for each role. The role name, description, and ID can be added to the app registration by modifying the app's manifest.

To configure app roles and assign AVD admin:
  1. In the Azure portal, navigate to Microsoft Entra ID > App Registrations > All Applications > nerdio-nmw-app (or custom app name) > Manifest.

  2. Replace the "appRoles": [] entry (typically on line 8) with the following value:

    "appRoles": [
        {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "Complete access to all areas of NMW.",
        "displayName": "WVD Admin",
        "id": "d1c2ade8-98f8-45fd-aa4a-6d06b947c66f",
        "isEnabled": true,
        "origin": "Application",
        "value": "WvdAdmin"
        },
        {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "Complete access to User sessions, ability to view Host Pools, hosts and restart them, but no ability to add/remove or change any settings.",
        "displayName": "Desktop Admin",
        "id": "ed0cdef0-4267-4470-bfff-5e0b6944f9e4",
        "isEnabled": true,
        "origin": "Application",
        "value": "DesktopAdmin"
        },
        {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "Complete access to User sessions only.",
        "displayName": "Help Desk",
        "id": "a94e83da-b314-4232-b8c8-94508c5ed533",
        "isEnabled": true,
        "origin": "Application",
        "value": "HelpDesk"
        },
        {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "View access to all areas of NMW; no ability to save or make changes.",
        "displayName": "Reviewer",
        "id": "0a1b7425-f55a-44a6-9caa-b9a5cc9448da",
        "isEnabled": true,
        "origin": "Application",
        "value": "Reviewer"
        },
        {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "View & manage own sessions (Message, Log off, Disconnect). Personal desktop users can restart, power off and power their personal desktops.",
        "displayName": "End-user",
        "id": "e856de81-1e53-486a-8668-7d564866ae39",
        "isEnabled": true,
        "origin": "Application",
        "value": "EndUser"
        },
        {
        "allowedMemberTypes": [
            "Application"
        ],
        "description": "Rest client description",
        "displayName": "Rest client display name",
        "id": "3807160f-e77a-4fcf-959a-df572bcc3767",
        "isEnabled": true,
        "origin": "Application",
        "value": "RestClient"
        }
    ],
  3. Select Save.

  4. In App Roles, confirm each role is available.

  5. Navigate to Microsoft Entra ID > Enterprise Applications > All Applications > nerdio-nmw-app (or custom app name) > Properties.

  6. Ensure that the option Visible to user? is set to Yes.

  7. Navigate to Microsoft Entra ID > Enterprise Applications > All Applications > nerdio-nmw-app (or custom app name) > Users and groups > Add user/group.

  8. Enter the following information:

    • Users and groups: Select and search for user completing the Nerdio Manager deployment in the Azure portal.

    • Select a role: From the drop-down list, select WVD Admin.

  9. Once you have entered all the required information, select Save.

  10. Alternatively, the following PowerShell script completes these changes:

    $userPrincipalName = "" # REQUIRED: Fill in UPN of subscription owner completing Nerdio Manager deployment
    $appName = "nerdio-nmw-app" # Optionally rename the default application
    Connect-MgGraph -Scopes "Application.ReadWrite.All","AppRoleAssignment.ReadWrite.All","User.Read.All"
    function BuildAppRole([string] $displayName, [string] $value, [Guid] $id, [string] $description) {
        return @{
            AllowedMemberTypes = @("User")
            Description        = $description
            DisplayName        = $displayName
            Id                 = $id
            IsEnabled          = $true
            Value              = $value
        }
    }
    function GetNewAppRoles() {
        $reviewer = BuildAppRole -displayName "Reviewer" -value "Reviewer" -id "0a1b7425-f55a-44a6-9caa-b9a5cc9448da" -description "View access to all areas of NMW; no ability to save or make changes."
        $helpDesk = BuildAppRole -displayName "Help Desk" -value "HelpDesk" -id "a94e83da-b314-4232-b8c8-94508c5ed533" -description "Complete access to User sessions only."
        $endUser = BuildAppRole -displayName "End-user" -value "EndUser" -id "e856de81-1e53-486a-8668-7d564866ae39" -description "View & manage own sessions (Message, Log off, Disconnect). Personal desktop users can restart, power off and power their personal desktops."
        $desktopAdmin = BuildAppRole -displayName "Desktop Admin" -value "DesktopAdmin" -id "ed0cdef0-4267-4470-bfff-5e0b6944f9e4" -description "Complete access to User sessions, ability to view Host Pools, hosts and restart them, but no ability to add/remove or change any settings."
        $wvdAdmin = BuildAppRole -displayName "WVD Admin" -value "WvdAdmin" -id 'd1c2ade8-98f8-45fd-aa4a-6d06b947c66f' -description "Complete access to all areas of NMW."
        return @($reviewer, $helpDesk, $endUser, $desktopAdmin, $wvdAdmin)
    }
    $application = Get-MgApplication -Filter "displayName eq '$appName'"
    if (-not $application) {
        throw "No application found with the display name '$appName'. Check that the app registration exists and the name matches exactly."
    }
    if (@($application).Count -gt 1) {
        throw "More than one application found with the display name '$appName'. Resolve this manually before continuing."
    }
    $user = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'"
    if (-not $user) {
        throw "No user found with the user principal name '$userPrincipalName'."
    }
    $servicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$($application.AppId)'"
    Update-MgApplication -ApplicationId $application.Id -AppRoles (GetNewAppRoles)
    $wvdAdminRoleId = 'd1c2ade8-98f8-45fd-aa4a-6d06b947c66f'
    New-MgUserAppRoleAssignment -UserId $user.Id -PrincipalId $user.Id -ResourceId $servicePrincipal.Id -AppRoleId $wvdAdminRoleId

Configure API permissions

The following procedure configures the API permissions.

To configure the API permissions:
  1. In the Azure portal, navigate to Microsoft Entra ID > App Registrations > All Applications > nerdio-nmw-app (or custom app name) > API permissions.

  2. Add or confirm the following permissions for Microsoft APIs > Microsoft Graph > Delegated permissions:

    • User.Read (included by default)

    • User.ReadBasic.All

    • User.Read.All

    • Group.Read.All

    • GroupMember.Read.All

    • Application.Read.All

    • Organization.Read.All

    • AppRoleAssignment.ReadWrite.All

    • Application.ReadWrite.All

    • Mail.Send (optional for enabling email notifications)

    • offline_access

    • openid

    • profile

  3. Add the following permissions for Microsoft APIs > Microsoft Graph > Application permissions:

    • Group.Read.All

    • GroupMember.Read.All

    • Organization.Read.All

    • User.Read.All

  4. Add the following permissions for Microsoft APIs > Azure Service Management > Delegated permissions:

    • user_impersonation

  5. (For WVD Classic Only) APIs my organization uses > Windows Virtual Desktop (Application ID 5a0aa725-4958-4b0c-80a9-34562e23f3b7):

    • Add Delegated permissions: user_impersonation

    • Add Application permissions: TenantCreator

  6. Alternatively, the following PowerShell script completes these changes:

    $appName = "nerdio-nmw-app" # Optionally rename the default application
    $wvdClassicEnabled = $False # Optionally include permissions for WVD Classic
    $readDelagatedPermissions = "User.Read|User.ReadBasic.All|User.Read.All|Group.Read.All|GroupMember.Read.All|Application.Read.All|Organization.Read.All"
    Connect-MgGraph -Scopes "Application.ReadWrite.All"
    function GetResourceAccess($servicePrincipal, [string]$requiredPermissions, [string]$permissionType) {
        if ($permissionType -eq "Scope") {
            $exposedPermissions = $servicePrincipal.Oauth2PermissionScopes
        }
        else {
            $exposedPermissions = $servicePrincipal.AppRoles
        }
        foreach ($permission in $requiredPermissions.Trim().Split("|")) {
            $match = $exposedPermissions | Where-Object { $_.Value -eq $permission }
            if ($match) {
                @{ Id = $match.Id; Type = $permissionType }
            }
            else {
                Write-Warning "Permission '$permission' (type '$permissionType') was not found on '$($servicePrincipal.DisplayName)' and was skipped."
            }
        }
    }
    function GetRequiredPermissions([string] $applicationId, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions) {
        $sp = Get-MgServicePrincipal -Filter "appId eq '$applicationId'"
        if (-not $sp) {
            throw "No service principal found with application ID '$applicationId' in this tenant."
        }
        $resourceAccess = @()
        if ($requiredDelegatedPermissions) {
            $resourceAccess += @(GetResourceAccess $sp $requiredDelegatedPermissions "Scope")
        }
        if ($requiredApplicationPermissions) {
            $resourceAccess += @(GetResourceAccess $sp $requiredApplicationPermissions "Role")
        }
        return @{ ResourceAppId = $applicationId; ResourceAccess = $resourceAccess }
    }
    $application = Get-MgApplication -Filter "displayName eq '$appName'"
    if (-not $application) {
        throw "No application found with the display name '$appName'. Check that the app registration exists and the name matches exactly."
    }
    if (@($application).Count -gt 1) {
        throw "More than one application found with the display name '$appName'. Resolve this manually before continuing."
    }
    $graphPermissions = GetRequiredPermissions -applicationId "00000003-0000-0000-c000-000000000000" -requiredDelegatedPermissions "$readDelagatedPermissions|AppRoleAssignment.ReadWrite.All|Application.ReadWrite.All|offline_access|openid|profile|Mail.Send" -requiredApplicationPermissions "Group.Read.All|GroupMember.Read.All|Organization.Read.All|User.Read.All"
    if ($wvdClassicEnabled) {
        $wvdClassicPermissions = GetRequiredPermissions -applicationId "5a0aa725-4958-4b0c-80a9-34562e23f3b7" -requiredDelegatedPermissions "user_impersonation" -requiredApplicationPermissions "TenantCreator" # Required for WVD classic only
    }
    $rmPermissions = GetRequiredPermissions -applicationId "797f4846-ba00-4fd7-ba43-dac1f8f63013" -requiredDelegatedPermissions "user_impersonation" # Windows Azure Service Management API
    $requiredResourcesAccess = @($graphPermissions, $rmPermissions)
    if ($wvdClassicEnabled) {
        $requiredResourcesAccess += $wvdClassicPermissions # Required for WVD classic only
    }
    Update-MgApplication -ApplicationId $application.Id -RequiredResourceAccess $requiredResourcesAccess

Assign Subscription Owner user account as Owner on the service principal

The following procedure assigns the Subscription Owner user account as Owner on the service principal.

To assign the subscription owner:
  1. In the Azure portal, navigate to Microsoft Entra ID > Enterprise Applications > All Applications > nerdio-nmw-app (or custom app name) > Owners > Add.

  2. Search for the user account that deployed Nerdio Manager from Azure Marketplace and is completing the installation and Select.

  3. Alternatively, the following PowerShell script completes these changes:

    $userPrincipalName = "" # REQUIRED: Fill in UPN of subscription owner completing Nerdio Manager deployment
    $appName = "nerdio-nmw-app" # Optionally rename the default application
    Connect-MgGraph -Scopes "Application.ReadWrite.All","User.Read.All"
    $application = Get-MgApplication -Filter "displayName eq '$appName'"
    if (-not $application) {
        throw "No application found with the display name '$appName'. Check that the app registration exists and the name matches exactly."
    }
    if (@($application).Count -gt 1) {
        throw "More than one application found with the display name '$appName'. Resolve this manually before continuing."
    }
    $user = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'"
    if (-not $user) {
        throw "No user found with the user principal name '$userPrincipalName'."
    }
    $servicePrincipal = Get-MgServicePrincipal -Filter "appId eq '$($application.AppId)'"
    New-MgServicePrincipalOwnerByRef -ServicePrincipalId $servicePrincipal.Id -BodyParameter @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)" }

Grant admin consent

The following procedure grants admin consent.

To grant admin consent:
  1. In the Azure portal, navigate to Microsoft Entra ID > App Registrations > All Applications > nerdio-nmw-app (or custom app name) > API permissions.

  2. Select 'Grant admin consent for <tenant_name>'.

  3. Select 'Grant consent on behalf of my organization' if prompted.

  4. Navigate to Microsoft Entra ID > Enterprise Applications > All Applications > nerdio-nmw-app (or custom app name) > Permissions.

  5. Select 'Grant admin consent for <tenant_name>'.

  6. Select 'Grant consent on behalf of my organization' if prompted.

  7. Alternatively, the following PowerShell script completes these changes:

    Note

    The script uses the tenant you sign in to. It displays a link instead of opening a browser, so you can also run it in Azure Cloud Shell. Open the link in a web browser and sign in as a Global Administrator to grant consent.

    $appName = "nerdio-nmw-app" # Optionally rename the default application
    $microsoftLogin = 'https://login.microsoftonline.com'
    Connect-MgGraph -Scopes "Application.Read.All"
    $tenantId = (Get-MgContext).TenantId
    $application = Get-MgApplication -Filter "displayName eq '$appName'"
    if (-not $application) {
        throw "No application found with the display name '$appName'. Check that the app registration exists and the name matches exactly."
    }
    if (@($application).Count -gt 1) {
        throw "More than one application found with the display name '$appName'. Resolve this manually before continuing."
    }
    $consentUrl = "$microsoftLogin/$tenantId/adminconsent?client_id=$($application.AppId)"
    Write-Host "Open the following link in a web browser and sign in as a Global Administrator to grant admin consent:"
    Write-Host $consentUrl

Run all steps in one script

As an alternative to completing the sections above one at a time, a Global Administrator, or a Privileged Role Administrator combined with Cloud Application Administrator, can run the following script once. It completes everything from Create the application and a service principal to Grant admin consent. It does not generate the application secret; complete that section separately.

To run all steps in one script:
  1. Open PowerShell or Azure Cloud Shell.

  2. Copy the script below and fill in the required values at the top:

    • $webAppName: The name of the app service provisioned in the marketplace deployment.

    • $userPrincipalName: The user principal name (UPN) of the subscription owner completing the Nerdio Manager deployment.

    # ============================================
    # Create the Nerdio Manager application in Entra ID (advanced install)
    # ============================================
    # Run as a Global Administrator, or as a Privileged Role Administrator
    # combined with Cloud Application Administrator.
    # This script:
    # 1. Creates the application and a service principal.
    # 2. Configures the application roles and assigns the AVD admin.
    # 3. Configures the API permissions.
    # 4. Assigns the subscription owner as owner of the service principal.
    # 5. Displays the link for granting admin consent.
    # It does not generate the application secret.
    # ============================================
    
    # --- Settings ---
    $webAppName = "" # REQUIRED: Name of app service provisioned in marketplace deployment
    $userPrincipalName = "" # REQUIRED: UPN of the subscription owner completing the deployment
    $appName = "nerdio-nmw-app" # Optionally rename the default application
    $wvdClassicEnabled = $False # Optionally include permissions for WVD Classic
    $microsoftLogin = 'https://login.microsoftonline.com'
    $readDelagatedPermissions = "User.Read|User.ReadBasic.All|User.Read.All|Group.Read.All|GroupMember.Read.All|Application.Read.All|Organization.Read.All"
    
    # --- Sign in and check the settings before creating anything ---
    Connect-MgGraph -Scopes "Application.ReadWrite.All","AppRoleAssignment.ReadWrite.All","User.Read.All"
    $tenantId = (Get-MgContext).TenantId
    if (-not $webAppName -or -not $userPrincipalName) {
        throw "Fill in webAppName and userPrincipalName before running the script."
    }
    $user = Get-MgUser -Filter "userPrincipalName eq '$userPrincipalName'"
    if (-not $user) {
        throw "No user found with the user principal name '$userPrincipalName'."
    }
    if (Get-MgApplication -Filter "displayName eq '$appName'") {
        throw "An application named '$appName' already exists. Choose a different name or remove the existing application first."
    }
    
    # --- Step 1: Create the application and a service principal ---
    $webAppUrl = 'https://' + $webAppName + '.azurewebsites.net/'
    $loginUrl = $webAppUrl + 'signin-oidc'
    $logoutUrl = $webAppUrl + 'signout-oidc'
    $application = New-MgApplication -DisplayName $appName -SignInAudience "AzureADMyOrg" -Web @{
        HomePageUrl           = $webAppUrl
        LogoutUrl             = $logoutUrl
        RedirectUris          = @($webAppUrl, $loginUrl)
        ImplicitGrantSettings = @{ EnableAccessTokenIssuance = $false; EnableIdTokenIssuance = $false }
    }
    $servicePrincipal = New-MgServicePrincipal -AppId $application.AppId -AppRoleAssignmentRequired:$true -Tags @("WindowsAzureActiveDirectoryIntegratedApp")
    # Allow time for the new application and service principal to replicate
    Start-Sleep -Seconds 15
    
    # --- Step 2: Configure application roles and assign the AVD admin ---
    function BuildAppRole([string] $displayName, [string] $value, [Guid] $id, [string] $description) {
        return @{
            AllowedMemberTypes = @("User")
            Description        = $description
            DisplayName        = $displayName
            Id                 = $id
            IsEnabled          = $true
            Value              = $value
        }
    }
    function GetNewAppRoles() {
        $reviewer = BuildAppRole -displayName "Reviewer" -value "Reviewer" -id "0a1b7425-f55a-44a6-9caa-b9a5cc9448da" -description "View access to all areas of NMW; no ability to save or make changes."
        $helpDesk = BuildAppRole -displayName "Help Desk" -value "HelpDesk" -id "a94e83da-b314-4232-b8c8-94508c5ed533" -description "Complete access to User sessions only."
        $endUser = BuildAppRole -displayName "End-user" -value "EndUser" -id "e856de81-1e53-486a-8668-7d564866ae39" -description "View & manage own sessions (Message, Log off, Disconnect). Personal desktop users can restart, power off and power their personal desktops."
        $desktopAdmin = BuildAppRole -displayName "Desktop Admin" -value "DesktopAdmin" -id "ed0cdef0-4267-4470-bfff-5e0b6944f9e4" -description "Complete access to User sessions, ability to view Host Pools, hosts and restart them, but no ability to add/remove or change any settings."
        $wvdAdmin = BuildAppRole -displayName "WVD Admin" -value "WvdAdmin" -id 'd1c2ade8-98f8-45fd-aa4a-6d06b947c66f' -description "Complete access to all areas of NMW."
        return @($reviewer, $helpDesk, $endUser, $desktopAdmin, $wvdAdmin)
    }
    Update-MgApplication -ApplicationId $application.Id -AppRoles (GetNewAppRoles)
    $wvdAdminRoleId = 'd1c2ade8-98f8-45fd-aa4a-6d06b947c66f'
    New-MgUserAppRoleAssignment -UserId $user.Id -PrincipalId $user.Id -ResourceId $servicePrincipal.Id -AppRoleId $wvdAdminRoleId
    
    # --- Step 3: Configure API permissions ---
    function GetResourceAccess($servicePrincipal, [string]$requiredPermissions, [string]$permissionType) {
        if ($permissionType -eq "Scope") {
            $exposedPermissions = $servicePrincipal.Oauth2PermissionScopes
        }
        else {
            $exposedPermissions = $servicePrincipal.AppRoles
        }
        foreach ($permission in $requiredPermissions.Trim().Split("|")) {
            $match = $exposedPermissions | Where-Object { $_.Value -eq $permission }
            if ($match) {
                @{ Id = $match.Id; Type = $permissionType }
            }
            else {
                Write-Warning "Permission '$permission' (type '$permissionType') was not found on '$($servicePrincipal.DisplayName)' and was skipped."
            }
        }
    }
    function GetRequiredPermissions([string] $applicationId, [string] $requiredDelegatedPermissions, [string]$requiredApplicationPermissions) {
        $sp = Get-MgServicePrincipal -Filter "appId eq '$applicationId'"
        if (-not $sp) {
            throw "No service principal found with application ID '$applicationId' in this tenant."
        }
        $resourceAccess = @()
        if ($requiredDelegatedPermissions) {
            $resourceAccess += @(GetResourceAccess $sp $requiredDelegatedPermissions "Scope")
        }
        if ($requiredApplicationPermissions) {
            $resourceAccess += @(GetResourceAccess $sp $requiredApplicationPermissions "Role")
        }
        return @{ ResourceAppId = $applicationId; ResourceAccess = $resourceAccess }
    }
    $graphPermissions = GetRequiredPermissions -applicationId "00000003-0000-0000-c000-000000000000" -requiredDelegatedPermissions "$readDelagatedPermissions|AppRoleAssignment.ReadWrite.All|Application.ReadWrite.All|offline_access|openid|profile|Mail.Send" -requiredApplicationPermissions "Group.Read.All|GroupMember.Read.All|Organization.Read.All|User.Read.All"
    if ($wvdClassicEnabled) {
        $wvdClassicPermissions = GetRequiredPermissions -applicationId "5a0aa725-4958-4b0c-80a9-34562e23f3b7" -requiredDelegatedPermissions "user_impersonation" -requiredApplicationPermissions "TenantCreator" # Required for WVD classic only
    }
    $rmPermissions = GetRequiredPermissions -applicationId "797f4846-ba00-4fd7-ba43-dac1f8f63013" -requiredDelegatedPermissions "user_impersonation" # Windows Azure Service Management API
    $requiredResourcesAccess = @($graphPermissions, $rmPermissions)
    if ($wvdClassicEnabled) {
        $requiredResourcesAccess += $wvdClassicPermissions # Required for WVD classic only
    }
    Update-MgApplication -ApplicationId $application.Id -RequiredResourceAccess $requiredResourcesAccess
    
    # --- Step 4: Assign the subscription owner as owner of the service principal ---
    New-MgServicePrincipalOwnerByRef -ServicePrincipalId $servicePrincipal.Id -BodyParameter @{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/$($user.Id)" }
    
    # --- Step 5: Display the values to save and the admin consent link ---
    $consentUrl = "$microsoftLogin/$tenantId/adminconsent?client_id=$($application.AppId)"
    Write-Host "Application ID to be saved in to app service: $($application.AppId)"
    Write-Host "Service Principal ID to be provided to Nerdio Manager deployment: $($servicePrincipal.Id)"
    Write-Host "Open the following link in a web browser and sign in as a Global Administrator to grant admin consent:"
    Write-Host $consentUrl
  3. Run the script.

  4. Save the Application ID and the Service Principal ID that the script displays. (To be provided for Nerdio Manager deployment.)

  5. Open the link that the script displays in a web browser, sign in as a Global Administrator, and grant consent.

    The script displays a link instead of opening a browser, so it also works in Azure Cloud Shell.

Generate new application secret (nerdio-nmw-app)

This step may optionally be completed by the subscription Owner user instead of Global Administrator, or Privileged Role Administrator combined with Cloud Application Administrator, user. In this case, the subscription Owner user should be added as Owner on the application under App Registrations, in addition to Owner added on the service principal under Enterprise Applications above.

To generate a new application secret:
  1. Navigate to Microsoft Entra ID > App Registrations > All Applications > nerdio-nmw-app (or custom app name) > Certificates & Secrets > New client secret.

  2. Enter the following information:

    • Description: Nerdio Manager Secret (optional)

    • Expires: Select Custom.

      • Start: Select today's date.

      • End: This should be set to the longest duration allowed by your company policy.

        Note

        The Entra ID portal may limit this value to a maximum of 2 years.

  3. Save the generated secret's value.

    Note

    This is required to complete the installation in the Azure portal (by subscription owner user). Ensure the value is used, not the ID of the secret.

  4. Alternatively, the following PowerShell script completes these changes:

    $appName = "nerdio-nmw-app" # Optionally rename the default application
    Connect-MgGraph -Scopes "Application.ReadWrite.All"
    $application = Get-MgApplication -Filter "displayName eq '$appName'"
    if (-not $application) {
        throw "No application found with the display name '$appName'. Check that the app registration exists and the name matches exactly."
    }
    if (@($application).Count -gt 1) {
        throw "More than one application found with the display name '$appName'. Resolve this manually before continuing."
    }
    $appSecret = Add-MgApplicationPassword -ApplicationId $application.Id -PasswordCredential @{ DisplayName = "Nerdio Manager Secret"; EndDateTime = (Get-Date).AddYears(10) }
    Write-Host "Application secret to be saved in Azure KeyVault: $($appSecret.SecretText)"

Finalize

Share the below values with the user completing Nerdio Manager deployment in the Azure portal (subscription owner):

  • Application ID

  • Application client secret (Make sure this is the generated value of the secret, not the ID.)

  • Service principal ID (This is the object ID of the application from Enterprise Applications.)

Was this article helpful?

0 out of 0 found this helpful
Have more questions? Submit a request

Comments (0 comments)

Please sign in to leave a comment.