Skip to content

[Az.Resources Policy] Add -NonComplianceMessage to Set/New-AzPolicyAssignment #15060

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

Merged
merged 6 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions src/Resources/ResourceManager/Components/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,17 @@ public static class Constants
/// <summary>
/// The default policy definition API version.
/// </summary>
public static readonly string PolicyDefinitionApiVersion = "2019-09-01";
public static readonly string PolicyDefinitionApiVersion = "2020-09-01";

/// <summary>
/// The default policy set definition API version.
/// </summary>
public static readonly string PolicySetDefintionApiVersion = "2019-09-01";
public static readonly string PolicySetDefintionApiVersion = "2020-09-01";

/// <summary>
/// The default policy assignment API version.
/// </summary>
public static readonly string PolicyAssignmentApiVersion = "2019-09-01";
public static readonly string PolicyAssignmentApiVersion = "2020-09-01";

/// <summary>
/// The default policy exemption API version.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Policy
{
using Newtonsoft.Json;

/// <summary>
/// The policy assignment non compliance message used to describe why a resource is non-compliant with a policy.
/// </summary>
public class NonComplianceMessage
{
/// <summary>
/// The message that describes why a resource is non-compliant with the policy. This is shown in 'deny' error messages and
/// on resource's non-compliant compliance results.
/// </summary>
[JsonProperty(Required = Required.Default)]
public string Message { get; set; }

/// <summary>
/// The policy definition reference ID within a policy set definition that the message is intended for. This is only applicable if
/// the policy assignment assigns a policy set definition. If this is not provided the message applies to all policies assigned by this
/// policy assignment.
/// </summary>
[JsonProperty(Required = Required.Default)]
public string PolicyDefinitionReferenceId { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,11 @@ public class PolicyAssignmentProperties
/// </summary>
[JsonProperty(Required = Required.Default)]
public JObject Parameters { get; set; }

/// <summary>
/// The non-compliance messages used to describe why a resource is non-compliant with the policy.
/// </summary>
[JsonProperty(Required = Required.Default)]
public NonComplianceMessage[] NonComplianceMessages { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation
{
using System;
using System.Collections;
using System.Linq;
using System.Management.Automation;

using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
Expand Down Expand Up @@ -74,7 +75,6 @@ public class NewAzurePolicyAssignmentCmdlet : PolicyCmdletBase, IDynamicParamete
/// <summary>
/// Gets or sets the policy assignment policy definition parameter.
/// </summary>
[CmdletParameterBreakingChange("PolicyDefinition", OldParamaterType = typeof(PSObject), NewParameterTypeName = "PsPolicyDefinition")]
[Parameter(Mandatory = false, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicyDefinitionHelp)]
[Parameter(ParameterSetName = PolicyCmdletBase.DefaultParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicyDefinitionHelp)]
[Parameter(ParameterSetName = PolicyCmdletBase.PolicyParameterObjectParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicyDefinitionHelp)]
Expand All @@ -84,7 +84,6 @@ public class NewAzurePolicyAssignmentCmdlet : PolicyCmdletBase, IDynamicParamete
/// <summary>
/// Gets or sets the policy assignment policy set definition parameter.
/// </summary>
[CmdletParameterBreakingChange("PolicySetDefinition", OldParamaterType = typeof(PSObject), NewParameterTypeName = "PsPolicySetDefinition")]
[Parameter(Mandatory = false, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicySetDefinitionHelp)]
[Parameter(ParameterSetName = PolicyCmdletBase.DefaultParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicySetDefinitionHelp)]
[Parameter(ParameterSetName = PolicyCmdletBase.PolicySetParameterObjectParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.NewPolicyAssignmentPolicySetDefinitionHelp)]
Expand Down Expand Up @@ -133,6 +132,13 @@ public class NewAzurePolicyAssignmentCmdlet : PolicyCmdletBase, IDynamicParamete
[LocationCompleter("Microsoft.ManagedIdentity/userAssignedIdentities")]
public string Location { get; set; }

/// <summary>
/// Gets or sets the messages that describe why a resource is non-compliant with the policy.
/// </summary>
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.PolicyAssignmentNonComplianceMessageHelp)]
[ValidateNotNull]
public PsNonComplianceMessage[] NonComplianceMessage { get; set; }

/// <summary>
/// Executes the cmdlet.
/// </summary>
Expand Down Expand Up @@ -206,7 +212,8 @@ private JToken GetResource()
NotScopes = this.NotScope ?? null,
Metadata = this.Metadata == null ? null : this.GetObjectFromParameter(this.Metadata, nameof(this.Metadata)),
EnforcementMode = EnforcementMode ?? PolicyAssignmentEnforcementMode.Default,
Parameters = this.GetParameters(this.PolicyParameter, this.PolicyParameterObject)
Parameters = this.GetParameters(this.PolicyParameter, this.PolicyParameterObject),
NonComplianceMessages = this.NonComplianceMessage?.Where(message => message != null).SelectArray(message => message.ToModel())
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public static class PolicyHelpStrings
public const string SetPolicyAssignmentInputObjectHelp = "The policy assignment object to update that was output from another cmdlet.";
public const string PolicyAssignmentAssignIdentityHelp = "Generate and assign an Azure Active Directory Identity for this policy assignment. The identity will be used when executing deployments for 'deployIfNotExists' and 'modify' policies. Location is required when assigning an identity.";
public const string PolicyAssignmentLocationHelp = "The location of the policy assignment's resource identity. This is required when the -AssignIdentity switch is used.";
public const string PolicyAssignmentNonComplianceMessageHelp = "The non-compliance messages that describe why a resource is non-compliant with the policy.";

/// <summary>
/// Policy definition cmdlet parameter help strings
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.Policy
{
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Policy;
using Newtonsoft.Json.Linq;

/// <summary>
/// The policy assignment non compliance message used to describe why a resource is non-compliant with a policy.
/// </summary>
public class PsNonComplianceMessage
{
/// <summary>
/// Creates an instance of <see cref="PsNonComplianceMessage"/>.
/// </summary>
public PsNonComplianceMessage()
{
}

/// <summary>
/// Creates an instance of <see cref="PsNonComplianceMessage"/> from the equivalent model representation.
/// </summary>
/// <param name="messageModel">The non-compliance message model.</param>
public PsNonComplianceMessage(NonComplianceMessage messageModel)
{
this.Message = messageModel.Message;
this.PolicyDefinitionReferenceId = messageModel.PolicyDefinitionReferenceId;
}

/// <summary>
/// The message that describes why a resource is non-compliant with the policy. This is shown in 'deny' error messages and
/// on resource's non-compliant compliance results.
/// </summary>
public string Message { get; set; }

/// <summary>
/// The policy definition reference ID within a policy set definition that the message is intended for. This is only applicable if
/// the policy assignment assigns a policy set definition. If this is not provided the message applies to all policies assigned by this
/// policy assignment.
/// </summary>
public string PolicyDefinitionReferenceId { get; set; }

/// <summary>
/// Convert to JSON
/// </summary>
/// <returns>JSON representatnion of the non-compliance message.</returns>
public JToken ToJToken()
{
var returnValue = new JObject();
if (this.Message != null)
{
returnValue["message"] = this.Message;
}

if (this.PolicyDefinitionReferenceId != null)
{
returnValue["policyDefinitionReferenceId"] = this.PolicyDefinitionReferenceId;
}

return returnValue;
}

/// <summary>
/// Converts the powershell model to the entity model.
/// </summary>
public NonComplianceMessage ToModel()
{
return new NonComplianceMessage
{
Message = this.Message,
PolicyDefinitionReferenceId = this.PolicyDefinitionReferenceId
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
using Newtonsoft.Json.Linq;

/// <summary>
/// Class that wraps a policy definition PSObject
/// Class that wraps a policy assignment PSObject
/// </summary>
public class PsPolicyAssignment
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.Policy
{
using System.Linq;
using System.Management.Automation;

using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Entities.Policy;
using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Extensions;

Expand All @@ -23,6 +23,10 @@ public PsPolicyAssignmentProperties(JToken input)
EnforcementMode = (PsPolicyAssignmentEnforcementMode)properties.EnforcementMode;
PolicyDefinitionId = properties.PolicyDefinitionId;
Parameters = properties.Parameters.ToPsObject();
NonComplianceMessages = properties
.NonComplianceMessages?
.Where(message => message != null)
.SelectArray(message => new PsNonComplianceMessage(message));
}

/// <summary>
Expand Down Expand Up @@ -65,6 +69,11 @@ public PsPolicyAssignmentProperties(JToken input)
/// </summary>
public PSObject Parameters { get; set; }

/// <summary>
/// The non-compliance messages used to describe why a resource is non-compliant with the policy.
/// </summary>
public PsNonComplianceMessage[] NonComplianceMessages { get; set; }

/// <summary>
/// Convert to JSON
/// </summary>
Expand Down Expand Up @@ -109,6 +118,11 @@ public JToken ToJToken()
returnValue["parameters"] = this.Parameters.ToJToken();
}

if (this.NonComplianceMessages != null)
{
returnValue["nonComplianceMessages"] = this.NonComplianceMessages.ToJToken();
}

return returnValue;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation
{
using System;
using System.Collections;
using System.Linq;
using System.Management.Automation;

using Microsoft.Azure.Commands.ResourceManager.Cmdlets.Components;
Expand Down Expand Up @@ -130,6 +131,13 @@ public class SetAzurePolicyAssignmentCmdlet : PolicyCmdletBase
[Parameter(ParameterSetName = PolicyCmdletBase.InputObjectParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.SetPolicyAssignmentInputObjectHelp)]
public PsPolicyAssignment InputObject { get; set; }

/// <summary>
/// Gets or sets the messages that describe why a resource is non-compliant with the policy.
/// </summary>
[Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true, HelpMessage = PolicyHelpStrings.PolicyAssignmentNonComplianceMessageHelp)]
[ValidateNotNull]
public PsNonComplianceMessage[] NonComplianceMessage { get; set; }

/// <summary>
/// Executes the cmdlet.
/// </summary>
Expand Down Expand Up @@ -166,7 +174,7 @@ protected override void OnProcessRecord()
/// </summary>
private JToken GetResource(string resourceId, string apiVersion)
{
var resource = this.GetExistingResource(resourceId, apiVersion).Result.ToResource();
var resource = this.GetExistingResource(resourceId, apiVersion).Result.ToResource<PolicyAssignmentProperties>();

// get incoming object properties if present
JObject inputMetadata = null;
Expand All @@ -178,36 +186,42 @@ private JToken GetResource(string resourceId, string apiVersion)

var parameterMetadata = this.Metadata != null ? this.GetObjectFromParameter(this.Metadata, nameof(this.Metadata)) : null;

PolicyAssignmentEnforcementMode? existingMode = null;
if (Enum.TryParse(resource.Properties["enforcementMode"]?.ToString(), true, out PolicyAssignmentEnforcementMode tempMode))
{
existingMode = tempMode;
}

PolicyAssignmentEnforcementMode? inputMode = null;
if (Enum.TryParse(this.InputObject?.Properties?.EnforcementMode?.ToString(), true, out PolicyAssignmentEnforcementMode tempMode1))
{
inputMode = tempMode1;
}

// Grab the non-compliance messages from the parameter or input object or existing resource
var nonComplianceMessages = this.NonComplianceMessage?.Where(message => message != null).SelectArray(message => message.ToModel());
if (nonComplianceMessages == null && this.InputObject?.Properties.NonComplianceMessages != null)
{
nonComplianceMessages = this.InputObject.Properties.NonComplianceMessages.Where(message => message != null).SelectArray(message => message.ToModel());
}
else if (nonComplianceMessages == null)
{
nonComplianceMessages = resource.Properties.NonComplianceMessages;
}

var policyAssignmentObject = new PolicyAssignment
{
Name = this.Name ?? this.InputObject?.Name ?? resource.Name,
Identity = this.AssignIdentity.IsPresent ? new ResourceIdentity { Type = ResourceIdentityType.SystemAssigned } : null,
Location = this.Location ?? this.InputObject?.Location ?? resource.Location,
Properties = new PolicyAssignmentProperties
{
DisplayName = this.DisplayName ?? this.InputObject?.Properties?.DisplayName ?? resource.Properties["displayName"]?.ToString(),
Description = this.Description ?? this.InputObject?.Properties?.Description ?? resource.Properties["description"]?.ToString(),
Scope = resource.Properties["scope"]?.ToString(),
NotScopes = this.NotScope ?? this.InputObject?.Properties?.NotScopes ?? resource.Properties["NotScopes"]?.ToString()?.Split(','),
PolicyDefinitionId = resource.Properties["policyDefinitionId"]?.ToString(),
Metadata = parameterMetadata ?? inputMetadata ?? resource.Properties["metadata"] as JObject,
EnforcementMode = this.EnforcementMode ?? inputMode ?? existingMode,
DisplayName = this.DisplayName ?? this.InputObject?.Properties?.DisplayName ?? resource.Properties.DisplayName,
Description = this.Description ?? this.InputObject?.Properties?.Description ?? resource.Properties.Description,
Scope = resource.Properties.Scope,
NotScopes = this.NotScope ?? this.InputObject?.Properties?.NotScopes ?? resource.Properties.NotScopes,
PolicyDefinitionId = resource.Properties.PolicyDefinitionId,
Metadata = parameterMetadata ?? inputMetadata ?? resource.Properties.Metadata,
EnforcementMode = this.EnforcementMode ?? inputMode ?? resource.Properties.EnforcementMode,
NonComplianceMessages = nonComplianceMessages,
Parameters =
this.GetParameters(this.PolicyParameter, this.PolicyParameterObject)
?? this.InputObject?.Properties?.Parameters?.ToResourcePropertiesBody() as JObject
?? resource.Properties["parameters"] as JObject
?? resource.Properties.Parameters
}
};

Expand Down
Loading