Using Azure Automation to Invite Entra ID B2B Users

In a previous article about using Entra ID B2B users, I needed to set up some user extensions. In that article, I also mentioned that many customers needed to access the environment, which meant that multiple users needed to be changed.
As this is an ongoing business and we like to automate these kinds of tasks, I looked for an automation option. Creating a script wasn’t the problem, but using it to connect to Entra ID by entering credentials every time or putting them into the script was the hard part of the search.

After some time, I stumbled upon Azure Automation, where you can create runbooks (scripts) and add them to a scheduled task.

What’s Azure automation

Azure Automation is a cloud-based service provided by Microsoft Azure that enables users to automate various manual, time-consuming, and repetitive tasks across Azure and on-premises environments. It offers tools for process automation, configuration management, and update management. Using PowerShell or Python to automate complex tasks, users can create, schedule, and manage runbooks (workflow scripts). Azure Automation also provides Desired State Configuration (DSC) to define and enforce system configurations. Additionally, it integrates with other Azure services, allowing users to orchestrate end-to-end workflows for efficient and consistent management of resources in the Azure cloud.

Creating an Azure Automation account

We must create an Azure Automation account before using Azure Automation and deploying our script. Sign in to the Azure portal and search for Azure Automation. Click on “Create” and follow the wizard, here, you will see the output of “Review + Create.”

Powershell Modules

When using Azure Automation, verifying the availability of the intended PowerShell modules is crucial. For this situation, the following modules are essential:

  • Microsoft.Graph.Authentication:
    Responsible for establishing connections (Connect-MgGraph) and severing connections (Disconnect-MgGraph) to MgGraph.
  • Microsoft.Graph.Groups:
    Required for retrieving group members (Get-MgGroupMember) and obtaining information based on a group ID (Get-MgGroup).
  • Microsoft.Graph.Identity.SignIns:
    Necessary for initiating invitations (New-MgInvitation) through identity sign-ins.
  • Microsoft.Graph.Users:
    Utilized for fetching user information (Get-MgUser) and facilitating user updates (Update-MgUser).

To add the modules to the Azure Automation account, open the account and go to “Modules,” then follow the below steps to add the module. Repeat these steps until all required modules are added:

  1. Click on “Add a Module”
  2. Select “Browse from gallery”
  3. Click on “Click here to browse from gallery”
    • Search the module and select
    • click on “Select” at the bottom of the page.
  4. Now set the “Runtime Version”. In our case, it’s 5.1.
  5. Click “Import”

To verify if all of the required Powershell modules are available or still being imported, click on “Modules” within the Azure Automation account.

After adding the appropriate Powershell modules, we need to add the required permissions.

Permissions

To get our script working with the appropriate permissions, we need the following permissions:

  • User.ReadWrite.All
  • User.Invite.All
  • Directory.Read.All

As we use a Managed Identity, we need to add the permissions using Graph Explorer. First, we must connect to the Graph Explorer: https://aka.ms/ge and sign in using an account in the correct tenant. You can’t switch tenant/directory when using Graph Explorer.

Step 1: Finding the Service Principal

We first need to find the service principal for our managed identity (automation account). To do so, we need to perform the following query, where displayname equals our Managed Identity name, in our case, “CTX-DaaS”:

HTML
GET https://graph.microsoft.com/v1.0/servicePrincipals?$search="displayName:CTX-Daas"

Write down the ID. We need this in step 4.

Step 2: Finding the Microsoft Graph Service principal ID

As the Microsoft Graph API always has the following AppId “00000003-0000-0000-c000-000000000000”, we can create a filter and only look for this AppId. We can use the following query to get the ID:

HTML
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '00000003-0000-0000-c000-000000000000'

Write down the ID. We need this in steps 3 and 4.

Step 3: Finding the Application Roles (Permissions) id

Now, we need to get the App Role IDs, as these are always the same and well-documented by Microsoft. We can search for them on the following site: https://learn.microsoft.com/en-us/graph/permissions-reference.

  • User.ReadWrite.All: “741f803b-c850-494e-b5df-cde7c675a1ca”
  • User.Invite.All: “09850681-111b-4a89-9bed-3f2cae46d706”
  • Directory.Read.All”: “7ab1d382-f21e-4acd-a863-ba3e13f7da61”

When you would like to use Graph Explorer, this is also possible, and you follow the below step: As we can’t add a filter to the query, we need to use the old-fashioned way and use CTRL+F to search for the appropriate IDs. First, we need to run the below query, replacing the {ServicePrincipalID} with the ID we found in step 2.

HTML
GET https://graph.microsoft.com/v1.0/servicePrincipals/{ServicePrincipalID}/approles

After running the query, we get a list of all the Application Roles. Luckily, the output only shows 100 results. Now, use CTRL+F to search for the permissions mentioned above and write down the IDs.

Step 4: Assigning the Application Roles to the Managed Identity

To assign the Application Roles, we need the following IDs:

  1. PrincipalId (step 1)
  2. ResourceId (step 2)
  3. AppRoleIId (step 3)

To add the Application Roles to the Managed Identity, we need to use a POST query in combination with a Content-Type application/json in the header and the following request body:

HTML
POST https://graph.microsoft.com/v1.0/servicePrincipals/{your-graph-serviceprincipal-id}/appRoleAssignedTo

Content-Type: application/json

JSON
{
"principalId": "{your-managed-identity-service-principal-id}",
"resourceId": "{your-graph-serviceprincipal-id}",
"appRoleId": "{your-app-role-id}"
}

Repeat step 4 until you assigned all the Application Roles.

Step 5: Verifying the permissions in Entra ID

Now that we have set the Application Roles (Permissions), we can verify this within the Entra ID portal. To do so, we go to Entra ID, Enterprise Applications, then set the Application Type to “Managed Identities”.

Now, we select the Managed Identity we create to perform the Azure Automation tasks and go to Permissions. Here, we will see the permissions we just assigned.

Creating the runbook

