Skip to content
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

Field Count Expression Support #1024

Closed
ArmaanMcleod opened this issue Mar 22, 2022 · 11 comments · Fixed by #1238
Closed

Field Count Expression Support #1024

ArmaanMcleod opened this issue Mar 22, 2022 · 11 comments · Fixed by #1238
Assignees
Labels
enhancement New feature or request feature: language Issues that affect language such keywords and variables
Milestone

Comments

@ArmaanMcleod
Copy link
Contributor

Azure Policies can use field count expressions, as shown here: https://docs.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure?msclkid=b8d82f12a9be11eca152412a44ae0022#field-count

We should include a similar expression in the PSRule engine.

@ArmaanMcleod ArmaanMcleod added the enhancement New feature or request label Mar 22, 2022
@BernieWhite BernieWhite added this to the v2.1.0 milestone Mar 22, 2022
@ArmaanMcleod ArmaanMcleod self-assigned this Mar 22, 2022
@BernieWhite BernieWhite added the feature: language Issues that affect language such keywords and variables label Apr 15, 2022
@BernieWhite BernieWhite modified the milestones: v2.1.0, v2.2.0 May 2, 2022
@BernieWhite
Copy link
Member

BernieWhite commented May 6, 2022

@ArmaanMcleod Can we write.

Example 1

Policy example:

{
    "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]"
    },
    "equals": 0
}

PSRule example:

field: properties.securityRules[*]
count: 0

Example 2

Policy example:

{
    "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
            "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].description",
            "equals": "My unique description"
        }
    },
    "equals": 1
}

PSRule example:

field: properties.securityRules[[email protected]=='My unique description']
count: 1

Example 3

Policy example:

{
    "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
            "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].description",
            "equals": "My common description"
        }
    },
    "greaterOrEquals": 1
}

PSRule example:

field: properties.securityRules[[email protected]=='My unique description']
greaterOrEquals: 1

Example 4

Policy example:

{
    "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
            "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].description",
            "equals": "description"
        }
    },
    "equals": "[length(field('Microsoft.Network/networkSecurityGroups/securityRules[*]'))]"
}

PSRule example: I don't think this is possible without complex expression work because we can't compare the right side as is.

Example 5

Policy example:

{
    "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
            "allOf": [
                {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
                    "equals": "Inbound"
                },
                {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
                    "equals": "Allow"
                },
                {
                    "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
                    "equals": "3389"
                }
            ]
        }
    },
    "greater": 0
}

PSRule example:

field: properties.securityRules[[email protected]=='Inbound' && @.access=='Allow' && @.destinationPortRange=='3389']
greater: 0

@ArmaanMcleod
Copy link
Contributor Author

@BernieWhite I think that should work fine. Example 4 could just be a gap until more complex expression support is introduced. The others can be converted when visiting the policy rules. Could probably move this issue to PSRule.Rules.Azure since the conversion could all be done there.

@BernieWhite
Copy link
Member

@ArmaanMcleod It think it is still worth leaving open to make sure we cover it in complex expressions. The first pass of #1045 may not provide all the final features we need.

We could also probably build some docs relating to my examples as well.

@ArmaanMcleod
Copy link
Contributor Author

ArmaanMcleod commented May 7, 2022

@BernieWhite Makes sense, Like that idea.

Also for the 2nd & 3rd example, should it be properties.securityRules[[email protected]=='My unique description'] instead?

since the alias paths are:

  • "Microsoft.Network/networkSecurityGroups/securityRules[*]": "properties.securityRules[*]"
  • "Microsoft.Network/networkSecurityGroups/securityRules[*].description": "properties.securityRules[*].properties.description"

@ArmaanMcleod
Copy link
Contributor Author

ArmaanMcleod commented May 8, 2022

@BernieWhite Can the below

{
  "count": {
    "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
    "where": {
      "allOf": [
        {
          "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].sourceAddressPrefix",
          "equals": "*"
        },
        {
          "anyOf": [
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
              "equals": "22"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
              "equals": "3389"
            }
          ]
        }
      ]
    }
  },
  "greater": 0
}

be written as:

field: properties.securityRules[[email protected] == '*' && (@.properties.destinationPortRange == '22' || @.properties.destinationPortRange == '3389')]
greater: 0

I havn't played around enough with object path syntax to know if it is possible 😄

@BernieWhite
Copy link
Member

@ArmaanMcleod Good question. The .. descendant operator is not supported but the grouping should work. We don't specifically have a test case for it, but ideally it should work if it doesn't.

@ArmaanMcleod
Copy link
Contributor Author

@BernieWhite With counts like below:

{
  "count": {
    "field": "Microsoft.Network/virtualNetworks/subnets[*]",
    "where": {
      "allOf": [
        {
          "exists": "false",
          "field": "Microsoft.Network/virtualNetworks/subnets[*].routeTable.id"
        },
        {
          "field": "Microsoft.Network/virtualNetworks/subnets[*].name",
          "notIn": [
            "AzureFirewallManagementSubnet",
            "SomeOtherSubnet"
          ]
        }
      ]
    }
  }
}

Thinking we can convert notIn to (@.name != 'AzureFirewallManagementSubnet' && @.name != 'SomeOtherSubnet').

@ArmaanMcleod
Copy link
Contributor Author

ArmaanMcleod commented May 22, 2022

@BernieWhite

Just checking with you if the below:

{
  "count": {
    "field": "Microsoft.Network/privateEndpoints/privateLinkServiceConnections[*].groupIds[*]",
    "where": {
      "field": "Microsoft.Network/privateEndpoints/privateLinkServiceConnections[*].groupIds[*]",
      "notIn": [
        "blob",
        "sqlServer"
      ]
    }
  },
  "greaterOrEquals": 1
}

would be converted to:

field: properties.privateLinkServiceConnections[*].properties.groupIds[?(@ != 'blob' && @ != 'sqlServer')]
greaterOrEquals: 1

@BernieWhite
Copy link
Member

@ArmaanMcleod That's how I'd do it. Does it work? I don't think we specifically have a test case for that but maybe worth adding any of these to tests at some point.

@ArmaanMcleod
Copy link
Contributor Author

@ArmaanMcleod That's how I'd do it. Does it work? I don't think we specifically have a test case for that but maybe worth adding any of these to tests at some point.

@BernieWhite Sweet. I'll add a bunch of tests to validate this works. I've found a couple of object path variations that don't have explicit tests. Will add them in at some point.

@BernieWhite
Copy link
Member

Sub-selectors and functions as they move forward should address the remaining points that are hard to do with YAML and JSON expressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature: language Issues that affect language such keywords and variables
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants