Azure Policy 定义结构策略规则

策略规则包括 ifthen 块。 在 if 块中,定义强制执行策略时指定的一个或多个条件。 可以对这些条件应用逻辑运算符,以精确定义策略的方案。

有关每种效果、评估顺序、属性和示例的完整详细信息,请参阅 Azure Policy 定义效果基本信息

then 块中,定义满足 if 条件时产生的效果。

{
  "if": {
      <condition> | <logical operator>
  },
  "then": {
    "effect": "deny | audit | modify | denyAction | append | auditIfNotExists | deployIfNotExists | disabled"
  }
}

有关 policyRule 的详细信息,请转到策略定义架构

逻辑运算符

支持的逻辑运算符为:

  • "not": {condition or operator}
  • "allOf": [{condition or operator},{condition or operator}]
  • "anyOf": [{condition or operator},{condition or operator}]

not 语法反转条件的结果。 allOf 语法(与逻辑 and 操作相似)要求所有条件都为 true。 anyOf 语法(与逻辑 or 操作相似)要求一个或多个条件为 true。

可以嵌套逻辑运算符。 以下示例显示了嵌套在 allOf 操作中的 not 操作。

"if": {
  "allOf": [
    {
      "not": {
        "field": "tags",
        "containsKey": "application"
      }
    },
    {
      "field": "type",
      "equals": "Microsoft.Storage/storageAccounts"
    }
  ]
},

条件

条件用于评估某个值是否符合特定的标准。 支持的条件有:

  • "equals": "stringValue"
  • "notEquals": "stringValue"
  • "like": "stringValue"
  • "notLike": "stringValue"
  • "match": "stringValue"
  • "matchInsensitively": "stringValue"
  • "notMatch": "stringValue"
  • "notMatchInsensitively": "stringValue"
  • "contains": "stringValue"
  • "notContains": "stringValue"
  • "in": ["stringValue1","stringValue2"]
  • "notIn": ["stringValue1","stringValue2"]
  • "containsKey": "keyName"
  • "notContainsKey": "keyName"
  • "less": "dateValue" | "less": "stringValue" | "less": intValue
  • "lessOrEquals": "dateValue" | "lessOrEquals": "stringValue" | "lessOrEquals": intValue
  • "greater": "dateValue" | "greater": "stringValue" | "greater": intValue
  • "greaterOrEquals": "dateValue" | "greaterOrEquals": "stringValue" | "greaterOrEquals": intValue
  • "exists": "bool"

对于 lesslessOrEqualsgreatergreaterOrEquals;如果属性类型与条件类型不匹配,则会引发错误。 字符串比较是使用 InvariantCultureIgnoreCase 进行。

使用 likenotLike 条件时,请在值中指定通配符 (*)。 值不应包含多个通配符。