Now that we have set all the appropriate permissions, we can start creating the runbook. For this case, we will look for users who are members of a certain security group called “CTX-DaaS-B2B” and verify if the Extension (Created in the previous blog) and CreationType are equal to Invitation. We will check this for each user who’s a member of the security group and check each setting, and if needed, we will apply changes.

Below, you can see the script we are using to automate the process of invitations and setting the extensions so the user can sign in to the Citrix Environment, which is mentioned in the previous blog.

PowerShell
Connect-MgGraph -Identity -NoWelcome
$Group = "CTX-DaaS-B2B"
$group = Get-MgGroup -Filter "DisplayName eq '$Group'"
$members = Get-MgGroupMember -GroupId $group.Id
$ExtensionUPN = "extension_c8ef4d2cb6694d76974c5a05be13affa_guestShadowUPN"
$ExtensionSID = "extension_c8ef4d2cb6694d76974c5a05be13affa_guestUserOnPremSID"
$CreationType = "Invitation" 
$RedirectURL = "https://newyard.Cloud.com"
$UserMessage = "You have been invited to the Citrix New Yard environment. `
                Redeem this invitation to get started and you’ll be redirected to the Citrix New Yard"

foreach ($member in $members) {
    $user = Get-MgUser -UserId $member.Id -Property ID, DisplayName, Mail, UserPrincipalName, UserType, CreationType, ExternalUserState, OnPremisesSecurityIdentifier

    if ($user.UserType -ne "Guest"){
        Write-output "User $($user.displayname) is Member, changing to Guest!"
        Update-MgUser -UserId $user.Id -UserType "Guest"
    } 

    $UserExtensions = (Get-mguser -UserID $user.Id -Property $ExtensionUPN).AdditionalProperties
    if ($UserExtensions.keys -NotContains $ExtensionUPN) {
        Write-Output "User $($user.displayname) has nu Extension, updating UPN and SID Extensions"
        Update-mguser -UserId $user.id -AdditionalProperties @{$ExtensionUPN = $user.UserPrincipalName}
        Update-mguser -UserId $user.id -AdditionalProperties @{$ExtensionSID = $user.OnPremisesSecurityIdentifier}
    }

    Write-output "$($user.displayname) has CreationType $($user.creationType)"
    if ($user.CreationType -ne $CreationType) {
        Write-output "User $($user.displayname) isn't invited, inviting user!"
        New-MgInvitation -InvitedUser $user -InvitedUserEmailAddress $user.Mail -InviteRedirectUrl $RedirectURL -SendInvitationMessage:$true -InvitedUserMessageInfo:@{CustomizedMessageBody=$UserMessage}
    }

}

disconnect-MgGraph

We published the runbook to production once we tested it (using the test pane in the PowerShell runbook) and verified everything was correct. This can be done using the Publish button inside the PowerShell runbook.

Create a Schedule

Now that we have the runbook published, we can start with scheduling the runbook. As we start migrating users to the new environment, we have set the schedule to run every hour. When the migration is done, we can change the schedule to once a day, as we normally don’t make that many changes or create multiple users a day.

Let’s start with creating the schedule. We go to the Schedules from the Automation account and add a schedule.

Once the schedule is created, we need to assign the runbook to the schedule. Therefore, we go to the runbook, click on the “Link to schedule” button, click on the link to a schedule in your runbook, select the correct schedule, and click OK. To verify if the runbook is successfully linked to the schedule, click “Schedules” within the runbook and verify that the correct schedule is linked.

Verify the Azure Automation Task

Now that we successfully have followed all the steps:

  1. Creating Azure Automation Account
  2. Add PowerShell Modules
  3. Set permissions
  4. Create the runbook
  5. Create and linked a Schedule

We can now see in the overview of the Azure Automation account if the runs are completed successfully. As you can see in the screenshot below, the job has run once and was successful. The users will receive an invite every hour unless they already received one.

Conclusion

We can conclude that Azure Automation is a powerful tool for automating repetitive tasks, such as sending invites to new guests. I couldn’t achieve this without the guidance of GoToGuy’s blog, where the Graph Explorer is explained to configure all the necessary permissions. I will undoubtedly utilize Azure Automation more frequently now that I know its capabilities.

Using Entra ID B2B with Citrix Cloud

In a recent project, I designed an environment for a client who offers a published application to a substantial customer base. They expressed their desire to incorporate the use of Entra ID. I recommended the adoption of Entra ID B2B accounts for this purpose. Entra ID B2B accounts enable access to external partners for applications and resources within the organizational environment.

About Entra ID B2B

Entra ID B2B, short for Business-to-Business, is a Microsoft cloud service that facilitates secure collaboration among organizations. It permits organizations to extend invitations to external entities, such as partners, suppliers, or customers, granting them access to resources hosted within their Entra ID environment (formerly Azure AD). This approach eliminates the necessity for external users to establish new accounts, streamlining the collaborative process. Entra ID B2B bolsters security by implementing conditional access policies and multi-factor authentication for guest users, ensuring that only authorized individuals can access shared resources. Read here for more about Entra ID B2B.

Issues during implementation

While implementing this solution within the Citrix Cloud using the default Entra ID connect option, I encountered issues during the sign-in process with B2B accounts. After asking around, Julian Jakob directed me to an article highlighting an issue related to the default Entra ID connection in conjunction with Citrix FAS (Federated Authentication Service).

Upon reaching out to Citrix for support, it was revealed that there had been a modification to their SAML 2.0 configuration. This change now offers the option to utilize UPN (User Principal Name) or SID (Security Identifier) for authentication, whereas both were previously required. Please refer to the screenshot below for visual reference to this configuration option.

Based on the information provided, we successfully implemented Entra ID B2B accounts. However, this implementation necessitated certain modifications to our On-Premises Active Directory. Specifically, to grant B2B access to individual users, updating their User Principal Name (UPN) to align with their respective email addresses was imperative. While this approach was effective, it may not be ideal, especially considering the potential complexities of managing multiple DNS suffixes within the environment. As such, I dedicated some time to exploring alternative solutions that would obviate the need for such changes within the Active Directory.

