Skip to content

Commit

Permalink
Fix #3717: improvements to EntraID app creation and Connect for custo…
Browse files Browse the repository at this point in the history
…m Azure env (#4219)

* Fix #3717: improvements to EntraID app creation and Connect for custom Azure env

* Fix internals

---------

Co-authored-by: Gautam Sheth <[email protected]>
  • Loading branch information
gautamdsheth and Gautam Sheth authored Aug 31, 2024
1 parent 7054277 commit 21958e0
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 25 deletions.
2 changes: 1 addition & 1 deletion documentation/Connect-PnPOnline.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ The Azure environment to use for authentication, the defaults to 'Production' wh
```yaml
Type: AzureEnvironment
Parameter Sets: Credentials, SharePoint ACS (Legacy) App Only, App-Only with Azure Active Directory, App-Only with Azure Active Directory using a certificate from the Windows Certificate Management Store by thumbprint, PnP Management Shell / DeviceLogin, Interactive, Access Token, Environment Variable
Parameter Sets: Credentials, SharePoint ACS (Legacy) App Only, App-Only with Azure Active Directory, App-Only with Azure Active Directory using a certificate from the Windows Certificate Management Store by thumbprint, PnP Management Shell / DeviceLogin, Interactive, Access Token, Environment Variable, Managed Identity
Aliases:
Accepted values: Production, PPE, China, Germany, USGovernment, USGovernmentHigh, USGovernmentDoD, Custom

Expand Down
28 changes: 28 additions & 0 deletions documentation/Register-PnPAzureADApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ Register-PnPAzureADApp -ApplicationName <String>
[-CertificatePassword <SecureString>]
[-NoPopup]
[-LogoFilePath <string>]
[-MicrosoftGraphEndPoint <string>]
[-EntraIDLoginEndPoint <string>]
```

### Existing Certificate
Expand Down Expand Up @@ -394,6 +396,32 @@ Position: Named
Accept pipeline input: False
```
### -EntraIDLoginEndPoint
Sets the EntraID login endpoint to be used for creation of the app. This only works if Azure Environment parameter is set to `Custom`

```yaml
Type: String
Parameter Sets: (All)
Required: False
Position: Named
Accept pipeline input: False
```

### -MicrosoftGraphEndPoint

Sets the Microsoft Graph endpoint to be used for creation of the app. This only works if Azure Environment parameter is set to `Custom`

```yaml
Type: String
Parameter Sets: (All)
Required: False
Position: Named
Accept pipeline input: False
```

## RELATED LINKS

[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
Expand Down
30 changes: 29 additions & 1 deletion documentation/Register-PnPEntraIDAppForInteractiveLogin.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ schema: 2.0.0
applicable: SharePoint Online
online version: https://pnp.github.io/powershell/cmdlets/Register-PnPEntraIDAppForInteractiveLogin.html
external help file: PnP.PowerShell.dll-Help.xml
title: Register-PnPAzureADApp
title: Register-PnPEntraIDAppForInteractiveLogin
---

# Register-PnPEntraIDAppForInteractiveLogin
Expand All @@ -25,6 +25,8 @@ Register-PnPEntraIDAppForInteractiveLogin -ApplicationName <String>
[-SharePointDelegatePermissions <Permission[]>]
[-NoPopup]
[-LogoFilePath <string>]
[-MicrosoftGraphEndPoint <string>]
[-EntraIDLoginEndPoint <string>]
```

### Generate App using Device Login
Expand Down Expand Up @@ -184,6 +186,32 @@ Position: Named
Accept pipeline input: False
```
### -EntraIDLoginEndPoint
Sets the EntraID login endpoint to be used for creation of the app. This only works if Azure Environment parameter is set to `Custom`

```yaml
Type: String
Parameter Sets: (All)
Required: False
Position: Named
Accept pipeline input: False
```

### -MicrosoftGraphEndPoint

Sets the Microsoft Graph endpoint to be used for creation of the app. This only works if Azure Environment parameter is set to `Custom`

```yaml
Type: String
Parameter Sets: (All)
Required: False
Position: Named
Accept pipeline input: False
```

## RELATED LINKS

[Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp)
Expand Down
24 changes: 15 additions & 9 deletions src/Commands/AzureAD/RegisterAzureADApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ public class RegisterAzureADApp : BasePSCmdlet, IDynamicParameters
[Parameter(Mandatory = false)]
public SwitchParameter SkipCertCreation;

[Parameter(Mandatory = false)]
public string MicrosoftGraphEndPoint;

[Parameter(Mandatory = false)]
public string EntraIDLoginEndPoint;

protected override void ProcessRecord()
{
if (ParameterSpecified(nameof(Store)) && !OperatingSystem.IsWindows())
Expand Down Expand Up @@ -128,7 +134,7 @@ protected override void ProcessRecord()

using (var authenticationManager = new AuthenticationManager())
{
loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment);
loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment) ?? EntraIDLoginEndPoint;
}

var permissionScopes = new PermissionScopes();
Expand Down Expand Up @@ -423,7 +429,7 @@ private string GetAuthToken(CmdletMessageWriter messageWriter)
{
Task.Factory.StartNew(() =>
{
token = AzureAuthHelper.AuthenticateDeviceLogin(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment);
token = AzureAuthHelper.AuthenticateDeviceLogin(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, MicrosoftGraphEndPoint);
if (token == null)
{
messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
Expand All @@ -436,7 +442,7 @@ private string GetAuthToken(CmdletMessageWriter messageWriter)
{
Task.Factory.StartNew(() =>
{
token = AzureAuthHelper.AuthenticateInteractive(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, Tenant);
token = AzureAuthHelper.AuthenticateInteractive(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, Tenant, MicrosoftGraphEndPoint);
if (token == null)
{
messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
Expand All @@ -460,7 +466,7 @@ private string GetAuthToken(CmdletMessageWriter messageWriter)
{
throw new PSArgumentException("Password is required or use -DeviceLogin or -Interactive");
}
token = AzureAuthHelper.AuthenticateAsync(Tenant, Username, Password, AzureEnvironment).GetAwaiter().GetResult();
token = AzureAuthHelper.AuthenticateAsync(Tenant, Username, Password, AzureEnvironment, MicrosoftGraphEndPoint).GetAwaiter().GetResult();
}

return token;
Expand Down Expand Up @@ -542,7 +548,7 @@ private bool AppExists(string appName, HttpClient httpClient, string token)
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var azureApps = RestHelper.Get<RestResultCollection<AzureADApp>>(httpClient, $"{graphEndpoint}/v1.0/applications?$filter=displayName eq '{appName}'&$select=Id", token);
Expand Down Expand Up @@ -591,7 +597,7 @@ private AzureADApp CreateApp(string loginEndPoint, HttpClient httpClient, string
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var azureApp = RestHelper.Post<AzureADApp>(httpClient, $"{graphEndpoint}/v1.0/applications", token, payload);
Expand Down Expand Up @@ -636,7 +642,7 @@ private void StartConsentFlow(string loginEndPoint, AzureADApp azureApp, string
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var resource = scopes.FirstOrDefault(s => s.resourceAppId == PermissionScopes.ResourceAppId_Graph) != null ? $"{graphEndpoint}/.default" : "https://microsoft.sharepoint-df.com/.default";
Expand Down Expand Up @@ -712,12 +718,12 @@ private void SetLogo(AzureADApp azureApp, string token)
{
try
{
WriteVerbose("Setting the logo for the En AD app");
WriteVerbose("Setting the logo for the EntraID app");

var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var endpoint = $"{graphEndpoint}/v1.0/applications/{azureApp.Id}/logo";
Expand Down
22 changes: 14 additions & 8 deletions src/Commands/AzureAD/RegisterEntraIDAppForInteractiveLogin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public class RegisterEntraIDAppForInteractiveLogin : BasePSCmdlet, IDynamicParam
[Parameter(Mandatory = false)]
public string LogoFilePath;

[Parameter(Mandatory = false)]
public string MicrosoftGraphEndPoint;

[Parameter(Mandatory = false)]
public string EntraIDLoginEndPoint;

protected override void ProcessRecord()
{
var redirectUri = "http://localhost";
Expand All @@ -60,7 +66,7 @@ protected override void ProcessRecord()

using (var authenticationManager = new AuthenticationManager())
{
loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment);
loginEndPoint = authenticationManager.GetAzureADLoginEndPoint(AzureEnvironment) ?? EntraIDLoginEndPoint;
}

var permissionScopes = new PermissionScopes();
Expand Down Expand Up @@ -344,7 +350,7 @@ private string GetAuthToken(CmdletMessageWriter messageWriter)
{
Task.Factory.StartNew(() =>
{
token = AzureAuthHelper.AuthenticateDeviceLogin(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment);
token = AzureAuthHelper.AuthenticateDeviceLogin(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, MicrosoftGraphEndPoint);
if (token == null)
{
messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
Expand All @@ -357,7 +363,7 @@ private string GetAuthToken(CmdletMessageWriter messageWriter)
{
Task.Factory.StartNew(() =>
{
token = AzureAuthHelper.AuthenticateInteractive(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, Tenant);
token = AzureAuthHelper.AuthenticateInteractive(cancellationTokenSource, messageWriter, NoPopup, AzureEnvironment, Tenant, MicrosoftGraphEndPoint);
if (token == null)
{
messageWriter.WriteWarning("Operation cancelled or no token retrieved.");
Expand All @@ -377,7 +383,7 @@ private bool AppExists(string appName, HttpClient httpClient, string token)
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var azureApps = RestHelper.Get<RestResultCollection<AzureADApp>>(httpClient, $"{graphEndpoint}/v1.0/applications?$filter=displayName eq '{appName}'&$select=Id", token);
Expand Down Expand Up @@ -408,7 +414,7 @@ private AzureADApp CreateApp(string loginEndPoint, HttpClient httpClient, string
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var azureApp = RestHelper.Post<AzureADApp>(httpClient, $"{graphEndpoint}/v1.0/applications", token, payload);
Expand Down Expand Up @@ -453,7 +459,7 @@ private void StartConsentFlow(string loginEndPoint, AzureADApp azureApp, string
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var resource = scopes.FirstOrDefault(s => s.resourceAppId == PermissionScopes.ResourceAppId_Graph) != null ? $"{graphEndpoint}/.default" : "https://microsoft.sharepoint-df.com/.default";
Expand Down Expand Up @@ -532,12 +538,12 @@ private void SetLogo(AzureADApp azureApp, string token)
{
try
{
WriteVerbose("Setting the logo for the En AD app");
WriteVerbose("Setting the logo for the EntraID app");

var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(AzureEnvironment)}";
if (AzureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? MicrosoftGraphEndPoint;
}

var endpoint = $"{graphEndpoint}/v1.0/applications/{azureApp.Id}/logo";
Expand Down
4 changes: 4 additions & 0 deletions src/Commands/Base/ConnectOnline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ public class ConnectOnline : BasePSCmdlet
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_ACCESSTOKEN)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_ENVIRONMENTVARIABLE)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_OSLOGIN)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_SYSTEMASSIGNEDMANAGEDIDENTITY)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYCLIENTID)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYPRINCIPALID)]
[Parameter(Mandatory = false, ParameterSetName = ParameterSet_USERASSIGNEDMANAGEDIDENTITYBYAZURERESOURCEID)]
public AzureEnvironment AzureEnvironment = AzureEnvironment.Production;

// [Parameter(Mandatory = true, ParameterSetName = ParameterSet_APPONLYCLIENTIDCLIENTSECRETAADDOMAIN)]
Expand Down
12 changes: 6 additions & 6 deletions src/Commands/Utilities/AzureAuthHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace PnP.PowerShell.Commands.Utilities
public static class AzureAuthHelper
{
private static string CLIENTID = "1950a258-227b-4e31-a9cf-717495945fc2"; // Well-known Azure Management App Id
internal static async Task<string> AuthenticateAsync(string tenantId, string username, SecureString password, AzureEnvironment azureEnvironment)
internal static async Task<string> AuthenticateAsync(string tenantId, string username, SecureString password, AzureEnvironment azureEnvironment, string customGraphEndpoint = "")
{
if (string.IsNullOrEmpty(tenantId))
{
Expand All @@ -22,13 +22,13 @@ internal static async Task<string> AuthenticateAsync(string tenantId, string use
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}";
if (azureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? customGraphEndpoint;
}
return await authManager.GetAccessTokenAsync(new[] { $"{graphEndpoint}/.default" });
}
}

internal static string AuthenticateDeviceLogin(CancellationTokenSource cancellationTokenSource, CmdletMessageWriter messageWriter, bool noPopup, AzureEnvironment azureEnvironment, string clientId = "1950a258-227b-4e31-a9cf-717495945fc2")
internal static string AuthenticateDeviceLogin(CancellationTokenSource cancellationTokenSource, CmdletMessageWriter messageWriter, bool noPopup, AzureEnvironment azureEnvironment, string clientId = "1950a258-227b-4e31-a9cf-717495945fc2", string customGraphEndpoint = "")
{
try
{
Expand All @@ -54,7 +54,7 @@ internal static string AuthenticateDeviceLogin(CancellationTokenSource cancellat
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}";
if (azureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? customGraphEndpoint;
}
return authManager.GetAccessTokenAsync(new string[] { $"{graphEndpoint}/.default" }, cancellationTokenSource.Token).GetAwaiter().GetResult();
}
Expand All @@ -71,7 +71,7 @@ internal static string AuthenticateDeviceLogin(CancellationTokenSource cancellat
return null;
}

internal static string AuthenticateInteractive(CancellationTokenSource cancellationTokenSource, CmdletMessageWriter messageWriter, bool noPopup, AzureEnvironment azureEnvironment, string tenantId)
internal static string AuthenticateInteractive(CancellationTokenSource cancellationTokenSource, CmdletMessageWriter messageWriter, bool noPopup, AzureEnvironment azureEnvironment, string tenantId, string customGraphEndpoint = "")
{
try
{
Expand All @@ -91,7 +91,7 @@ internal static string AuthenticateInteractive(CancellationTokenSource cancellat
var graphEndpoint = $"https://{AuthenticationManager.GetGraphEndPoint(azureEnvironment)}";
if (azureEnvironment == AzureEnvironment.Custom)
{
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process);
graphEndpoint = Environment.GetEnvironmentVariable("MicrosoftGraphEndPoint", EnvironmentVariableTarget.Process) ?? customGraphEndpoint;
}
return authManager.GetAccessTokenAsync(new string[] { $"{graphEndpoint}/.default" }, cancellationTokenSource.Token).GetAwaiter().GetResult();
}
Expand Down

0 comments on commit 21958e0

Please sign in to comment.