policyDefStructure.tests.ps1

#Requires -Version 7

[CmdletBinding()]
Param (
  [Parameter(Mandatory = $true)]
  [ValidateScript({Test-Path -Path $_})]
  [string] $Path,

  [Parameter(Mandatory = $false)]
  [string[]] $ExcludePath
)
Write-Verbose "Path: '$Path'"

function GetPolicyEffect {
  param(
    [object] $policyObject
  )
  $parameterRegex = "^\[{1,2}parameters\(\'(\S+)\'\)\]$"
  $effect = $policyObject.properties.policyRule.then.effect
  #check if the effect is a parameterised value
  if ($effect -imatch $parameterRegex) {
    $effectParameterName = $matches[1]
    $policyEffectAllowedValues = $policyObject.properties.parameters.$effectParameterName.allowedValues
    $policyEffectDefaultValue = $policyObject.properties.parameters.$effectParameterName.defaultValue
    $effects = @()
    if ($policyEffectAllowedValues) {
      $effects += $policyEffectAllowedValues
    } else {
      $effects += $policyEffectDefaultValue
    }
    $result = @{
      effects            = $effects
      defaultEffectValue = $policyEffectDefaultValue
      isHardCoded        = $false
    }
  } else {
    $result = @{
      effects            = @($effect)
      defaultEffectValue = $null
      isHardCoded        = $true
    }
  }
  $result
}

# Get JSON files
if ((Get-Item $path).PSIsContainer) {
  Write-Verbose "Specified path '$path' is a directory"
  $gciParams = @{
    Path    = $Path
    Include = '*.json', '*.jsonc'
    Recurse = $true
  }
  $files = Get-ChildItem @gciParams
  # -Exclude parameter in Get-ChildItem only works on file name, not parent folder name hence it's not used in get-childitem
  if ($ExcludePath) {
    $ExcludePath = $ExcludePath -join '|'
    $files = $files | Where-Object -FilterScript {$_.FullName -notmatch $ExcludePath }
  }

} else {
  Write-Verbose "Specified path '$path' is a file"
  $files = Get-Item $path -Include '*.json', '*.jsonc'
}