Earlier in the project, I had come across an article from Citrix that introduced the concept of a ClaimsMappingPolicy. This approach leverages Azure AD Preview modules and a SAML Tech preview. It is worth noting that I refrained from testing this solution previously due to its preview status. Please refer to the provided article link for detailed information on this alternative approach.

Configuring the Entra ID as IdP

To initiate the configuration of Entra ID as the Identity Provider (IdP), the following key steps must be taken:

  1. Enterprise Application Setup: The first step involves the configuration of the Enterprise Application within the designated environment.
  2. Microsoft Graph Integration: Integrating with Microsoft Graph services is essential to facilitate the required functionalities.
  3. Claims Policy Mapping: Mapping of claims policies is necessary to define and manage the assertion of identity attributes.
  4. SAML 2.0 Connection in Citrix Cloud: Establishing a SAML 2.0 connection within the Citrix Cloud infrastructure enables secure authentication and authorization processes.

Enterprise Application Setup

To initiate the setup and configuration of a custom Entra ID Enterprise SAML application, please follow the provided steps. I have followed the guide created by Citrix, which can be accessed.

  1. Azure Portal Sign-In:
    • Sign in to the Azure portal.
  2. Navigate to Entra ID:
    • In the portal menu, select “Entra ID.
  3. Access Enterprise Applications:
    • From the left pane, under “Manage,” select “Enterprise Applications.”
  4. Create New Application:
    • In the working pane’s command bar, select “New Application.”
  5. Choose Custom Application:
    • Select “Create your own application” from the command bar. Avoid using the Citrix Cloud SAML SSO enterprise application template, as it restricts modifications to the list of claims and SAML attributes.
  6. Application Name and Integration:
    • Enter a name for the application.
    • Select “Integrate any other application you don’t find in the gallery (Non-gallery).”
    • Click “Create.” This action will lead you to the application overview page.
  7. Configure Single Sign-On (SSO):
    • From the left pane, select “Single sign-on.”
    • In the working pane, choose “SAML.”
  8. Basic SAML Configuration:
  9. User and Group Assignment:
    • From the left pane, select “Users and Groups.”
      • In the top bar, select “Add user/group.”
      • Under “Users,” select “None Selected.”
      • Choose the user you wish to test with, and click “Select.”
    • Click “Assign.” This action allows the designated user to be used for testing purposes.

These steps will enable creating and configuring a custom Entra ID Enterprise SAML application for your specific requirements.

Microsoft Graph Integration

The Citrix article I came across relies on Azure AD PowerShell modules, which have been planned for deprecation. To conduct effective testing, I needed to use something that was officially supported to ensure reliable functionality. Consequently, I embarked on a transition journey, migrating all the Azure AD commands to Microsoft Graph. Fortunately, I stumbled upon an informative article outlining the Microsoft Graph commands, which are direct replacements for the deprecated Azure AD Commands. This migration strategy aligns with industry best practices and guarantees continued operational effectiveness by leveraging the Microsoft Graph framework while circumventing the challenges posed by deprecated Azure AD PowerShell modules.

Modules

After migrating all Azure AD commands to Microsoft Graph, I comprehensively understood the requisite modules. Below, you’ll find an enumeration of all the essential commands, complete with their corresponding modules and the associated permissions indispensable for their execution:

CommandModulePermission
get-mguserMicrosoft.Graph.UsersUser.Read.All
Update-mguserMicrosoft.Graph.UsersUser.ReadWrite.All
Get-MgApplicationMicrosoft.Graph.Identity.SignInsApplication.Read.All
Get-MgApplicationExtensionPropertyMicrosoft.Graph.ApplicationsApplication.Read.All
Get-MgServicePrincipaMicrosoft.Graph.ApplicationsApplication.Read.All
New-MgApplicationExtensionPropertyMicrosoft.Graph.ApplicationsApplication.ReadWrite.All
New-MgServicePrincipalClaimMappingPolicyByRefMicrosoft.Graph.ApplicationsApplication.ReadWrite.All
new-MgPolicyClaimMappingPolicyMicrosoft.Graph.Identity.SignInsPolicy.ReadWrite.ApplicationConfiguration
Get-MgPolicyClaimMappingPolicyMicrosoft.Graph.Identity.SignInsPolicy.Read.All

To initiate work with Microsoft Graph, the first step involved the installation of the necessary modules. To achieve this, an Elevated PowerShell session needs to be opened, and the following commands need to be executed:

PowerShell
Install-Module Microsoft.Graph.Users
Install-Module Microsoft.Graph.Identity.SignIns
Install-Module Microsoft.Graph.Applications

You can append- force to each command if you prefer to streamline the installation process without individual prompts.

Permissions

When establishing a connection to Microsoft Graph, it is essential to define the scope, which dictates the permissions granted for that session. As illustrated in the table above, a comprehensive set of permissions is required, which can be summarized as follows:

  • Policy.Read.All
  • Application.ReadWrite.All
  • Policy.ReadWrite.ApplicationConfiguration
  • User.ReadWrite.All


With these permissions delineated, we can proceed to connect to Microsoft Graph. To initiate this process, follow these steps:

  1. Open an Elevated PowerShell window.
  2. Connect to Microsoft Graph using the following command:
PowerShell
Connect-MgGraph -Scopes Policy.Read.All, Application.ReadWrite.All,Policy.ReadWrite.ApplicationConfiguration, User.ReadWrite.All
  1. A popup window will appear upon executing the command, prompting you to authenticate and grant Microsoft Graph the specified permissions, as detailed in the command above.
Microsoft Graph Permissions

This authentication step ensures the session has the necessary privileges to interact with Microsoft Graph effectively.

Configuring Claims Policy Mapping

Now that we are ready to configure the necessary settings for the Claims Mapping Policy, we continue from the PowerShell window we previously used to connect to Microsoft Graph.

Claims Mapping Policies are used in identity management to map and transform attributes (claims) exchanged between identity providers and service providers during authentication. They enable customization of how user attributes are processed, ensuring proper handling and alignment of user data between systems for secure access control.

Preparing the Application

To utilize the Claims Mapping Policy for Entra ID B2B users, where the User Principal Name (UPN) and Security Identifier (SID) do not align with the On-Premises Active Directory, we need to edit Entra ID with specific Extensions and bind them to the application. In this context, we will require the name of the previously created application, which, for this demonstration, is referred to as “CTX-DaaS-B2B.”

  1. Retrieve the application using the following command:
PowerShell
$MGApp = Get-MgApplication -Filter "displayname eq 'CTX-DaaS-B2B' "
  1. Next, we need to add the Extensions, both of which are set to target User objects:
    • guestUserOnPremSID
    • guestShadowUPN
PowerShell
New-MgApplicationExtensionProperty -ApplicationId $MGApp.id -Name "guestUserOnPremSID" -TargetObjects User
PowerShell
New-MgApplicationExtensionProperty -ApplicationId $MGApp.id -Name "guestShadowUPN" -TargetObjects User
  1. Note down the names of these Extensions by executing the following command:
PowerShell
Get-MgApplicationExtensionProperty -ApplicationId $mgapp.Id | fl *
Get the extension IDs for the use with the JSON code
  1. Add the Claims Mapping Policy by providing the policy in JSON format. Before doing so, replace the ExtensionIDs with the Extension names obtained in step 3. Take note of the following names:
    • Variable = $params
    • displayName = “CitrixCustomClaims
JSON
$params = @{
	definition = @(
		'{"ClaimsMappingPolicy":
            {"Version":1,
            "IncludeBasicClaimSet":"true",
            "ClaimsSchema": [
                {
                "Source":"user",
                "ID":"displayname",
                "name": "displayName",
                "SamlClaimType":"displayName"
                },
                {
                "Source":"user",
                "ID":"givenname",
                "name": "givenName",
                "SamlClaimType":"givenName"
                },
                {
                "Source":"user",
                "ID":"userprincipalname",
                "name": "cip_upn",
                "SamlClaimType":"cip_upn",
                "AppliesToUserType": "members"
                },
                {
                "Source":"user",
                "ExtensionID":"extension_c8ef4d2cb6694d76974c5a05be13affa_guestShadowUPN",
                "name": "cip_upn",
                "SamlClaimType": "cip_upn",
                "AppliesToUserType": "allGuests"
                },
                {
                "Source":"user",
                "ID":"objectid",
                "name": "cip_oid",
                "SamlClaimType":"cip_oid"
                },
                {
                "Source":"user",
                "ID":"userprincipalname",
                "name": "cip_email",
                "SamlClaimType":"cip_email"
                },
                {
                "Source":"user",
                "ID":"onPremiseSecurityIdentifier",
                "name": "cip_sid",
                "SamlClaimType":"cip_sid",
                "AppliesToUserType": "members"
                },
                {
                "Source":"user",
                "ExtensionID":"extension_c8ef4d2cb6694d76974c5a05be13affa_guestUserOnPremSID",
                "name": "cip_sid",
                "SamlClaimType": "cip_sid",
                "AppliesToUserType": "allGuests"
                }
            ]
            }
        }'
	)
	displayName = "CitrixCustomClaims"
}

This process ensures the application has the necessary Extensions and Claims Mapping Policy to handle Entra ID B2B user scenarios where UPNs and SIDs do not correspond to the On-Premises Active Directory.

  1. Obtain the Service Principal
    Proceed by retrieving the Service Principal with the following command, where Displayname is the name of the app, in this case CTX-DaaS-B2B:
PowerShell
$ServicePrincipal = Get-MgServicePrincipal -Filter "Displayname eq 'CTX-DaaS-B2B' "
  1. Create the Claims Mapping Policy
    Now, create the Claims Mapping Policy, utilizing the $Params variable, which represents the variable name assigned to the Policy when pasting the JSON code. Execute the following command to establish the policy:
PowerShell
New-MgPolicyClaimMappingPolicy -BodyParameter $params
  1. Map the Claims Policy to the Service Principal
    A Graph body parameter must be created using the OData protocol to map the Claims policy to the Service Principal. Follow these steps:
    • Execute the command below, replacing “CitrixCustomClaims” with the name you assigned to your Claims Policy (found in the JSON command. In this case, it’s “CitrixCustomClaims“):
PowerShell
Get-MgPolicyClaimMappingPolicy -Filter "Displayname eq 'CitrixCustomClaims'"| select DisplayName,Description, Id
  • Retrieve the ID and modify the following URL, replacing the ID at the end with the one obtained in the previous step:
    • https://graph.microsoft.com/beta/policies/claimsMappingPolicies/<ID from previous step>
  • Create a variable in PowerShell with the modified URL, like so:
PowerShell
$odata = @{
	"@odata.id" = "https://graph.microsoft.com/beta/policies/claimsMappingPolicies/37720c4a-bb51-43ee-aae0-afa5eece4805"
}
  • Finally, link the Odata to the Service Principal using the following command:
PowerShell
New-MgServicePrincipalClaimMappingPolicyByRef -ServicePrincipalId $servicePrincipal.id -BodyParameter $odata

These steps facilitate the establishment of the Claims Mapping Policy and its connection to the Service Principal, ensuring proper configuration for your desired scenario.

Configuring the User

To configure the test user, follow the steps outlined below:

  1. Retrieve Current UPN and SID:
    Execute the following command to obtain the current User Principal Name (UPN) and Security Identifier (SID). Replace “[email protected]” with the Entra ID UPN you are configuring (for this test, it’s “[email protected]“):
User Entra ID overview to get the User Principal Name (UPN).
PowerShell
$user = get-mguser -UserId Sjoerd@lab.newyard.online -Property Id,DisplayName,UserPrincipalName, OnPremisesSecurityIdentifier
  1. Update User Extensions:
    Now, proceed to update the user’s Extension with the appropriate values. Replace the Extension name with the one you acquired earlier and execute the following commands:
  • Update User’s On-Premises Security Identifier (SID):
