Skip to content

Commit 1776f73

Browse files
authored
Fix exemption cmdlet parameter to accept empty array (#21824)
* Fix exemption cmdlet parameter to take empty array * Update changelog * Fixed the wrong method call * Addressed comments
1 parent 87eb3c9 commit 1776f73

File tree

9 files changed

+529
-177
lines changed

9 files changed

+529
-177
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Resources
16+
{
17+
using Newtonsoft.Json.Linq;
18+
19+
/// <summary>
20+
/// The resource definition object.
21+
/// </summary>
22+
public class ResourceWithSystemData<TProperties> : Resource<TProperties>
23+
{
24+
/// <summary>
25+
/// Gets or sets the resource system data.
26+
/// </summary>
27+
public JObject SystemData { get; set; }
28+
}
29+
}

src/Resources/ResourceManager/Extensions/ResourceExtensions.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,5 +108,14 @@ internal static Resource<TType> ToResource<TType>(this JToken jtoken)
108108
{
109109
return jtoken.ToObject<Resource<TType>>(JsonExtensions.JsonMediaTypeSerializer);
110110
}
111+
112+
/// <summary>
113+
/// Converts a <see cref="JToken"/> to a <see cref="ResourceWithSystemData{JToken}"/>.
114+
/// </summary>
115+
/// <param name="jtoken">The <see cref="JToken"/>.</param>
116+
internal static ResourceWithSystemData<JToken> ToResourceWithSystemData(this JToken jtoken)
117+
{
118+
return jtoken.ToObject<ResourceWithSystemData<JToken>>(JsonExtensions.JsonMediaTypeSerializer);
119+
}
111120
}
112121
}

src/Resources/ResourceManager/Implementation/Policy/PolicyHelpStrings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public static class PolicyHelpStrings
135135
public const string GetPolicyExemptionScopeHelp = "The scope of the policy exemption to get, e.g. /providers/managementGroups/{managementGroupName}, defaults to current subscription.";
136136
public const string GetPolicyExemptionIdHelp = "The fully qualified policy exemption ID to get, including the scope, e.g. /subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/Microsoft.Authorization/policyExemptions/{policyExemptionName}.";
137137
public const string GetPolicyExemptionFilterHelp = "Limits the list of returned policy exemptions to those assigning the policy assignment identified by this fully qualified Id.";
138-
public const string GetPolicyExemptionIncludeDescendentsHelp = "Causes the list of returned policy exemptions to include all exemptions related to the given scope, including those from ancestor scopes and those from descendent scopes.";
138+
public const string GetPolicyExemptionIncludeDescendentsHelp = "Causes the list of returned policy exemptions to include all exemptions related to the given scope, including those from ancestor scopes and those from descendent scopes. This parameter doesn't work when the requested scope is a management group scope.";
139139
public const string GetPolicyExemptionDoesNothingHelp = "This parameter is ignored if provided with -Name or -Id parameters.";
140140
public const string NewPolicyExemptionNameHelp = "The name of the new policy exemption.";
141141
public const string NewPolicyExemptionScopeHelp = "The scope of the new policy exemption, e.g. /providers/managementGroups/{managementGroupName}, defaults to current subscription.";

src/Resources/ResourceManager/Implementation/Policy/PsPolicyExemption.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ public class PsPolicyExemption
2626
{
2727
public PsPolicyExemption(JToken input)
2828
{
29-
var resource = input.ToResource();
29+
var resource = input.ToResourceWithSystemData();
3030
Name = resource.Name;
3131
Properties = new PsPolicyExemptionProperties(resource.Properties);
3232
ResourceId = resource.Id;
3333
ResourceName = resource.Name;
3434
ResourceGroupName = string.IsNullOrEmpty(resource.Id) ? null : ResourceIdUtility.GetResourceGroupName(resource.Id);
3535
ResourceType = resource.Type;
3636
SubscriptionId = string.IsNullOrEmpty(resource.Id) ? null : ResourceIdUtility.GetSubscriptionId(resource.Id);
37+
SystemData = resource.SystemData.ToPsObject();
3738
}
3839

3940
/// <summary>

src/Resources/ResourceManager/Implementation/Policy/SetAzurePolicyExemption.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public class SetAzurePolicyExemptionCmdlet : PolicyCmdletBase
7777
/// Gets or sets the policy definition reference ID list when the associated policy assignment is for a policy set (initiative).
7878
/// </summary>
7979
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.SetPolicyExemptionPolicyDefinitionReferenceIdHelp)]
80-
[ValidateNotNullOrEmpty]
80+
[ValidateNotNull]
8181
public string[] PolicyDefinitionReferenceId { get; set; }
8282

