Azure / enterprise-azure-policy-as-code

Enterprise-ready Azure Policy-as-Code (PaC) solution (includes Az DevOps pipeline)
https://azure.github.io/enterprise-azure-policy-as-code/
MIT License
415 stars 219 forks source link

Unable to set the scope for Assignments and Exemptions on a subnets #737

Closed JerJon closed 1 week ago

JerJon commented 2 weeks ago

Describe the bug In Azure you can create assignments and exemptions scoped on a single resource (subnet), but with EPAC you can't due to the fact that EPAC has no knowledge of the subnet resources.

So every exemption scoped on a single subnet will result in a warning (Skip new exemption (resource does not exist):) and the exemption will be skipped.

Possibly this will happen for other sub resources as well.

Get-AzResourceListRestMethod.ps1 uses the rest api "resources". This rest api doesn't list subnets and therefore exemptions can't be made on these resource types while working with EPAC.

This function is currently using ApiVersion 2021-04-01. I tested it also with ApiVersion 2022-12-01 but this would also not return subnets as a resource.

To Reproduce

  1. Create a Vnet with a Subnet
  2. Assign policy "82778e1e-cf94-498c-8bd0-234fc9b688d8 / Deny subnets missing suffix"
  3. Create an exemption scoped on the just created Subnet for the just assigned policy.
  4. Execute Build-ExemptionsPlans.ps1

Expected behavior Every valid exemption should be deployable with EPAC.

Screenshots With this test change the subnets will be added to the resource list and therefore the exemptions with a subnet as a scope will be created.

This is no optimized code but just a quick setup for testing.

function Get-AzResourceListRestMethod {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        $SubscriptionId,

        [string] $ApiVersion = "2021-04-01",
        [string] $SnetApiVersion = "2024-01-01"
    )

    $path = "/subscriptions/$SubscriptionId/resources?api-version=$ApiVersion"
    $response = Invoke-AzRestMethod -Path $path -Method GET

    # Process response
    $statusCode = $response.StatusCode
    if ($statusCode -lt 200 -or $statusCode -ge 300) {
        $content = $response.Content
        Write-Warning "Policy Exemption error for scope '$Scope' $($statusCode) -- $($content)"
        Write-Output @() -NoEnumerate
    }

    $content = $response.Content
    $resources = $content | ConvertFrom-Json -Depth 100 -AsHashtable
    $nextLink = ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).nextLink
    while ($null -ne $nextLink) {
        $appendURL = (([uri]$nextlink).Query -split '&')[-1]
        $response = Invoke-AzRestMethod -Path ($path + '&' + $appendURL)  -Method GET
        $resources.value += ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).value
        $nextLink = ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).nextLink
    }

    # Get all subnets and add them to the resources list
    $snets = $($resources.value | Where-Object { $_.type -eq 'Microsoft.Network/virtualNetworks' })
    foreach ($snet in $snets) {
        $path = "$($snet.id)/subnets?api-version=$SnetApiVersion"
        $response = Invoke-AzRestMethod -Path $path -Method GET

        # Process response
        $statusCode = $response.StatusCode
        if ($statusCode -lt 200 -or $statusCode -ge 300) {
            $content = $response.Content
            Write-Warning "Policy Exemption error for scope '$Scope' $($statusCode) -- $($content)"
            Write-Output @() -NoEnumerate
        }

        $content = $response.Content
        $snetResources = $content | ConvertFrom-Json -Depth 100 -AsHashtable
        $nextLink = ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).nextLink
        while ($null -ne $nextLink) {
            $appendURL = (([uri]$nextlink).Query -split '&')[-1]
            $response = Invoke-AzRestMethod -Path ($path + '&' + $appendURL)  -Method GET
            $snetResources.value += ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).value
            $nextLink = ($response.Content | ConvertFrom-Json -Depth 100 -AsHashtable).nextLink
        }

        $resources.value += $snetResources.value
    }

    Write-Output $resources.value -NoEnumerate
}

EPAC Version Version of EPAC module you are using. 10.5.7