PowerShell
Update-mguser -UserId $user.id -AdditionalProperties @{'extension_dcf32e98a0de4910ba50bd486eef0335_guestUserOnPremSID' = $user.OnPremisesSecurityIdentifier}
  • Update User’s Shadow User Principal Name (UPN):
PowerShell
Update-mguser -UserId $user.id -AdditionalProperties @{'extension_dcf32e98a0de4910ba50bd486eef0335_guestShadowUPN' = $user.UserPrincipalName}

These steps ensure the test user’s proper configuration, including updating relevant Extensions with the required values, effectively facilitating the Entra ID B2B user scenario.

Configuring the SAML 2.0 Connection in Citrix Cloud

With Entra ID prepared, let’s proceed to configure Citrix Cloud. To initiate this process, follow these steps:

  1. Sign in to the Citrix Cloud administration console and navigate to the Single Sign-On settings of the previously created Enterprise App.
Create SAML 2.0 connection
  1. Access the Citrix Cloud Console, and under “Identity and Access Management,” click on “Connect” within the SAML 2.0 option.
  2. Create a Custom Administrator URL.
Citrix Cloud Custom Administrator URL
  1. In the SAML configuration, populate the following settings using the Single Sign-On URLs obtained from Entra ID within the Enterprise App:
    • Identity Provider Entity ID = Microsoft Entra ID Identifier.
    • SSO Service URL = Login URL.
    • Logout URL (optional) = Logout URL.
    • Refer to the screenshot below for reference. On the “Set up single sign-on with SAML” page, in the SAML Signing Certificate section, locate “Certificate (PEM)” and select “Download” to save the certificate to your computer. Upload this certificate to the X.509 section of the SAML configuration within Citrix Cloud.
    • Proceed by clicking “Test and Finish.”
  1. Now that SAML 2.0 has been configured, it is necessary to update the Workspace Configuration to employ the newly created authentication method:
    • Within the Citrix Cloud Administration Console, navigate to “Workspace Configuration.”
    • Proceed to the “Authentication” tab.
    • Select “SAML 2.0” and confirm your selection by clicking “OK” in the pop-up dialog.
      Allow a brief moment (approximately 1 minute) before proceeding, as immediate functionality may not always be observed.
Citrix Cloud SAML 2.0 config to Entra ID.

By following these steps, you configure the SAML 2.0 connection in Citrix Cloud, enabling seamless integration with Entra ID for authentication and access management.

Testing

Now that we have configured all required settings, we can start with the test.

  1. Visit the Workspace URL and sign in using the test user employed previously:
    1. After launching the desktop, open a command prompt or PowerShell and run the “whoami” command. You will observe that the sign-in is performed using the local Active Directory (AD) account.
Show the ouput of Whoami to see which user is signed in.

Conclusion

After an extensive search and engagement with Citrix support, a functional Entra ID B2B scenario has been successfully resolved. While the initial solution relied on outdated information, it has been updated with current PowerShell commands, resulting in a functional setup. Consequently, organizations can seamlessly integrate Entra ID B2B and regular Entra ID users into their Citrix Cloud deployments, enhancing their access and identity management capabilities.

Please feel free to comment or ask questions regarding this solution. I am working on another article to guide you on using Azure Automation to update all B2B users with the new extension. So, stay tuned.
UPDATE: As mentioned earlier, I would create a blog post about using Azure Automation to automate this. You can read it here.

Setting up a double-hop with Azure AD as IdP

As more and more companies are moving from on-premises environments to a cloud and would like to make the best of their investments, we see a great demand in moving to Azure Active Directory (AAD). AAD gives you some benefits as you could utilize Azure Multi-Factor Authentication (MFA) with Conditional Access. With Conditional Access, it’s possible to give users the option to only sign on with username and password from trusted locations (think about HQ and branch offices).

This article explains how to set up a double-hop scenario with Citrix Virtual Apps and Desktop (CVAD) services (Citrix Cloud), where your identity provider (IdP) is Azure AD.

What’s a Double-Hop, and why use it?

A double-hop scenario is when you start a published app from other resources within your published desktop. An example:

You have an app that doesn’t support the OS you would like all users to have. In most situations, this will be Windows Server 2019 for multi-session purposes. The one app that you would like users to open is a legacy app and only supports Windows 2012 R2 as the latest supported OS. As your organization states that you only work with supported configurations, you can’t install this app on Windows 2019. To ensure the user can access the app, you create a dedicated delivery group based on Windows 2012R2 VM’s and use this delivery group to give users access to the app. Those users start this app within their published desktop as a seamless app and don’t notice if they are local or remote.

The above scenario is called a double-hop. As a company, you could have many reasons to use this scenario. Think about:

  • The app is for archive only;
  • The app doesn’t support the same OS as the desktop (mentioned in the example);
  • The app has conflicts with other apps;
  • The app can only be used by some users, and licenses don’t allow us to install it on the same image as the desktop.

Challenges when using double-hop with Azure AD as IdP

