Skip to content

Commit c834195

Browse files
authored
[KeyVault] Supported accepting rotation policy in a JSON file (#18448)
* Supported accepting rotation policy in a JSON file * suppress breaking change issues * upgrade Azure.Security.KeyVault.Keys to 4.3.0
1 parent d770913 commit c834195

File tree

13 files changed

+355
-130
lines changed

13 files changed

+355
-130
lines changed

src/CosmosDB/CosmosDB/CosmosDB.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<Import Project="$(MSBuildThisFileDirectory)..\..\Az.props" />
88
<ItemGroup>
9-
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.7" />
9+
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0" />
1010
<PackageReference Include="Microsoft.Azure.Management.CosmosDB" Version="3.7.0-preview" />
1111
</ItemGroup>
1212

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"lifetimeActions": [
3+
{
4+
"trigger": {
5+
"timeAfterCreate": "P18M",
6+
"timeBeforeExpiry": null
7+
},
8+
"action": {
9+
"type": "Rotate"
10+
}
11+
},
12+
{
13+
"trigger": {
14+
"timeBeforeExpiry": "P30D"
15+
},
16+
"action": {
17+
"type": "Notify"
18+
}
19+
}
20+
],
21+
"attributes": {
22+
"expiryTime": "P2Y"
23+
}
24+
}

src/KeyVault/KeyVault/ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
- Additional information about change #1
1919
-->
2020
## Upcoming Release
21+
* Supported accepting rotation policy in a JSON file
22+
* [Breaking Change] Changed parameter `ExpiresIn` in `Set-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string. It must be an ISO 8601 duration like "P30D" for 30 days.
23+
* [Breaking Change] Changed output properties `ExpiresIn`, `TimeAfterCreate` and `TimeBeforeExpiry` of `Set-AzKeyVaultKeyRotationPolicy` and `Get-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string.
2124
* Supported creating/updating key with release policy in a Managed HSM
2225

2326
## Version 4.5.0

src/KeyVault/KeyVault/Commands/Key/KeyRotation/SetAzKeyVaultKeyRotationPolicy.cs

Lines changed: 148 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,113 @@
1-
using Microsoft.Azure.Commands.KeyVault.Models;
1+
using Microsoft.Azure.Commands.Common.Exceptions;
2+
using Microsoft.Azure.Commands.KeyVault.Models;
23
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
4+
using Microsoft.WindowsAzure.Commands.Utilities.Common;
5+
6+
using Newtonsoft.Json;
37

48
using System;
59
using System.Management.Automation;
10+
using Track2Sdk = Azure.Security.KeyVault.Keys;
11+
using System.IO;
12+
using Microsoft.WindowsAzure.Commands.Common;
13+
using Microsoft.Azure.Commands.KeyVault.Properties;
14+
using System.Collections.Generic;
615

