基于其中的一个字段,在 Powershell 中比较 2 个对象的对象。 JSON 和 XML 填充的对象

Compare-Object in Powershell for 2 objects based on a field within. Objects populated by JSON and XML

抱歉我缺乏 powershell 知识,我一直在广泛寻找解决方案,因为我不是一个程序员。

背景:

我目前正在尝试标准化 Incapsula 中的一些站点设置。为此,我想维护一个带有规则的本地 XML 并使用一些 powershell 来下拉现有规则并将它们与现有规则进行比较以确保我不会加倍。我正在采用这种尝试仅将增量应用为的方法:

  1. 对于大多数设置,incapsula 不够智能,无法知道它已经存在
  2. 可以张贴到 API 的内容与 return 由 API
  3. 编辑的内容不同

示例:

下面是 API 将根据要求 return 的示例,这是 JSON 格式。

JSON FROM WEBSITE

{
"security": {
        "waf": {
            "rules": [{
                "id": "api.threats.sql_injection",
                "exceptions": [{
                    "values": [{
                        "urls": [{
                            "value": "google.com/thisurl",
                            "pattern": "EQUALS"
                        }],
                        "id": "api.rule_exception_type.url",
                        "name": "URL"
                    }],
                    "id": 256354634
                }]
            }, {
                "id": "api.threats.cross_site_scripting",
                "action": "api.threats.action.block_request",
                "exceptions": [{
                    "values": [{
                        "urls": [{
                            "value": "google.com/anotherurl",
                            "pattern": "EQUALS"
                        }],
                        "id": "api.rule_exception_type.url",
                        "name": "URL"
                    }],
                    "id": 78908790780
                }]
            }]
        }
    }
}

这是 XML 的格式,其中包含我们的特定站点设置

OUR XML RULES
    <waf>
    <ruleset>
        <rule>
            <id>api.threats.sql_injection</id>
                <exceptions>
                    <exception>
                        <type>api.rule_exception_type.url</type>
                        <url>google.com/thisurl</url>   
                    </exception>
                    <exception>
                        <type>api.rule_exception_type.url</type>
                        <url>google.com/thisanotherurl</url>
                    </exception>
                </exceptions>
        </rule>
        <rule> 
            <id>api.threats.cross_site_scripting</id>
                <exceptions>
                    <exception>
                        <type>api.rule_exception_type.url</type>
                        <url>google.com/anotherurl</url>
                    </exception>
                    <exception>
                        <type>api.rule_exception_type.url</type>
                        <url>google.com/anotherurl2</url>
                    </exception> 
                </exceptions>
        </rule> 
    </ruleset>
</waf>

我已经成功地能够使用比较对象命令将站点的其他设置与 XML 进行比较,但是它们的嵌套稍微简单一些,并没有给我带来太多麻烦。我坚持到底是逻辑问题还是比较对象的限制。下面是一个示例代码,它需要将提供的 json 和 xml 保存为堆栈。json/xml 在同一目录中,并且应该产生上述结果:

    $existingWaf = Get-Content -Path stack.json | ConvertFrom-Json
    [xml]$xmlFile = Get-Content -Path stack.xml 

    foreach ($rule in $xmlFile)
    {
        $ruleSet = $rule.waf.ruleset
    }

    foreach ($siteRule in $ExistingWaf.security.waf.rules)
        {
            foreach ($xmlRule in $ruleSet)
            {
                if ($xmlRule.rule.id -eq $siteRule.id)
                    {
                        write-output "yes"
                        $delta = Compare-Object -ReferenceObject @($siteRule.exceptions.values.urls.value | Select-Object) -DifferenceObject @($xmlRule.rule.exceptions.exception.url | Select-Object) -IncludeEqual | where {$xmlRule.rule.id -eq $siteRule.id}
                        $delta

                    }
            }
        }

这是可行的,但不是我想要的。我确实在对象之间进行了比较,但没有针对特定 ID 进行比较,它向我显示了以下结果:

    InputObject                                 SideIndicator
    -----------                                 -------------
    google.com/thisurl                               ==
    google.com/thisanotherurl                        =>
    google.com/anotherurl                            =>
    google.com/anotherurl2                           =>

    google.com/anotherurl                            ==
    google.com/thisurl                               =>
    google.com/thisanotherurl                        =>
    google.com/anotherurl2                           =>

之后我在哪里
    InputObject                                 SideIndicator
    -----------                                 -------------
    google.com/thisurl                               ==
    google.com/thisanotherurl                        =>

    google.com/anotherurl                            ==
    google.com/anotherurl2                           =>

希望这是有道理的。

是否可以仅对 ID 匹配的值进行比较?

如果您还有其他问题,请告诉我。

谢谢。

问题是您的迭代逻辑错误地处理了 XML 文档中的多个 规则在单次迭代中:

  • foreach ($xmlRule in $ruleSet) 没有枚举任何东西——而是处理了单个 <ruleset> 元素;要枚举 child <rule> 元素,您必须使用 $ruleSet.rule.

  • $xmlRule.rule.exceptions.exception.url 然后 隐式地 迭代 all <rule> child ren 并因此报告了 所有 的 URL,这解释了 Compare-Object 输出中的额外行。

这是您的代码的简化版、带注释的版本:

$existingWaf = Get-Content -LiteralPath stack.json | ConvertFrom-Json
$xmlFile = [xml] (Get-Content -raw -LiteralPath stack.xml )

# No need for a loop; $xmlFile is a single [System.Xml.XmlDocument] instance.
$ruleSet = $xmlFile.waf.ruleset

foreach ($siteRule in $ExistingWaf.security.waf.rules)
{
    # !! Note the addition of `.rule`, which ensures that the rules
    # !! are enumerated *one by one*. 
    foreach ($xmlRule in $ruleSet.rule)
    {
        if ($xmlRule.id -eq $siteRule.id)
        {
          # !! Note: `$xmlRule` is now a single, rule, therefore:
          # `$xmlRule.rule.[...]-> `$xmlRule.[...]`
          # Also note that neither @(...) nor Select-Object are needed, and
          # the `| where ...` (Where-Object) is not needed.
          Compare-Object -ReferenceObject $siteRule.exceptions.values.urls.value `
                         -DifferenceObject $xmlRule.exceptions.exception.url -IncludeEqual
        }
    }
}

关于您的代码的其他观察结果

  • 不需要保证传递给Compare-Object的操作数是数组,所以不需要用数组[=101]包裹起来=]运算符@(...)Compare-Object 可以很好地处理标量操作数。

  • ... | Select-Object 是虚拟的 no-op - 输入 object 通过 传递 [1]

  • ... | Where-Object {$xmlRule.rule.id -eq $siteRule.id} 是没有意义的,因为它复制了封闭的 foreach 循环的条件。

    • 一般来说,因为您没有通过自动变量 $_ 引用手头的管道输入 object,您的 Where-Object 过滤器是 static 并将匹配 all 输入 objects (如您的情况)或 none.

[1]一个微妙的、不可见的副作用,通常不会产生什么影响: Select-Object 在输入 object 周围添加了一个不可见的 [psobject] 包装器,这在极少数情况下会导致以后出现不同的行为 - 见 this GitHub issue.