Skip to content

Commit c089f4b

Browse files
authored
Merge pull request Azure#3476 from luanshixia/dev
Policy parameters support in New-AzureRmPolicyDefinition and New-AzureRmPolicyAssignment
2 parents 7c7ae6a + 5cf1478 commit c089f4b

File tree

18 files changed

+1671
-13
lines changed

18 files changed

+1671
-13
lines changed

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Components/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public static class Constants
6363
/// <summary>
6464
/// The default policy API version.
6565
/// </summary>
66-
public static readonly string PolicyApiVersion = "2016-04-01";
66+
public static readonly string PolicyApiVersion = "2016-12-01";
6767

6868
/// <summary>
6969
/// The default providers API version.

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Entities/Policy/PolicyAssignmentProperties.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Policy
1616
{
1717
using Newtonsoft.Json;
18+
using Newtonsoft.Json.Linq;
1819

1920
/// <summary>
2021
/// The policy assignment properties.
@@ -38,5 +39,11 @@ public class PolicyAssignmentProperties
3839
/// </summary>
3940
[JsonProperty(Required = Required.Always)]
4041
public string PolicyDefinitionId { get; set; }
42+
43+
/// <summary>
44+
/// The parameter values.
45+
/// </summary>
46+
[JsonProperty(Required = Required.Default)]
47+
public JObject Parameters { get; set; }
4148
}
4249
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Entities/Policy/PolicyDefinitionProperties.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,11 @@ public class PolicyDefinitionProperties
3939
/// </summary>
4040
[JsonProperty(Required = Required.Always)]
4141
public JObject PolicyRule { get; set; }
42+
43+
/// <summary>
44+
/// The parameters declaration.
45+
/// </summary>
46+
[JsonProperty(Required = Required.Default)]
47+
public JObject Parameters { get; set; }
4248
}
4349
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Extensions/JsonExtensions.cs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions
1919
using Newtonsoft.Json.Converters;
2020
using Newtonsoft.Json.Linq;
2121
using System;
22+
using System.Collections;
2223
using System.Collections.Generic;
2324
using System.Globalization;
2425
using System.IO;
@@ -212,5 +213,31 @@ public static bool TryConvertTo<TType>(this JToken jobject, out TType result)
212213
result = default(TType);
213214
return false;
214215
}
216+
217+
/// <summary>
218+
/// Converts an <see cref="IDictionary"/> object to <see cref="JObject"/> with an extra level labeled as "value".
219+
/// </summary>
220+
/// <param name="dict">The <see cref="IDictionary"/> object.</param>
221+
/// <param name="keys">The key set to extract from the <see cref="IDictionary"/> object. Default is all keys.</param>
222+
/// <returns>The conversion result.</returns>
223+
public static JObject ToJObjectWithValue(this IDictionary dict, IEnumerable keys = null)
224+
{
225+
if (dict == null)
226+
{
227+
return null;
228+
}
229+
if (keys == null)
230+
{
231+
keys = dict.Keys;
232+
}
233+
var parameterObject = new JObject();
234+
foreach (string paramKey in keys)
235+
{
236+
var valueObject = new JObject();
237+
valueObject.Add("value", dict[paramKey].ToJToken());
238+
parameterObject.Add(paramKey, valueObject);
239+
}
240+
return parameterObject;
241+
}
215242
}
216-
}
243+
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Extensions/PsObjectExtensions.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,5 +133,32 @@ private static JToken ToJToken(object value)
133133

