Skip to content

Commit b877850

Browse files
authored
Provide certificate file login method (#14738)
* Use certificate file as input * Address review comments
1 parent a820ce7 commit b877850

File tree

8 files changed

+442
-46
lines changed

8 files changed

+442
-46
lines changed

src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
using System;
1616
using System.Collections.Concurrent;
17+
using System.Linq;
1718
using System.Management.Automation;
1819
using System.Security;
1920
using System.Threading;
@@ -52,6 +53,7 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
5253
public const string UserWithCredentialParameterSet = "UserWithCredential";
5354
public const string ServicePrincipalParameterSet = "ServicePrincipalWithSubscriptionId";
5455
public const string ServicePrincipalCertificateParameterSet= "ServicePrincipalCertificateWithSubscriptionId";
56+
public const string ServicePrincipalCertificateFileParameterSet = "ServicePrincipalCertificateFileWithSubscriptionId";
5557
public const string AccessTokenParameterSet = "AccessTokenWithSubscriptionId";
5658
public const string ManagedServiceParameterSet = "ManagedServiceLogin";
5759
public const string MSIEndpointVariable = "MSI_ENDPOINT";
@@ -79,12 +81,16 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
7981

8082
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
8183
Mandatory = true, HelpMessage = "SPN")]
84+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
85+
Mandatory = true, HelpMessage = "SPN")]
8286
public string ApplicationId { get; set; }
8387

8488
[Parameter(ParameterSetName = ServicePrincipalParameterSet,
8589
Mandatory = true)]
8690
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
8791
Mandatory = false)]
92+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
93+
Mandatory = false)]
8894
public SwitchParameter ServicePrincipal { get; set; }
8995