当使用 matchnotMatch 条件时,请提供井号标签 (#) 来匹配数字,提供问号 (?) 来匹配字母,提供句点 (.) 来匹配所有字符,并提供任何其他字符来匹配该实际字符。 尽管 matchnotMatch 区分大小写,但计算 stringValue 的所有其他条件都不区分大小写。 matchInsensitivelynotMatchInsensitively 中提供了不区分大小写的替代项。

字段

可以使用 field 表达式来构建条件,用于评估资源请求有效负载中的属性值是否符合特定的标准。 支持以下字段:

  • name

  • fullName

    • 返回资源全名。 资源全名是最前面为任意父资源名称的资源名称(例如 myServer/myDatabase)。
  • kind

  • type

  • location

    • 位置字段已规范化,支持各种格式。 例如,China East 2 被视为等于 chinaeast2
    • 对于不限位置的资源,请使用 global
  • id

    • 返回所评估的资源的资源 ID。
    • 示例: /subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/myRG/providers/Microsoft.KeyVault/vaults/myVault
  • identity.type

  • tags

  • tags['<tagName>']

    • 此括号语法支持具有标点符号的标记名称,例如连字符、句点或空格。
    • 其中,tagName 是要验证其条件的标记的名称。
    • 示例:tags['Acct.CostCenter'],其中 Acct.CostCenter 是标记的名称。
  • tags['''<tagName>''']

    • 此括号语法通过双撇号进行转义,可支持在其中包含撇号的标记名称。
    • 其中,tagName 是要验证其条件的标记的名称。
    • 示例:tags['''My.Apostrophe.Tag'''],其中 'My.Apostrophe.Tag' 是标记的名称。

    注意

    tags.<tagName>tags[tagName]tags[tag.with.dots] 仍然是可接受的用于声明标记字段的方式。 但是,首选表达式是上面列出的那些。

  • 属性别名 - 有关列表,请参阅别名

    注意

    在引用数组别名 [*]field 表达式中,将在元素之间使用逻辑 and 对数组中的每个元素分别进行评估。 有关详细信息,请参阅引用数组资源属性

使用 field 表达式的条件可以替换用于写入操作的旧策略定义语法 "source": "action"。 例如,此项不再受支持:

{
  "source": "action",
  "like": "Microsoft.Network/publicIPAddresses/*"
}

但可以使用 field 逻辑实现所需的行为:

{
  "field": "type",
  "equals": "Microsoft.Network/publicIPAddresses"
}

使用带参数的标记

参数值可以传递给标记字段。 将参数传递给标记字段可在策略分配期间提高策略定义的灵活性。

在以下示例中,concat 用于为名为 tagName 参数值的标记创建标记字段查找。 如果该标记不存在,系统会使用 modify 效果,通过 resourcegroup() 查找函数使用在已审计资源父资源组上具有相同名称的标签值来添加该标记。

{
  "if": {
    "field": "[concat('tags[', parameters('tagName'), ']')]",
    "exists": "false"
  },
  "then": {
    "effect": "modify",
    "details": {
      "operations": [
        {
          "operation": "add",
          "field": "[concat('tags[', parameters('tagName'), ']')]",
          "value": "[resourcegroup().tags[parameters('tagName')]]"
        }
      ],
      "roleDefinitionIds": [
        "/providers/microsoft.authorization/roleDefinitions/4a9ae827-6dc8-4573-8ac7-8239d42aa03f"
      ]
    }
  }
}

用于评估某个值是否符合特定标准的条件可以使用 value 表达式来构建。 值可以是文本、参数的值,也可以是任何受支持的模板函数的返回值。

警告

如果模板函数的结果是一个错误,则策略评估会失败。 评估失败是一种隐式 deny。 有关详细信息,请参阅避免模板失败。 使用 doNotEnforceenforcementMode,以防止在测试和验证新策略定义期间,由于新的或更新的资源评估失败而受到影响。

Value 示例

此策略规则示例使用 valueresourceGroup() 函数和返回的 name 属性的结果与 *netrglike 条件进行对比。 此规则拒绝名称以 *netrg 结尾的任何资源组中不为 Microsoft.Network/* type 的资源。

{
  "if": {
    "allOf": [
      {
        "value": "[resourceGroup().name]",
        "like": "*netrg"
      },
      {
        "field": "type",
        "notLike": "Microsoft.Network/*"
      }
    ]
  },
  "then": {
    "effect": "deny"
  }
}

此策略规则示例使用 value 来检查多个嵌套函数的结果是否 equals true。 此规则拒绝并没有至少三个标记的资源。

{
  "mode": "indexed",
  "policyRule": {
    "if": {
      "value": "[less(length(field('tags')), 3)]",
      "equals": "true"
    },
    "then": {
      "effect": "deny"
    }
  }
}

避免模板失败

如果在 value 中使用模板函数,则可以使用许多复杂嵌套函数。 如果模板函数的结果是一个错误,则策略评估会失败。 评估失败是一种隐式 deny。 在某些情况下失败的 value 示例:

{
  "policyRule": {
    "if": {
      "value": "[substring(field('name'), 0, 3)]",
      "equals": "abc"
    },
    "then": {
      "effect": "audit"
    }
  }
}

上面的示例策略规则使用 substring()name 的前三个字符与 abc 进行比较。 如果 name 少于三个字符,substring() 函数会导致错误。 此错误会导致策略变为 deny 效果。

请转而使用 if() 函数来检查 name 的前三个字符是否等于 abc,不允许 name 少于三个字符,这会导致错误:

{
  "policyRule": {
    "if": {
      "value": "[if(greaterOrEquals(length(field('name')), 3), substring(field('name'), 0, 3), 'not starting with abc')]",
      "equals": "abc"
    },
    "then": {
      "effect": "audit"
    }
  }
}

使用修订后的策略规则,if() 会先检查 name的长度,然后尝试在少于三个字符的值上获取 substring()。 如果 name 太短,则会改为返回“不是以 abc 开头”的值,并将其与 abc 进行比较。 短名称不是以 abc 开头的资源仍会导致策略规则失败,但在评估过程中不会再造成错误。

计数

用于统计数组中有多少成员符合特定标准的条件可以使用 count 表达式来构建。 常见方案是检查是“至少有一个”数组成员、“正好有一个”数组成员、“所有”数组成员还是“没有”数组成员满足条件。 count 会根据条件表达式评估每个数组成员,并将 true 结果求和,然后将其与表达式运算符进行比较。

字段计数

统计请求有效负载中的数组有多少成员符合条件表达式。 field count 表达式的结构如下:

{
  "count": {
    "field": "<[*] alias>",
    "where": {
      /* condition expression */
    }
  },
  "<condition>": "<compare the count of true condition expression array members to this value>"
}