134134
return new JValue(value.ToString());
135135
}
136+
137+
/// <summary>
138+
/// Gets nested property values from a <see cref="PSObject"/> easily using property path.
139+
/// </summary>
140+
/// <param name="propertyObject">The <see cref="PSObject"/> to get property value from.</param>
141+
/// <param name="propertyPath">The nested property path, e.g. "metadata.description".</param>
142+
/// <returns>The value of the specified property.</returns>
143+
internal static object GetPSObjectProperty(this PSObject propertyObject, string propertyPath)
144+
{
145+
var propertyNames = propertyPath.Split('.');
146+
var tmpObject = propertyObject;
147+
foreach (var propertyName in propertyNames)
148+
{
149+
var propertyInfo = tmpObject.Properties[propertyName];
150+
if (propertyInfo != null)
151+
{
152+
if (propertyInfo.Value is PSObject)
153+
{
154+
tmpObject = propertyInfo.Value as PSObject;
155+
continue;
156+
}
157+
return propertyInfo.Value;
158+
}
159+
return null;
160+
}
161+
return tmpObject;
162+
}
136163
}
137164
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Implementation/Policy/NewAzurePolicyAssignment.cs

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,26 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation
1717
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Components;
1818
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Policy;
1919
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions;
20+
using Microsoft.Azure.Commands.Common.Authentication;
21+
using Microsoft.WindowsAzure.Commands.Utilities.Common;
2022
using Newtonsoft.Json.Linq;
2123
using System.Management.Automation;
24+
using System;
25+
using System.Linq;
26+
using System.Collections;
2227