8383
/// <summary>
@@ -144,7 +144,7 @@ protected override void OnProcessRecord()
144144
/// </summary>
145145
private JToken GetResource(string resourceId, string apiVersion)
146146
{
147-
var resource = this.GetExistingResource(resourceId, apiVersion).Result.ToResource();
147+
var resource = this.GetExistingResource(resourceId, apiVersion).Result.ToResource<PolicyExemptionProperties>();
148148

149149
// get incoming object properties if present
150150
JObject inputMetadata = null;
@@ -154,25 +154,19 @@ private JToken GetResource(string resourceId, string apiVersion)
154154
inputMetadata = newProperties["metadata"] as JObject;
155155
}
156156

157-
DateTime? existingExpiration = null;
158-
if (DateTime.TryParse(resource.Properties["expiresOn"]?.ToString(), out var expiresOn))
159-
{
160-
existingExpiration = expiresOn;
161-
}
162-
163157
var parameterMetadata = this.Metadata == null ? null : this.GetObjectFromParameter(this.Metadata, nameof(this.Metadata));
164158
var policyExemption = new PolicyExemption
165159
{
166160
Name = this.Name ?? this.InputObject?.Name ?? resource.Name,
167161
Properties = new PolicyExemptionProperties
168162
{
169-
DisplayName = this.DisplayName ?? this.InputObject?.Properties?.DisplayName ?? resource.Properties["displayName"]?.ToString(),
170-
Description = this.Description ?? this.InputObject?.Properties?.Description ?? resource.Properties["description"]?.ToString(),
171-
ExemptionCategory = this.ExemptionCategory ?? this.InputObject?.Properties?.ExemptionCategory ?? resource.Properties["exemptionCategory"]?.ToString(),
172-
PolicyAssignmentId = resource.Properties["policyAssignmentId"]?.ToString(),
173-
PolicyDefinitionReferenceIds = this.PolicyDefinitionReferenceId ?? this.InputObject?.Properties?.PolicyDefinitionReferenceIds ?? resource.Properties["policyDefinitionReferenceIds"]?.ToString()?.Split(','),
174-
ExpiresOn = this.ClearExpiration.IsPresent ? null : this.ExpiresOn?.ToUniversalTime() ?? this.InputObject?.Properties?.ExpiresOn ?? existingExpiration,
175-
Metadata = parameterMetadata ?? inputMetadata ?? resource.Properties["metadata"] as JObject,
163+
DisplayName = this.DisplayName ?? this.InputObject?.Properties?.DisplayName ?? resource.Properties.DisplayName,
164+
Description = this.Description ?? this.InputObject?.Properties?.Description ?? resource.Properties.Description,
165+
ExemptionCategory = this.ExemptionCategory ?? this.InputObject?.Properties?.ExemptionCategory ?? resource.Properties.ExemptionCategory,
166+
PolicyAssignmentId = resource.Properties.PolicyAssignmentId,
167+
PolicyDefinitionReferenceIds = this.PolicyDefinitionReferenceId ?? this.InputObject?.Properties?.PolicyDefinitionReferenceIds ?? resource.Properties.PolicyDefinitionReferenceIds,
168+
ExpiresOn = this.ClearExpiration.IsPresent ? null : this.ExpiresOn?.ToUniversalTime() ?? this.InputObject?.Properties?.ExpiresOn ?? resource.Properties.ExpiresOn,
169+
Metadata = parameterMetadata ?? inputMetadata ?? resource.Properties.Metadata,
176170
}
177171
};
178172

src/Resources/Resources.Test/ScenarioTests/PolicyTests.ps1

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,12 @@ function Test-PolicyExemptionCRUD
15031503
Assert-NotNull $exemption.Properties.Metadata
15041504
Assert-AreEqual $metadataValue $exemption.Properties.Metadata.$metadataName
15051505
Assert-Null $exemption.Properties.ExpiresOn
1506+
Assert-NotNull $exemption.SystemData.createdBy
1507+
Assert-NotNull $exemption.SystemData.createdByType
1508+
Assert-NotNull $exemption.SystemData.createdAt
1509+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1510+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1511+
Assert-NotNull $exemption.SystemData.lastModifiedAt
15061512

15071513
# get the exemption by name
15081514
$exemption = Get-AzPolicyExemption -Name testExemption -Scope $rg.ResourceId
@@ -1516,6 +1522,12 @@ function Test-PolicyExemptionCRUD
15161522
Assert-NotNull $exemption.Properties.Metadata
15171523
Assert-AreEqual $metadataValue $exemption.Properties.Metadata.$metadataName
15181524
Assert-Null $exemption.Properties.ExpiresOn
1525+
Assert-NotNull $exemption.SystemData.createdBy
1526+
Assert-NotNull $exemption.SystemData.createdByType
1527+
Assert-NotNull $exemption.SystemData.createdAt
1528+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1529+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1530+
Assert-NotNull $exemption.SystemData.lastModifiedAt
15191531