以下属性与 field count 搭配使用:

  • count.field(必需):包含数组路径,且必须为数组别名。
  • count.where(可选):用于分别评估 count.field 的每个数组别名数组成员的条件表达式。 如果未提供此属性,具有“字段”路径的所有数组成员将评估为 true。 任何条件都可在此属性内使用。 可在此属性中使用逻辑运算符来创建复杂的评估要求。
  • condition(必需):该值与符合 count.where 条件表达式的项数进行比较。 应使用数字条件

若要详细了解如何在 Azure Policy 中使用数组属性(包括关于如何计算 field count 表达式的详细说明),请参阅引用数组资源属性

值计数

统计数组中有多少成员符合条件。 数组可以是文本数组,也可以是对数组参数的引用value count 表达式的结构如下:

{
  "count": {
    "value": "<literal array | array parameter reference>",
    "name": "<index name>",
    "where": {
      /* condition expression */
    }
  },
  "<condition>": "<compare the count of true condition expression array members to this value>"
}

以下属性与 value count 搭配使用:

  • count.value(必需):要评估的数组。
  • count.name(必需):索引名称,由英文字母和数字构成。 定义在当前迭代中评估的数组成员的值的名称。 该名称用于引用 count.where 条件内的当前值。 当 count 表达式不在另一个 count 表达式的子级中时,此项为可选。 如果未提供此项,则索引名称将隐式设置为 "default"
  • count.where(可选):条件表达式,用于分别对 count.value 的每个数组成员进行评估。 如果未提供此属性,则所有数组成员都将评估为 true。 任何条件都可在此属性内使用。 可在此属性中使用逻辑运算符来创建复杂的评估要求。 可以通过调用 current 函数来访问当前枚举的数组成员的值。
  • condition(必需):该值与符合 count.where 条件表达式的项数进行比较。 应使用数字条件

current 函数

current() 函数仅在 count.where 条件内可用。 它返回当前通过 count 表达式评估枚举的数组成员的值。

值计数用法

  • current(<index name defined in count.name>)。 例如:current('arrayMember')
  • current()。 只有当 value count 表达式不是另一个 count 表达式的子级时,才允许使用此函数。 返回与上面的值相同的值。

如果调用返回的值是一个对象,则支持属性访问器。 例如:current('objectArrayMember').property

字段计数用法

  • current(<the array alias defined in count.field>)。 例如,current('Microsoft.Test/resource/enumeratedArray[*]')
  • current()。 只有当 field count 表达式不是另一个 count 表达式的子级时,才允许使用此函数。 返回与上面的值相同的值。
  • current(<alias of a property of the array member>)。 例如,current('Microsoft.Test/resource/enumeratedArray[*].property')

字段计数示例

示例 1:检查数组是否为空

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

示例 2:检查是否只有一个数组成员符合条件表达式

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

示例 3:检查是否至少有一个数组成员符合条件表达式

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

示例 4:检查是否所有对象数组成员都符合条件表达式

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

示例 5:检查是否至少有一个数组成员与条件表达式中的多个属性匹配

{
  "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
}

示例 6:在 where 条件中使用 current() 函数来访问模板函数中当前枚举的数组成员的值。 此条件检查虚拟网络是否包含不在 10.0.0.0/24 CIDR 范围内的某个地址前缀。

{
  "count": {
    "field": "Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]",
    "where": {
      "value": "[ipRangeContains('10.0.0.0/24', current('Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]'))]",
      "equals": false
    }
  },
  "greater": 0
}

示例 7:在 where 条件中使用 field() 函数来访问当前枚举的数组成员的值。 此条件检查虚拟网络是否包含不在 10.0.0.0/24 CIDR 范围内的某个地址前缀。