2328
/// <summary>
2429
/// Creates a policy assignment.
2530
/// </summary>
26-
[Cmdlet(VerbsCommon.New, "AzureRmPolicyAssignment"), OutputType(typeof(PSObject))]
27-
public class NewAzurePolicyAssignmentCmdlet : PolicyAssignmentCmdletBase
31+
[Cmdlet(VerbsCommon.New, "AzureRmPolicyAssignment", DefaultParameterSetName = ParameterlessPolicyParameterSetName), OutputType(typeof(PSObject))]
32+
public class NewAzurePolicyAssignmentCmdlet : PolicyAssignmentCmdletBase, IDynamicParameters
2833
{
34+
protected RuntimeDefinedParameterDictionary dynamicParameters = new RuntimeDefinedParameterDictionary();
35+
36+
protected const string PolicyParameterObjectParameterSetName = "Policy assignment with parameters via policy parameter object";
37+
protected const string PolicyParameterStringParameterSetName = "Policy assignment with parameters via policy parameter string";
38+
protected const string ParameterlessPolicyParameterSetName = "Policy assignment without parameters";
39+
2940
/// <summary>
3041
/// Gets or sets the policy assignment name parameter.
3142
/// </summary>
@@ -50,9 +61,30 @@ public class NewAzurePolicyAssignmentCmdlet : PolicyAssignmentCmdletBase
5061
/// <summary>
5162
/// Gets or sets the policy assignment policy definition parameter.
5263
/// </summary>
53-
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The pollicy definition object.")]
64+
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The policy definition object.")]
65+
[Parameter(ParameterSetName = ParameterlessPolicyParameterSetName,
66+
Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The policy definition object.")]
67+
[Parameter(ParameterSetName = PolicyParameterObjectParameterSetName,
68+
Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The policy definition object.")]
69+
[Parameter(ParameterSetName = PolicyParameterStringParameterSetName,
70+
Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The policy definition object.")]
5471
public PSObject PolicyDefinition { get; set; }
5572

73+
/// <summary>
74+
/// Gets or sets the policy assignment policy parameter object.
75+
/// </summary>
76+
[Parameter(ParameterSetName = PolicyParameterObjectParameterSetName,
77+
Mandatory = true, ValueFromPipelineByPropertyName = false, HelpMessage = "The policy parameter object.")]
78+
public Hashtable PolicyParameterObject { get; set; }
79+
80+
/// <summary>
81+
/// Gets or sets the policy assignment policy parameter file path or policy parameter string.
82+
/// </summary>
83+
[Parameter(ParameterSetName = PolicyParameterStringParameterSetName,
84+
Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The policy parameter file path or policy parameter string.")]
85+
[ValidateNotNullOrEmpty]
86+
public string PolicyParameter { get; set; }
87+
5688
/// <summary>
5789
/// Executes the cmdlet.
5890
/// </summary>
@@ -112,11 +144,72 @@ private JToken GetResource()
112144
{
113145
DisplayName = this.DisplayName ?? null,
114146
PolicyDefinitionId = this.PolicyDefinition.Properties["policyDefinitionId"].Value.ToString(),
115-
Scope = this.Scope
147+
Scope = this.Scope,
148+
Parameters = this.GetParameters()
116149
}
117150
};
118151

119152
return policyassignmentObject.ToJToken();
120153
}
154+
155+
object IDynamicParameters.GetDynamicParameters()
156+
{
157+
if (this.PolicyDefinition != null)
158+
{
159+
var parameters = this.PolicyDefinition.GetPSObjectProperty("Properties.parameters") as PSObject;
160+
if (parameters != null)
161+
{
162+
foreach (var param in parameters.Properties)
163+
{
164+
var type = (param.Value as PSObject).Properties["type"];
165+
var typeString = type != null ? type.Value.ToString() : string.Empty;
166+
var description = (param.Value as PSObject).GetPSObjectProperty("metadata.description");
167+
var helpString = description != null ? description.ToString() : string.Format("The {0} policy parameter.", param.Name);
168+
var dp = new RuntimeDefinedParameter
169+
{
170+
Name = param.Name,
171+
ParameterType = typeString.Equals("array", StringComparison.OrdinalIgnoreCase) ? typeof(string[]) : typeof(string)
172+
};
173+
dp.Attributes.Add(new ParameterAttribute
174+
{
175+
ParameterSetName = ParameterlessPolicyParameterSetName,
176+
Mandatory = true,
177+
ValueFromPipelineByPropertyName = false,
178+
HelpMessage = helpString
179+
});
180+
this.dynamicParameters.Add(param.Name, dp);
181+
}
182+
}
183+
}
184+
185+
return this.dynamicParameters;
186+
}
187+
188+
private JObject GetParameters()
189+
{
190+
// Load parameters from local file or literal
191+
if (this.PolicyParameter != null)
192+
{
193+
string policyParameterFilePath = this.TryResolvePath(this.PolicyParameter);
194+
return FileUtilities.DataStore.FileExists(policyParameterFilePath)
195+
? JObject.Parse(FileUtilities.DataStore.ReadFileAsText(policyParameterFilePath))
196+
: JObject.Parse(this.PolicyParameter);
197+
}
198+
199+
// Load from PS object
200+
if (this.PolicyParameterObject != null)
201+
{
202+
return this.PolicyParameterObject.ToJObjectWithValue();
203+
}
204+
205+
// Load dynamic parameters
206+
var parameters = PowerShellUtilities.GetUsedDynamicParameters(dynamicParameters, MyInvocation);
207+
if (parameters.Count() > 0)
208+
{
209+
return MyInvocation.BoundParameters.ToJObjectWithValue(parameters.Select(p => p.Name));
210+
}
211+
212+
return null;
213+
}
121214
}
122-
}
215+
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Implementation/Policy/NewAzurePolicyDefinition.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,19 @@ public class NewAzurePolicyDefinitionCmdlet : PolicyDefinitionCmdletBase
5252
public string Description { get; set; }
5353

5454
/// <summary>
55-
/// Gets or sets the policy parameter
55+
/// Gets or sets the policy definition policy rule parameter
5656
/// </summary>
5757
[Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The rule for policy definition. This can either be a path to a file name containing the rule, or the rule as string.")]
5858
[ValidateNotNullOrEmpty]
5959
public string Policy { get; set; }
6060

61+
/// <summary>
62+
/// Gets or sets the policy definition parameters parameter
63+
/// </summary>
64+
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = "The parameters declaration for policy definition. This can either be a path to a file name containing the parameters declaration, or the parameters declaration as string.")]
65+
[ValidateNotNullOrEmpty]
66+
public string Parameter { get; set; }
67+
6168
/// <summary>
6269
/// Executes the cmdlet.
6370
/// </summary>
@@ -113,7 +120,8 @@ private JToken GetResource()
113120
{
114121
Description = this.Description ?? null,
115122
DisplayName = this.DisplayName ?? null,
116-
PolicyRule = JObject.Parse(GetPolicyRuleObject().ToString())
123+
PolicyRule = JObject.Parse(this.GetPolicyRuleObject().ToString()),
124+
Parameters = this.Parameter == null ? null : JObject.Parse(this.GetParametersObject().ToString())
117125
}
118126
};
119127

@@ -131,5 +139,17 @@ private JToken GetPolicyRuleObject()
131139
? JToken.FromObject(FileUtilities.DataStore.ReadFileAsText(policyFilePath))
132140
: JToken.FromObject(this.Policy);
133141
}
142+
143+
/// <summary>
144+
/// Gets the parameters object
145+
/// </summary>
146+
private JToken GetParametersObject()
147+
{
148+
string parametersFilePath = this.TryResolvePath(this.Parameter);
149+
150+
return File.Exists(parametersFilePath)
151+
? JToken.FromObject(FileUtilities.DataStore.ReadFileAsText(parametersFilePath))
152+
: JToken.FromObject(this.Parameter);
153+
}
134154
}
135155
}

