From 94c157973c302919f6769d48f4c8f5bd53b3c804 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Mon, 21 Apr 2025 21:50:12 +0300 Subject: [PATCH 1/8] Add support for Federated Identity Credentials in connection methods and parameters --- documentation/Connect-PnPOnline.md | 44 +++++++- src/Commands/Base/ConnectOnline.cs | 66 ++++++++++++ src/Commands/Base/PnPConnection.cs | 132 +++++++++++++++-------- src/Commands/Enums/InitializationType.cs | 3 +- src/Commands/Model/ConnectionMethod.cs | 5 +- 5 files changed, 197 insertions(+), 53 deletions(-) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index cbde984cf..c7825c0ca 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -112,6 +112,21 @@ Connect-PnPOnline -OSLogin [-ReturnConnection] [-Url] [-PersistLogin] [ [-ClientId ] [-AzureEnvironment ] [-TenantAdminUrl ] [-ForceAuthentication] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] ``` +### Federated Identity Credentials with User Assigned Managed Identity by Client Id +```powershell +Connect-PnPOnline [-Url ] [-Tenant ] -FederatedIdentityCredentials -UserAssignedManagedIdentityClientId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] +``` + +### Federated Identity Credentials with User Assigned Managed Identity by Principal Id +```powershell +Connect-PnPOnline [-Url ] -FederatedIdentityCredentials -UserAssignedManagedIdentityObjectId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] +``` + +### Federated Identity Credentials with User Assigned Managed Identity by Azure Resource Id +```powershell +Connect-PnPOnline [-Url ] -FederatedIdentityCredentials -UserAssignedManagedIdentityAzureResourceId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] +``` + ## DESCRIPTION Connects to a SharePoint site or another API and creates a context that is required for the other PnP Cmdlets. See https://pnp.github.io/powershell/articles/connecting.html for more information on the options to connect. @@ -289,6 +304,13 @@ Connect to SharePoint using Credentials (username and password) from Credential On Windows, this entry needs to be under "Generic Credentials". +### EXAMPLE 20 +```powershell +Connect-PnPOnline -Url "https://contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -Tenant 'contoso.onmicrosoft.com' -FederatedIdentityCredentials -UserAssignedManagedIdentityObjectId 363c1b31-6872-47fd-a616-574d3aec2a51 +``` + +Connect to SharePoint/Microsoft Graph using federated identity credentials. + ## PARAMETERS ### -AccessToken @@ -715,7 +737,7 @@ Can be used in combination with `-ManagedIdentity` to specify the object/princip ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Principal Id +Parameter Sets: User Assigned Managed Identity by Principal Id, Federated Identity Credentials, Federated Identity Credentials by Principal Id Aliases: UserAssignedManagedIdentityPrincipalId Required: False @@ -730,7 +752,7 @@ Can be used in combination with `-ManagedIdentity` to specify the client id of t ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Client Id +Parameter Sets: User Assigned Managed Identity by Client Id, Federated Identity Credentials, Federated Identity Credentials by Client Id Aliases: Required: False @@ -745,7 +767,7 @@ Can be used in combination with `-ManagedIdentity` to specify the Azure Resource ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Azure Resource Id +Parameter Sets: User Assigned Managed Identity by Azure Resource Id, Federated Identity Credentials, Federated Identity Credentials by Azure Resource Id Aliases: Required: False @@ -876,6 +898,22 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -FederatedIdentityCredentials + +Connects using Federated Identity credentials. For more information on this, you can visit [this link](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-rest). + +```yaml +Type: SwitchParameter +Parameter Sets: Federated Identity Credentials, Federated Identity Credentials by Client Id, Federated Identity Credentials by Principal Id, Federated Identity Credentials by Azure Resource Id +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ## RELATED LINKS [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index e6cd06d99..50da1712a 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -37,6 +37,10 @@ public class ConnectOnline : BasePSCmdlet private const string ParameterSet_ENVIRONMENTVARIABLE = "Environment Variable"; private const string ParameterSet_AZUREAD_WORKLOAD_IDENTITY = "Azure AD Workload Identity"; private const string ParameterSet_OSLOGIN = "OS login"; + private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALS = "Federated Identity Credentials"; + private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID = "Federated Identity Credentials by Client Id"; + private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID = "Federated Identity Credentials by Principal Id"; + private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID = "Federated Identity Credentials by Azure Resource Id"; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] @@ -52,6 +56,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public SwitchParameter ReturnConnection; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -68,6 +76,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID, ValueFromPipeline = true)] public SwitchParameter ValidateConnection; [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -84,6 +96,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE, ValueFromPipeline = true)] [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID, ValueFromPipeline = true)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID, ValueFromPipeline = true)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID, ValueFromPipeline = true)] public string Url; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -140,6 +156,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_INTERACTIVE)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] [Alias("ApplicationId")] public string ClientId; @@ -153,6 +173,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string Tenant; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)] @@ -184,6 +208,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYCLIENTID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public Framework.AzureEnvironment AzureEnvironment = Framework.AzureEnvironment.Production; // [Parameter(Mandatory = true, ParameterSetName = ParameterSet_APPONLYCLIENTIDCLIENTSECRETAADDOMAIN)] @@ -204,14 +232,23 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] public SwitchParameter ManagedIdentity; + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] + public SwitchParameter FederatedIdentityCredentials; + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] [Alias("UserAssignedManagedIdentityPrincipalId")] public string UserAssignedManagedIdentityObjectId; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYCLIENTID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] public string UserAssignedManagedIdentityClientId; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string UserAssignedManagedIdentityAzureResourceId; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -244,6 +281,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string MicrosoftGraphEndPoint; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -259,6 +300,10 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string AzureADLoginEndPoint; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] @@ -376,6 +421,12 @@ protected void Connect(ref CancellationToken cancellationToken) case ParameterSet_OSLOGIN: newConnection = ConnectWithOSLogin(); break; + case ParameterSet_FEDERATEDIDENTITYCREDENTIALS: + case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID: + case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID: + case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID: + newConnection = ConnectFederatedIdentityCredentials(); + break; } // Ensure a connection instance has been created by now @@ -916,6 +967,21 @@ private PnPConnection ConnectWithOSLogin() return PnPConnection.CreateWithInteractiveLogin(new Uri(Url.ToLower()), ClientId, TenantAdminUrl, AzureEnvironment, cancellationTokenSource, ForceAuthentication, Tenant, true, PersistLogin, Host); } + private PnPConnection ConnectFederatedIdentityCredentials() + { + // Add validation for FederatedIdentityCredentials to ensure at least one identity parameter is specified + if (!ParameterSpecified(nameof(UserAssignedManagedIdentityClientId)) && + !ParameterSpecified(nameof(UserAssignedManagedIdentityObjectId)) && + !ParameterSpecified(nameof(UserAssignedManagedIdentityAzureResourceId))) + { + throw new PSArgumentException("When using FederatedIdentityCredentials, you must specify at least one of the following parameters: UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityObjectId, or UserAssignedManagedIdentityAzureResourceId."); + } + LogDebug("Connecting using Federated Identity Credentials"); + + var tenantId = TenantExtensions.GetTenantIdByUrl(Url, AzureEnvironment); + + return PnPConnection.CreateWithFederatedIdentityCredentials(Url, TenantAdminUrl, ClientId, tenantId, UserAssignedManagedIdentityObjectId, UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityAzureResourceId); + } #endregion #region Helper methods diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index d1b2b2f9f..4e573d55c 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -378,27 +378,6 @@ internal static PnPConnection CreateWithCert(Uri url, string clientId, string te /// Instantiated PnPConnection internal static PnPConnection CreateWithManagedIdentity(string url, string tenantAdminUrl, string userAssignedManagedIdentityObjectId = null, string userAssignedManagedIdentityClientId = null, string userAssignedManagedIdentityAzureResourceId = null) { - var endPoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT"); - PnP.Framework.Diagnostics.Log.Debug("PnPConnection", $"Using identity endpoint: {endPoint}"); - //cmdlet.LogDebug($"Using identity endpoint: {endPoint}"); - - var identityHeader = Environment.GetEnvironmentVariable("IDENTITY_HEADER"); - PnP.Framework.Diagnostics.Log.Debug("PnPConnection", $"Using identity header: {identityHeader}"); - //cmdlet.LogDebug($"Using identity header: {identityHeader}"); - - if (string.IsNullOrEmpty(endPoint)) - { - endPoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT"); - identityHeader = Environment.GetEnvironmentVariable("MSI_SECRET"); - } - if (string.IsNullOrEmpty(endPoint)) - { - // additional fallback - // using well-known endpoint for Instance Metadata Service, useful in Azure VM scenario. - // https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token#get-a-token-using-http - endPoint = "http://169.254.169.254/metadata/identity/oauth2/token"; - } - // Define the type of Managed Identity that will be used ManagedIdentityType managedIdentityType = ManagedIdentityType.SystemAssigned; string managedIdentityUserAssignedIdentifier = null; @@ -426,32 +405,35 @@ internal static PnPConnection CreateWithManagedIdentity(string url, string tenan } // Set up the AuthenticationManager in PnP Framework to use a Managed Identity context - using var authManager = new Framework.AuthenticationManager(endPoint, identityHeader, managedIdentityType, managedIdentityUserAssignedIdentifier); - PnPClientContext context = null; - ConnectionType connectionType = ConnectionType.O365; - if (url != null) + using (var authManager = Framework.AuthenticationManager.CreateWithManagedIdentity(null, null, managedIdentityType, managedIdentityUserAssignedIdentifier)) { - context = PnPClientContext.ConvertFrom(authManager.GetContext(url.ToString())); - context.ApplicationName = Resources.ApplicationName; - context.DisableReturnValueCache = true; - context.ExecutingWebRequest += (sender, e) => - { - e.WebRequestExecutor.WebRequest.UserAgent = $"NONISV|SharePointPnP|PnPPS/{((AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version} ({System.Environment.OSVersion.VersionString})"; - }; - if (IsTenantAdminSite(context)) + PnPClientContext context = null; + ConnectionType connectionType = ConnectionType.O365; + if (url != null) { - connectionType = ConnectionType.TenantAdmin; + context = PnPClientContext.ConvertFrom(authManager.GetContext(url.ToString())); + context.ApplicationName = Resources.ApplicationName; + context.DisableReturnValueCache = true; + context.ExecutingWebRequest += (sender, e) => + { + e.WebRequestExecutor.WebRequest.UserAgent = $"NONISV|SharePointPnP|PnPPS/{((AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version} ({System.Environment.OSVersion.VersionString})"; + }; + if (IsTenantAdminSite(context)) + { + connectionType = ConnectionType.TenantAdmin; + } } - } - // Set up PnP PowerShell to use a Managed Identity - var connection = new PnPConnection(context, connectionType, null, url?.ToString(), tenantAdminUrl, PnPPSVersionTag, InitializationType.ManagedIdentity) - { - UserAssignedManagedIdentityObjectId = userAssignedManagedIdentityObjectId, - UserAssignedManagedIdentityClientId = userAssignedManagedIdentityClientId, - UserAssignedManagedIdentityAzureResourceId = userAssignedManagedIdentityAzureResourceId - }; - return connection; + // Set up PnP PowerShell to use a Managed Identity + var connection = new PnPConnection(context, connectionType, null, url?.ToString(), tenantAdminUrl, PnPPSVersionTag, InitializationType.ManagedIdentity) + { + UserAssignedManagedIdentityObjectId = userAssignedManagedIdentityObjectId, + UserAssignedManagedIdentityClientId = userAssignedManagedIdentityClientId, + UserAssignedManagedIdentityAzureResourceId = userAssignedManagedIdentityAzureResourceId, + ConnectionMethod = ConnectionMethod.ManagedIdentity, + }; + return connection; + } } internal static PnPConnection CreateWithCredentials(Cmdlet cmdlet, Uri url, PSCredential credentials, bool currentCredentials, string tenantAdminUrl, bool persistLogin, System.Management.Automation.Host.PSHost host, AzureEnvironment azureEnvironment = AzureEnvironment.Production, string clientId = null, string redirectUrl = null, bool onPrem = false, InitializationType initializationType = InitializationType.Credentials) @@ -675,6 +657,66 @@ internal static PnPConnection CreateWithAzureADWorkloadIdentity(string url, stri return connection; } } + + internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, string tenantAdminUrl, string appClientId, string tenantId, string userAssignedManagedIdentityObjectId = null, string userAssignedManagedIdentityClientId = null, string userAssignedManagedIdentityAzureResourceId = null) + { + // Define the type of Managed Identity that will be used + ManagedIdentityType managedIdentityType = ManagedIdentityType.SystemAssigned; + string managedIdentityUserAssignedIdentifier = null; + + if (!string.IsNullOrEmpty(userAssignedManagedIdentityObjectId)) + { + managedIdentityType = ManagedIdentityType.UserAssignedByObjectId; + managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityObjectId; + } + if (!string.IsNullOrEmpty(userAssignedManagedIdentityClientId)) + { + managedIdentityType = ManagedIdentityType.UserAssignedByClientId; + managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityClientId; + } + if (!string.IsNullOrEmpty(userAssignedManagedIdentityAzureResourceId)) + { + managedIdentityType = ManagedIdentityType.UserAssignedByResourceId; + managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityAzureResourceId; + } + + // Ensure if its not a System Assigned Managed Identity, that we an identifier pointing to the user assigned Managed Identity + if (managedIdentityType != ManagedIdentityType.SystemAssigned && string.IsNullOrEmpty(managedIdentityUserAssignedIdentifier)) + { + throw new InvalidOperationException("Unable to use a User Assigned Managed Identity without passing in an identifier for the User Assigned Managed Identity."); + } + + // Set up the AuthenticationManager in PnP Framework to use a Managed Identity context + using (var authManager = Framework.AuthenticationManager.CreateWithManagedIdentityFederatedIdentityCredential(null, null, appClientId, tenantId, managedIdentityType, managedIdentityUserAssignedIdentifier)) + { + PnPClientContext context = null; + ConnectionType connectionType = ConnectionType.O365; + if (url != null) + { + context = PnPClientContext.ConvertFrom(authManager.GetContext(url.ToString())); + context.ApplicationName = Resources.ApplicationName; + context.DisableReturnValueCache = true; + context.ExecutingWebRequest += (sender, e) => + { + e.WebRequestExecutor.WebRequest.UserAgent = $"NONISV|SharePointPnP|PnPPS/{((AssemblyFileVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttribute(typeof(AssemblyFileVersionAttribute))).Version} ({System.Environment.OSVersion.VersionString})"; + }; + if (IsTenantAdminSite(context)) + { + connectionType = ConnectionType.TenantAdmin; + } + } + + // Set up PnP PowerShell to use a Managed Identity + var connection = new PnPConnection(context, connectionType, null, url?.ToString(), tenantAdminUrl, PnPPSVersionTag, InitializationType.FederatedIdentityCredentials) + { + UserAssignedManagedIdentityObjectId = userAssignedManagedIdentityObjectId, + UserAssignedManagedIdentityClientId = userAssignedManagedIdentityClientId, + UserAssignedManagedIdentityAzureResourceId = userAssignedManagedIdentityAzureResourceId, + ConnectionMethod = ConnectionMethod.FederatedIdentityCredentials, + }; + return connection; + } + } #endregion #region Constructors @@ -701,10 +743,6 @@ private PnPConnection(ClientContext context, { connectionMethod = ConnectionMethod.AzureADWorkloadIdentity; } - else if (initializationType == InitializationType.ManagedIdentity) - { - connectionMethod = ConnectionMethod.ManagedIdentity; - } if (context != null) { diff --git a/src/Commands/Enums/InitializationType.cs b/src/Commands/Enums/InitializationType.cs index b724f212d..7d2c56afc 100644 --- a/src/Commands/Enums/InitializationType.cs +++ b/src/Commands/Enums/InitializationType.cs @@ -15,6 +15,7 @@ public enum InitializationType GraphDeviceLogin, ManagedIdentity, EnvironmentVariable, - AzureADWorkloadIdentity + AzureADWorkloadIdentity, + FederatedIdentityCredentials } } diff --git a/src/Commands/Model/ConnectionMethod.cs b/src/Commands/Model/ConnectionMethod.cs index b80cfbb28..b6bfaf952 100644 --- a/src/Commands/Model/ConnectionMethod.cs +++ b/src/Commands/Model/ConnectionMethod.cs @@ -32,7 +32,8 @@ public enum ConnectionMethod /// Using a System Assigned or User Assigned Managed Identity /// ManagedIdentity, - - AzureADWorkloadIdentity + + AzureADWorkloadIdentity, + FederatedIdentityCredentials } } From ca99b17b58b7299aaf7450c5e516c091c27535b6 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 3 May 2025 21:26:49 +0300 Subject: [PATCH 2/8] Update exception message Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Commands/Base/ConnectOnline.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 50da1712a..71ee82850 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -974,7 +974,7 @@ private PnPConnection ConnectFederatedIdentityCredentials() !ParameterSpecified(nameof(UserAssignedManagedIdentityObjectId)) && !ParameterSpecified(nameof(UserAssignedManagedIdentityAzureResourceId))) { - throw new PSArgumentException("When using FederatedIdentityCredentials, you must specify at least one of the following parameters: UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityObjectId, or UserAssignedManagedIdentityAzureResourceId."); + throw new PSArgumentException("When using FederatedIdentityCredentials, you must specify exactly one of the following mutually exclusive parameters: UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityObjectId, or UserAssignedManagedIdentityAzureResourceId."); } LogDebug("Connecting using Federated Identity Credentials"); From 57a7de2b0d0e8ed3c43350bc69f23ec8cacb116f Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 12:02:33 +0300 Subject: [PATCH 3/8] Refactor authentication methods to support Federated Identity - Updated PnPConnection to streamline the creation of connections using Federated Identity credentials. - Modified TokenHandler to include methods for acquiring tokens using Federated Identity in GitHub Actions and Azure DevOps. - Adjusted PnPSharePointCmdlet to handle token retrieval for Federated Identity connections. - Changed InitializationType and ConnectionMethod enums to reflect the new Federated Identity terminology. - Removed unnecessary parameters related to user-assigned managed identities. --- documentation/Connect-PnPOnline.md | 24 +--- src/Commands/Base/ConnectOnline.cs | 47 +----- src/Commands/Base/PnPConnection.cs | 51 +++---- src/Commands/Base/PnPSharePointCmdlet.cs | 10 ++ src/Commands/Base/TokenHandler.cs | 176 +++++++++++++++++++++-- src/Commands/Enums/InitializationType.cs | 2 +- src/Commands/Model/ConnectionMethod.cs | 2 +- 7 files changed, 204 insertions(+), 108 deletions(-) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index c7825c0ca..9da2ed7e7 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -112,19 +112,9 @@ Connect-PnPOnline -OSLogin [-ReturnConnection] [-Url] [-PersistLogin] [ [-ClientId ] [-AzureEnvironment ] [-TenantAdminUrl ] [-ForceAuthentication] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] ``` -### Federated Identity Credentials with User Assigned Managed Identity by Client Id +### Federated Identity Credentials ```powershell -Connect-PnPOnline [-Url ] [-Tenant ] -FederatedIdentityCredentials -UserAssignedManagedIdentityClientId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] -``` - -### Federated Identity Credentials with User Assigned Managed Identity by Principal Id -```powershell -Connect-PnPOnline [-Url ] -FederatedIdentityCredentials -UserAssignedManagedIdentityObjectId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] -``` - -### Federated Identity Credentials with User Assigned Managed Identity by Azure Resource Id -```powershell -Connect-PnPOnline [-Url ] -FederatedIdentityCredentials -UserAssignedManagedIdentityAzureResourceId [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] +Connect-PnPOnline [-Url ] [-Tenant ] -FederatedIdentityCredentials [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] ``` ## DESCRIPTION @@ -306,7 +296,7 @@ On Windows, this entry needs to be under "Generic Credentials". ### EXAMPLE 20 ```powershell -Connect-PnPOnline -Url "https://contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -Tenant 'contoso.onmicrosoft.com' -FederatedIdentityCredentials -UserAssignedManagedIdentityObjectId 363c1b31-6872-47fd-a616-574d3aec2a51 +Connect-PnPOnline -Url "https://contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -Tenant 'contoso.onmicrosoft.com' -FederatedIdentityCredentials ``` Connect to SharePoint/Microsoft Graph using federated identity credentials. @@ -737,7 +727,7 @@ Can be used in combination with `-ManagedIdentity` to specify the object/princip ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Principal Id, Federated Identity Credentials, Federated Identity Credentials by Principal Id +Parameter Sets: User Assigned Managed Identity by Principal Id Aliases: UserAssignedManagedIdentityPrincipalId Required: False @@ -752,7 +742,7 @@ Can be used in combination with `-ManagedIdentity` to specify the client id of t ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Client Id, Federated Identity Credentials, Federated Identity Credentials by Client Id +Parameter Sets: User Assigned Managed Identity by Client Id Aliases: Required: False @@ -767,7 +757,7 @@ Can be used in combination with `-ManagedIdentity` to specify the Azure Resource ```yaml Type: String -Parameter Sets: User Assigned Managed Identity by Azure Resource Id, Federated Identity Credentials, Federated Identity Credentials by Azure Resource Id +Parameter Sets: User Assigned Managed Identity by Azure Resource Id Aliases: Required: False @@ -904,7 +894,7 @@ Connects using Federated Identity credentials. For more information on this, you ```yaml Type: SwitchParameter -Parameter Sets: Federated Identity Credentials, Federated Identity Credentials by Client Id, Federated Identity Credentials by Principal Id, Federated Identity Credentials by Azure Resource Id +Parameter Sets: Federated Identity Credentials Aliases: Required: False diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 71ee82850..62997b0eb 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -38,9 +38,6 @@ public class ConnectOnline : BasePSCmdlet private const string ParameterSet_AZUREAD_WORKLOAD_IDENTITY = "Azure AD Workload Identity"; private const string ParameterSet_OSLOGIN = "OS login"; private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALS = "Federated Identity Credentials"; - private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID = "Federated Identity Credentials by Client Id"; - private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID = "Federated Identity Credentials by Principal Id"; - private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID = "Federated Identity Credentials by Azure Resource Id"; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] @@ -57,9 +54,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public SwitchParameter ReturnConnection; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -77,9 +71,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID, ValueFromPipeline = true)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID, ValueFromPipeline = true)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID, ValueFromPipeline = true)] public SwitchParameter ValidateConnection; [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -97,9 +88,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] - [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID, ValueFromPipeline = true)] - [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID, ValueFromPipeline = true)] - [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID, ValueFromPipeline = true)] public string Url; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -157,9 +145,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] [Alias("ApplicationId")] public string ClientId; @@ -174,9 +159,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string Tenant; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)] @@ -209,9 +191,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public Framework.AzureEnvironment AzureEnvironment = Framework.AzureEnvironment.Production; // [Parameter(Mandatory = true, ParameterSetName = ParameterSet_APPONLYCLIENTIDCLIENTSECRETAADDOMAIN)] @@ -233,22 +212,16 @@ public class ConnectOnline : BasePSCmdlet public SwitchParameter ManagedIdentity; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public SwitchParameter FederatedIdentityCredentials; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] [Alias("UserAssignedManagedIdentityPrincipalId")] public string UserAssignedManagedIdentityObjectId; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYCLIENTID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] public string UserAssignedManagedIdentityClientId; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string UserAssignedManagedIdentityAzureResourceId; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -282,9 +255,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string MicrosoftGraphEndPoint; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -301,9 +271,6 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID)] public string AzureADLoginEndPoint; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] @@ -422,9 +389,6 @@ protected void Connect(ref CancellationToken cancellationToken) newConnection = ConnectWithOSLogin(); break; case ParameterSet_FEDERATEDIDENTITYCREDENTIALS: - case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYAZURERESOURCEID: - case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYCLIENTID: - case ParameterSet_FEDERATEDIDENTITYCREDENTIALSBYPRINCIPALID: newConnection = ConnectFederatedIdentityCredentials(); break; } @@ -969,18 +933,9 @@ private PnPConnection ConnectWithOSLogin() private PnPConnection ConnectFederatedIdentityCredentials() { - // Add validation for FederatedIdentityCredentials to ensure at least one identity parameter is specified - if (!ParameterSpecified(nameof(UserAssignedManagedIdentityClientId)) && - !ParameterSpecified(nameof(UserAssignedManagedIdentityObjectId)) && - !ParameterSpecified(nameof(UserAssignedManagedIdentityAzureResourceId))) - { - throw new PSArgumentException("When using FederatedIdentityCredentials, you must specify exactly one of the following mutually exclusive parameters: UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityObjectId, or UserAssignedManagedIdentityAzureResourceId."); - } LogDebug("Connecting using Federated Identity Credentials"); - var tenantId = TenantExtensions.GetTenantIdByUrl(Url, AzureEnvironment); - - return PnPConnection.CreateWithFederatedIdentityCredentials(Url, TenantAdminUrl, ClientId, tenantId, UserAssignedManagedIdentityObjectId, UserAssignedManagedIdentityClientId, UserAssignedManagedIdentityAzureResourceId); + return PnPConnection.CreateWithFederatedIdentityCredentials(Url, TenantAdminUrl, ClientId, Tenant); } #endregion diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index 6098fe38d..1516e8bcc 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -567,7 +567,7 @@ internal static PnPConnection CreateWithInteractiveLogin(Uri uri, string clientI WriteCacheEnabledMessage(host); } - var htmlMessageSuccess ="PnP PowerShell - Sign InPnP PowerShell
You are signed in now and can close this page.
"; + var htmlMessageSuccess = "PnP PowerShell - Sign InPnP PowerShell
You are signed in now and can close this page.
"; var htmlMessageFailure = "PnP PowerShell - Sign InPnP PowerShell
An error occured while signing in: {0}
"; PnP.Framework.AuthenticationManager authManager = null; @@ -579,7 +579,7 @@ internal static PnPConnection CreateWithInteractiveLogin(Uri uri, string clientI else { authManager = PnP.Framework.AuthenticationManager.CreateWithInteractiveWebBrowserLogin(clientId, (url, port) => - { + { BrowserHelper.OpenBrowserForInteractiveLogin(url, port, cancellationTokenSource); }, tenant, @@ -658,36 +658,20 @@ internal static PnPConnection CreateWithAzureADWorkloadIdentity(string url, stri } } - internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, string tenantAdminUrl, string appClientId, string tenantId, string userAssignedManagedIdentityObjectId = null, string userAssignedManagedIdentityClientId = null, string userAssignedManagedIdentityAzureResourceId = null) + internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, string tenantAdminUrl, string appClientId, string tenantId) { - // Define the type of Managed Identity that will be used - ManagedIdentityType managedIdentityType = ManagedIdentityType.SystemAssigned; - string managedIdentityUserAssignedIdentifier = null; - - if (!string.IsNullOrEmpty(userAssignedManagedIdentityObjectId)) - { - managedIdentityType = ManagedIdentityType.UserAssignedByObjectId; - managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityObjectId; - } - if (!string.IsNullOrEmpty(userAssignedManagedIdentityClientId)) - { - managedIdentityType = ManagedIdentityType.UserAssignedByClientId; - managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityClientId; - } - if (!string.IsNullOrEmpty(userAssignedManagedIdentityAzureResourceId)) + string defaultResource = "https://graph.microsoft.com/.default"; + if (url != null) { - managedIdentityType = ManagedIdentityType.UserAssignedByResourceId; - managedIdentityUserAssignedIdentifier = userAssignedManagedIdentityAzureResourceId; + var resourceUri = new Uri(url); + defaultResource = $"{resourceUri.Scheme}://{resourceUri.Authority}/.default"; } - // Ensure if its not a System Assigned Managed Identity, that we an identifier pointing to the user assigned Managed Identity - if (managedIdentityType != ManagedIdentityType.SystemAssigned && string.IsNullOrEmpty(managedIdentityUserAssignedIdentifier)) - { - throw new InvalidOperationException("Unable to use a User Assigned Managed Identity without passing in an identifier for the User Assigned Managed Identity."); - } + PnP.Framework.Diagnostics.Log.Debug("PnPConnection", "Acquiring token for resource " + defaultResource); + var accessToken = TokenHandler.GetFederatedIdentityTokenAsync(appClientId, tenantId, defaultResource).GetAwaiter().GetResult(); // Set up the AuthenticationManager in PnP Framework to use a Managed Identity context - using (var authManager = Framework.AuthenticationManager.CreateWithManagedIdentityFederatedIdentityCredential(null, null, appClientId, tenantId, managedIdentityType, managedIdentityUserAssignedIdentifier)) + using (var authManager = new PnP.Framework.AuthenticationManager(new System.Net.NetworkCredential("", accessToken).SecurePassword)) { PnPClientContext context = null; ConnectionType connectionType = ConnectionType.O365; @@ -706,14 +690,9 @@ internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, } } - // Set up PnP PowerShell to use a Managed Identity - var connection = new PnPConnection(context, connectionType, null, url?.ToString(), tenantAdminUrl, PnPPSVersionTag, InitializationType.FederatedIdentityCredentials) - { - UserAssignedManagedIdentityObjectId = userAssignedManagedIdentityObjectId, - UserAssignedManagedIdentityClientId = userAssignedManagedIdentityClientId, - UserAssignedManagedIdentityAzureResourceId = userAssignedManagedIdentityAzureResourceId, - ConnectionMethod = ConnectionMethod.FederatedIdentityCredentials, - }; + var connection = new PnPConnection(context, connectionType, null, url != null ? url.ToString() : null, tenantAdminUrl, PnPPSVersionTag, InitializationType.FederatedIdentity); + connection.ClientId = appClientId; + connection.Tenant = tenantId; return connection; } } @@ -743,6 +722,10 @@ private PnPConnection(ClientContext context, { connectionMethod = ConnectionMethod.AzureADWorkloadIdentity; } + else if (initializationType == InitializationType.FederatedIdentity) + { + connectionMethod = ConnectionMethod.FederatedIdentity; + } if (context != null) { diff --git a/src/Commands/Base/PnPSharePointCmdlet.cs b/src/Commands/Base/PnPSharePointCmdlet.cs index bf29824bd..e56d99490 100644 --- a/src/Commands/Base/PnPSharePointCmdlet.cs +++ b/src/Commands/Base/PnPSharePointCmdlet.cs @@ -49,6 +49,12 @@ protected string AccessToken var defaultResource = $"{resourceUri.Scheme}://{resourceUri.Authority}/.default"; return TokenHandler.GetAzureADWorkloadIdentityTokenAsync(defaultResource).GetAwaiter().GetResult(); } + else if (Connection.ConnectionMethod == ConnectionMethod.FederatedIdentity) + { + var resourceUri = new Uri(Connection.Url); + var defaultResource = $"{resourceUri.Scheme}://{resourceUri.Authority}/.default"; + return TokenHandler.GetFederatedIdentityTokenAsync(Connection.ClientId, Connection.Tenant, defaultResource).GetAwaiter().GetResult(); + } else { if (Connection.Context != null) @@ -81,6 +87,10 @@ public string GraphAccessToken { return TokenHandler.GetAzureADWorkloadIdentityTokenAsync($"https://{Connection.GraphEndPoint}/.default").GetAwaiter().GetResult(); } + else if (Connection?.ConnectionMethod == ConnectionMethod.FederatedIdentity) + { + return TokenHandler.GetFederatedIdentityTokenAsync(Connection.ClientId, Connection.Tenant, $"https://{Connection.GraphEndPoint}/.default").GetAwaiter().GetResult(); + } else { if (Connection?.Context != null) diff --git a/src/Commands/Base/TokenHandler.cs b/src/Commands/Base/TokenHandler.cs index ec4271147..4a9cb04e7 100644 --- a/src/Commands/Base/TokenHandler.cs +++ b/src/Commands/Base/TokenHandler.cs @@ -1,12 +1,15 @@ using Microsoft.Identity.Client; using Microsoft.SharePoint.Client; using PnP.PowerShell.Commands.Model; +using PnP.PowerShell.Commands.Utilities; using System; +using System.Collections.Generic; using System.Linq; using System.Management.Automation; +using System.Net.Http; using System.Text; +using System.Text.Json; using System.Threading.Tasks; -using PnP.PowerShell.Commands.Utilities; namespace PnP.PowerShell.Commands.Base { @@ -84,12 +87,12 @@ internal static Enums.ResourceTypeName DefineResourceTypeFromAudience(string aud var sanitizedAudience = audience?.TrimEnd('/').ToLowerInvariant(); if (sanitizedAudience.StartsWith("http://")) sanitizedAudience = sanitizedAudience.Substring(7); if (sanitizedAudience.StartsWith("https://")) sanitizedAudience = sanitizedAudience.Substring(8); - + // TODO: Extend with all options Enums.ResourceTypeName resource = sanitizedAudience switch { "graph" or "graph.microsoft.com" or "graph.microsoft.us" or "graph.microsoft.de" or "microsoftgraph.chinacloudapi.cn" or "dod-graph.microsoft.us" or "00000003-0000-0000-c000-000000000000" => Enums.ResourceTypeName.Graph, - "azure" or "management.azure.com" or "management.chinacloudapi.cn" or "management.usgovcloudapi.net" or "management.usgovcloudapi.net" or "management.usgovcloudapi.net" => Enums.ResourceTypeName.AzureManagementApi, + "azure" or "management.azure.com" or "management.chinacloudapi.cn" or "management.usgovcloudapi.net" or "management.usgovcloudapi.net" or "management.usgovcloudapi.net" => Enums.ResourceTypeName.AzureManagementApi, "exchangeonline" or "outlook.office.com" or "outlook.office365.com" => Enums.ResourceTypeName.ExchangeOnline, "flow" or "service.flow.microsoft.com" => Enums.ResourceTypeName.PowerAutomate, "powerapps" or "api.powerapps.com" => Enums.ResourceTypeName.PowerApps, @@ -139,16 +142,16 @@ internal static void EnsureRequiredPermissionsAvailableInAccessTokenAudience(Typ { exceptionTextBuilder.Append($"{string.Join(" and ", permissionEvaluationResponses[i].MissingPermissions.Select(s => s.Scope))}"); - if(i < permissionEvaluationResponses.Length - 1) + if (i < permissionEvaluationResponses.Length - 1) { exceptionTextBuilder.Append(" or "); } } // Log a warning that the permission check failed. Deliberately not throwing an exception here, as the permission attributes might be wrong, thus will try to execute anyway. - PnP.Framework.Diagnostics.Log.Error("TokenHandler",exceptionTextBuilder.ToString().Replace(Environment.NewLine," ")); + PnP.Framework.Diagnostics.Log.Error("TokenHandler", exceptionTextBuilder.ToString().Replace(Environment.NewLine, " ")); //cmdlet.LogWarning(exceptionTextBuilder.ToString()); - } + } /// /// Returns an oAuth JWT access token @@ -165,9 +168,15 @@ internal static string GetAccessToken(string audience, PnPConnection connection) string accessToken = null; if (connection.ConnectionMethod == ConnectionMethod.AzureADWorkloadIdentity) { - PnP.Framework.Diagnostics.Log.Debug("TokenHandler",$"Acquiring token for resource {connection.GraphEndPoint} using Azure AD Workload Identity"); + PnP.Framework.Diagnostics.Log.Debug("TokenHandler", $"Acquiring token for resource {connection.GraphEndPoint} using Azure AD Workload Identity"); //cmdlet.LogDebug("Acquiring token for resource " + connection.GraphEndPoint + " using Azure AD Workload Identity"); - accessToken = GetAzureADWorkloadIdentityTokenAsync($"{audience.TrimEnd('/')}/.default").GetAwaiter().GetResult(); + accessToken = GetAzureADWorkloadIdentityTokenAsync($"{audience.TrimEnd('/')}").GetAwaiter().GetResult(); + } + else if (connection.ConnectionMethod == ConnectionMethod.FederatedIdentity) + { + PnP.Framework.Diagnostics.Log.Debug("TokenHandler", $"Acquiring token for resource {connection.GraphEndPoint} using Federated Identity"); + //cmdlet.LogDebug("Acquiring token for resource " + connection.GraphEndPoint + " using Federated Identity"); + accessToken = GetFederatedIdentityTokenAsync(connection.ClientId, connection.Tenant, $"{audience.TrimEnd('/')}").GetAwaiter().GetResult(); } else { @@ -189,7 +198,7 @@ internal static string GetAccessToken(string audience, PnPConnection connection) } if (string.IsNullOrEmpty(accessToken)) { - PnP.Framework.Diagnostics.Log.Debug("TokenHandler",$"Unable to acquire token for resource {connection.GraphEndPoint}"); + PnP.Framework.Diagnostics.Log.Debug("TokenHandler", $"Unable to acquire token for resource {connection.GraphEndPoint}"); //cmdlet.LogDebug($"Unable to acquire token for resource {connection.GraphEndPoint}"); return null; } @@ -251,5 +260,154 @@ internal static async Task GetAzureADWorkloadIdentityTokenAsync(string r } return result.AccessToken; } + + /// + /// Returns an access token based on a Azure AD Workload Identity. Only works within Azure components supporting workload identities. + /// + /// The cmdlet scope in which this code runs. Used to write logging to. + /// The HttpClient that will be reused to fetch the token to avoid port exhaustion + /// The permission scope to be requested, in the format https:///, i.e. https://graph.microsoft.com/Group.Read.All + /// Access token + /// Thrown if unable to retrieve an access token through a managed identity + internal static async Task GetFederatedIdentityTokenAsync(string clientId, string tenant, string requiredScope) + { + var actionsIdTokenRequestUrl = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_URL"); + var actionsIdTokenRequestToken = Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN"); + var systemOidcRequestUri = Environment.GetEnvironmentVariable("SYSTEM_OIDCREQUESTURI"); + + if (!string.IsNullOrEmpty(actionsIdTokenRequestUrl) && !string.IsNullOrEmpty(actionsIdTokenRequestToken)) + { + Framework.Diagnostics.Log.Debug("TokenHandler", "ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN env variables found. The context is GitHub Actions..."); + + var federationToken = await GetFederationTokenFromGithubAsync(); + return await GetAccessTokenWithFederatedTokenAsync(clientId, tenant, requiredScope, federationToken); + } + else if (!string.IsNullOrEmpty(systemOidcRequestUri)) + { + Framework.Diagnostics.Log.Debug("TokenHandler", "SYSTEM_OIDCREQUESTURI env variable found. The context is Azure DevOps..."); + + var systemAccessToken = Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"); + if (string.IsNullOrEmpty(systemAccessToken)) + { + throw new Exception("The SYSTEM_ACCESSTOKEN environment variable is not available. Please check the Azure DevOps pipeline task configuration. It should contain 'SYSTEM_ACCESSTOKEN: $(System.AccessToken)' in the env section."); + } + + var serviceConnectionId = Environment.GetEnvironmentVariable("AZURESUBSCRIPTION_SERVICE_CONNECTION_ID"); + var serviceConnectionAppId = Environment.GetEnvironmentVariable("AZURESUBSCRIPTION_CLIENT_ID"); + var serviceConnectionTenantId = Environment.GetEnvironmentVariable("AZURESUBSCRIPTION_TENANT_ID"); + var useServiceConnection = !string.IsNullOrEmpty(serviceConnectionId) && + !string.IsNullOrEmpty(serviceConnectionAppId) && + !string.IsNullOrEmpty(serviceConnectionTenantId); + + if (!useServiceConnection) + { + throw new PSInvalidOperationException("The Azure DevOps pipeline task is not configured to use a service connection. Please check the pipeline configuration and ensure that the service connection is set up correctly."); + } + + var federationToken = await GetFederationTokenFromAzureDevOpsAsync(serviceConnectionId); + return await GetAccessTokenWithFederatedTokenAsync(clientId, tenant, requiredScope, federationToken); + } + else + { + throw new PSInvalidOperationException("Federated identity is currently only supported in GitHub Actions and Azure DevOps."); + } + } + + private static async Task GetFederationTokenFromGithubAsync() + { + try + { + Framework.Diagnostics.Log.Debug("TokenHandler", "Retrieving GitHub federation token..."); + + var requestUrl = $"{Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_URL")}&audience={Uri.EscapeDataString("api://AzureADTokenExchange")}"; + + var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); + httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN")}"); + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); + + var response = await httpClient.GetAsync(requestUrl); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + var tokenResponse = JsonSerializer.Deserialize>(content); + + return tokenResponse["value"].ToString(); + } + catch (Exception ex) + { + throw new PSInvalidOperationException($"Failed to retrieve GitHub federation token: {ex.Message}", ex); + } + } + + private static async Task GetFederationTokenFromAzureDevOpsAsync(string serviceConnectionId = null) + { + try + { + Framework.Diagnostics.Log.Debug("TokenHandler", "Retrieving Azure DevOps federation token..."); + + var urlSuffix = !string.IsNullOrEmpty(serviceConnectionId) ? $"&serviceConnectionId={serviceConnectionId}" : ""; + var requestUrl = $"{Environment.GetEnvironmentVariable("SYSTEM_OIDCREQUESTURI")}?api-version=7.1{urlSuffix}"; + + using var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); + httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN")}"); + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); + + var response = await httpClient.PostAsync(requestUrl, new StringContent("", Encoding.UTF8, "application/json")); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + var tokenResponse = JsonSerializer.Deserialize>(content); + + return tokenResponse["oidcToken"].ToString(); + } + catch (Exception ex) + { + throw new PSInvalidOperationException($"Failed to retrieve Azure DevOps federation token: {ex.Message}", ex); + } + } + + private static async Task GetAccessTokenWithFederatedTokenAsync(string clientId, string tenant, string resource, string federatedToken) + { + try + { + Framework.Diagnostics.Log.Debug("TokenHandler", "Retrieving Entra ID access Token with federated token..."); + + var queryParams = new List + { + "grant_type=client_credentials", + $"scope={Uri.EscapeDataString($"{resource}/.default")}", + $"client_id={clientId}", + $"client_assertion_type={Uri.EscapeDataString("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")}", + $"client_assertion={federatedToken}" + }; + + var requestData = string.Join("&", queryParams); + var requestUrl = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"; + + using var httpClient = new HttpClient(); + var content = new StringContent(requestData, Encoding.UTF8, "application/x-www-form-urlencoded"); + + httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); + httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); + + var response = await httpClient.PostAsync(requestUrl, content); + response.EnsureSuccessStatusCode(); + + var responseContent = await response.Content.ReadAsStringAsync(); + var tokenResponse = JsonSerializer.Deserialize>(responseContent); + + var expiresIn = int.Parse(tokenResponse["expires_in"].ToString()) * 1000; + var now = DateTime.UtcNow; + var expiresOn = now.AddMilliseconds(expiresIn); + + return tokenResponse["access_token"].ToString(); + } + catch (Exception ex) + { + throw new PSInvalidOperationException($"Failed to retrieve access token with federated token: {ex.Message}", ex); + } + } } } \ No newline at end of file diff --git a/src/Commands/Enums/InitializationType.cs b/src/Commands/Enums/InitializationType.cs index 7d2c56afc..39d6253bb 100644 --- a/src/Commands/Enums/InitializationType.cs +++ b/src/Commands/Enums/InitializationType.cs @@ -16,6 +16,6 @@ public enum InitializationType ManagedIdentity, EnvironmentVariable, AzureADWorkloadIdentity, - FederatedIdentityCredentials + FederatedIdentity } } diff --git a/src/Commands/Model/ConnectionMethod.cs b/src/Commands/Model/ConnectionMethod.cs index b6bfaf952..042b6755e 100644 --- a/src/Commands/Model/ConnectionMethod.cs +++ b/src/Commands/Model/ConnectionMethod.cs @@ -34,6 +34,6 @@ public enum ConnectionMethod ManagedIdentity, AzureADWorkloadIdentity, - FederatedIdentityCredentials + FederatedIdentity } } From 58a52f3c68205255013d75585489f2d496cad5e4 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 12:26:59 +0300 Subject: [PATCH 4/8] Update Federated Identity terminology and methods for consistency --- documentation/Connect-PnPOnline.md | 12 ++++++------ src/Commands/Base/ConnectOnline.cs | 30 +++++++++++++++--------------- src/Commands/Base/PnPConnection.cs | 15 +++++++++++++-- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index 9da2ed7e7..d4a2168a4 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -112,9 +112,9 @@ Connect-PnPOnline -OSLogin [-ReturnConnection] [-Url] [-PersistLogin] [ [-ClientId ] [-AzureEnvironment ] [-TenantAdminUrl ] [-ForceAuthentication] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] ``` -### Federated Identity Credentials +### Federated Identity ```powershell -Connect-PnPOnline [-Url ] [-Tenant ] -FederatedIdentityCredentials [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] +Connect-PnPOnline [-Url ] [-Tenant ] -FederatedIdentity [-AzureEnvironment ] [-TenantAdminUrl ] [-ValidateConnection] [-MicrosoftGraphEndPoint ] [-AzureADLoginEndPoint ] [-Connection ] ``` ## DESCRIPTION @@ -296,7 +296,7 @@ On Windows, this entry needs to be under "Generic Credentials". ### EXAMPLE 20 ```powershell -Connect-PnPOnline -Url "https://contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -Tenant 'contoso.onmicrosoft.com' -FederatedIdentityCredentials +Connect-PnPOnline -Url "https://contoso.sharepoint.com" -ClientId 6c5c98c7-e05a-4a0f-bcfa-0cfc65aa1f28 -Tenant 'contoso.onmicrosoft.com' -FederatedIdentity ``` Connect to SharePoint/Microsoft Graph using federated identity credentials. @@ -888,13 +888,13 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### -FederatedIdentityCredentials +### -FederatedIdentity -Connects using Federated Identity credentials. For more information on this, you can visit [this link](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-rest). +Connects using Federated Identity. For more information on this, you can visit [this link](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust?pivots=identity-wif-apps-methods-rest). ```yaml Type: SwitchParameter -Parameter Sets: Federated Identity Credentials +Parameter Sets: Federated Identity Aliases: Required: False diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 62997b0eb..8a17a909f 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -37,7 +37,7 @@ public class ConnectOnline : BasePSCmdlet private const string ParameterSet_ENVIRONMENTVARIABLE = "Environment Variable"; private const string ParameterSet_AZUREAD_WORKLOAD_IDENTITY = "Azure AD Workload Identity"; private const string ParameterSet_OSLOGIN = "OS login"; - private const string ParameterSet_FEDERATEDIDENTITYCREDENTIALS = "Federated Identity Credentials"; + private const string ParameterSet_FEDERATEDIDENTITY = "Federated Identity"; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACSAPPONLY, ValueFromPipeline = true)] @@ -53,7 +53,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] public SwitchParameter ReturnConnection; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -70,7 +70,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITY, ValueFromPipeline = true)] public SwitchParameter ValidateConnection; [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_CREDENTIALS, ValueFromPipeline = true)] @@ -87,7 +87,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE, ValueFromPipeline = true)] [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] - [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS, ValueFromPipeline = true)] + [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITY, ValueFromPipeline = true)] public string Url; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -144,7 +144,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_INTERACTIVE)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] [Alias("ApplicationId")] public string ClientId; @@ -158,7 +158,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_DEVICELOGIN)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] public string Tenant; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_APPONLYAADCERTIFICATE)] @@ -190,7 +190,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYCLIENTID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] public Framework.AzureEnvironment AzureEnvironment = Framework.AzureEnvironment.Production; // [Parameter(Mandatory = true, ParameterSetName = ParameterSet_APPONLYCLIENTIDCLIENTSECRETAADDOMAIN)] @@ -211,8 +211,8 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] public SwitchParameter ManagedIdentity; - [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] - public SwitchParameter FederatedIdentityCredentials; + [Parameter(Mandatory = true, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] + public SwitchParameter FederatedIdentity; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Alias("UserAssignedManagedIdentityPrincipalId")] @@ -254,7 +254,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] public string MicrosoftGraphEndPoint; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] @@ -270,7 +270,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)] [Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)] - [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITYCREDENTIALS)] + [Parameter(Mandatory = false, ParameterSetName = ParameterSet_FEDERATEDIDENTITY)] public string AzureADLoginEndPoint; [Parameter(Mandatory = true, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY)] @@ -388,8 +388,8 @@ protected void Connect(ref CancellationToken cancellationToken) case ParameterSet_OSLOGIN: newConnection = ConnectWithOSLogin(); break; - case ParameterSet_FEDERATEDIDENTITYCREDENTIALS: - newConnection = ConnectFederatedIdentityCredentials(); + case ParameterSet_FEDERATEDIDENTITY: + newConnection = ConnectFederatedIdentity(); break; } @@ -931,11 +931,11 @@ private PnPConnection ConnectWithOSLogin() return PnPConnection.CreateWithInteractiveLogin(new Uri(Url.ToLower()), ClientId, TenantAdminUrl, AzureEnvironment, cancellationTokenSource, ForceAuthentication, Tenant, true, PersistLogin, Host); } - private PnPConnection ConnectFederatedIdentityCredentials() + private PnPConnection ConnectFederatedIdentity() { LogDebug("Connecting using Federated Identity Credentials"); - return PnPConnection.CreateWithFederatedIdentityCredentials(Url, TenantAdminUrl, ClientId, Tenant); + return PnPConnection.CreateWithFederatedIdentity(Url, TenantAdminUrl, ClientId, Tenant); } #endregion diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index 1516e8bcc..6a1843f47 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -658,7 +658,18 @@ internal static PnPConnection CreateWithAzureADWorkloadIdentity(string url, stri } } - internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, string tenantAdminUrl, string appClientId, string tenantId) + /// + /// Creates a PnPConnection using a Federated Identity + /// + /// Url to the SharePoint Online site to connect to + /// Url to the SharePoint Online Admin Center site to connect to + /// The Client ID of the Federated Identity application + /// The Tenant ID of the Federated Identity application + /// Instantiated PnPConnection + /// + /// This method is used to create a PnPConnection using a Federated Identity, which allows for authentication without the need for a client secret. + /// + internal static PnPConnection CreateWithFederatedIdentity(string url, string tenantAdminUrl, string appClientId, string tenantId) { string defaultResource = "https://graph.microsoft.com/.default"; if (url != null) @@ -670,7 +681,7 @@ internal static PnPConnection CreateWithFederatedIdentityCredentials(string url, PnP.Framework.Diagnostics.Log.Debug("PnPConnection", "Acquiring token for resource " + defaultResource); var accessToken = TokenHandler.GetFederatedIdentityTokenAsync(appClientId, tenantId, defaultResource).GetAwaiter().GetResult(); - // Set up the AuthenticationManager in PnP Framework to use a Managed Identity context + // Set up the AuthenticationManager in PnP Framework to use a Federated Identity context using (var authManager = new PnP.Framework.AuthenticationManager(new System.Net.NetworkCredential("", accessToken).SecurePassword)) { PnPClientContext context = null; From 6f86ce265dfe674f61643a90f6530b4b621862c4 Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 12:46:36 +0300 Subject: [PATCH 5/8] Update PnPConnection to support Managed Identity connection method --- documentation/Connect-PnPOnline.md | 2 +- src/Commands/Base/PnPConnection.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/documentation/Connect-PnPOnline.md b/documentation/Connect-PnPOnline.md index d4a2168a4..024d302ea 100644 --- a/documentation/Connect-PnPOnline.md +++ b/documentation/Connect-PnPOnline.md @@ -897,7 +897,7 @@ Type: SwitchParameter Parameter Sets: Federated Identity Aliases: -Required: False +Required: True Position: Named Default value: None Accept pipeline input: False diff --git a/src/Commands/Base/PnPConnection.cs b/src/Commands/Base/PnPConnection.cs index 6a1843f47..66889316e 100644 --- a/src/Commands/Base/PnPConnection.cs +++ b/src/Commands/Base/PnPConnection.cs @@ -733,6 +733,10 @@ private PnPConnection(ClientContext context, { connectionMethod = ConnectionMethod.AzureADWorkloadIdentity; } + else if (initializationType == InitializationType.ManagedIdentity) + { + connectionMethod = ConnectionMethod.ManagedIdentity; + } else if (initializationType == InitializationType.FederatedIdentity) { connectionMethod = ConnectionMethod.FederatedIdentity; From 1cb39770515c456d0275d2fdade5a64663e5eadc Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 12:54:36 +0300 Subject: [PATCH 6/8] Refactor TokenHandler to use PSInvalidOperationException for error handling and streamline HttpClient instantiation --- src/Commands/Base/TokenHandler.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Commands/Base/TokenHandler.cs b/src/Commands/Base/TokenHandler.cs index 4a9cb04e7..dd9dc7cef 100644 --- a/src/Commands/Base/TokenHandler.cs +++ b/src/Commands/Base/TokenHandler.cs @@ -289,7 +289,7 @@ internal static async Task GetFederatedIdentityTokenAsync(string clientI var systemAccessToken = Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"); if (string.IsNullOrEmpty(systemAccessToken)) { - throw new Exception("The SYSTEM_ACCESSTOKEN environment variable is not available. Please check the Azure DevOps pipeline task configuration. It should contain 'SYSTEM_ACCESSTOKEN: $(System.AccessToken)' in the env section."); + throw new PSInvalidOperationException("The SYSTEM_ACCESSTOKEN environment variable is not available. Please check the Azure DevOps pipeline task configuration. It should contain 'SYSTEM_ACCESSTOKEN: $(System.AccessToken)' in the env section."); } var serviceConnectionId = Environment.GetEnvironmentVariable("AZURESUBSCRIPTION_SERVICE_CONNECTION_ID"); @@ -349,7 +349,7 @@ private static async Task GetFederationTokenFromAzureDevOpsAsync(string var urlSuffix = !string.IsNullOrEmpty(serviceConnectionId) ? $"&serviceConnectionId={serviceConnectionId}" : ""; var requestUrl = $"{Environment.GetEnvironmentVariable("SYSTEM_OIDCREQUESTURI")}?api-version=7.1{urlSuffix}"; - using var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); + var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN")}"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); @@ -386,7 +386,7 @@ private static async Task GetAccessTokenWithFederatedTokenAsync(string c var requestData = string.Join("&", queryParams); var requestUrl = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"; - using var httpClient = new HttpClient(); + var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); var content = new StringContent(requestData, Encoding.UTF8, "application/x-www-form-urlencoded"); httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); @@ -396,11 +396,7 @@ private static async Task GetAccessTokenWithFederatedTokenAsync(string c response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); - var tokenResponse = JsonSerializer.Deserialize>(responseContent); - - var expiresIn = int.Parse(tokenResponse["expires_in"].ToString()) * 1000; - var now = DateTime.UtcNow; - var expiresOn = now.AddMilliseconds(expiresIn); + var tokenResponse = JsonSerializer.Deserialize>(responseContent); return tokenResponse["access_token"].ToString(); } From 7e238796f32beaebb4fc56fa6e3469205fea8aab Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 13:18:12 +0300 Subject: [PATCH 7/8] Refactor TokenHandler to use HttpRequestMessage for API calls and improve header management --- src/Commands/Base/TokenHandler.cs | 35 ++++++++++++++++++------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/Commands/Base/TokenHandler.cs b/src/Commands/Base/TokenHandler.cs index dd9dc7cef..680c8b17c 100644 --- a/src/Commands/Base/TokenHandler.cs +++ b/src/Commands/Base/TokenHandler.cs @@ -322,11 +322,13 @@ private static async Task GetFederationTokenFromGithubAsync() var requestUrl = $"{Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_URL")}&audience={Uri.EscapeDataString("api://AzureADTokenExchange")}"; var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); - httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN")}"); - httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); - httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); - var response = await httpClient.GetAsync(requestUrl); + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUrl); + requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN")); + requestMessage.Headers.Add("Accept", "application/json"); + requestMessage.Headers.Add("x-anonymous", "true"); + var response = await httpClient.SendAsync(requestMessage); + response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); @@ -350,11 +352,14 @@ private static async Task GetFederationTokenFromAzureDevOpsAsync(string var requestUrl = $"{Environment.GetEnvironmentVariable("SYSTEM_OIDCREQUESTURI")}?api-version=7.1{urlSuffix}"; var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); - httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN")}"); - httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); - httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); - var response = await httpClient.PostAsync(requestUrl, new StringContent("", Encoding.UTF8, "application/json")); + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, requestUrl); + requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json"); + requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN")); + requestMessage.Headers.Add("Accept", "application/json"); + requestMessage.Headers.Add("x-anonymous", "true"); + + var response = await httpClient.SendAsync(requestMessage); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); @@ -373,6 +378,7 @@ private static async Task GetAccessTokenWithFederatedTokenAsync(string c try { Framework.Diagnostics.Log.Debug("TokenHandler", "Retrieving Entra ID access Token with federated token..."); + var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); var queryParams = new List { @@ -386,17 +392,16 @@ private static async Task GetAccessTokenWithFederatedTokenAsync(string c var requestData = string.Join("&", queryParams); var requestUrl = $"https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"; - var httpClient = Framework.Http.PnPHttpClient.Instance.GetHttpClient(); - var content = new StringContent(requestData, Encoding.UTF8, "application/x-www-form-urlencoded"); - - httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); - httpClient.DefaultRequestHeaders.Add("x-anonymous", "true"); + using var request = new HttpRequestMessage(HttpMethod.Post, requestUrl); + request.Content = new StringContent(requestData, Encoding.UTF8, "application/x-www-form-urlencoded"); + request.Headers.Add("Accept", "application/json"); + request.Headers.Add("x-anonymous", "true"); - var response = await httpClient.PostAsync(requestUrl, content); + var response = await httpClient.SendAsync(request); response.EnsureSuccessStatusCode(); var responseContent = await response.Content.ReadAsStringAsync(); - var tokenResponse = JsonSerializer.Deserialize>(responseContent); + var tokenResponse = JsonSerializer.Deserialize>(responseContent); return tokenResponse["access_token"].ToString(); } From a38045251b1521cf1c2adb3ddecc10b4ef52257d Mon Sep 17 00:00:00 2001 From: Gautam Sheth Date: Sat, 7 Jun 2025 17:24:00 +0300 Subject: [PATCH 8/8] Update ConnectOnline and TokenHandler to enforce mandatory parameters for Federated Identity and clarify token retrieval documentation --- src/Commands/Base/ConnectOnline.cs | 2 +- src/Commands/Base/TokenHandler.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Commands/Base/ConnectOnline.cs b/src/Commands/Base/ConnectOnline.cs index 8a17a909f..8a3df771c 100644 --- a/src/Commands/Base/ConnectOnline.cs +++ b/src/Commands/Base/ConnectOnline.cs @@ -87,7 +87,7 @@ public class ConnectOnline : BasePSCmdlet [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE, ValueFromPipeline = true)] [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_AZUREAD_WORKLOAD_IDENTITY, ValueFromPipeline = true)] [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_OSLOGIN, ValueFromPipeline = true)] - [Parameter(Mandatory = false, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITY, ValueFromPipeline = true)] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = ParameterSet_FEDERATEDIDENTITY, ValueFromPipeline = true)] public string Url; [Parameter(Mandatory = false, ParameterSetName = ParameterSet_CREDENTIALS)] diff --git a/src/Commands/Base/TokenHandler.cs b/src/Commands/Base/TokenHandler.cs index 680c8b17c..01c15a76b 100644 --- a/src/Commands/Base/TokenHandler.cs +++ b/src/Commands/Base/TokenHandler.cs @@ -262,7 +262,7 @@ internal static async Task GetAzureADWorkloadIdentityTokenAsync(string r } /// - /// Returns an access token based on a Azure AD Workload Identity. Only works within Azure components supporting workload identities. + /// Returns an access token based on a Federated Identity. Only works within Azure components supporting federated identities like GitHub/AzureDevOps. /// /// The cmdlet scope in which this code runs. Used to write logging to. /// The HttpClient that will be reused to fetch the token to avoid port exhaustion