Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
// ----------------------------------------------------------------------------------

using Azure.Identity;

using Microsoft.Azure.Commands.Common.Authentication;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Core;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Interfaces;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Models;
using Microsoft.Azure.Commands.Common.Authentication.Config.Models;
using Microsoft.Azure.Commands.Common.Authentication.Factories;
Expand All @@ -41,7 +39,6 @@
using Microsoft.WindowsAzure.Commands.Common.Sanitizer;
using Microsoft.WindowsAzure.Commands.Common.Utilities;
using Microsoft.WindowsAzure.Commands.Utilities.Common;

using System;
using System.Collections.Concurrent;
using System.Linq;
Expand Down Expand Up @@ -237,6 +234,10 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
[ValidateNotNullOrEmpty]
public string FederatedToken { get; set; }

[Parameter(ParameterSetName = UserParameterSet, Mandatory = false, HelpMessage = "Specifies the claims challenge with base64 encoding.")]
[ValidateNotNullOrEmpty]
public string ClaimsChallenge { get; set; }

protected override IAzureContext DefaultContext
{
get
Expand Down Expand Up @@ -353,7 +354,6 @@ public override void ExecuteCmdlet()
{
subscriptionName = Subscription;
}

}
else if (AzureSession.Instance.TryGetComponent<IConfigManager>(nameof(IConfigManager), out var configManager))
{
Expand All @@ -373,6 +373,15 @@ public override void ExecuteCmdlet()
}
}

string claimsChallenge = null;
if (this.IsParameterBound(c => c.ClaimsChallenge))
{
if (!ClaimsChallengeUtilities.TryParseClaimsChallenge(ClaimsChallenge, out claimsChallenge))
{
throw new PSArgumentException(Resources.InvalidClaimsChallenge, nameof(ClaimsChallenge));
}
}

var azureAccount = new AzureAccount();

switch (ParameterSetName)
Expand Down Expand Up @@ -548,6 +557,7 @@ public override void ExecuteCmdlet()
SkipValidation,
new OpenIDConfiguration(Tenant, baseUri: _environment.ActiveDirectoryAuthority, httpClientFactory: httpClientFactory),
WriteWarningEvent, //Could not use WriteWarning directly because it may be in worker thread
claimsChallenge,
name,
shouldPopulateContextList,
MaxContextPopulation,
Expand Down
1 change: 1 addition & 0 deletions src/Accounts/Accounts/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
-->

## Upcoming Release
* Added new parameter `-ClaimsChallenge` to `Connect-AzAccount` to support claims challenge authentication for MFA.
* Refined the error message when a cmdlet fails because of policy violations about Multi-Factor Authentication (MFA) to provide more actionable guidance.

## Version 5.1.1
Expand Down
50 changes: 27 additions & 23 deletions src/Accounts/Accounts/Models/RMProfileClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using Microsoft.Azure.Commands.Common.Authentication;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions.Interfaces;
Expand All @@ -22,13 +23,11 @@
using Microsoft.Azure.Commands.Profile.Utilities;
using Microsoft.Rest.Azure;
using Microsoft.WindowsAzure.Commands.Common;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Security;