src/ResourceManager/Resources/Commands.ResourceManager/Cmdlets/Implementation/Policy/SetAzurePolicyAssignment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,4 @@ protected string GetResourceId()
145145
extensionResourceName: this.Name);
146146
}
147147
}
148-
}
148+
}

src/ResourceManager/Resources/Commands.Resources.Test/Commands.Resources.Test.csproj

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,15 @@
274274
<None Include="keyVaultTemplateParams.json">
275275
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
276276
</None>
277+
<None Include="SamplePolicyAssignmentParameters.json">
278+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
279+
</None>
280+
<None Include="SamplePolicyDefinitionParameters.json">
281+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
282+
</None>
283+
<None Include="SamplePolicyDefinitionWithParameters.json">
284+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
285+
</None>
277286
<None Include="SamplePolicyDefinition.json">
278287
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
279288
</None>
@@ -395,8 +404,14 @@
395404
<None Include="SessionRecords\Microsoft.Azure.Commands.Resources.Test.ScenarioTests.PolicyTests\TestPolicyAssignmentCRUD.json">
396405
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
397406
</None>
407+
<None Include="SessionRecords\Microsoft.Azure.Commands.Resources.Test.ScenarioTests.PolicyTests\TestPolicyAssignmentWithParameters.json">
408+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
409+
</None>
398410
<None Include="SessionRecords\Microsoft.Azure.Commands.Resources.Test.ScenarioTests.PolicyTests\TestPolicyDefinitionCRUD.json">
399-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
411+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
412+
</None>
413+
<None Include="SessionRecords\Microsoft.Azure.Commands.Resources.Test.ScenarioTests.PolicyTests\TestPolicyDefinitionWithParameters.json">
414+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
400415
</None>
401416
<None Include="SessionRecords\Microsoft.Azure.Commands.Resources.Test.ScenarioTests.ProviderFeatureTests\TestAzureProviderFeature.json">
402417
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"listOfAllowedLocations": {
3+
"value": [
4+
"West US",
5+
"West US 2"
6+
]
7+
}
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"listOfAllowedLocations": {
3+
"type": "array",
4+
"metadata": {
5+
"description": "An array of permitted locations for resources.",
6+
"strongType": "location",
7+
"displayName": "List of locations"
8+
}
9+
}
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"if": {
3+
"not": {
4+
"field": "location",
5+
"in": "[parameters('listOfAllowedLocations')]"
6+
}
7+
},
8+
"then": {
9+
"effect": "deny"
10+
}
11+
}

src/ResourceManager/Resources/Commands.Resources.Test/ScenarioTests/PolicyTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,19 @@ public void TestPolicyAssignmentCRUD()
4141
{
4242
ResourcesController.NewInstance.RunPsTest("Test-PolicyAssignmentCRUD");
4343
}
44+
45+
[Fact]
46+
[Trait(Category.AcceptanceType, Category.CheckIn)]
47+
public void TestPolicyDefinitionWithParameters()
48+
{
49+
ResourcesController.NewInstance.RunPsTest("Test-PolicyDefinitionWithParameters");
50+
}
51+
52+
[Fact]
53+
[Trait(Category.AcceptanceType, Category.CheckIn)]
54+
public void TestPolicyAssignmentWithParameters()
55+
{
56+
ResourcesController.NewInstance.RunPsTest("Test-PolicyAssignmentWithParameters");
57+
}
4458
}
4559
}

0 commit comments

Comments
 (0)