tutorial - PowerShell DSC: ¿cómo pasar los parámetros de configuración a ScriptResources?
powershell dsc resources (4)
Estoy teniendo muchos problemas al intentar que un script de configuración de estado deseado de PowerShell funcione para configurar una aplicación interna. La raíz del problema es que parece que no puedo pasar mis datos de configuración a un ScriptResource (al menos no con la forma en que estoy tratando de hacerlo).
Se supone que mi script cree una carpeta de configuración para nuestra aplicación interna y luego escriba algunas configuraciones en un archivo:
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"
}
}
}
Para referencia, mis datos de configuración se ven así:
$config = @{
AllNodes = @(
@{
NodeName = "*"
ConfigFolder = "C:/myapp/config"
ConfigFile = "C:/myapp/config/config.txt"
}
@{
NodeName = "ServerA"
Enabled = "true"
}
@{
NodeName = "ServerB"
Enabled = "false"
}
)
}
Y estoy aplicando DSC con lo siguiente:
$mof = MyApp -ConfigurationData $config;
Start-DscConfiguration MyApp –Wait –Verbose;
Cuando aplico esta configuración, crea felizmente la carpeta, pero no puede hacer nada con el archivo de configuración. Al observar la salida a continuación, es obvio que es porque la variable $ Node está nula dentro del alcance de ConfigurationFile / TestScript, pero no tengo idea de cómo hacer referencia a ella desde ese bloque.
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.
He quemado un día entero buscando este problema específico en línea, pero todos los ejemplos de variables, parámetros y datos de configuración utilizan recursos de Archivos y Registro u otros recursos que no son scripts, que ya he trabajado en el "ConfigurationFolder "bloque en mi script. En lo que estoy atascado es cómo hacer referencia a los datos de configuración desde un recurso de Script como mi "ConfigurationFile".
He dibujado un espacio en blanco completo por lo que cualquier ayuda sería muy apreciada. Si todo lo demás falla, es posible que tenga que crear una secuencia de comandos de "configuración" por servidor y codificar los valores, lo que realmente no quiero hacer si es posible.
Aclamaciones,
Micro
Basándome en la respuesta de David, escribí una función de utilidad que convierte mi bloque de script en una cadena y luego realiza una búsqueda y reemplazo muy ingenuos para expandir las referencias a los datos de configuración de la siguiente manera.
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;
}
Mi SetScript se convierte entonces en:
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);
}
Debe tener en cuenta las citas y escapes en sus datos de configuración porque Format-DscScriptBlock solo realiza una sustitución literal, pero esto fue lo suficientemente bueno para mis propósitos.
Cambie esto: $Node.ConfigFolder
a $using:Node.ConfigFolder
.
Si tiene una variable llamada $Foo
y desea que se pase a un recurso de script DSC, use $using:Foo
ConfigurationData solo existe en el momento en que se compilan los archivos MOF, no en tiempo de ejecución cuando el motor DSC aplica sus scripts. Los atributos SetScript, GetScript y TestScript del recurso Script son en realidad cadenas, no bloques de script.
Es posible generar esas cadenas de script (con todos los datos requeridos de su ConfigurationData ya expandidos), pero debe tener cuidado de usar escapes, subexpresiones y comillas correctamente.
Publiqué un breve ejemplo de esto en el hilo original de TechNet en http://social.technet.microsoft.com/Forums/en-US/2eb97d67-f1fb-4857-8840-de9c4cb9cae0/dsc-configuration-data-for-script-resources?forum=winserverpowershell
Una forma bastante elegante de resolver este problema es trabajar con los marcadores de posición {0}
regulares. Al aplicar el operador -f
, los marcadores de posición se pueden reemplazar con sus valores reales.
El único inconveniente de este método es que no puede usar las llaves {} para otra cosa que no sean marcadores de posición (es decir, una tabla hash o un bucle for), porque el operador -f
requiere que las llaves contengan un número entero.
Su código se ve así:
SetScript = ({
Set-ItemProperty "IIS:/AppPools/{0}" "managedRuntimeVersion" "v4.0"
Set-ItemProperty "IIS:/AppPools/{0}" "managedPipelineMode" 1 # 0 = Integrated, 1 = Classic
} -f @($ApplicationPoolName))
Además, una buena manera de saber si lo está haciendo bien es simplemente viendo el archivo .mof generado con un editor de texto; Si observa los miembros generados de TestScript / GetScript / SetScript, verá que el fragmento de código realmente es una cadena. Los valores de marcador de posición $ ya deberían haber sido reemplazados allí.