I'm trying to use PowerShell DSC to do multiple file copies. My configuration has a list of source/target files that need to be copied. However, the File resource needs to have a unique name with it so that you can do dependencies on the resource.
I'm new to PowerShell and I'm trying to figure out the right format for the DSC script (.ps1) to allow a foreach around the File resource. Currently, my code gives me a "Duplicate Resource Identifier" error since it looks like the File resource isn't getting a unique name.
Configuration (psd1 file):
{
AllNodes = @(
@{
NodeName = '*'
BuildOutputRoot = 'C:\_BuildDrop\'
FilesToCopy = @(
@{
SourcePath = 'C:\_BuildDrop\SampleConfig.xml'
TargetPath = 'C:\SampleCode\SampleConfig.xml'
},
@{
SourcePath = 'C:\_BuildDrop\SampleConfig2.xml'
TargetPath = 'C:\SampleCode\SampleConfig2.xml'
},
Powershell ps1 file for DSC (snippet):
Configuration MachineToolsFilesAndDirectories
{
# Copy files on all machines
Node $AllNodes.NodeName
{
foreach ($FileToCopy in $Node.FilesToCopy)
{
File $FileToCopy$Number
{
Ensure = "Present"
Type = "File"
Recurse = $false
SourcePath = $FileToCopy.SourcePath
DestinationPath = $FileToCopy.TargetPath
}
}
One of the most common types of loops you'll use in PowerShell is the foreach type of loop. A foreach loop reads a set of objects (iterates) and completes when it's finished with the last one. The collection of objects that are read is typically represented by an array or a hashtable.
To easily configure your environment correctly, just run Set-WsManQuickConfig -Force in an elevated PowerShell Terminal. You can apply Configuration documents (MOF files) to a machine with the Start-DscConfiguration cmdlet.
It looks like you never define or change the value of $Number
so each File
resource ends up with the same name. Try something like this.
Configuration MachineToolsFilesAndDirectories
{
# Copy files on all machines
Node $AllNodes.NodeName
{
$Number = 0
foreach ($FileToCopy in $Node.FilesToCopy)
{
$Number += 1
$thisFile = "$FileToCopy$Number"
File $thisFile
{
Ensure = "Present"
Type = "File"
Recurse = $false
SourcePath = $FileToCopy.SourcePath
DestinationPath = $FileToCopy.TargetPath
}
}
}
I'm not sure if it's what everyone does, but I alway name my resources after the key value in the resource so in the MOF each resource is obviously named after what it does. The only thing to remember is that you have to sanitise the resource name as only alphanumeric and a few other characters are allowed (Specifically not colons in the case for file paths).
For example:
File $FileToCopy.TargetPath.Replace(':','\')
{
Ensure = "Present"
Type = "File"
Recurse = $false
SourcePath = $FileToCopy.SourcePath
DestinationPath = $FileToCopy.TargetPath
}
Which would equate to:
File 'C\\SampleCode\SampleConfig.xml'
{
Ensure = "Present"
Type = "File"
Recurse = $false
SourcePath = 'C:\_BuildDrop\SampleConfig.xml'
DestinationPath = 'C:\SampleCode\SampleConfig.xml'
}
If it was populated from the following:
@{
SourcePath = 'C:\_BuildDrop\SampleConfig.xml'
TargetPath = 'C:\SampleCode\SampleConfig.xml'
}
I realise using the .Replace method is a bit of a sucky way to do it - I should probably build a regex to catch all of the possibilities I occur (Which has been $ for shares and colons in file paths so far).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With