Compare-Object в Powershell для 2 объектов на основе поля внутри. Объекты, заполненные JSON и XML
Извиняюсь за недостаток знаний PowerShell, искал решение повсюду, потому что я не очень программист.
Фон:
В настоящее время я пытаюсь стандартизировать некоторые настройки сайта в Incapsula. Чтобы сделать это, я хочу поддерживать локальный XML с правилами и использовать некоторые PowerShell, чтобы раскрыть существующие правила и сравнить их с тем, что есть, чтобы убедиться, что я не удваиваюсь. Я использую такой подход, пытаясь применить только дельты как:
- Для большинства настроек инкапсула недостаточно умна, чтобы знать, что она уже существует
- То, что может быть опубликовано в API, отличается от того, что возвращается API
Примеры:
Ниже приведен пример того, что API будет возвращать по запросу, это в формате 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 с помощью команды compare-object, однако они были немного проще и не доставляли мне особых хлопот. Я застрял в том, является ли это логической проблемой или ограничением по сравнению с объектом сравнения. Пример кода приведен ниже, он потребует, чтобы поставленные json и xml были сохранены как stack.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
}
}
}
Это отчасти работает, но не совсем то, что я хотел. Я получаю сравнение между объектами, но не для конкретных идентификаторов, это показывает мне результаты ниже:
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 =>
Надеюсь, это имеет смысл.
Можно ли сравнивать только те значения, где совпадают идентификаторы?
Пожалуйста, дайте мне знать, если у вас есть дополнительные вопросы.
Благодарю.
1 ответ
Проблема была в вашей логике итерации, которая по ошибке обрабатывала несколько правил из документа XML за одну итерацию:
foreach ($xmlRule in $ruleSet)
ничего не перечислил - вместо этого он обработал единственный<ruleset>
элемент; перечислить ребенка<rule>
элементы, вы должны использовать$ruleSet.rule
,$xmlRule.rule.exceptions.exception.url
затем неявно повторяется по всем<rule>
дети и, следовательно, сообщили 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
являются массивами, поэтому нет необходимости заключать их в оператор подвыражения массива@(...)
,Compare-Object
отлично справляется со скалярными операндами.... | Select-Object
является виртуальным запретом - входной объект пропущен через [1]... | Where-Object {$xmlRule.rule.id -eq $siteRule.id}
бессмысленно, потому что он дублирует вложениеforeach
состояние петли.- Вообще говоря, потому что вы не обращаетесь к входному объекту конвейера под рукой через автоматическую переменную
$_
, вашWhere-Object
Фильтр статичен и будет соответствовать либо всем входным объектам (как в вашем случае), либо ни одному.
- Вообще говоря, потому что вы не обращаетесь к входному объекту конвейера под рукой через автоматическую переменную
[1] Существует тонкий, невидимый побочный эффект, который обычно не имеет значения: Select-Object
добавляет невидимое [psobject]
Обертка вокруг входного объекта, которая в редких случаях вызывает другое поведение - см. эту проблему GitHub.