9096
[Parameter(ParameterSetName = UserParameterSet,
@@ -97,6 +103,8 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
97103
Mandatory = false, HelpMessage = "Tenant name or ID")]
98104
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
99105
Mandatory = true, HelpMessage = "Tenant name or ID")]
106+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
107+
Mandatory = true, HelpMessage = "Tenant name or ID")]
100108
[Parameter(ParameterSetName = ManagedServiceParameterSet,
101109
Mandatory = false, HelpMessage = "Optional tenant name or ID")]
102110
[Alias("Domain", "TenantId")]
@@ -138,6 +146,8 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
138146
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
139147
[Parameter(ParameterSetName = ServicePrincipalCertificateParameterSet,
140148
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
149+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet,
150+
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
141151
[Parameter(ParameterSetName = AccessTokenParameterSet,
142152
Mandatory = false, HelpMessage = "Subscription Name or ID", ValueFromPipeline = true)]
143153
[Parameter(ParameterSetName = ManagedServiceParameterSet,
@@ -187,6 +197,7 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
187197
[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.")]
188198
[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.")]
189199
[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.")]
200+
[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.")]
190201
[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.")]
191202
[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.")]
192203
[PSDefaultValue(Help = DefaultMaxContextPopulationString, Value = DefaultMaxContextPopulation)]
@@ -201,9 +212,17 @@ public class ConnectAzureRmAccountCommand : AzureContextModificationCmdlet, IMod
201212
[Parameter(Mandatory = false, HelpMessage = "Overwrite the existing context with the same name, if any.")]
202213
public SwitchParameter Force { get; set; }
203214

204-
[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.")]
215+
[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.")]
216+
[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.")]
205217
public SwitchParameter SendCertificateChain { get; set; }
206218

219+
220+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, Mandatory = true, HelpMessage = "The path of certficate file in pkcs#12 format.")]
221+
public String CertificatePath { get; set; }
222+
223+
[Parameter(ParameterSetName = ServicePrincipalCertificateFileParameterSet, HelpMessage = "The password required to access the pkcs#12 certificate file.")]
224+
public SecureString CertificatePassword { get; set; }
225+
207226
protected override IAzureContext DefaultContext
208227
{
209228
get
@@ -305,6 +324,7 @@ public override void ExecuteCmdlet()
305324
azureAccount.SetProperty(AzureAccount.Property.KeyVaultAccessToken, KeyVaultAccessToken);
306325
break;
307326
case ServicePrincipalCertificateParameterSet:
327+
case ServicePrincipalCertificateFileParameterSet:
308328
case ServicePrincipalParameterSet:
309329
azureAccount.Type = AzureAccount.AccountType.ServicePrincipal;
310330
break;
@@ -345,7 +365,23 @@ public override void ExecuteCmdlet()
345365
azureAccount.SetThumbprint(CertificateThumbprint);
346366
}
347367

348-
if (ParameterSetName == ServicePrincipalCertificateParameterSet && SendCertificateChain)
368+
if( !string.IsNullOrWhiteSpace(CertificatePath))
369+
{
370+
var resolvedPath = this.SessionState.Path.GetResolvedPSPathFromPSPath(CertificatePath).FirstOrDefault()?.Path;
371+
if (string.IsNullOrEmpty(resolvedPath))
372+
{
373+
var parametersLog = $"- Invalid certificate path :'{CertificatePath}'.";
374+
throw new InvalidOperationException(parametersLog);
375+
}
376+
azureAccount.SetProperty(AzureAccount.Property.CertificatePath, resolvedPath);
377+
if (CertificatePassword != null)
378+
{
379+
azureAccount.SetProperty(AzureAccount.Property.CertificatePassword, CertificatePassword.ConvertToString());
380+
}
381+
}
382+
383+
if ((ParameterSetName == ServicePrincipalCertificateParameterSet || ParameterSetName == ServicePrincipalCertificateFileParameterSet)
384+
&& SendCertificateChain)
349385
{
350386
azureAccount.SetProperty(AzureAccount.Property.SendCertificateChain, SendCertificateChain.ToString());
351387
bool supressWarningOrError = false;
@@ -368,7 +404,7 @@ public override void ExecuteCmdlet()
368404
azureAccount.SetProperty(AzureAccount.Property.Tenants, Tenant);
369405
}
370406

371-
if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && string.IsNullOrEmpty(CertificateThumbprint))
407+
if (azureAccount.Type == AzureAccount.AccountType.ServicePrincipal && password != null)
372408
{
373409
azureAccount.SetProperty(AzureAccount.Property.ServicePrincipalSecret, password.ConvertToString());
374410
if (GetContextModificationScope() == ContextModificationScope.CurrentUser)

src/Accounts/Accounts/ChangeLog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
-->
2020

2121
## Upcoming Release
22+
* Supported certificate file as input parameter of Connect-AzAccount
2223

2324
## Version 2.3.0
2425
* Upgraded Azure.Identity to 1.4 and MSAL to 4.30.1
2526
* Removed obsolete parameters `ManagedServiceHostName`, `ManagedServicePort` and `ManagedServiceSecret` of cmdlet `Connect-AzAccount`, environment variables `MSI_ENDPOINT` and `MSI_SECRET` could be used instead
26-
* Customize display format of PSAzureRmAccount to hide secret of service principal [#14208]
27+
* Customized display format of PSAzureRmAccount to hide secret of service principal [#14208]
2728
* Added optional parameter `AuthScope` to `Connect-AzAccount` to support enhanced authentication of data plane features
2829
* Set retry times by environment variable [#14748]
2930
* Supported subject name issuer authentication

src/Accounts/Accounts/help/Connect-AzAccount.md

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ Connect-AzAccount [-Environment <String>] -CertificateThumbprint <String> -Appli
4545
[<CommonParameters>]
4646
```
4747

48+
### ServicePrincipalCertificateFileWithSubscriptionId
49+
```
50+
Connect-AzAccount [-Environment <String>] -ApplicationId <String> [-ServicePrincipal] -Tenant <String>
51+
[-Subscription <String>] [-ContextName <String>] [-SkipContextPopulation] [-MaxContextPopulation <Int32>]
52+
[-Force] [-SendCertificateChain] -CertificatePath <String> [-CertificatePassword <SecureString>]
53+
[-Scope <ContextModificationScope>] [-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
54+
[<CommonParameters>]
55+
```
56+
4857
### AccessTokenWithSubscriptionId
4958
```
5059
Connect-AzAccount [-Environment <String>] [-Tenant <String>] -AccessToken <String> [-GraphAccessToken <String>]
@@ -184,21 +193,21 @@ more information on creating a self-signed certificates and assigning them permi
184193
[Use Azure PowerShell to create a service principal with a certificate](/azure/active-directory/develop/howto-authenticate-service-principal-powershell)
185194

186195
```powershell
187-
$Thumbprint = '0SZTNJ34TCCMUJ5MJZGR8XQD3S0RVHJBA33Z8ZXV'
188-
$TenantId = '4cd76576-b611-43d0-8f2b-adcb139531bf'
189-
$ApplicationId = '3794a65a-e4e4-493d-ac1d-f04308d712dd'
196+
$Thumbprint = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
197+
$TenantId = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy'
198+
$ApplicationId = '00000000-0000-0000-0000-00000000'
190199
Connect-AzAccount -CertificateThumbprint $Thumbprint -ApplicationId $ApplicationId -Tenant $TenantId -ServicePrincipal
191200
```
192201

193202
```Output
194-
Account SubscriptionName TenantId Environment
195-
------- ---------------- -------- -----------
196-
xxxx-xxxx-xxxx-xxxx Subscription1 xxxx-xxxx-xxxx-xxxx AzureCloud
203+
Account SubscriptionName TenantId Environment
204+
------- ---------------- -------- -----------
205+
xxxxxxxx-xxxx-xxxx-xxxxxxxxx Subscription1 yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy AzureCloud
197206
198-
Account : 3794a65a-e4e4-493d-ac1d-f04308d712dd
207+
Account : xxxxxxxx-xxxx-xxxx-xxxxxxxx
199208
SubscriptionName : MyTestSubscription
200-
SubscriptionId : 85f0f653-1f86-4d2c-a9f1-042efc00085c
201-
TenantId : 4cd76576-b611-43d0-8f2b-adcb139531bf
209+
SubscriptionId : zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzz
210+
TenantId : yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy
202211
Environment : AzureCloud
203212
```
204213

@@ -216,6 +225,24 @@ Account SubscriptionName TenantId Environment
216225
yyyy-yyyy-yyyy-yyyy Subscription1 xxxx-xxxx-xxxx-xxxx AzureCloud
217226
```
218227

228+
### Example 9: Connect using certificate file
229+
230+
This example connects to an Azure account using certificate-based service principal authentication.
231+
The certificate file, which is specified by `CertficatePath`, should contains both certificate and private key as the input.
232+
233+
```powershell
234+
$securePassword = $plainPassword | ConvertTo-SecureString -AsPlainText -Force
235+
$TenantId = 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy'
236+
$ApplicationId = 'zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzz'
237+
Connect-AzAccount -ServicePrincipal -ApplicationId $ApplicationId -TenantId $TenantId -CertificatePath './certificatefortest.pfx' -CertificatePassword $securePassword
238+
```
239+
240+
```Output
241+
Account SubscriptionName TenantId Environment
242+
------- ---------------- -------- -----------
243+
xxxxxxxx-xxxx-xxxx-xxxxxxxx Subscription1 yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyy AzureCloud
244+
```
245+
219246
## PARAMETERS
220247

221248
### -AccessToken
@@ -275,7 +302,7 @@ Application ID of the service principal.
275302
276303
```yaml
277304
Type: System.String
278-
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
305+
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
279306
Aliases:
280307

281308
Required: True
@@ -300,6 +327,36 @@ Accept pipeline input: False
300327
Accept wildcard characters: False
301328
```
302329
330+
### -CertificatePassword
331+
The password required to access the pkcs#12 certificate file.
332+
333+
```yaml
334+
Type: System.Security.SecureString
335+
Parameter Sets: ServicePrincipalCertificateFileWithSubscriptionId
336+
Aliases:
337+
338+
Required: False
339+
Position: Named
340+
Default value: None
341+
Accept pipeline input: False
342+
Accept wildcard characters: False
343+
```
344+
345+
### -CertificatePath
346+
The path of certficate file in pkcs#12 format.
347+
348+
```yaml
349+
Type: System.String
350+
Parameter Sets: ServicePrincipalCertificateFileWithSubscriptionId
351+
Aliases:
352+
353+
Required: True
354+
Position: Named
355+
Default value: None
356+
Accept pipeline input: False
357+
Accept wildcard characters: False
358+
```
359+
303360
### -CertificateThumbprint
304361
305362
Certificate Hash or Thumbprint.
@@ -486,7 +543,7 @@ Specifies if the x5c claim (public key of the certificate) should be sent to the
486543

487544
```yaml
488545
Type: System.Management.Automation.SwitchParameter
489-
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
546+
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
490547
Aliases:
491548
492549
Required: False
@@ -514,7 +571,7 @@ Accept wildcard characters: False
514571

515572
```yaml
516573
Type: System.Management.Automation.SwitchParameter
517-
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId
574+
Parameter Sets: ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
518575
Aliases:
519576
520577
Required: False
@@ -594,7 +651,7 @@ Accept wildcard characters: False
594651

595652
```yaml
596653
Type: System.String
597-
Parameter Sets: ServicePrincipalWithSubscriptionId, ServicePrincipalCertificateWithSubscriptionId
654+
Parameter Sets: ServicePrincipalWithSubscriptionId, ServicePrincipalCertificateWithSubscriptionId, ServicePrincipalCertificateFileWithSubscriptionId
598655
Aliases: Domain, TenantId
599656
600657
Required: True

0 commit comments

Comments
 (0)