When you sign on to Citrix Cloud with your Azure and open your published desktop, your on-premises environment uses Federated Authentication Services (FAS) to sign on to the VDA. Look at the below diagram to see how FAS is working (thanks to Daniel Feller (https://virtualfeller.com/)).

When you would like to have SSO from your published desktop to Citrix cloud, it’s only possible to use Azure Active Directory Seamless Single Sign-On or using an on-premises StoreFront environment that supports FAS authentication. Azure seamless SSO requires the use of Pass-through Authentication (PTA) or password hash synchronization. The customer where I’m currently implementing CVAD services is transitioning to Azure AD, and they don’t have seamless SSO enabled. That’s the reason we need to use another option to create the double-hop scenario. The only other option, as mentioned, is to install an on-premises StoreFront that supports FAS authentication.

Configuring StoreFront and FAS

In this example, I only have one StoreFront server and one FAS server to keep this guide simple. Below you could see how the authentication is working using a double-hop scenario.

StoreFront & FAS

After you installed and configured StoreFront, we configure it to allow FAS as an authentication method.

  1. First, enable FAS support on your Store, change “/Citrix/Store” to your store name:

    Get-Module “Citrix.StoreFront.*” -ListAvailable | Import-Module
    $StoreVirtualPath = “/Citrix/Store”
    $store = Get-STFStoreService -VirtualPath $StoreVirtualPath
    $auth = Get-STFAuthenticationService -StoreService $store
    Set-STFClaimsFactoryNames -AuthenticationService $auth -ClaimsFactoryName “FASClaimsFactory”
    Set-STFStoreLaunchOptions -StoreService $store -VdaLogonDataProvider “FASLogonDataProvider”

  2. Secondly configuring the Delivery Controllers

    Click Manage Delivery Controllers
    Usually, you would add your On-Premises Delivery Controller here, but now you need to add the Cloud Connectors here. You need to use port 80 (HTTP) and can’t use 443 (HTTPS) as a transport type.

  3. Set Authentication Methods

    Click Manage Authentication Methods
    Enable Domain Pass-Through

  4. Set the GPO Federated Authentication Service

    As FAS is already working, the ADMX files are probably in the PolicyDefinitions central Store, so I skip the step to copy these ADMX files. For the StoreFront server, configure a GPO, so it knows which servers are FAS servers.Open the GPO and navigate to Computer Configuration/Policies/Administrative Templates/Citrix Components/Authentication
    Edit the Federated Authentication Service
    configure the DNS addresses of your FAS servers
    Perform a gpupdate /force to make sure the GPO is applied.

  5. As FAS is configured to access your VDA, we only need to change the rule you use for FAS.

  6. Open the FAS Administration Console and go to the tab rules

  7. I only have 1 rule named “Default” click on the Pencil to edit the rule.

  8. Go to Access Control
    Click Manage StoreFront access permissions

  9. Here you add the Computer account of your StoreFront server.
    Make sure you remove the default “Domain Computers” as that one denies access.
    Click OK

  10. Now click Apply, and your FAS configuration is ready for testing.

Testing

After you configured all the settings as described above, It’s time to test the configuration.

When you sign in to your published desktop, your Citrix workspace app (when configured correctly) is signing in to the StoreFront site, and you could start an app that’s published from your start menu. Please try, and let me know if this is working.

Using Zoom with SBC\VDI

Working from home (WFH) requires having a lot of video calls. I noticed a great demand from users that need to attend or organize a video call. The downside is that there are multiple vendors and that a company can’t force everybody only to use one solution. Users receive invitations from multiple organizations, and they all need their own client applications. When using an SBC\VDI based remote workplace, you need to manage all the clients and make sure the video call works great. Luckily, the vendors are working together and making sure that you could have a great experience even when working in a remote session. One of those vendors is Zoom, they have created a client that is created for a VDI deployment. Zoom currently supports Citrix, VMware, and WVD.

How it works

When making a video call the video rendering requires CPU and Memory, when using a multi-session OS this could cause other users to have a degraded performance. The users that are making the video call could notice latency during the call because every moving image has to be rendered and send to the client. To get a better experience Zoom created software that could offload the rendering to the local clients. How is that offloading working?

In most cases, the media plugin will offload the video encoding and decoding and communicates directly to the Zoom cloud, bypassing the VDI infrastructure. All other information is sent over the VDI channel. There are three options:

The three options
The three options
  1. Everything is within the VDI and all the resources are used within the VDI, communication is from the VDi to the Zoom Cloud
    This won’t work with Citrix as described here: https://support.citrix.com/article/CTX270931
  2. Only the Video encoding and decoding are rendered on the client and all communication is going from the Zoom media plugin through the VDI channel to the VDI and from the VDI to the Zoom Cloud.
  3. Most of the communication is going from the Zoom Media Plugin to the Zoom cloud and only the Authentication and window location is going from the Zoom Media Plugin through the VDI channel to the VDI and then connects to the Zoom Cloud.

Installing

Time needed: 15 minutes

Installing the Zoom VDI Installer in your base image and your client.

  1. Download the latest Zoom VDI Installer client and the corresponding Zoom Media Plugin

    https://support.zoom.us/hc/en-us/articles/360041602711
    You need to download Zoom Media Plugin based on the environment you have: Citrix, VMware, or WVD.

  2. Install the Zoom VDI Installer within your VDI

    This is basically a Next,Next,Finish installation.

  3. Install the Zoom Media Plugin on the Client

    To make sure you don’t receive an error when using Zoom, you need to run the installer as an Administrator.
    Close all Citrix, VMware, or WVD clients before starting the installation.
    Then follow the wizard.

Verifying Zoom installation

After installing the Zoom VDI and Zoom Media Plugin, the testing starts. I made a Zoom call with myself. As I’m not so photogenic, I will spare you these images. To give you a good view of how it’s working. First, I made a Zoom call (without video in my session as this isn’t working for Citrix https://support.citrix.com/article/CTX270931), you can see there is 9.7% CPU usage. This isn’t that much, but when there are more people on a VM and you share the CPU, this could be affecting the user experience of other users.

Zoom call without the plugin enabled

Below you can see that that the Zoom client shows it’s not connected to any plugin and is working as a regular Zoom client.

Zoom session without a working plugin
Zoom without VDI plugin connected

After the first test, I installed the Zoom Media Plugin (for Citrix) and made another Zoom call. This time I was able to use my local Webcam (Laptop integrated) and as you could see below the CPU usage is 0% within the Citrix Session (First Image) and uses 8.4% CPU on the local workstation (Second image).

Zoom Session CPU usage within the Citrix Session
Zoom Session CPU usage within the Citrix Session
Zoom Session CPU usage local workstation
Zoom Session CPU usage local workstation

When looking at the statistics within the Zoom client you now see that the Zoom VDI plugin is Connected. The rendering is now completely offloaded to the Clients workstation and this saves you CPU and Memory on the VM (SBCB\VDI) and gives the user a good user experience during the call.

Zoom Media Plugin Connected
Zoom Media Plugin Connected

Features

When using the Zoom VDI client and the Zoom Media Plugin, you don’t have all the features that are available when using the Full Desktop version of Zoom. As this changes with every new release, I advise you to look at the comparison matrix that Zoom created and hopefully kees updated: https://support.zoom.us/hc/en-us/articles/360031441671-VDI-client-features-comparison

Tips and Tricks

I will update the Tips and Tricks section if new information is available or I needed to solve an issue that could help you as reader.

Registry Settings

Zoom has a list of Registry Settings that can help you troubleshoot or control the client. Here is a list of all available registry keys: https://support.zoom.us/hc/en-us/articles/360032343371

Error Zoom VDI

As mentioned in step 3, if you don’t install the Zoom Plugin on the local device, you could see an error. I only tested with the Citrix Media Plugin and can’t verify if the same happens with VMware or WVD. The error I received is:

Zoom.exe is using NTDLL.dll from an unknown publisher. Are you sure you want to run this software?

Error when Zoom Media Plugin is wrongly installed.
Error when Zoom Media Plugin is wrongly installed.

You could click on Yes as often as you like but that’s not going to work.

Looking back at 2020 and Work From Home

The end of 2020 is nearing so it’s the time to look back at this year, I can say it was a hectic year for me as a Consultant.

From PoC to production

We just started a Proof of Concept (PoC) with a new customer in February to show that working with a 3D modeling program on a VDI was an option for their clients. Then everybody started talking about some kind of virus in China and started reporting lockdowns. Just days before the Dutch first lockdown (23rd of March), the customer started asking to change the PoC to production and expanding from 10 people to 150 remote workers. We managed to achieve that. You can read more about this in my previous article: From PoC to Production during Covid-19

During the customer’s request to expand their PoC, all other customers requested more Work From Home (WFH) possibilities because of Covid-19. They requested the implementation of Microsoft Teams ASAP, my colleagues and I had to achieve this during the lockdown. Simply enabling Microsoft Teams or creating all the accounts in Microsoft 365 isn’t the hardest part. That came when they all wanted it on their sometimes outdated Citrix environments. 

Microsoft Teams

As mentioned before, many customers asked for Microsoft Teams. Luckily Citrix made its first Teams optimized for Virtual Apps and Desktops (VAD) available in June 2019 (VAD 1906). It’s a pity that most of the projects started before the availability of VAD 1906 and that they were all working with VAD 7.15 (LTSR). So before we could eventually start implementing Microsoft Teams optimized for Citrix VAD we had to upgrade all of these customers to VAD 1906 or newer. We prefer the use of the LTSR version, so we upgraded everyone to VAD 1912 LTSR. They also had to upgrade all of their clients with the latest Citrix Workspace App. 

The lessons we learned here is that we as an engineer didn’t like this kind of implementations. my colleagues and I all needed to find the best settings. Due to the stress and lack of time we all made different choices, we all reinvented the wheel. This leads to different experiences with Teams at some of our customers.

After the upgrade of the Citrix VAD and making all the necessary changes to get Teams optimized, we ran into some issues. The Clients!!! We faced a couple of issues:

  1. They didn’t have any Device Management solution in-place to upgrade all the Citrix Receivers to the latest workspace app;
  2. They have older (Thin) Clients that aren’t compatible with the latest Workspace App;

The lack of a compatible Workspace App resulted in the excessive CPU and Memory usage, which resulted in other users complaining about a slow User Experience. 

By now we have a default set of steps and requirements to get Teams working correctly, but it keeps hard to manage the CPU and Memory usage. 

Working from home as a consultant

Not only our customers needed to work from home, but us as a consultant also. I’m used to working at the customer’s location because that makes me part of the project. When on-premises you hear a lot of extra information that could help your project make it a success. Working from home removes all those benefits and makes it sometimes hard to understand the issue a customer is facing. When they try to explain an issue we as a consultant don’t understand it and vice versa.

Besides not understanding each other, the most thing I missed (and I think everybody is) is the personal contact with people. Microsoft Teams is making WFH possible and can give you some kind of personal contact, it’s not the same.

Looking at 2021

Now December is ending but the infection numbers are still going up, so we keep working from home. That means we keep helping our customers implementing and upgrading their environments to meet their WFH requirements. I just started a new project, where I can implement all the lessons learned.

For 2021 I set myself some goals, I’m not going to tell them because then I need to tell you next December I’ve not made my goals. 😊 But some nice projects coming up, personally and business-wise. We get vaccinated, meet others again, have a beer in the pub, enjoy a festival, and finally have a normal conference.

I hope you all are well and despite the current situation enjoy Christmas and have a happy new year.

See you in 2021.

Creating a Memory dump Citrix MCS/PVS and Hypervisor

When you need to debug some issues with your current deployment, you’re probably asked to create a memory dump. When you’re deployment is a traditional VM than that’s no issue, if you are using PVS than the memory dump is probably disabled because the PVS optimization disabled it or you used the Citrix optimizer which also disables the creation of memory dumps.

Just enabling the creation of a memory dump isn’t working, you need to specify a location for the memory dump to be created. Citrix created different articles on how to create a memory dump for PVS and MCS, CTX127871 and CTX261722

PVS

When using PVS you probably enabled “Cache on RAM with overflow on disk” and added a dedicated Write Cache Disk (WCD) for the vdiskdif.vhdx file. Because all the best practices tell you to change the location of the Page file and the Log files to a dedicated disk you probably use the WCD as alternative location.

When enabling Memory Dump creation you don’t want the space on the WCD got full because the DedicatedDumpFile is created on that disk. I recommend using a separate disk just for creating the Memory Dump, I make the size of that disk 1GB bigger than the assigned memory for that VM. Because I have situations were we have 40-60 VM’s, I don’t assign a Memory Dump disk to every device, because I do not have the storage available.

After the creation of the dedicated disk for the memory dump and you reboot your VM, you will notice that the vdiskdif.vhdx is located on the new disk. To make sure this doesn’t happen anymore you need to create a file with the following name and place it in the root of the new disk: {9E9023A4-7674-41be-8D71-B6C9158313EF}.VDESK.VOL.GUID See CTX218221.

Enabling the Memory Dump

I always set the location for the Memory Dump to the E: drive, for both PVS and MCS I create a dedicated disk as mentioned earlier.

To enable the creation of the memory dump just add the following registry keys as explained within the previous mentioned articles:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CrashControl

  • AutoReboot
  • CrashDumpEnabled
  • LogEvent
  • Overwrite
  • DumpFileSize
  • IgnorePagefileSize
  • DedicatedDumpFile
  • AlwaysKeepMemoryDump

I attached a TXT file that I always use to set it correctly, it’s at the end of this article. When you downloaded it, you need to rename it to .reg and you can merge the settings.

Creating a memory Dump

Because we mostly work with the Citrix Hypervisor (previous called XenServer), I have written down the steps to create a Memory Dump on this hypervisor. When I have to create a Memory Dump on VMware or Hyper-V I will write this down here to.

Ok, we now have a situation where you need to create a memory dump of the VM. After Identifying the VM, write down or copy the UUID.

Getting the UUID of the VM

Then run the following command within the console of the Hypervisor, where VM_UUID is the UUID you just written down or copied:

list_domains | grep -i <VM_UUID>

Then you get a Number which you have to write down as you can see in the following screenshot.

Receiving the Domain ID (## 239)

Then you run the following command, where Domain ID is the number you got from the previous step.

xen-hvmcrash [domain ID]

For this example that would be: xen-hvmcrash 239

Making the VM crash 🙂

Now the machine will crash and you can look at a BSOD within the console and after the machine rebooted you can find the Memory Dump on the E: drive. You can copy the memory dump and upload it or analyze it yourself.

When you have questions regarding these steps, please let me know.

Below the reg file, it’s a txt file within a zip file. You have to rename the txt to reg.

Remote DPI scaling for Windows

With the release of Windows 2016 Microsoft changed the way users could change their DPI size in a Remote Desktop Session, it inherited the setting from the end point. This would be the Laptop/Desktop or Thin Client the users are using to connect to the Remote desktop. When users needed to had a larger text, they need to change it on the local device, as this works for dedicated workplaces, it’s not working for the user that changes to different workplaces throughout the week.

As you can image, they connect and see they have the wrong size, have to sign out, change it on the local device and have to connect again to their remote session. That’s not very user friendly.

Citrix Workspace App

Citrix added an option to their Workspace App to change the DPI size in the current setting, after you change it you need to resize your window and everything is bigger or smaller depending on your choice. But also this is a setting a users need to change every time they connect from another device.

Setting the DPI size within a session
Setting the DPI size within a session using the Workspace App

The downside of this is that you also need the have the correct setting within your Workspace App locally. within the Advanced PreferencesHigh DPIScale the session for high resolution, you need to set it to YES. This is also a device settings, which means that you have to set it on every device you connect to or need to have a GPO that sets this.

Text Size

To change the DPI size to a different size you just need to change a registry key, which is a per user setting. Below are the registry values:

HKEY_CURRENT_USER\Control Panel\Desktop\
DWORD: LogPixels (create if it doesn’t exist)
Value: see below in Decimal:
96   –   Smaller 100%
120   –  Medium 125%
144  –   Larger 150%
192   –   Extra Large 200%

This is nice for Admins or users that are familiar with the registry, and know how to change a value without breaking their user settings.

Text Size App

Because most users don’t know how to change the registry or the Admins don’t want users to open Regedit. I created an App (basically a script which runs like an App) that lets users change their DPI size and sets the correct registry value. After setting this, the users gets asked to Sign out and sign-in again to get the changes to work.

I created the app in Dutch and English (based on the system local), but if requested I can add different languages to the App.

Changing the DPI size using the Text Size app

You can download the app for free and use it everywhere you want. Please let me know if you have any questions or just let me know that you are using the app.

Reset Windows Rearm count

When working with Machine Creation Services we discovered that the Clients all have the same CMID. A different CMID is needed to activate using KMS services. We searched for a solution and everyone mentioned rearming the machine, but then we received the message that we can’t rearm. We then searched for a solution but everyone was pointing out to set the Skiprearm to 1. When testing this we discovered that the CMID keeps the same after rearming the Windows machine.

We discovered that the “Remaining Windows Rearm Count” was 0. which means that when you set SkipRearm then you don’t rearm the machine. Knowing this we searched how to reset the Rearm count and found an article that explained how we could reset the rearm count. http://www.daniel-mitchell.com/blog/reset-windows-7-rearm-count/

To do this follow the below instructions:

  1. Create reset.bat on C:\
  2. add the following code:

    reg load HKLM\MY_SYSTEM “%~dp0Windows\System32\config\system”
    reg delete HKLM\MY_SYSTEM\WPA /f
    reg unload HKLM\MY_SYSTEM

  3. Restart your machine to repair mode using the F8 key.
  4. In the System Recovery Options menu, select Command Prompt.
  5. Now type in D:\reset.bat. The C-drive is used as a recovery partition.
  6. If the script is executed successfully, you should get the message “The operation completed successfully”.
  7. Reboot your machine.

Now the rearm count is reset to 5. You can check this using slmgr /dlv.

rearm count

Now set make sure the Skiprearm is set to 0 at the following location: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\SoftwareProtectionPlatform

Now the Image is ready to deploy and the KMS server will receive different CMID’s from the servers.

New Policy Settings in Windows Server 2012 and Windows 8

With Windows server 2012 and Windows 8 Microsoft added some new features, they also created the proper Group Policy configuration options for this. With Windows server 2012 and Windows 8 they added 160 new items which are only compatible with Windows server 2012, Windows 8 or Windows RT. They added a total of 350 items which are compatible with earlier versions and the new Windows Server 2012 and Windows 8.

You can download the complete list of available Group Policy items here: http://www.microsoft.com/en-us/download/details.aspx?id=25250