15201532
# get the exemption by id
15211533
$exemption = Get-AzPolicyExemption -Id $exemption.ResourceId
@@ -1529,6 +1541,12 @@ function Test-PolicyExemptionCRUD
15291541
Assert-NotNull $exemption.Properties.Metadata
15301542
Assert-AreEqual $metadataValue $exemption.Properties.Metadata.$metadataName
15311543
Assert-Null $exemption.Properties.ExpiresOn
1544+
Assert-NotNull $exemption.SystemData.createdBy
1545+
Assert-NotNull $exemption.SystemData.createdByType
1546+
Assert-NotNull $exemption.SystemData.createdAt
1547+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1548+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1549+
Assert-NotNull $exemption.SystemData.lastModifiedAt
15321550

15331551
# update the policy exemption, validate the result
15341552
$future1 = [DateTime]::Parse('3021-03-09T07:30:10Z').ToUniversalTime()
@@ -1600,6 +1618,12 @@ function Test-PolicyExemptionCRUDOnPolicySet
16001618
Assert-Null $exemption.Properties.Metadata
16011619
Assert-Null $exemption.Properties.PolicyDefinitionReferenceIds
16021620
Assert-AreEqual $future1 $exemption.Properties.ExpiresOn.ToUniversalTime()
1621+
Assert-NotNull $exemption.SystemData.createdBy
1622+
Assert-NotNull $exemption.SystemData.createdByType
1623+
Assert-NotNull $exemption.SystemData.createdAt
1624+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1625+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1626+
Assert-NotNull $exemption.SystemData.lastModifiedAt
16031627

16041628
# update the policy exemption set policy definition reference Id using piping, validate the result
16051629
$future2 = $future1.AddDays(1).ToUniversalTime()
@@ -1614,6 +1638,12 @@ function Test-PolicyExemptionCRUDOnPolicySet
16141638
Assert-NotNull $exemption.Properties.PolicyDefinitionReferenceIds
16151639
Assert-AreEqual 1 $exemption.Properties.PolicyDefinitionReferenceIds.Count
16161640
Assert-AreEqual $policySet.Properties.PolicyDefinitions[0].policyDefinitionReferenceId $exemption.Properties.PolicyDefinitionReferenceIds[0]
1641+
Assert-NotNull $exemption.SystemData.createdBy
1642+
Assert-NotNull $exemption.SystemData.createdByType
1643+
Assert-NotNull $exemption.SystemData.createdAt
1644+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1645+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1646+
Assert-NotNull $exemption.SystemData.lastModifiedAt
16171647

16181648
# update the policy exemption set policy definition reference Id using parameters, validate the result
16191649
$exemption = Set-AzPolicyExemption -Name testExemption -DisplayName 'testDisplay1' -ExemptionCategory Waiver -PolicyDefinitionReferenceId @($policySet.Properties.PolicyDefinitions[0].policyDefinitionReferenceId)
@@ -1623,12 +1653,26 @@ function Test-PolicyExemptionCRUDOnPolicySet
16231653
Assert-NotNull $exemption.Properties.PolicyDefinitionReferenceIds
16241654
Assert-AreEqual 1 $exemption.Properties.PolicyDefinitionReferenceIds.Count
16251655
Assert-AreEqual $policySet.Properties.PolicyDefinitions[0].policyDefinitionReferenceId $exemption.Properties.PolicyDefinitionReferenceIds[0]
1656+
Assert-NotNull $exemption.SystemData.createdBy
1657+
Assert-NotNull $exemption.SystemData.createdByType
1658+
Assert-NotNull $exemption.SystemData.createdAt
1659+
Assert-NotNull $exemption.SystemData.lastModifiedBy
1660+
Assert-NotNull $exemption.SystemData.lastModifiedByType
1661+
Assert-NotNull $exemption.SystemData.lastModifiedAt
16261662

16271663
# update the exemption to clear the expiration
16281664
$exemption.Properties.PolicyDefinitionReferenceIds = @()
16291665
$exemption = $exemption | Set-AzPolicyExemption
16301666
Assert-AreEqual 0 @($exemption.Properties.PolicyDefinitionReferenceIds).Count
16311667

1668+
# update the exemption without pipeline input
1669+
$exemption = Set-AzPolicyExemption -Name testExemption -ExpiresOn $future1
1670+
Assert-AreEqual $future1 $exemption.Properties.ExpiresOn.ToUniversalTime()
1671+
1672+
# update policy definition reference Ids with empty array
1673+
$exemption = Set-AzPolicyExemption -Name testExemption -policyDefinitionReferenceId @()
1674+
Assert-AreEqual 0 @($exemption.Properties.PolicyDefinitionReferenceIds).Count
1675+
16321676
# make another policy exemption, ensure both are present
16331677
$exemption2 = $assignment | New-AzPolicyExemption -Name testExemption2 -ExemptionCategory Mitigated -DisplayName $description
16341678
$list = Get-AzPolicyExemption | ?{ $_.Name -in @('testExemption', 'testExemption2') }

0 commit comments

Comments
 (0)