{"id":8994,"date":"2025-06-30T12:13:30","date_gmt":"2025-06-30T12:13:30","guid":{"rendered":"https:\/\/pariswells.com\/blog\/?p=8994"},"modified":"2025-07-01T04:41:51","modified_gmt":"2025-07-01T04:41:51","slug":"sso-for-avd","status":"publish","type":"post","link":"https:\/\/pariswells.com\/blog\/research\/sso-for-avd","title":{"rendered":"SSO for AVD"},"content":{"rendered":"\n<ol class=\"wp-block-list\">\n<li>Changing SSO will change the Resource in Conditional Access from Windows 365 \\ Windows Virtual Desktop to Windows Cloud Login so make sure you exclude or include this new resource where available <\/li>\n\n\n\n<li>Change the Session Lock Behaviour <a href=\"https:\/\/docs.azure.cn\/en-us\/virtual-desktop\/configure-session-lock-behavior?tabs=group-policy\">Configure the session lock behavior for Azure Virtual Desktop | Azure Docs<\/a> as this start to disconnect users when you enable SSO<\/li>\n\n\n\n<li>Add AVD users the right RBAC Role<br>When using Entra Joined Azure Virtual Desktop Sessionhosts, you will need to assign a RBAC role to the user group which will login to the sessionhosts. The RBAC role of Virtual Machine User Login, located at the resource group which holds the sessionhost virtual machines. In this post:&nbsp;<a href=\"https:\/\/www.gettothe.cloud\/azure-deploy-azure-virtual-desktop\/\">Azure | Deploy Azure Virtual Desktop \u2013 GetToTheCloud<\/a>&nbsp;I explained that the user group that needs to access the Azure Virtual Desktop require the&nbsp;<strong><em>Virtual Machine User Login<\/em><\/strong>&nbsp;RBAC role.<\/li>\n<\/ol>\n\n\n\n<p>If Hybrid &#8211; <a href=\"https:\/\/learn.microsoft.com\/en-us\/entra\/identity\/authentication\/howto-authentication-passwordless-security-key-on-premises#create-a-kerberos-server-object\">Passwordless security key sign-in to on-premises resources &#8211; Microsoft Entra ID | Microsoft Learn<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\"># Specify the on-premises Active Directory domain. A new Microsoft Entra ID\n# Kerberos Server object will be created in this Active Directory domain.\n$domain = $env:USERDNSDOMAIN\n\n# Enter a UPN of a Hybrid Identity Administrator\n$userPrincipalName = \"administrator@contoso.onmicrosoft.com\"\n\n# Create the new Microsoft Entra ID Kerberos Server object in Active Directory\n# and then publish it to Azure Active Directory.\n# Open an interactive sign-in prompt with given username to access the Microsoft Entra ID.\nSet-AzureADKerberosServer -Domain $domain -UserPrincipalName $userPrincipalName<\/code><\/pre>\n\n\n\n<p>Script<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;#\n.SYNOPSIS\nEnable AVD Single Sign-On (SSO) configuration in Microsoft Entra ID Tenant.\n \n.DESCRIPTION\nThis PowerShell script is used to enable AVD Single Sign-On (SSO) configuration in\nMicrosoft Entra ID Tenant.\nIt uses the Microsoft Graph Module to interact with the Microsoft Entra ID Tenant.\n \nThe script will enable the following:\n- Microsoft Remote Desktop\n- Windows Cloud Login\n \nThe script will output the following:\n- Service Principal Name\n- Remote Desktop Protocol Enabled\n- Target Device Groups\n \nThe script starts by defining the required variables for the Microsoft Entra ID Group.\n \nThen the script begins by importing the necessary modules: Microsoft.Graph.Authentication and Microsoft.Graph.Applications.\nThese modules provide the cmdlets that the script uses to interact with the Microsoft Graph API.\n \nNext, the script defines the required scopes for the Microsoft Graph API.\nThese scopes are permissions that the script needs to perform its operations.\nThe Connect-MgGraph command is then used to connect to the Microsoft Graph API with these scopes.\n \nThe script then defines a list of application names. These are the names of the applications that the script will\nconfigure SSO settings for. The script creates an empty ArrayList to store the service principals for these applications.\n \nThe script then loops over the application names. For each application name, it uses the Get-MgServicePrincipal command\nto get the service principal for the application. The service principal is an identity that is used by a service or\napplication to log in and access resources. The service principal is then added to the ArrayList.\n \nNext, the script loops over the service principals in the ArrayList. For each service principal, it gets the ID and outputs\nthe display name. It then checks if the Remote Desktop Protocol is enabled for the service principal. If it is not, it uses\nthe Update-MgServicePrincipalRemoteDesktopSecurityConfiguration command to enable it.\n \nThe script then creates a new TargetDeviceGroup object and sets its ID and display name.\nIt uses the New-MgServicePrincipalRemoteDesktopSecurityConfigurationTargetDeviceGroup command to create a new\ntarget device group for the service principal.\n \nFinally, the script validates the changes by getting the current remote desktop security configuration and the\ntarget device groups for the service principal. It outputs the status of the Remote Desktop Protocol and the display names\nof the target device groups.\n \n.NOTES\nAuthor: Stefan Beckmann\nDate: January 09, 2024\nhttps:\/\/www.beckmann.ch\/blog\/2023\/11\/29\/sso-for-azure-virtual-desktop-with-ad-users\/?lang=en\nhttps:\/\/learn.microsoft.com\/en-us\/azure\/virtual-desktop\/configure-single-sign-on#create-a-kerberos-server-object\nVersion: 1.0\n \n.LINK\nGitHub Repository: https:\/\/github.com\/alphasteff\n \n.EXAMPLE\n.\\Enable-AvdSSO.ps1\n \n#&gt;\n \n# Define the target device group, values need to be added\n$groupName = 'AVD Devices'\n$groupId = '3a32d3ce-edfe-43d7-b6ff-75afaf10f165'\n \n### DO NOT MODIFY BELOW THIS LINE ###\n \nImport-Module Microsoft.Graph.Authentication\nImport-Module Microsoft.Graph.Applications\n \n$RequiredScopes = @(\n    'Application-RemoteDesktopConfig.ReadWrite.All',\n    'Application.ReadWrite.All'\n)\n \nConnect-MgGraph -Scopes $RequiredScopes\n \n$applicationIds = @(\n    'a4a365df-50f1-4397-bc59-1a1564b8bb9c', # Microsoft Remote Desktop\n    '270efc09-cd0d-444b-a71f-39af4910ec45'  # Windows Cloud Login\n)\n \n$applicationList = [System.Collections.ArrayList]@()\nForEach ($applicationId in $applicationIds) {\n    $sp = Get-MgServicePrincipal -Filter \"AppId eq `'$applicationId`'\"\n    $null = $applicationList.Add($sp)\n}\n \nForEach ($application in $applicationList) {\n    $servicePrincipalId = $application.Id\n    Write-Output (\"Service Principal Name: \" + $application.DisplayName)\n \n    If ((Get-MgServicePrincipalRemoteDesktopSecurityConfiguration -ServicePrincipalId $servicePrincipalId) -ne $true) {\n        $null = Update-MgServicePrincipalRemoteDesktopSecurityConfiguration -ServicePrincipalId $servicePrincipalId -IsRemoteDesktopProtocolEnabled\n    }\n \n    $tdg = New-Object -TypeName Microsoft.Graph.PowerShell.Models.MicrosoftGraphTargetDeviceGroup\n    $tdg.Id = $groupId\n    $tdg.DisplayName = $groupName\n \n    $null = New-MgServicePrincipalRemoteDesktopSecurityConfigurationTargetDeviceGroup -ServicePrincipalId $servicePrincipalId -BodyParameter $tdg\n \n    $validateConfiguration = Get-MgServicePrincipalRemoteDesktopSecurityConfiguration -ServicePrincipalId $servicePrincipalId\n    $validateDeviceGroup = Get-MgServicePrincipalRemoteDesktopSecurityConfigurationTargetDeviceGroup -ServicePrincipalId $servicePrincipalId\n \n    Write-Output (\"Remote Desktop Protocol Enabled: \" + $validateConfiguration.isRemoteDesktopProtocolEnabled)\n    Write-Output (\"Target Device Groups: \" + ($validateDeviceGroup.DisplayName | Out-String))\n}<\/code><\/pre>\n\n\n\n<p>Enable<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/pariswells.com\/blog\/wp-content\/uploads\/2025\/06\/image-6.png\"><img loading=\"lazy\" decoding=\"async\" width=\"993\" height=\"376\" src=\"https:\/\/pariswells.com\/blog\/wp-content\/uploads\/2025\/06\/image-6.png\" alt=\"\" class=\"wp-image-8995 img-responsive\" srcset=\"https:\/\/pariswells.com\/blog\/wp-content\/uploads\/2025\/06\/image-6.png 993w, https:\/\/pariswells.com\/blog\/wp-content\/uploads\/2025\/06\/image-6-300x114.png 300w, https:\/\/pariswells.com\/blog\/wp-content\/uploads\/2025\/06\/image-6-768x291.png 768w\" sizes=\"auto, (max-width: 993px) 100vw, 993px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">AVD SSO Sign in Loop<\/h2>\n\n\n\n<p>Recently, while implementing single sign-on (SSO) for Azure Virtual Desktop using Microsoft Entra ID authentication, our team encountered an unexpected hurdle: an authentication loop. In this post, I\u2019ll share our experience and the solution we found.<\/p>\n\n\n\n<p>Further investigation revealed that the issue stemmed from Active Directory user accounts being direct or indirect members of certain protected Active Directory groups.<\/p>\n\n\n\n<p>These groups include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Account Operators<\/li>\n\n\n\n<li>Administrator<\/li>\n\n\n\n<li>Administrators<\/li>\n\n\n\n<li>Backup Operators<\/li>\n\n\n\n<li>Domain Admins<\/li>\n\n\n\n<li>Domain Controllers<\/li>\n\n\n\n<li>Enterprise Admins<\/li>\n\n\n\n<li>Krbtgt<\/li>\n\n\n\n<li>Print Operators<\/li>\n\n\n\n<li>Read-only Domain Controllers<\/li>\n\n\n\n<li>Replicator<\/li>\n\n\n\n<li>Schema Admins<\/li>\n\n\n\n<li>Server Operators<\/li>\n<\/ul>\n\n\n\n<p>These groups are protected for security reasons, and membership in them can interfere with certain authentication processes, leading to issues like the authentication loop we encountered.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If Hybrid &#8211; Passwordless security key sign-in to on-premises resources &#8211; Microsoft Entra ID | Microsoft Learn Script Enable AVD SSO Sign in Loop Recently, while implementing [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-8994","post","type-post","status-publish","format-standard","hentry","category-research"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8994","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/comments?post=8994"}],"version-history":[{"count":2,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8994\/revisions"}],"predecessor-version":[{"id":8999,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/posts\/8994\/revisions\/8999"}],"wp:attachment":[{"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/media?parent=8994"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/categories?post=8994"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pariswells.com\/blog\/wp-json\/wp\/v2\/tags?post=8994"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}