我在尝试使用PowerShell所需的状态配置脚本来配置内部应用程序时遇到了很多麻烦。问题的根源是我似乎无法将我的配置数据传递给ScriptResource(至少不是我尝试这样做的方式)。
我的脚本应该为我们的内部应用程序创建一个配置文件夹,然后将一些设置写入文件:
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变量在ConfigurationFile / TestScript范围内是null,但是我不知道如何从该块中引用它
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.
我在网上搜索这个特定问题已经烧了一整天,但变量,参数和配置数据的所有示例都使用文件和注册表资源或其他非脚本资源,我已经这样做了已经在" ConfigurationFolder"在我的脚本中阻止。我所坚持的是如何从脚本资源中引用配置数据,例如我的" ConfigurationFile"。
我已经画了一个完整的空白,所以任何帮助将不胜感激。如果所有其他方法都失败了,我可能需要创建一个单独的配置"每个服务器的脚本并对值进行硬编码,如果可能的话,我真的不想这样做。
干杯,
麦克
答案 0 :(得分:32)
将此更改为$Node.ConfigFolder
至$using:Node.ConfigFolder
。
如果您有一个名为$Foo
的变量,并且您希望将其传递给脚本DSC资源,那么请使用$using:Foo
答案 1 :(得分:10)
根据David的回答,我编写了一个实用程序函数,它将我的脚本块转换为字符串,然后执行非常天真的搜索和替换,以扩展对配置数据的引用,如下所示。 / p>
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;
}
然后我的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只执行文字替换,但这对我的目的来说已经足够了。
答案 2 :(得分:8)
解决此问题的一种非常优雅的方法是使用常规{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成员,您将看到代码片段确实是一个字符串。 $占位符值应该已经在那里被替换。
答案 3 :(得分:7)
ConfigurationData仅在编译MOF文件时存在,而不是在DSC引擎应用脚本时在运行时存在。 Script资源的SetScript,GetScript和TestScript属性实际上是字符串,而不是脚本块。
可以生成这些脚本字符串(已经扩展了ConfigurationData中的所有必需数据),但是必须小心正确使用转义符,子表达式和引号。
的原始TechNet主题上发布了一个简短的示例