PowerShell DSC - как передать параметры конфигурации в ScriptResources?
У меня много проблем с попыткой получить конфигурацию желаемого состояния PowerShell script, работающую над настройкой собственного приложения. Корень проблемы заключается в том, что я не могу передать свои данные конфигурации в ScriptResource (по крайней мере, не так, как я пытаюсь это сделать).
My script должен создать конфигурационную папку для нашего внутреннего приложения, а затем записать некоторые настройки в файл:
configuration MyApp {
param (
[string[]] $ComputerName = $env:ComputerName
)
node $ComputerName {
File ConfigurationFolder {
Type = "Directory"
DestinationPath = $Node.ConfigFolder
Ensure = "Present"
}
Script ConfigurationFile {
SetScript = {
write-verbose "running ConfigurationFile.SetScript";
write-verbose "folder = $($Node.ConfigFolder)";
write-verbose "filename = $($Node.ConfigFile)";
[System.IO.File]::WriteAllText($Node.ConfigFile, "enabled=" + $Node.Enabled);
}
TestScript = {
write-verbose "running ConfigurationFile.TestScript";
write-verbose "folder = $($Node.ConfigFolder)";
write-verbose "filename = $($Node.ConfigFile)";
return (Test-Path $Node.ConfigFile);
}
GetScript = { @{Configured = (Test-Path $Node.ConfigFile)} }
DependsOn = "[File]ConfigurationFolder"
}
}
}
Для справки мои данные конфигурации выглядят следующим образом:
$config = @{
AllNodes = @(
@{
NodeName = "*"
ConfigFolder = "C:\myapp\config"
ConfigFile = "C:\myapp\config\config.txt"
}
@{
NodeName = "ServerA"
Enabled = "true"
}
@{
NodeName = "ServerB"
Enabled = "false"
}
)
}
И я применяю DSC со следующим:
$mof = MyApp -ConfigurationData $config;
Start-DscConfiguration MyApp –Wait –Verbose;
Когда я применяю эту конфигурацию, он с радостью создает папку, но ничего не может сделать с конфигурационным файлом. Рассматривая вывод ниже, очевидно, что это потому, что переменная $ Node имеет значение null внутри области ConfigurationFile/TestScript, но я понятия не имею, как ссылаться на нее из этого блока.
LCM: [ Start Resource ] [[Script]ConfigurationFile]
LCM: [ Start Test ] [[Script]ConfigurationFile]
[[Script]ConfigurationFile] running ConfigurationFile.TestScript
[[Script]ConfigurationFile] node is null = True
[[Script]ConfigurationFile] folder =
[[Script]ConfigurationFile] filename =
LCM: [ End Test ] [[Script]ConfigurationFile] in 0.4850 seconds.
Я сжег целый день в Интернете, чтобы узнать об этой конкретной проблеме, но все примеры переменных, параметров и данных конфигурации используют ресурсы File и Registry или другие ресурсы без script, которые у меня уже есть работая в блоке "ConfigurationFolder" в моем script. Я застрял в том, как ссылаться на данные конфигурации из ресурса script, например, на мой "ConfigurationFile".
Я нарисовал полный пробел, поэтому любая помощь была бы весьма признательна. Если все остальное не удастся, мне может потребоваться создать отдельную "конфигурацию" script на сервер и скорректировать значения, которые я действительно не хочу делать, если это вообще возможно.
Приветствия,
Mike
Ответы
Ответ 1
ConfigurationData существует только в момент компиляции файлов MOF, а не во время выполнения, когда движок DSC применяет ваши скрипты. Атрибуты SetScript, GetScript и TestScript ресурса Script на самом деле являются строками, а не Script.
Возможно создание этих Script строк (со всеми необходимыми данными из вашего ConfigurationData, которые уже были расширены), но вы должны быть осторожны, чтобы правильно использовать escape-последовательности, подвыражения и кавычки.
Я привел краткий пример этого в исходном потоке TechNet в http://social.technet.microsoft.com/Forums/en-US/2eb97d67-f1fb-4857-8840-de9c4cb9cae0/dsc-configuration-data-for-script-resources?forum=winserverpowershell
Ответ 2
Измените это: $Node.ConfigFolder
на $using:Node.ConfigFolder
.
Если у вас есть переменная с именем $Foo
, и вы хотите, чтобы она была передана в ресурс script DSC, затем используйте $using:Foo
Ответ 3
Основываясь на ответе Дэвида, я написал функцию утилиты, которая преобразует мой блок script в строку, а затем выполняет очень наивный поиск и замену, чтобы развернуть ссылки на данные конфигурации следующим образом.
function Format-DscScriptBlock()
{
param(
[parameter(Mandatory=$true)]
[System.Collections.Hashtable] $node,
[parameter(Mandatory=$true)]
[System.Management.Automation.ScriptBlock] $scriptBlock
)
$result = $scriptBlock.ToString();
foreach( $key in $node.Keys )
{
$result = $result.Replace("`$Node.$key", $node[$key]);
}
return $result;
}
Затем My SetScript становится:
SetScript = Format-DscScriptBlock -Node $Node -ScriptBlock {
write-verbose "running ConfigurationFile.SetScript";
write-verbose "folder = $Node.ConfigFolder";
write-verbose "filename = $Node.ConfigFile)";
[System.IO.File]::WriteAllText("$Node.ConfigFile", "enabled=" + $Node.Enabled);
}
Вы должны помнить о кавычках и экранах в данных конфигурации, поскольку Format-DscScriptBlock выполняет только литеральную замену, но это было достаточно для моих целей.
Ответ 4
Очень элегантный способ решить эту проблему - работать с обычными заполнителями {0}
. Применяя оператор -f
, заполнители могут быть заменены их фактическими значениями.
Единственным недостатком этого метода является то, что вы не можете использовать фигурные скобки {} для чего угодно, кроме заполнителей (например, хеш-таблицы или цикла for), потому что оператор -f
требует, чтобы фигурные скобки содержали целое число.
Затем ваш код выглядит следующим образом:
SetScript = ({
Set-ItemProperty "IIS:\AppPools\{0}" "managedRuntimeVersion" "v4.0"
Set-ItemProperty "IIS:\AppPools\{0}" "managedPipelineMode" 1 # 0 = Integrated, 1 = Classic
} -f @($ApplicationPoolName))
Кроме того, хороший способ узнать, правильно ли вы делаете это, - просто просмотреть сгенерированный файл .mof с помощью текстового редактора; если вы посмотрите на сгенерированные элементы TestScript/GetScript/SetScript, вы увидите, что фрагмент кода действительно является строкой. Значения $placeholder уже должны быть заменены там.