716
namespace Microsoft.Azure.Commands.KeyVault.Commands.Key
817
{
918
/// <summary>
1019
/// Updates the KeyRotationPolicy for the specified key in Key Vault.
1120
/// </summary>
12-
[Cmdlet(VerbsCommon.Set, ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultKeyRotationPolicy", SupportsShouldProcess = true, DefaultParameterSetName = ByVaultNameParameterSet)]
21+
[Cmdlet(VerbsCommon.Set, ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultKeyRotationPolicy", SupportsShouldProcess = true, DefaultParameterSetName = SetByExpandedPropertiesViaVaultName)]
1322
[OutputType(typeof(PSKeyRotationPolicy))]
14-
public class SetAzKeyVaultKeyRotationPolicy: KeyVaultOnlyKeyCmdletBase
23+
public class SetAzKeyVaultKeyRotationPolicy: KeyVaultCmdletBase
1524
{
1625
#region Parameter Set Names
1726

18-
internal const string ByKeyRotationPolicyInputObjectParameterSet = "ByKeyRotationPolicyInputObject";
27+
private const string SetByExpandedPropertiesViaVaultName = "ByVaultName";
28+
private const string SetByRotationPolicyFileViaVaultName = "SetByRotationPolicyFileViaVaultName";
29+
30+
private const string SetByExpandedPropertiesViaKeyInputObject = "ByKeyInputObject";
31+
private const string SetByRotationPolicyFileViaKeyInputObject = "SetByRotationPolicyFileViaKeyInputObject";
32+
33+
private const string ByKeyRotationPolicyInputObjectParameterSet = "ByKeyRotationPolicyInputObject";
1934

2035
#endregion
2136

2237
#region Input Parameter Definitions
2338

24-
[Parameter(Mandatory = true,
25-
Position = 0,
26-
ParameterSetName = ByKeyRotationPolicyInputObjectParameterSet,
27-
ValueFromPipeline = true,
28-
HelpMessage = "PSKeyRotationPolicy object.")]
39+
/// <summary>
40+
/// Vault name
41+
/// </summary>
42+
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByExpandedPropertiesViaVaultName, HelpMessage = "Vault name.")]
43+
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByRotationPolicyFileViaVaultName)]
44+
[ResourceNameCompleter("Microsoft.KeyVault/vaults", "FakeResourceGroupName")]
45+
[ValidateNotNullOrEmpty]
46+
public string VaultName { get; set; }
47+
48+
/// <summary>
49+
/// Key name.
50+
/// </summary>
51+
[Parameter(Mandatory = true, Position = 1, ParameterSetName = SetByExpandedPropertiesViaVaultName, HelpMessage = "Key name.")]
52+
[Parameter(Mandatory = true, Position = 1, ParameterSetName = SetByRotationPolicyFileViaVaultName)]
53+
[ValidateNotNullOrEmpty]
54+
[Alias(Constants.KeyName)]
55+
public string Name { get; set; }
56+
57+
/// <summary>
58+
/// Key object
59+
/// </summary>
60+
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByExpandedPropertiesViaKeyInputObject,
61+
ValueFromPipeline = true, HelpMessage = "Key object")]
62+
[Parameter(Mandatory = true, Position = 0, ParameterSetName = SetByRotationPolicyFileViaKeyInputObject,
63+
ValueFromPipeline = true)]
64+
[ValidateNotNullOrEmpty]
65+
[Alias("Key")]
66+
public PSKeyVaultKeyIdentityItem InputObject { get; set; }
67+
68+
[Parameter(Mandatory = true, Position = 0, ParameterSetName = ByKeyRotationPolicyInputObjectParameterSet,
69+
ValueFromPipeline = true, HelpMessage = "PSKeyRotationPolicy object.")]
2970
public PSKeyRotationPolicy KeyRotationPolicy { get; set; }
3071

31-
[Parameter(ParameterSetName = ByVaultNameParameterSet,
32-
HelpMessage = "The time span when the key rotation policy will expire. It should be at least 28 days.")]
33-
[Parameter(ParameterSetName = ByKeyInputObjectParameterSet)]
34-
public TimeSpan ExpiresIn { get; set; }
72+
[Parameter(ParameterSetName = SetByExpandedPropertiesViaVaultName,
73+
HelpMessage = "The expiryTime will be applied on the new key version. It should be at least 28 days. It will be in ISO 8601 Format. Examples: 90 days: P90D, 3 months: P3M, 48 hours: PT48H, 1 year and 10 days: P1Y10D")]
74+
[Parameter(ParameterSetName = SetByExpandedPropertiesViaKeyInputObject)]
75+
public string ExpiresIn { get; set; }
3576

3677

