Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide certificate file login method #14738

Merged
merged 2 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
41 changes: 38 additions & 3 deletions src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
public const string UserWithCredentialParameterSet = "UserWithCredential";
public const string ServicePrincipalParameterSet = "ServicePrincipalWithSubscriptionId";
public const string ServicePrincipalCertificateParameterSet= "ServicePrincipalCertificateWithSubscriptionId";
public const string ServicePrincipalCertificateFileParameterSet = "ServicePrincipalCertificateFileWithSubscriptionId";
public const string AccessTokenParameterSet = "AccessTokenWithSubscriptionId";
public const string ManagedServiceParameterSet = "ManagedServiceLogin";
public const string MSIEndpointVariable = "MSI_ENDPOINT";
Expand Down Expand Up @@ -79,12 +80,16 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod

[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
Mandatory = true, HelpMessage = "SPN")]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
Mandatory = true, HelpMessage = "SPN")]
public string ApplicationId { get; set; }

[Parameter(ParameterSetName = ServicePrincipalParameterSet,
Mandatory = true)]
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
Mandatory = false)]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
Mandatory = false)]
public SwitchParameter ServicePrincipal { get; set; }

[Parameter(ParameterSetName = UserParameterSet,
Expand All @@ -97,6 +102,8 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
Mandatory = false, HelpMessage = "Tenant name or ID")]
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
Mandatory = true, HelpMessage = "Tenant name or ID")]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
Mandatory = true, HelpMessage = "Tenant name or ID")]
[Parameter(ParameterSetName = ManagedServiceParameterSet,
Mandatory = false, HelpMessage = "Optional tenant name or ID")]
[Alias("Domain", "TenantId")]
Expand Down Expand Up @@ -138,6 +145,8 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
[Parameter(ParameterSetName = AccessTokenParameterSet,
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
[Parameter(ParameterSetName = ManagedServiceParameterSet,
Expand Down Expand Up @@ -187,6 +196,7 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
[Parameter(ParameterSetName = UserWithCredentialParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[Parameter(ParameterSetName = ServicePrincipalParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[Parameter(ParameterSetName = AccessTokenParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[Parameter(ParameterSetName = ManagedServiceParameterSet, Mandatory = false, HelpMessage = "Max subscription number to populate contexts after login. Default is " + DefaultMaxContextPopulationString + ". To populate all subscriptions to contexts, set to -1.")]
[PSDefaultValue(Help = DefaultMaxContextPopulationString, Value = DefaultMaxContextPopulation)]
Expand All @@ -201,9 +211,17 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
[Parameter(Mandatory = false, HelpMessage = "Overwrite the existing context with the same name, if any.")]
public SwitchParameter Force { get; set; }

[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet, Mandatory = false, HelpMessage = "Specifies if the x5c claim (public key of the certificate) should be sent to the STS to achieve easy certificate rollover in Azure AD.")]
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet, HelpMessage = "Specifies if the x5c claim (public key of the certificate) should be sent to the STS to achieve easy certificate rollover in Azure AD.")]
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, HelpMessage = "Specifies if the x5c claim (public key of the certificate) should be sent to the STS to achieve easy certificate rollover in Azure AD.")]
public SwitchParameter SendCertificateChain { get; set; }


[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, Mandatory = true, HelpMessage = "The path of certficate file in pkcs#12 format.")]
public String CertificatePath { get; set; }

[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, HelpMessage = "The password required to access the pkcs#12 certificate file.")]
public SecureString CertificatePassword { get; set; }

protected override IAzureContext DefaultContext
{
get
Expand Down Expand Up @@ -305,6 +323,7 @@ public override void ExecuteCmdlet()
azureAccount.SetProperty(AzureAccount.Property.KeyVaultAccessToken, KeyVaultAccessToken);
break;
case ServicePrincipalCertificateParameterSet:
case ServicePrincipalCertificateFileParameterSet:
case ServicePrincipalParameterSet:
azureAccount.Type = AzureAccount.AccountType.ServicePrincipal;
break;
Expand Down Expand Up @@ -345,7 +364,23 @@ public override void ExecuteCmdlet()
azureAccount.SetThumbprint(CertificateThumbprint);
}

if (ParameterSetName == ServicePrincipalCertificateParameterSet && SendCertificateChain)
if( !string.IsNullOrWhiteSpace(CertificatePath))
{
var resolvedPath = this.SessionState.Path.GetResolvedPSPathFromPSPath(CertificatePath).MaxOrDefault(p => p.Path, string.Empty);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MaxOrDefault or FirstOrDefault?

if (string.IsNullOrEmpty(resolvedPath))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we verify whether file exist here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If file doesn't exist, GetResolvedPSPathFromPSPath will throw an exception with the error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test in Linux.
Please refer to the following pic for the error output
certificateFileLogin

{
var parametersLog = $"- Invalid certificate path :'{CertificatePath}'.";
throw new InvalidOperationException(parametersLog);
}
azureAccount.SetProperty(AzureAccount.Property.CertificatePath, resolvedPath);
if (CertificatePassword != null)
{
azureAccount.SetProperty(AzureAccount.Property.CertificatePassword, CertificatePassword.ConvertToString());
}
}

if ((ParameterSetName == ServicePrincipalCertificateParameterSet || ParameterSetName == ServicePrincipalCertificateFileParameterSet)
&& SendCertificateChain)
{
azureAccount.SetProperty(AzureAccount.Property.SendCertificateChain, SendCertificateChain.ToString());
bool supressWarningOrError = false;
Expand All @@ -368,7 +403,7 @@ public override void ExecuteCmdlet()
azureAccount.SetProperty(AzureAccount.Property.Tenants, Tenant);
}

if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && string.IsNullOrEmpty(CertificateThumbprint))
if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && string.IsNullOrEmpty(CertificateThumbprint) && password != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to use if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && password != null)

{
azureAccount.SetProperty(AzureAccount.Property.ServicePrincipalSecret, password.ConvertToString());
if (GetContextModificationScope() == ContextModificationScope.CurrentUser)
Expand Down
3 changes: 2 additions & 1 deletion src/Accounts/Accounts/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
-->

## Upcoming Release
* Supported certificate file as input parameter of Connect-AzAccount

## Version 2.3.0
* Upgraded Azure.Identity to 1.4 and MSAL to 4.30.1
* Removed obsolete parameters `ManagedServiceHostName`, `ManagedServicePort` and `ManagedServiceSecret` of cmdlet `Connect-AzAccount`, environment variables `MSI_ENDPOINT` and `MSI_SECRET` could be used instead
* Customize display format of PSAzureRmAccount to hide secret of service principal [#14208]
* Customized display format of PSAzureRmAccount to hide secret of service principal [#14208]
* Added optional parameter `AuthScope` to `Connect-AzAccount` to support enhanced authentication of data plane features
* Set retry times by environment variable [#14748]
* Supported subject name issuer authentication
Expand Down
83 changes: 70 additions & 13 deletions src/Accounts/Accounts/help/Connect-AzAccount.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ Connect-AzAccount [-Environment <String>] -CertificateThumbprint <String> -Appli
[<CommonParameters>]
```

### ServicePrincipalCertificateFileWithSubscriptionId
```
Connect-AzAccount [-Environment <String>] -ApplicationId <String> [-ServicePrincipal] -Tenant <String>
[-Subscription <String>] [-ContextName <String>] [-SkipContextPopulation] [-MaxContextPopulation <Int32>]
[-Force] [-SendCertificateChain] -CertificatePath <String> [-CertificatePassword <SecureString>]
[-Scope <ContextModificationScope>] [-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
[<CommonParameters>]
```

### AccessTokenWithSubscriptionId
```
Connect-AzAccount [-Environment <String>] [-Tenant <String>] -AccessToken <String> [-GraphAccessToken <String>]
Expand Down Expand Up @@ -184,21 +193,21 @@ more information on creating a self-signed certificates and assigning them permi
[Use Azure PowerShell to create a service principal with a certificate](/azure/active-directory/develop/howto-authenticate-service-principal-powershell)

```powershell
$Thumbprint = '0SZTNJ34TCCMUJ5MJZGR8XQD3S0RVHJBA33Z8ZXV'
$TenantId = '4cd76576-b611-43d0-8f2b-adcb139531bf'
$ApplicationId = '3794a65a-e4e4-493d-ac1d-f04308d712dd'
$Thumbprint = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
$TenantId = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy'
$ApplicationId = '00000000-0000-0000-0000-00000000'
Connect-AzAccount -CertificateThumbprint $Thumbprint -ApplicationId $ApplicationId -Tenant $TenantId -ServicePrincipal
```

```Output
Account SubscriptionName TenantId Environment
------- ---------------- -------- -----------
xxxx-xxxx-xxxx-xxxx Subscription1 xxxx-xxxx-xxxx-xxxx AzureCloud
Account SubscriptionName TenantId Environment
------- ---------------- -------- -----------
xxxxxxxx-xxxx-xxxx-xxxxxxxxx Subscription1 yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy AzureCloud
Account : 3794a65a-e4e4-493d-ac1d-f04308d712dd
Account : xxxxxxxx-xxxx-xxxx-xxxxxxxx
SubscriptionName : MyTestSubscription
SubscriptionId : 85f0f653-1f86-4d2c-a9f1-042efc00085c
TenantId : 4cd76576-b611-43d0-8f2b-adcb139531bf
SubscriptionId : zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzz
TenantId : yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy
Environment : AzureCloud
```

Expand All @@ -216,6 +225,24 @@ Account SubscriptionName TenantId Environment
yyyy-yyyy-yyyy-yyyy Subscription1 xxxx-xxxx-xxxx-xxxx AzureCloud
```

### Example 9: Connect using certificate file

This example connects to an Azure account using certificate-based service principal authentication.
The certificate file, which is specified by `CertficatePath`, should contains both certificate and private key as the input.

```powershell
$securePassword = $plainPassword | ConvertTo-SecureString -AsPlainText -Force
$TenantId = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy'
$ApplicationId = 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzz'
Connect-AzAccount -ServicePrincipal -ApplicationId $ApplicationId -TenantId $TenantId -CertificatePath './certificatefortest.pfx' -CertificatePassword $securePassword
```

```Output
Account SubscriptionName TenantId Environment
------- ---------------- -------- -----------
xxxxxxxx-xxxx-xxxx-xxxxxxxx Subscription1 yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy AzureCloud
```

## PARAMETERS

### -AccessToken
Expand Down Expand Up @@ -275,7 +302,7 @@ Application ID of the service principal.
```yaml
Type: System.String
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
Aliases:

Required: True
Expand All @@ -300,6 +327,36 @@ Accept pipeline input: False
Accept wildcard characters: False
```
### -CertificatePassword
The password required to access the pkcs#12 certificate file.
```yaml
Type: System.Security.SecureString
Parameter Sets: ServicePrincipalCertificateFileWithSubscriptionId
Aliases:

Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -CertificatePath
The path of certficate file in pkcs#12 format.
```yaml
Type: System.String
Parameter Sets: ServicePrincipalCertificateFileWithSubscriptionId
Aliases:

Required: True
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -CertificateThumbprint
Certificate Hash or Thumbprint.
Expand Down Expand Up @@ -486,7 +543,7 @@ Specifies if the x5c claim (public key of the certificate) should be sent to the

```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
Aliases:
Required: False
Expand Down Expand Up @@ -514,7 +571,7 @@ Accept wildcard characters: False

```yaml
Type: System.Management.Automation.SwitchParameter
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
Aliases:
Required: False
Expand Down Expand Up @@ -594,7 +651,7 @@ Accept wildcard characters: False

```yaml
Type: System.String
Parameter Sets: ServicePrincipalWithSubscriptionId, ServicePrincipalCertificateWithSubscriptionId
Parameter Sets: ServicePrincipalWithSubscriptionId, ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
Aliases: Domain, TenantId
Required: True
Expand Down
Loading