# Policy Definition Tests
foreach ($file in $files) {
  Write-Verbose "Test '$file'" -verbose
  $fileName = (get-item $file).name
  $fileFullName = (get-item $file).FullName
  $fileRelativePath = GetRelativeFilePath -path $fileFullName
  #check if the file is inside a git repository
  $json = ConvertFrom-Json -InputObject (Get-Content -Path $file -Raw) -ErrorAction SilentlyContinue
  $testCase = @{
    fileName         = $fileName
    json             = $json
    policyEffect     = GetPolicyEffect -policyObject $json
    fileRelativePath = $fileRelativePath
  }
  Write-Verbose "[$file] Policy Effect: $($testCase.policyEffect.effects)"

  # Start Pester tests
  Describe "[$fileRelativePath]: Policy Definition Syntax Test" -Tag 'policyDefSyntax' {

    BeforeAll {
      # Variables - Use Script scope to make PSScriptAnalyzer happy <https://github.com/PowerShell/PSScriptAnalyzer/issues/1641>
      $Script:ValidEffects = [string[]](
        'AddToNetworkGroup',
        'Append',
        'Audit',
        'AuditIfNotExists',
        'Deny',
        'DenyAction',
        'DeployIfNotExists',
        'Disabled',
        'Manual',
        'Modify',
        'Mutate'
      )
      $Script:ValidModes = [string[]](
        'All',
        'Indexed',
        'Microsoft.DataFactory.Data',
        'Microsoft.KeyVault.Data',
        'Microsoft.Kubernetes.Data',
        'Microsoft.MachineLearningServices.v2.Data',
        'Microsoft.ManagedHSM.Data',
        'Microsoft.Network.Data'
      )
      $Script:ModifyConflictEffectsValidValues = [string[]](
        'audit',
        'deny',
        'disabled'
      )
      $Script:ValidParameterTypes = [string[]](
        'array',
        'boolean',
        'datetime',
        'float',
        'integer',
        'object',
        'string'
      )
    }

    Context 'Required Top-Level Elements Test' -Tag 'TopLevelElements' {

      It -Name 'Should contain top-level element name' -TestCases $testCase -Tag 'NameExists' -Test {
        param(
          [object] $json
        )
        $json.PSobject.Properties.name -cmatch 'name' | Should -Not -Be $Null
      }

      It -Name 'Should contain top-level element - properties' -TestCases $testCase -Tag 'PropertiesExists' -Test {
        param(
          [object] $json
        )
        $json.PSobject.Properties.name -cmatch 'properties' | Should -Not -Be $Null
      }
    }

    Context 'Policy Definition Elements Value Test' -Tag 'PolicyElements' {
      It -Name 'Name value must not be null' -TestCases $testCase -Tag 'NameNotNull' -Test {
        param(
          [object] $json
        )
        $json.name.length | Should -BeGreaterThan 0
      }

      It -Name 'Name value must not be longer than 64 characters' -TestCases $testCase -Tag 'NameLength' -Test {
        param(
          [object] $json
        )
        $json.name.length | Should -BeLessOrEqual 64
      }

      It -Name 'Name value must not contain spaces' -TestCases $testCase -Tag 'NoSpaceInName' -Test {
        param(
          [object] $json
        )
        $json.name -match ' ' | Should -Be $false
      }

      It -Name 'Name value must not contain forbidden characters' -TestCases $testCase -Tag 'NoForbiddenCharsInName' -Test {
        param(
          [object] $json
        )
        $json.name -match '[<>*%&:\\?.+\/]' | Should -Be $false
      }

    }

    Context 'Policy Definition Properties Value Test' -Tag 'PolicyProperties' {
      It -Name "Properties must contain 'displayName' element" -TestCases $testCase -Tag 'DisplayNameExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'displayName' | Should -Not -Be $Null
      }

      It -Name "'displayName' value must not be longer than 128 characters" -TestCases $testCase -Tag 'DisplayNameLength' -Test {
        param(
          [object] $json
        )
        $json.properties.displayName.length | Should -BeLessOrEqual 128
      }

      It -Name "'displayName' value must not have leading and trailing spaces" -TestCases $testCase -Tag 'DisplayNameStartsOrEndsWithSpace' -Test {
        param(
          [object] $json
        )
        $json.properties.displayName.length -eq $json.properties.displayName.trim().length | Should -Be $true
      }

      It -Name "'displayName' value must not end with a period '.'" -TestCases $testcase -Tag 'DisplayNameNotEndsWithPeriod' -Test {
        param(
          [object] $json
        )
        $json.properties.displayName.substring($json.properties.displayName.length -1, 1) | Should -Not -Be '.'
      }

      It -Name "Properties must contain 'description' element" -TestCases $testCase -Tag 'DescriptionExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'description' | Should -Not -Be $Null
      }

      It -Name "'description' value must not be longer than 512 characters" -TestCases $testCase -Tag 'DescriptionLength' -Test {
        param(
          [object] $json
        )
        $json.properties.description.length | Should -BeLessOrEqual 512
      }

      It -Name "'description' value must not have leading and trailing spaces" -TestCases $testCase -Tag 'DescriptionStartsOrEndsWithSpace' -Test {
        param(
          [object] $json
        )
        $json.properties.description.length -eq $json.properties.description.trim().length | Should -Be $true
      }

      It -Name "Properties must contain 'metadata' element" -TestCases $testCase -Tag 'MetadataExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'metadata' | Should -Not -Be $Null
      }

      It -Name "Properties must contain 'mode' element" -TestCases $testCase -Tag 'ModeExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'mode' | Should -Not -Be $Null
      }

      It -Name 'Policy mode must have a valid value.' -TestCases $testCase -Tag 'ValidMode' -Test {
        param(
          [object] $json
        )
        $ValidModes -contains $json.properties.mode | Should -Be $true
      }

      It -Name "Properties must contain 'parameters' element" -TestCases $testCase -Tag 'ParametersExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'parameters' | Should -Not -Be $Null
      }

      It -Name "'parameters' element must contain at least one (1) item" -TestCases $testCase -Tag 'ParametersMinCount' -Test {
        param(
          [object] $json
        )
        $json.properties.parameters.PSObject.Properties.count | Should -BeGreaterThan 0
      }

      It -Name "'parameters' element must contain no more than twenty (20) items" -TestCases $testCase -Tag 'ParametersMaxCount' -Test {
        param(
          [object] $json
        )
        $json.properties.parameters.PSObject.Properties.count | Should -BeLessOrEqual 20
      }

      It -Name "Properties must contain 'policyRule' element" -TestCases $testCase -Tag 'PolicyRuleExists' -Test {
        param(
          [object] $json
        )
        $json.properties.PSobject.Properties.name -cmatch 'policyRule' | Should -Not -Be $Null
      }

      It -Name "'DisplayName' value must not be blank" -TestCases $testCase -Tag 'DisplayNameNotBlank' -Test {
        param(
          [object] $json
        )
        $json.properties.displayName.length | Should -BeGreaterThan 0
      }

      It -Name "'Description' value must not be blank" -TestCases $testCase -Tag 'DescriptionNotBlank' -Test {
        param(
          [object] $json
        )
        $json.properties.description.length | Should -BeGreaterThan 0
      }

      It -Name "Must contain 'Category' metadata" -TestCases $testCase -Tag 'CategoryExists' -Test {
        param(
          [object] $json
        )
        $json.properties.metadata.category.length | Should -BeGreaterThan 0
      }

      It -Name "Must contain 'Version' metadata" -TestCases $testCase -Tag 'VersionExists' -Test {
        param(
          [object] $json
        )
        $json.properties.metadata.version.length | Should -BeGreaterThan 0
      }

      It -Name "'Version' metadata value must be a valid semantic version" -TestCases $testCase -Tag 'VersionIsSemantic' -Test {
        param(
          [object] $json
        )
        $json.properties.metadata.version -cmatch '^\d+\.\d+.\d+(-preview|-deprecated)?$' | Should -Be $true
      }
    }

    Context 'Parameters Tests' -Tag 'Parameters' {
      foreach ($parameterName in $json.properties.parameters.PSObject.Properties.Name) {
        $parameterConfig = $json.properties.parameters.$parameterName
        $parameterTestCase = @{
          parameterName   = $parameterName
          parameterConfig = $parameterConfig
        }

        It -Name "Parameter [<parameterName>] must contain 'type' element" -TestCases $parameterTestCase -Tag 'ParameterTypeExists' -Test {
          param(
            [object] $parameterConfig
          )
          $parameterConfig.PSobject.Properties.name -cmatch 'type' | Should -Not -Be $null
        }

        It -Name 'Parameter [<parameterName>] default value must be a member of allowed values' -TestCases (
          $parameterTestCase | Where-Object -FilterScript {
            $_.parameterConfig.PSObject.properties.name -icontains 'allowedValues' -and
            $_.parameterConfig.PSObject.properties.name -icontains 'defaultValue'
          }
        ) -Tag 'ParameterDefaultValueValid' -Test {
          param(
            [object] $parameterConfig
          )
          if ($parameterConfig.allowedValues) {
            if ($parameterConfig.type -ieq 'array') {
              $allInAllowedValues = $true
              foreach ($d in $parameterConfig.defaultValue) {
                if ($parameterConfig.allowedValues -notcontains $d) {$allInAllowedValues = $false}
              }
              $allInAllowedValues | Should -Be $true
            } else {
              $parameterConfig.allowedValues -contains $parameterConfig.defaultValue | Should -Be $true
            }
          }
        }

        It -Name "Parameter [<parameterName>] must have a valid value for the 'type' element" -TestCases $parameterTestCase -Tag 'ParameterTypeValid' -Test {
          param(
            [object] $parameterConfig
          )
          $ValidParameterTypes -contains $parameterConfig.type | Should -Be $true
        }

        It -Name "Parameter [<parameterName>] metadata must contain 'displayName' element" -TestCases $parameterTestCase -Tag 'ParameterDisplayNameExists' -Test {
          param(
            [object] $parameterConfig
          )
          $parameterConfig.metadata.PSobject.Properties.name -cmatch 'displayName' | Should -Not -Be $null
        }

        It -Name "Parameter [<parameterName>] metadata must contain 'description' element" -TestCases $parameterTestCase -Tag 'ParameterDescriptionExists' -Test {
          param(
            [object] $parameterConfig
          )
          $parameterConfig.metadata.PSobject.Properties.name -cmatch 'description' | Should -Not -Be $null
        }
      }
    }

    Context 'Policy Rule Test' -Tag 'PolicyRule' {
      It -Name "Policy Rule must contain 'if' element" -TestCases $testCase -Tag 'PolicyRuleIfExists' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.PSobject.Properties.name -cmatch 'if' | Should -Not -Be $Null
      }
      It -Name "Policy Rule must contain 'then' element" -TestCases $testCase -Tag 'PolicyRuleThenExists' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.PSobject.Properties.name -cmatch 'then' | Should -Not -Be $Null
      }
    }

    Context 'Policy Effect Test' -Tag 'PolicyEffect' {
      It -Name 'Policy Rule should have parameterised effect' -TestCases $testCase -Tag 'PolicyEffectParameterised' -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.isHardCoded | Should -Be $false
      }

      It -Name "Policy Rule parameterised effect should contain 'Disabled' effect" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.isHardCoded -eq $false }
      ) -Tag 'PolicyEffectParameterContainsDisabled' -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.effects -contains 'Disabled' | Should -Be $true
      }

      It -Name 'Policy Rule parameterised effect should have a default value' -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.isHardCoded -eq $false }
      ) -Tag 'PolicyEffectParameterHasDefaultValue' -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.defaultEffectValue | Should -Not -Be $null
      }

      It -Name 'Policy Rule must use a valid effect' -Tag 'PolicyEffectIsValid' -TestCases $testCase -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.effects.Where({$_ -in $ValidEffects},'First').'Count' | Should -BeExactly 1
      }

      It -Name "Policy rule with 'Deny' effect must also support 'Audit' Effect" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Deny'}
      ) -Tag 'PolicyDenyEffectAlsoSupportAudit' -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.effects -contains 'Audit' | Should -Be $true
      }

      It -Name "Policy rule with 'Audit' effect must also support 'Deny' Effect" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Audit'}
      ) -Tag 'PolicyAuditEffectAlsoSupportDeny' -Test {
        param(
          [hashtable] $policyEffect
        )
        $policyEffect.effects -contains 'Deny' | Should -Be $true
      }
    }

    Context 'Non DeployIfNotExists or Modify Effect Policy Configuration Test' -Tag NonDINEorModifyConfig {
      It -Name "Policy rule must not contain a 'roleDefinitionIds' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -notcontains 'DeployIfNotExists' -and $_.policyEffect.effects -notcontains 'Modify'}
      ) -Tag 'NonDINEorModifyRoleDefinition' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'roleDefinitionIds' | Should -Not -Be $true
      }
    }

    Context 'DeployIfNotExists Effect Policy Configuration Test' -Tag 'DINEConfig' {
      It -Name "Policy rule 'then' element Must contain a 'details' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINEDetails' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.PSobject.Properties.name -cmatch 'details' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a embedded 'deployment' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINEDeployment' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'deployment' | Should -Not -Be $Null
      }

      It -Name "Deployment mode for the policy rule must be 'incremental'" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINEIncrementalDeployment' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.mode -cmatch 'incremental' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a 'evaluationDelay' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINEEvaluationDelay' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'evaluationDelay' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a 'existenceCondition' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}) -Tag 'DINEExistenceCondition' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'existenceCondition' | Should -Not -Be $Null
      }

      It -Name 'Policy rule existenceCondition must not be empty' -TestCases (
        $testCase | Where-Object -FilterScript {
          $_.policyEffect.effects -contains 'DeployIfNotExists' -and
          $_.json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'existenceCondition'
        }
      ) -Tag 'DINEExistenceConditionNotEmpty' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.existenceCondition | ConvertTo-Json -Depth 10 | Should -Not -Be '{}'
      }

      It -Name "Policy rule must contain a 'roleDefinitionIds' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINERoleDefinition' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'roleDefinitionIds' | Should -Not -Be $Null
      }

      It -Name "'roleDefinitionIds' element must contain at least one item" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINERoleDefinitionCount' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.roleDefinitionIds.count | Should -BeGreaterThan 0
      }
    }

    Context 'DeployIfNotExists Effect Policy Embedded ARM Template Test' -Tag 'DINETemplate' {
      It -Name 'Embedded template Must have a valid schema' -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateSchema' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template."`$schema" | Should -BeLike 'https://schema.management.azure.com/schemas/*'
      }

      It -Name 'Embedded template Must contain a valid contentVersion' -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateContentVersion' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template.contentVersion | Should -BeGreaterThan ([version]'0.0.0.1')
      }

      It -Name "Embedded template Must contain a 'parameters' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateParameters' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template.PSobject.Properties.name -cmatch 'parameters' | Should -Not -Be $Null
      }

      It -Name "Embedded template Must contain a 'variables' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateVariables' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template.PSobject.Properties.name -cmatch 'variables' | Should -Not -Be $Null
      }

      It -Name "Embedded template Must contain a 'resources' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateResources' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template.PSobject.Properties.name -cmatch 'resources' | Should -Not -Be $Null
      }

      It -Name "Embedded template Must contain a 'outputs' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'DeployIfNotExists'}
      ) -Tag 'DINETemplateOutputs' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.deployment.properties.template.PSobject.Properties.name -cmatch 'outputs' | Should -Not -Be $Null
      }
    }

    Context 'Modify Effect Configuration Test' -Tag 'ModifyConfig' {
      It -Name "Policy rule 'then' element Must contain a 'details' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      )  -Tag 'ModifyDetails' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.PSobject.Properties.name -cmatch 'details' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a 'roleDefinitionIds' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      ) -Tag 'ModifyRoleDefinition' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'roleDefinitionIds' | Should -Not -Be $Null
      }

      It -Name "'roleDefinitionIds' element must contain at least one item" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      ) -Tag 'ModifyRoleDefinitionCount' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.roleDefinitionIds.count | Should -BeGreaterThan 0
      }

      It -Name "Policy rule must contain a 'conflictEffect' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      ) -Tag 'ModifyConflictEffect' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'conflictEffect' | Should -Not -Be $Null
      }

      It -Name "'conflictEffect' element must have a valid value" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      ) -Tag 'ModifyConflictEffectValid' -Test {
        param(
          [object] $json
        )
        $ModifyConflictEffectsValidValues -contains $json.properties.policyRule.then.details.conflictEffect | Should -Be $true
      }

      It -Name "Policy rule must contain an 'operations' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'Modify'}
      ) -Tag 'ModifyOperations' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'operations' | Should -Not -Be $Null
      }
    }

    Context 'AuditIfNotExists Effect Configuration Test' -Tag 'AuditIfNotExistsConfig' {
      It -Name "Policy rule 'then' element Must contain a 'details' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'AuditIfNotExists'}
      ) -Tag 'AINEDetails' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.PSobject.Properties.name -cmatch 'details' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a 'evaluationDelay' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'AuditIfNotExists'}
      ) -Tag 'AINEEvaluationDelay' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'evaluationDelay' | Should -Not -Be $Null
      }

      It -Name "Policy rule must contain a 'existenceCondition' element" -TestCases (
        $testCase | Where-Object -FilterScript {$_.policyEffect.effects -contains 'AuditIfNotExists'}
      ) -Tag 'AINEExistenceCondition' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'existenceCondition' | Should -Not -Be $Null
      }

      It -Name 'Policy rule existenceCondition must not be empty' -TestCases (
        $testCase | Where-Object -FilterScript {
          $_.policyEffect.effects -contains 'AuditIfNotExists' -and
          $_.json.properties.policyRule.then.details.PSobject.Properties.name -cmatch 'existenceCondition'
        }
      ) -Tag 'AINEExistenceConditionNotEmpty' -Test {
        param(
          [object] $json
        )
        $json.properties.policyRule.then.details.existenceCondition | ConvertTo-Json -Depth 10 | Should -Not -Be '{}'
      }
    }
  }
}