37-
[Parameter(ParameterSetName = ByVaultNameParameterSet,
78+
[Parameter(ParameterSetName = SetByExpandedPropertiesViaVaultName,
3879
HelpMessage = "PSKeyRotationLifetimeAction object.")]
39-
[Parameter(ParameterSetName = ByKeyInputObjectParameterSet)]
80+
[Parameter(ParameterSetName = SetByExpandedPropertiesViaKeyInputObject)]
4081
public PSKeyRotationLifetimeAction[] KeyRotationLifetimeAction { get; set; }
4182

83+
[Parameter(Mandatory = true, ParameterSetName = SetByRotationPolicyFileViaVaultName,
84+
HelpMessage = "A path to the rotation policy file that contains JSON policy definition.")]
85+
[Parameter(Mandatory = true, ParameterSetName = SetByRotationPolicyFileViaKeyInputObject)]
86+
public string PolicyPath { get; set; }
4287
#endregion
4388

44-
internal override void NormalizeParameterSets()
89+
protected override void BeginProcessing()
90+
{
91+
PolicyPath = this.TryResolvePath(PolicyPath);
92+
base.BeginProcessing();
93+
}
94+
95+
internal void ValidateParameters()
96+
{
97+
if((this.ParameterSetName.Equals(SetByExpandedPropertiesViaVaultName) ||
98+
this.ParameterSetName.Equals(SetByExpandedPropertiesViaKeyInputObject)) &&
99+
null == ExpiresIn && null == KeyRotationLifetimeAction)
100+
{
101+
throw new ArgumentException(string.Format("Must specify ExpiresIn or KeyRotationLifetimeAction."));
102+
}
103+
104+
if(this.IsParameterBound(c => c.PolicyPath) && !File.Exists(PolicyPath))
105+
{
106+
throw new AzPSFileNotFoundException(string.Format(Resources.FileNotFound, PolicyPath), nameof(PolicyPath));
107+
}
108+
}
109+
110+
internal void NormalizeParameterSets()
45111
{
46112
if (null != InputObject)
47113
{
@@ -57,33 +123,84 @@ internal override void NormalizeParameterSets()
57123
}
58124
}
59125

60-
if (!this.ParameterSetName.Equals(ByKeyRotationPolicyInputObjectParameterSet))
126+
switch (this.ParameterSetName)
61127
{
62-
63-
// Only update specified parameter, others keep same
64-
KeyRotationPolicy = Track2DataClient.GetKeyRotationPolicy(VaultName, Name) ??
65-
new PSKeyRotationPolicy()
66-
{
128+
case SetByRotationPolicyFileViaVaultName:
129+
case SetByRotationPolicyFileViaKeyInputObject:
130+
KeyRotationPolicy = ConstructKeyRotationPolicyFromFile(PolicyPath);
131+
break;
132+
case SetByExpandedPropertiesViaVaultName:
133+
case SetByExpandedPropertiesViaKeyInputObject:
134+
KeyRotationPolicy = new PSKeyRotationPolicy()
135+
{
67136
VaultName = VaultName,
68137
KeyName = Name,
69-
ExpiresIn = null,
70-
LifetimeActions = null
138+
ExpiresIn = ExpiresIn ?? Track2DataClient.GetKeyRotationPolicy(VaultName, Name).ExpiresIn,
139+
LifetimeActions = KeyRotationLifetimeAction ?? Track2DataClient.GetKeyRotationPolicy(VaultName, Name).LifetimeActions
71140
};
141+
break;
142+
default:
143+
// do nothing
144+
break;
145+
}
72146

73-
if (MyInvocation.BoundParameters.ContainsKey("ExpiresIn"))
74-
{
75-
KeyRotationPolicy.ExpiresIn = ExpiresIn;
76-
}
147+
}
148+
149+
private PSKeyRotationPolicy ConstructKeyRotationPolicyFromFile(string policyPath)
150+
{
151+
try
152+
{
153+
string content = File.ReadAllText(policyPath);
154+
// first-level dictionary
155+
var dict = JsonUtilities.DeserializeJson(content, true);
77156

78-
if (MyInvocation.BoundParameters.ContainsKey("KeyRotationLifetimeAction"))
157+
// second-level dictionary
158+
var attributes = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(dict["attributes"]), true);
159+
var lifetimeActionsArray = JsonConvert.DeserializeObject<object[]>(JsonConvert.SerializeObject(dict["lifetimeActions"]));
160+
161+
// third-level dictionary
162+
string expiresIn = attributes["expiryTime"].ToString();
163+
164+
var lifetimeActions = new List<PSKeyRotationLifetimeAction>();
165+
lifetimeActionsArray?.ForEach((lifetimeAction) =>
79166
{
80-
KeyRotationPolicy.LifetimeActions = KeyRotationLifetimeAction;
81-
}
167+
var lifetimeActionDict = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeAction), true);
168+
var action = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeActionDict["action"]), true);
169+
var trigger = JsonUtilities.DeserializeJson(JsonConvert.SerializeObject(lifetimeActionDict["trigger"]), true);
170+
171+
// 4th-level dictionary
172+
string actionType = action["type"].ToString();
173+
174+
// timeAfterCreate or timeBeforeExpiry may absent
175+
string timeAfterCreate = trigger.ContainsKey("timeAfterCreate") ? trigger["timeAfterCreate"]?.ToString() : null;
176+
string timeBeforeExpiry = trigger.ContainsKey("timeBeforeExpiry") ? trigger["timeBeforeExpiry"]?.ToString() : null;
177+
178+
lifetimeActions.Add(new PSKeyRotationLifetimeAction()
179+
{
180+
Action = actionType,
181+
TimeAfterCreate = timeAfterCreate,
182+
TimeBeforeExpiry = timeBeforeExpiry
183+
});
184+
});
185+
186+
return new PSKeyRotationPolicy()
187+
{
188+
VaultName = this.VaultName,
189+
KeyName = this.Name,
190+
ExpiresIn = expiresIn,
191+
LifetimeActions = lifetimeActions
192+
};
82193
}
194+
catch
195+
{
196+
throw new AzPSArgumentException(string.Format("Deserialize {0} failed", policyPath), nameof(PolicyPath));
197+
}
198+
83199
}
84200

