microsoft / PSRule

Validate infrastructure as code (IaC) and objects using PowerShell rules.
https://microsoft.github.io/PSRule/v2/
MIT License
375 stars 49 forks source link

Add support for reading XML files #1537

Open ronaldbosma opened 1 year ago

ronaldbosma commented 1 year ago

I would like to be able to read XML files so I can create custom rules to check the XML. In particular, I want to check my Azure API Management policies. We define our policies in separate XML files, so I would like to be able to load these and check them with PSRule.

An extra XML option for Input.Format would most likely be the best approach. Handling both elements & attributes might be a bit tricky, but both should be supported. Also note that elements with the same name can occur multiple times, while attribute names should be unique for a given element.

Alternatives I looked at:

The PSRule.Rules.Azure suite provides the Azure.APIM.PolicyBase rule that asserts the contents of a policy. This rule expects the policy XML to be defined directly in the Bicep file. We use separate XML files for our policies because these are more readable and maintainable. So, the approach used for Azure.APIM.PolicyBase doesn't work for us.

I've tried converting the Bicep into an JSON ARM template and then using a similar approach as is done by Azure.APIM.PolicyBase, but the policy contents is set via a generated variable as you can see in the sample below. Most likely because we use the loadTextContent Bicep function when loading the policy XML file.

"variables": {
  "$fxv#1": "<policies>\r\n   ... \r\n</policies>"
},
"resources": [
  {
    "type": "Microsoft.ApiManagement/service/apis/policies",
    "apiVersion": "2022-08-01",
    "name": "[format('{0}/{1}/{2}', parameters('apimResourceName'), parameters('apiName'), 'policy')]",
    "properties": {
      "format": "rawxml",
      "value": "[variables('$fxv#1')]"
    },
    "dependsOn": [
      "[resourceId('Microsoft.ApiManagement/service/apis', parameters('apimResourceName'), parameters('apiName'))]"
    ]
  },
github-actions[bot] commented 1 year ago

Thanks for raising your first issue, the team appreciates the time you have taken 😉

BernieWhite commented 1 year ago

@ronaldbosma Thanks for raising the issue. We'd like to improve XML support too since we found a few things that could be better such as #1518.

Currently you can use PowerShell based rules to work with XML by using a convention. Similar to this https://github.com/microsoft/PSRule-samples/tree/main/samples/azure/BicepModuleRequires

For example:

# Synopsis: Imports XML policy in for analysis.
Export-PSRuleConvention 'APIMPolicy.Import' -Initialize {
    $policies = @(Get-ChildItem -Path 'policies/' -Include '*.xml' -Recurse -File | ForEach-Object {
        $name = $_.Name
        [PSCustomObject]@{
            Name = $name
            Content = [Xml](Get-Content -Path $_.FullName -Raw)
        }
    })
    $PSRule.ImportWithType('APIMPolicy', $policies);
}

# Synopsis: Your rule
Rule 'Rule001' -Type 'APIMPolicy' {
    $policy = $TargetObject.Content
}

See conventions

ronaldbosma commented 1 year ago

Hi @BernieWhite. Thanks for answering and for your suggestion to use a convention. I was working a workaround which had several issues, but using a convention that includes the file name and importing them as custom types fixed all of them.

ronaldbosma commented 3 weeks ago

Hi @BernieWhite I was wondering if the above still applies for v3. Do I still need to use a custom convention with PowerShell based rules to check XML?

BernieWhite commented 3 weeks ago

@ronaldbosma The current plan for this is v3.1.0, as shipping the breaking changes we need to move forward is the main priority for v3.0, which is very close now. So yes, at GA drop v3.0 will still require the convention.

If you are trying with the current v3 prerelease you may need to set Input.FileObjects to true if there is no files found. This will be defaulted to true in the next prerelease.

See: https://microsoft.github.io/PSRule/v3/concepts/PSRule/en-US/about_PSRule_Options/#inputfileobjects