{
  "count": {
    "field": "Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]",
    "where": {
      "value": "[ipRangeContains('10.0.0.0/24', first(field(('Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]')))]",
      "equals": false
    }
  },
  "greater": 0
}

值计数示例

示例 1:检查资源名称是否与任何给定的名称模式匹配。

{
  "count": {
    "value": [
      "prefix1_*",
      "prefix2_*"
    ],
    "name": "pattern",
    "where": {
      "field": "name",
      "like": "[current('pattern')]"
    }
  },
  "greater": 0
}

示例 2:检查资源名称是否与任何给定的名称模式匹配。 current() 函数未指定索引名称。 结果与前面的示例相同。

{
  "count": {
    "value": [
      "prefix1_*",
      "prefix2_*"
    ],
    "where": {
      "field": "name",
      "like": "[current()]"
    }
  },
  "greater": 0
}

示例 3:检查资源名称是否与数组参数提供的任何给定的名称模式匹配。

{
  "count": {
    "value": "[parameters('namePatterns')]",
    "name": "pattern",
    "where": {
      "field": "name",
      "like": "[current('pattern')]"
    }
  },
  "greater": 0
}

示例 4:检查是否有任何虚拟网络地址前缀不在批准的前缀的列表下。

{
  "count": {
    "field": "Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]",
    "where": {
      "count": {
        "value": "[parameters('approvedPrefixes')]",
        "name": "approvedPrefix",
        "where": {
          "value": "[ipRangeContains(current('approvedPrefix'), current('Microsoft.Network/virtualNetworks/addressSpace.addressPrefixes[*]'))]",
          "equals": true
        },
      },
      "equals": 0
    }
  },
  "greater": 0
}

示例 5:检查是否在 NSG 中定义了所有保留的 NSG 规则。 保留的 NSG 规则的属性是在一个包含对象的数组参数中定义的。

参数值:

[
  {
    "priority": 101,
    "access": "deny",
    "direction": "inbound",
    "destinationPortRange": 22
  },
  {
    "priority": 102,
    "access": "deny",
    "direction": "inbound",
    "destinationPortRange": 3389
  }
]

策略:

{
  "count": {
    "value": "[parameters('reservedNsgRules')]",
    "name": "reservedNsgRule",
    "where": {
      "count": {
        "field": "Microsoft.Network/networkSecurityGroups/securityRules[*]",
        "where": {
          "allOf": [
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].priority",
              "equals": "[current('reservedNsgRule').priority]"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].access",
              "equals": "[current('reservedNsgRule').access]"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].direction",
              "equals": "[current('reservedNsgRule').direction]"
            },
            {
              "field": "Microsoft.Network/networkSecurityGroups/securityRules[*].destinationPortRange",
              "equals": "[current('reservedNsgRule').destinationPortRange]"
            }
          ]
        }
      },
      "equals": 1
    }
  },
  "equals": "[length(parameters('reservedNsgRules'))]"
}

策略函数

函数可用于将其他逻辑引入策略规则。 在策略定义的策略规则内以及分配给计划中的策略定义的参数值内解析它们。

除了以下函数和用户定义的函数外,所有资源管理器模板函数均可在策略规则中使用:

  • copyIndex()
  • dateTimeAdd()
  • dateTimeFromEpoch
  • dateTimeToEpoch
  • deployment()
  • environment()
  • extensionResourceId()
  • lambda() 有关详细信息,请转到 lambda
  • listAccountSas()
  • listKeys()
  • listSecrets()
  • list*
  • managementGroup()
  • newGuid()
  • pickZones()
  • providers()
  • reference()
  • resourceId()
  • subscriptionResourceId()
  • tenantResourceId()
  • tenant()
  • variables()

注意

deployIfNotExists 策略定义中,仍可在其模板部署的 details.deployment.properties.template 部分中使用这些函数。

以下函数可在策略规则中使用,但与在 Azure 资源管理器模板(ARM 模板)中使用不同:

  • utcNow() - 与 ARM 模板不同,该属性可以在 defaultValue 之外使用。
    • 以通用 ISO 8601 日期/时间格式“yyyy-MM-ddTHH:mm:ss.fffffffZ”返回设置为当前日期和时间的字符串。