85201
public override void ExecuteCmdlet()
86202
{
203+
ValidateParameters();
87204
NormalizeParameterSets();
88205

89206
ConfirmAction(KeyRotationPolicy.KeyName, Properties.Resources.SetKeyRotationPolicy, () =>

src/KeyVault/KeyVault/Commands/Key/UpdateAzureKeyVaultKey.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,11 @@ public class UpdateAzureKeyVaultKey : KeyVaultCmdletBase
132132
public SwitchParameter Immutable { get; set; }
133133

134134
/// <summary>
135-
/// A path to a file containing JSON policy definition. The policy rules under which a key can be exported.
135+
/// A path to the release policy file that contains JSON policy definition. The policy rules under which a key can be exported.
136136
/// </summary>
137137
[Parameter(Mandatory = false,
138138
ParameterSetName = HsmInteractiveParameterSet,
139-
HelpMessage = "A path to a file containing JSON policy definition. The policy rules under which a key can be exported.")]
139+
HelpMessage = "A path to the release policy file that contains JSON policy definition. The policy rules under which a key can be exported.")]
140140
public string ReleasePolicyPath { get; set; }
141141

142142

src/KeyVault/KeyVault/KeyVault.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<ItemGroup>
1515
<PackageReference Include="Azure.Security.KeyVault.Administration" Version="4.0.0" />
16-
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0-beta.7" />
16+
<PackageReference Include="Azure.Security.KeyVault.Keys" Version="4.3.0" />
1717
<PackageReference Include="Portable.BouncyCastle" Version="1.8.8" />
1818
<PackageReference Include="Microsoft.Azure.KeyVault" Version="3.0.1" />
1919
<PackageReference Include="Microsoft.Azure.KeyVault.WebKey" Version="3.0.1" />

src/KeyVault/KeyVault/Models/Key/PSKeyRotationLifetimeAction.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,32 @@ public class PSKeyRotationLifetimeAction
1414
// Gets or sets he Azure.Security.KeyVault.Keys.KeyRotationPolicyAction that will
1515
// be executed.
1616
public string Action { get; set; }
17-
//
18-
// Summary:
19-
// Gets or sets the System.TimeSpan after creation to attempt to rotate. It only
20-
// applies to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate.
21-
public TimeSpan? TimeAfterCreate { get; set; }
22-
//
23-
// Summary:
24-
// Gets or sets the System.TimeSpan before expiry to attempt to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate
25-
// or Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Notify.
26-
public TimeSpan? TimeBeforeExpiry { get; set; }
17+
18+
/// <summary>
19+
/// Gets or sets the ISO 8601 duration after creation to attempt to rotate. It only
20+
/// applies to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate.
21+
/// </summary>
22+
/// <example>
23+
/// ISO 8601 duration examples:
24+
/// • P90D – 90 days
25+
/// • P3M – 3 months
26+
/// • P1Y10D – 1 year and 10 days
27+
/// </example>
28+
public string TimeAfterCreate { get; set; }
29+
30+
/// <summary>
31+
/// Gets or sets the ISO 8601 duration before expiry to attempt to Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Rotate
32+
/// or Azure.Security.KeyVault.Keys.KeyRotationPolicyAction.Notify.
33+
/// </summary>
34+
public string TimeBeforeExpiry { get; set; }
2735

2836
public PSKeyRotationLifetimeAction() { }
2937

3038
public PSKeyRotationLifetimeAction(KeyRotationLifetimeAction keyRotationLifetimeAction)
3139
{
3240
Action = keyRotationLifetimeAction.Action.ToString();
33-
TimeAfterCreate = string.IsNullOrEmpty(keyRotationLifetimeAction.TimeAfterCreate) ? null : XmlConvert.ToTimeSpan(keyRotationLifetimeAction.TimeAfterCreate) as TimeSpan?;
34-
TimeBeforeExpiry = string.IsNullOrEmpty(keyRotationLifetimeAction.TimeBeforeExpiry) ? null : XmlConvert.ToTimeSpan(keyRotationLifetimeAction.TimeBeforeExpiry) as TimeSpan?;
41+
TimeAfterCreate = keyRotationLifetimeAction.TimeAfterCreate;
42+
TimeBeforeExpiry = keyRotationLifetimeAction.TimeBeforeExpiry;
3543
}
3644

3745
public override string ToString()

src/KeyVault/KeyVault/Models/Key/PSKeyRotationPolicy.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@ public class PSKeyRotationPolicy
3636
// Summary:
3737
// Gets the actions that will be performed by Key Vault over the lifetime of a key.
3838
public IList<PSKeyRotationLifetimeAction> LifetimeActions { get; set; }
39-
//
40-
// Summary:
41-
// Gets or sets the System.TimeSpan when the Azure.Security.KeyVault.Keys.KeyRotationPolicy
42-
// will expire. It should be at least 28 days.
43-
public TimeSpan? ExpiresIn { get; set; }
39+
40+
/// <summary>
41+
/// The expiryTime will be applied on the new key version. It should be at least 28 days.
42+
/// It will be in ISO 8601 Format. Examples: 90 days: P90D, 3 months: P3M, 48 hours: PT48H, 1 year and 10 days: P1Y10D"
43+
/// </summary>
44+
public string ExpiresIn { get; set; }
4445
//
4546
// Summary:
4647
// Gets a System.DateTimeOffset indicating when the Azure.Security.KeyVault.Keys.KeyRotationPolicy
@@ -65,7 +66,7 @@ public PSKeyRotationPolicy(KeyRotationPolicy keyRotationPolicy, string vaultName
6566
LifetimeActions.Add(new PSKeyRotationLifetimeAction(action));
6667
}
6768

68-
ExpiresIn = string.IsNullOrEmpty(keyRotationPolicy.ExpiresIn) ? null : XmlConvert.ToTimeSpan(keyRotationPolicy.ExpiresIn) as TimeSpan?;
69+
ExpiresIn = keyRotationPolicy.ExpiresIn;
6970
CreatedOn = keyRotationPolicy.CreatedOn;
7071
UpdatedOn = keyRotationPolicy.UpdatedOn;
7172
}

src/KeyVault/KeyVault/Track2Models/Track2HsmClient.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -543,17 +543,16 @@ internal PSKeyRotationPolicy SetKeyRotationPolicy(PSKeyRotationPolicy psKeyRotat
543543
var client = CreateKeyClient(psKeyRotationPolicy.VaultName);
544544
var policy = new KeyRotationPolicy()
545545
{
546-
ExpiresIn = psKeyRotationPolicy.ExpiresIn.HasValue ? XmlConvert.ToString(psKeyRotationPolicy.ExpiresIn.Value) : null,
546+
ExpiresIn = psKeyRotationPolicy.ExpiresIn,
547547
LifetimeActions = { }
548548
};
549549

550550
psKeyRotationPolicy.LifetimeActions?.ForEach(
551551
psKeyRotationLifetimeAction => policy.LifetimeActions.Add(
552-
new KeyRotationLifetimeAction()
552+
new KeyRotationLifetimeAction(new KeyRotationPolicyAction(psKeyRotationLifetimeAction.Action))
553553
{
554-
Action = psKeyRotationLifetimeAction.Action,
555-
TimeAfterCreate = psKeyRotationLifetimeAction.TimeAfterCreate.HasValue ? XmlConvert.ToString(psKeyRotationLifetimeAction.TimeAfterCreate.Value) : null,
556-
TimeBeforeExpiry = psKeyRotationLifetimeAction.TimeBeforeExpiry.HasValue ? XmlConvert.ToString(psKeyRotationLifetimeAction.TimeBeforeExpiry.Value) : null
554+
TimeAfterCreate = psKeyRotationLifetimeAction.TimeAfterCreate,
555+
TimeBeforeExpiry = psKeyRotationLifetimeAction.TimeBeforeExpiry
557556
}
558557
));
559558

0 commit comments

Comments
 (0)