using AuthenticationMessages = Microsoft.Azure.Commands.Common.Authentication.Properties.Resources;
using ProfileMessages = Microsoft.Azure.Commands.Profile.Properties.Resources;
using ResourceMessages = Microsoft.Azure.Commands.ResourceManager.Common.Properties.Resources;
Expand All @@ -52,7 +51,7 @@ private IAzureContext DefaultContext
{
get
{
if(_profile == null || _profile.DefaultContext == null || _profile.DefaultContext.Account == null)
if (_profile == null || _profile.DefaultContext == null || _profile.DefaultContext.Account == null)
{
throw new PSInvalidOperationException(ResourceMessages.RunConnectAccount);
}
Expand Down Expand Up @@ -130,21 +129,22 @@ public AzureRmProfile Login(
bool skipValidation,
IOpenIDConfiguration openIDConfigDoc,
Action<string> promptAction,
string claimsChallenge = null,
string name = null,
bool shouldPopulateContextList = true,
int maxContextPopulation = Profile.ConnectAzureRmAccountCommand.DefaultMaxContextPopulation,
string authScope = null,
bool IsInteractiveContextSelectionEnabled = true)
{

WriteInteractiveInformationMessage($"{PSStyle.ForegroundColor.BrightYellow}{Resources.PleaseSelectAccount}{PSStyle.Reset}{System.Environment.NewLine}");

IAzureSubscription defaultSubscription = null;
IAzureTenant defaultTenant = null;
List<AzureSubscription> subscriptions = new List<AzureSubscription>();
List<AzureSubscription> tempSubscriptions = null;
string tenantName = null;

bool selectSubscriptionFromList = AzureAccount.AccountType.User.Equals(account.Type) &&
IsInteractiveContextSelectionEnabled &&
string.IsNullOrEmpty(subscriptionId) &&
Expand All @@ -161,9 +161,9 @@ public AzureRmProfile Login(
SubscritpionClientCandidates.Reset();

bool needDataPlanAuthFirst = !string.IsNullOrEmpty(authScope);
if(needDataPlanAuthFirst)
if (needDataPlanAuthFirst)
{
var token = AcquireAccessToken(account, environment, tenantIdOrName, password, promptBehavior, promptAction, authScope);
var token = AcquireAccessToken(account, environment, tenantIdOrName, password, promptBehavior, promptAction, claimsChallenge, authScope);
promptBehavior = ShowDialog.Never;
}

Expand Down Expand Up @@ -202,7 +202,8 @@ public AzureRmProfile Login(
tenantIdOrName,
password,
promptBehavior,
promptAction);
promptAction,
claimsChallenge);

if (!Guid.TryParse(tenantIdOrName, out Guid _))
{
Expand All @@ -229,7 +230,7 @@ public AzureRmProfile Login(
}
}
}
catch(Exception e)
catch (Exception e)
{
string baseMessage = string.Format(ProfileMessages.TenantDomainNotFound, tenantIdOrName);
var typeMessageMap = new Dictionary<string, string>
Expand Down Expand Up @@ -293,7 +294,7 @@ public AzureRmProfile Login(

try
{
token = AcquireAccessToken(account, environment, tenant.Id, password, ShowDialog.Auto, null);
token = AcquireAccessToken(account, environment, tenant.Id, password, ShowDialog.Auto, null, claimsChallenge);
if (accountId == null)
{
accountId = account.Id;
Expand All @@ -314,7 +315,7 @@ public AzureRmProfile Login(
token = null;
}
}
catch(Exception e)
catch (Exception e)
{
WriteWarningMessage(string.Format(ProfileMessages.UnableToAqcuireToken, tenant.Id, e.Message));
WriteDebugMessage(string.Format(ProfileMessages.UnableToAqcuireToken, tenant.Id, e.ToString()));
Expand All @@ -334,7 +335,7 @@ public AzureRmProfile Login(
defaultTenant = tempTenant;
}
}
if(tempSubscription != null)
if (tempSubscription != null)
{
subscriptions.AddRange(tempSubscriptions);
}
Expand Down Expand Up @@ -397,7 +398,7 @@ public AzureRmProfile Login(
{
var defaultContext = _profile.DefaultContext;
var populatedSubscriptions = (maxContextPopulation < 0 || selectSubscriptionFromList) ? ListSubscriptions(tenantIdOrName) : ListSubscriptions(tenantIdOrName).Take(maxContextPopulation);

foreach (var subscription in populatedSubscriptions)
{
IAzureTenant tempTenant = InteractiveSubscriptionSelectionHelper.GetDetailedTenantFromQueryHistory(_queriedTenants, subscription.GetProperty(AzureSubscription.Property.Tenants)) ?? new AzureTenant()
Expand Down Expand Up @@ -449,7 +450,7 @@ public IAzureContext SetCurrentContext(string subscriptionNameOrId, string tenan
}

var tenantFromSubscription = subscription.GetTenant();
tenant = string.IsNullOrWhiteSpace(tenantId) ? (string.IsNullOrEmpty(tenantFromSubscription) ? context.Tenant : CreateTenant(tenantFromSubscription)): CreateTenant(tenantId);
tenant = string.IsNullOrWhiteSpace(tenantId) ? (string.IsNullOrEmpty(tenantFromSubscription) ? context.Tenant : CreateTenant(tenantFromSubscription)) : CreateTenant(tenantId);
}
else if (!string.IsNullOrWhiteSpace(tenantId))
{
Expand Down Expand Up @@ -536,14 +537,14 @@ public bool TryGetSubscriptionListByName(string tenantId, string subscriptionNam
HashSet<Guid> existedSubscriptionIds = new HashSet<Guid>();

// Consider subscription in Home tenant first, exclude duplicate subscriptions by id.
foreach(IAzureSubscription subscription in subscriptions)
foreach (IAzureSubscription subscription in subscriptions)
{
if (subscription is PSAzureSubscription && subscription.GetTenant() != null
if (subscription is PSAzureSubscription && subscription.GetTenant() != null
&& subscription.GetHomeTenant().Equals(subscription.GetTenant()) && existedSubscriptionIds.Add(subscription.GetId()))
{
subscriptionList.Add(subscription);
}

}
// Consider other subscriptions.
foreach (IAzureSubscription subscription in subscriptions)
Expand Down Expand Up @@ -679,6 +680,7 @@ private IAccessToken AcquireAccessToken(
SecureString password,
string promptBehavior,
Action<string> promptAction,
string claimsChallenge = null,
string resourceId = AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId)
{
if (account.Type == AzureAccount.AccountType.AccessToken)
Expand All @@ -689,11 +691,13 @@ private IAccessToken AcquireAccessToken(

var optionalParameters = new Dictionary<string, object>()
{
{AuthenticationFactory.TokenCacheParameterName, _cache},
{AuthenticationFactory.ResourceIdParameterName, resourceId },
{AuthenticationFactory.CmdletContextParameterName, CmdletContext }
{ AuthenticationFactory.ResourceIdParameterName, resourceId },
{ AuthenticationFactory.ClaimsChallengeParameterName, claimsChallenge },
{ AuthenticationFactory.TokenCacheParameterName, _cache },
{ AuthenticationFactory.CmdletContextParameterName, CmdletContext }
};


return AzureSession.Instance.AuthenticationFactory.Authenticate(
account,
environment,
Expand Down Expand Up @@ -814,7 +818,7 @@ private List<AzureTenant> ListAccountTenants(

result = SubscriptionAndTenantClient?.ListAccountTenants(commonTenantToken, environment);
}
catch(Exception e)
catch (Exception e)
{
WriteWarningMessage(string.Format(ProfileMessages.UnableToAqcuireToken, commonTenant, e.Message));
WriteDebugMessage(string.Format(ProfileMessages.UnableToAqcuireToken, commonTenant, e.ToString()));
Expand Down Expand Up @@ -861,7 +865,7 @@ private IEnumerable<AzureSubscription> ListAllSubscriptionsForTenant(
{
accessToken = AcquireAccessToken(account, environment, tenantId, password, promptBehavior, null);
}
catch(Exception e)
catch (Exception e)
{
WriteWarningMessage(string.Format(ProfileMessages.UnableToAqcuireToken, tenantId, e.Message));
WriteDebugMessage(string.Format(ProfileMessages.UnableToAqcuireToken, tenantId, e.ToString()));
Expand All @@ -881,7 +885,7 @@ private void WriteWarningMessage(string message)

private void WriteDebugMessage(string message)
{
if(DebugLog != null)
if (DebugLog != null)
{
DebugLog(message);
}
Expand Down
9 changes: 9 additions & 0 deletions src/Accounts/Accounts/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Accounts/Accounts/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -637,4 +637,7 @@
<data name="RopcDeprecationSovereignClouds" xml:space="preserve">
<value>Using authentication with username and password in the command line is strongly discouraged. Consider using one of the recommended authentication methods. For more details, see https://go.microsoft.com/fwlink/?linkid=2276971</value>
</data>
<data name="InvalidClaimsChallenge" xml:space="preserve">
<value>Invalid claims challenge format. It should be a valid base64 encoded string.</value>
</data>
</root>
Loading
Loading