Azure / PSRule.Rules.Azure

Rules to validate Azure resources and infrastructure as code (IaC) using PSRule.
https://azure.github.io/PSRule.Rules.Azure/
MIT License
387 stars 84 forks source link

Handle tags when converting policies to rules #1872

Open VeraBE opened 1 year ago

VeraBE commented 1 year ago

Description of the issue

Tags are not handled by Export-AzPolicyAssignmentRuleData

To Reproduce

Steps to reproduce the issue:

Run Get-AzPolicyAssignmentDataSource | Export-AzPolicyAssignmentRuleData -Verbose against an assignment file with this content:

[
    {
        "Identity":  null,
        "Location":  null,
        "Name":  "12e4c75dcb1d3c43",
        "ResourceId":  "...",
        "ResourceName":  "12e4c75dcb1d3c43",
        "ResourceGroupName":  null,
        "ResourceType":  "Microsoft.Authorization/policyAssignments",
        "SubscriptionId":  null,
        "Sku":  null,
        "PolicyAssignmentId":  "...",
        "Properties":  {
                           "Scope":  "...",
                           "NotScopes":  null,
                           "DisplayName":  "...",
                           "Description":  "...",
                           "Metadata":  {
                                            "createdBy":  "d5f68f99-e07e-4453-add5-cebdba7b3c92",
                                            "createdOn":  "2020-05-12T22:14:43.4731105Z",
                                            "updatedBy":  "d5f68f99-e07e-4453-add5-cebdba7b3c92",
                                            "updatedOn":  "2022-05-10T22:06:19.9892111Z"
                                        },
                           "EnforcementMode":  0,
                           "PolicyDefinitionId":  "...",
                           "Parameters":  {
                                              "allowedLocations":  {
                                                                       "value":  [
                                                                                     "centraluseuap",
                                                                                     "eastus2euap",
                                                                                     "westcentralus",
                                                                                     "northcentralus",
                                                                                     "westus",
                                                                                     "eastasia",
                                                                                     "australiacentral",
                                                                                     "australiaeast",
                                                                                     "canadacentral",
                                                                                     "northeurope",
                                                                                     "japaneast",
                                                                                     "koreacentral",
                                                                                     "uksouth",
                                                                                     "centralus",
                                                                                     "eastus",
                                                                                     "westus2",
                                                                                     "francecentral",
                                                                                     "southafricanorth",
                                                                                     "germanynorth",
                                                                                     "norwayeast",
                                                                                     "switzerlandnorth",
                                                                                     "uaenorth",
                                                                                     "southeastasia",
                                                                                     "australiacentral2",
                                                                                     "australiasoutheast",
                                                                                     "canadaeast",
                                                                                     "westeurope",
                                                                                     "japanwest",
                                                                                     "koreasouth",
                                                                                     "ukwest",
                                                                                     "eastus2",
                                                                                     "southcentralus",
                                                                                     "brazilsouth",
                                                                                     "francesouth",
                                                                                     "southafricawest",
                                                                                     "germanywestcentral",
                                                                                     "norwaywest",
                                                                                     "uaecentral",
                                                                                     "switzerlandwest",
                                                                                     "brazilus",
                                                                                     "global",
                                                                                     "brazilsoutheast",
                                                                                     "westus3",
                                                                                     "swedencentral",
                                                                                     "swedensouth",
                                                                                     "jioindiawest",
                                                                                     "jioindiacentral",
                                                                                     "centralindia",
                                                                                     "southindia",
                                                                                     "westindia",
                                                                                     "qatarcentral",
                                                                                     "qatarcentral2",
                                                                                     "eastusslv",
                                                                                     "israelcentral",
                                                                                     "polandcentral",
                                                                                     "taiwannorth",
                                                                                     "taiwannorthwest",
                                                                                     "italynorth"
                                                                                 ]
                                                                   },
                                              "effect":  {
                                                             "value":  "AuditIfNotExists"
                                                         },
                                              "tagname":  {
                                                              "value":  "SQLADSExcluded"
                                                          }
                                          },
                           "NonComplianceMessages":  null
                       },
        "PolicyDefinitions":  [
                                  {
                                      "Name":  "386c8ebacef0fc43",
                                      "ResourceId":  "...",
                                      "ResourceName":  "386c8ebacef0fc43",
                                      "ResourceType":  "Microsoft.Authorization/policyDefinitions",
                                      "SubscriptionId":  null,
                                      "Properties":  {
                                                         "Description":  null,
                                                         "DisplayName":  "...",
                                                         "Metadata":  {
                                                                          "createdBy":  "d5f68f99-e07e-4453-add5-cebdba7b3c92",
                                                                          "createdOn":  "2020-05-12T22:14:42.9642707Z",
                                                                          "updatedBy":  null,
                                                                          "updatedOn":  null
                                                                      },
                                                         "Mode":  "All",
                                                         "Parameters":  {
                                                                            "allowedLocations":  {
                                                                                                     "type":  "Array",
                                                                                                     "metadata":  {
                                                                                                                      "displayName":  "Allowed locations",
                                                                                                                      "description":  "The list of locations that can be specified when deploying resources.",
                                                                                                                      "strongType":  "location"
                                                                                                                  }
                                                                                                 },
                                                                            "tagname":  {
                                                                                            "type":  "String",
                                                                                            "metadata":  {
                                                                                                             "displayName":  "Exclusion Tag Name",
                                                                                                             "description":  "Rule is not deployed if this tag exists on the SQL Server"
                                                                                                         }
                                                                                        },
                                                                            "effect":  {
                                                                                           "type":  "String",
                                                                                           "metadata":  {
                                                                                                            "displayName":  "Effect",
                                                                                                            "description":  "Enable or disable the execution of the policy"
                                                                                                        },
                                                                                           "allowedValues":  [
                                                                                                                 "AuditIfNotExists",
                                                                                                                 "Disabled"
                                                                                                             ],
                                                                                           "defaultValue":  "AuditIfNotExists"
                                                                                       }
                                                                        },
                                                         "PolicyRule":  {
                                                                            "if":  {
                                                                                       "allOf":  [
                                                                                                     {
                                                                                                         "field":  "type",
                                                                                                         "equals":  "Microsoft.Sql/servers"
                                                                                                     },
                                                                                                     {
                                                                                                         "field":  "location",
                                                                                                         "in":  "[parameters(\u0027allowedLocations\u0027)]"
                                                                                                     },
                                                                                                     {
                                                                                                         "field":  "[concat(\u0027tags[\u0027, parameters(\u0027tagname\u0027), \u0027]\u0027)]",
                                                                                                         "exists":  "false"
                                                                                                     },
                                                                                                     {
                                                                                                         "value":  "[resourceGroup().tags[parameters(\u0027tagname\u0027)]]",
                                                                                                         "equals":  ""
                                                                                                     },
                                                                                                     {
                                                                                                         "value":  "[subscription().tags[parameters(\u0027tagname\u0027)]]",
                                                                                                         "equals":  ""
                                                                                                     }
                                                                                                 ]
                                                                                   },
                                                                            "then":  {
                                                                                         "effect":  "[parameters(\u0027effect\u0027)]",
                                                                                         "details":  {
                                                                                                         "type":  "Microsoft.Sql/servers/securityAlertPolicies",
                                                                                                         "name":  "default",
                                                                                                         "existenceCondition":  {
                                                                                                                                    "field":  "Microsoft.Sql/servers/securityAlertPolicies/disabledAlerts[*]",
                                                                                                                                    "equals":  ""
                                                                                                                                }
                                                                                                     }
                                                                                     }
                                                                        },
                                                         "PolicyType":  1
                                                     },
                                      "PolicyDefinitionId":  "..."
                                  }
                              ]
    }
]

I cleaned up all the information that was potentially private replacing it with "..."

Error output

Export-AzPolicyAssignmentRuleData : An error occurred evaluating expression '[resourceGroup().tags[parameters('tagname')]]' line 161. Object reference not set to an instance of an object.
At C:\Users\verabo.REDMOND\OneDrive - Microsoft\Documents\Policy\WithPSRule\policy.ps1:7 char:36
+ ... ataSource | Export-AzPolicyAssignmentRuleData -OutputPath "rulesFromP ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: ({
  "name": "3...onId": "..."
}:JObject) [Export-AzPolicyAssignmentRuleData], ExpressionEvaluationException
    + FullyQualifiedErrorId : System.NullReferenceException,Export-AzPolicyAssignmentRuleData

Module in use and version:

Captured output from $PSVersionTable:

Name                           Value
----                           -----
PSVersion                      5.1.22621.608
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.22621.608
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
BernieWhite commented 1 year ago

@VeraBE Thanks for reporting the issue.

BernieWhite commented 1 year ago

Tags for the parent resource group or subscription are tricky we'll need to add support for this ([resourceGroup().tags[parameters('tagname')]]).

Currently we are resolving most of the policy at rule export however we won't be able to resolve these context variables because they are associated with the resource, which is not known when the policy is converted to a rule.