以下函数仅适用于策略规则:

  • addDays(dateTime, numberOfDaysToAdd)

    • dateTime:[必需] 字符串 - 通用 ISO 8601 日期/时间格式“yyyy-MM-ddTHH:mm:ss.FFFFFFFZ”的字符串
    • numberOfDaysToAdd:[必需] 整数 - 要增加的天数
  • field(fieldName)

    • fieldName:[必需] 字符串 - 要检索的字段的名称
    • 从 If 条件正在评估的资源返回该字段的值。
    • field 主要用于 auditIfNotExistsdeployIfNotExists,以引用所评估资源上的字段。 可以在 DeployIfNotExists 示例中看到这种用法的示例。
  • requestContext().apiVersion

    • 返回已触发策略评估的请求的 API 版本(示例:2021-09-01)。 该值是 PUT/PATCH 请求中用于对资源创建/更新进行评估的 API 版本。 在对现有资源进行符合性评估时,将会一律使用最新的 API 版本。
  • policy()

    • 返回有关正在评估的策略的下列信息。 可以从返回的对象访问属性,例如:[policy().assignmentId]

      {
        "assignmentId": "/subscriptions/11111111-1111-1111-1111-111111111111/providers/Microsoft.Authorization/policyAssignments/myAssignment",
        "definitionId": "/providers/Microsoft.Authorization/policyDefinitions/34c877ad-507e-4c82-993e-3452a6e0ad3c",
        "setDefinitionId": "/providers/Microsoft.Authorization/policySetDefinitions/42a694ed-f65e-42b2-aa9e-8052e9740a92",
        "definitionReferenceId": "StorageAccountNetworkACLs"
      }
      
  • ipRangeContains(range, targetRange)

    • range:[必需]字符串 - 指定 IP 地址范围的字符串,用于检查 targetRange 是否在其中。
    • targetRange:[必需]字符串 - 此字符串指定一个 IP 地址范围,需要验证它是否包含在 range 内。
    • 返回一个布尔值,指示 range IP 地址范围是否包含 targetRange IP 地址范围。 空范围或 IP 系列之间的混合是不允许的,这会导致评估失败。

    支持的格式:

    • 单个 IP 地址(示例:10.0.0.02001:0DB8::3:FFFE
    • CIDR 范围(示例:10.0.0.0/242001:0DB8::/110
    • 由起始 IP 地址和结束 IP 地址定义的范围(示例:192.168.0.1-192.168.0.92001:0DB8::-2001:0DB8::3:FFFF
  • current(indexName)

策略函数示例

此策略规则示例使用 resourceGroup 资源函数获取 name 属性,并将该属性与 concat 数组和对象函数结合使用以构建 like 条件,该条件强制资源名称以资源组名称开头。

{
  "if": {
    "not": {
      "field": "name",
      "like": "[concat(resourceGroup().name,'*')]"
    }
  },
  "then": {
    "effect": "deny"
  }
}

策略规则限制

创作期间强制实施的限制

在创作或分配策略期间,将强制实施对策略规则结构的限制。 尝试创建或分配超出这些限制的策略定义将会失败。

限制 其他详细信息
if 条件中的条件表达式 4096
then 程序块中的条件表达式 128 适用于 auditIfNotExistsdeployIfNotExists 策略的 existenceCondition
每个策略规则的策略函数 2048
参数的策略函数数目 128 示例: [function('parameter1', 'parameter2', ...)]
嵌套策略函数深度 64 示例: [function(nested1(nested2(...)))]
策略函数表达式字符串长度 81920 示例:"[function(....)]" 的长度
每个数组的 Field count 表达式 5
每个策略规则的 Value count 表达式 10
Value count 表达式迭代计数 100 对于嵌套的 Value count 表达式,这还包括父表达式的迭代计数

评估期间强制实施的限制

在策略评估期间由策略函数处理的对象的大小限制。 根据评估内容,这些限制在创作期间不会一直强制执行。 例如:

{
  "field": "name",
  "equals": "[concat(field('stringPropertyA'), field('stringPropertyB'))]"
}

concat() 函数创建的字符串的长度取决于评估资源中属性的值。

限制 示例
函数返回的字符串的长度 131072 [concat(field('longString1'), field('longString2'))]
作为参数提供给函数或由函数返回的复杂对象的深度 128 [union(field('largeObject1'), field('largeObject2'))]
作为参数提供给函数或由函数返回的复杂对象的节点数 32768 [concat(field('largeArray1'), field('largeArray2'))]

警告

在评估期间超过上述限制的策略将实际上成为 deny 策略,并可以阻止传入请求。 使用复杂函数编写策略时,请注意这些限制,并针对可能超过这些限制的资源测试这些策略。

后续步骤