xRemoteFile: Key should be URI rather than remote destination?
Problem description
The use case of downloading multiple remote files into the same destination path, where the destination path is a directory, seems like it would be a common one.
With that in mind, the xRemoteFile resource should perhaps be keyed on uri instead of destination?
Verbose logs
Test-ConflictingResources : A conflict was detected between resources '[xRemoteFile]PrinterDriver0
(D:\DscService\Build\Modules\someCompositeModule\0.1.0.42\someCompositeModule.psm1::784::9::xRemoteFile)' and
'[xRemoteFile]PrinterDriver1
(D:\DscService\Build\Modules\someCompositeModule\0.1.0.42\someCompositeModule.psm1::784::9::xRemoteFile)' in
node 'someserver.somedomain.sometld'. Resources have identical key properties but there are differences in the following
non-key properties: 'Uri'. Values 'https://anotherserver.somedomain.sometld:8083/PrinterDrivers/HP Color LaserJet M554-M555
PCL-6.zip' don't match values 'https://anotherserver.somedomain.sometld:8083/PrinterDrivers/PaperCut Global PostScript.zip'.
DSC configuration
$dscPrinterDriverPath = 'C:\ProgramData\DSC\PrinterDriver'
$printerDrivers = @{
'HP Color LaserJet M554-M555 PCL-6' = @{
SourceInf = "$dscPrinterDriverPath\HP Color LaserJet M554-M555 PCL-6\hpma08734_x64.inf"
StoreInf = 'C:\Windows\System32\DriverStore\FileRepository\hpma08734_x64.inf_amd64_4cbc9f21081b2c3d\hpma08734_x64.inf'
}
'PaperCut Global PostScript' = @{
SourceInf = "$dscPrinterDriverPath\PaperCut Global PostScript\PCGlobal.inf"
StoreInf = 'C:\Windows\System32\DriverStore\FileRepository\pcglobal.inf_amd64_e25f98e06af28150\pcglobal.inf'
}
}
File DscPrinterDriversFolder {
DestinationPath = $dscPrinterDriverPath
Ensure = 'Present'
Type = 'Directory'
}
$i = 0
foreach ($printerDriver in $printerDrivers.GetEnumerator()) {
xRemoteFile "PrinterDriver$i" {
Uri = "https://$DSCPullServerSoftwareRepo/PrinterDrivers/$($printerDriver.Name).zip"
DestinationPath = "$dscPrinterDriverPath/$($printerDriver.Name).zip"
DependsOn = '[File]DscPrinterDriversFolder'
}
File "DscPrinterDriverFolder$i" {
DestinationPath = "$dscPrinterDriverPath\$($printerDriver.Name)"
Ensure = 'Present'
Type = 'Directory'
DependsOn = "[xRemoteFile]PrinterDriver$i"
}
xArchive "ExpandPrinterDriverArchive$i" {
Path = "$dscPrinterDriverPath\$($printerDriver.Name).zip"
Destination = "$dscPrinterDriverPath\$($printerDriver.Name)"
Ensure = 'Present'
DependsOn = "[File]DscPrinterDriverFolder$i"
}
Script "AddPrinterDriverToDriverStore$i" {
GetScript = {
return @{
result = Get-WindowsDriver -Online -All -ErrorAction SilentlyContinue | `
Where-Object {
$_.ClassName -eq 'Printer' -and
$_.OriginalFileName -eq $using:printerDriver.Value.StoreInf
}
}
}
SetScript = {
$processStartInfo = [System.Diagnostics.ProcessStartInfo]::new('C:\Windows\System32\pnputil.exe', "/add-driver `"$($using:printerDriver.Value.SourceInf)`"")
$processStartInfo.CreateNoWindow = $false
$processStartInfo.WorkingDirectory = 'C:\Windows\system32'
$processStartInfo.UseShellExecute = $false
$processStartInfo.RedirectStandardError = $true
$processStartInfo.RedirectStandardOutput = $true
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo = $processStartInfo
Write-Verbose -Message ("Running process `'{0}`' with arguments `'{1}`'" -f $process.StartInfo.FileName, $process.StartInfo.Arguments)
$process.Start()
$stdout = $process.StandardOutput.ReadToEnd()
$stderr = $process.StandardError.ReadToEnd()
$process.WaitForExit()
if (0 -ne $process.ExitCode) {
Write-Verbose -Message ('Printer driver ({0}) not successfully added to driver store. pnputil.exe returned non-zero ({1}) exit code' -f $using:printerDriver.Name, $exitCode)
Write-Verbose -Message ("`tStandard Output: {0}" -f $stdout)
Write-Verbose -Message ("`tStandard Error: {0}" -f $stderr)
throw [System.Configuration.ConfigurationException] 'Printer driver ({0}) not successfully added to driver store. pnputil.exe returned non-zero ({1}) exit code' -f $using:printerDriver.Name, $exitCode
}
if ($null -eq (Get-WindowsDriver -Online -All -ErrorAction SilentlyContinue | `
Where-Object {
$_.ClassName -eq 'Printer' -and
$_.OriginalFileName -eq $using:printerDriver.Value.StoreInf
}
)) {
Write-Verbose -Message ('Printer driver ({0}) is missing from driver store after running pnputil.exe' -f $using:printerDriver.Name)
throw [System.Configuration.ConfigurationException] 'Printer driver ({0}) is missing from driver store after running pnputil.exe' -f $using:printerDriver.Name
}
}
TestScript = {
return ($true -eq (Get-WindowsDriver -Online -All -ErrorAction SilentlyContinue | `
Where-Object {
$_.ClassName -eq 'Printer' -and
$_.OriginalFileName -eq $using:printerDriver.Value.StoreInf
}
))
}
DependsOn = "[xArchive]ExpandPrinterDriverArchive$i"
}
Script "InstallPrinterDriver$i" {
GetScript = {
return @{
result = (Get-PrinterDriver -Name $using:printerDriver.Name -ErrorAction SilentlyContinue).Name
}
}
SetScript = {
Add-PrinterDriver -Name $using:PrinterDriver.Name -InfPath $using:printerDriver.Value.StoreInf
if ($null -eq (Get-PrinterDriver -Name $using:printerDriver.Name -ErrorAction SilentlyContinue)) {
Write-Verbose -Message ("Printer driver ({0}) not successfully installed with 'Add-PrinterDriver'" -f $using:printerDriver.Name)
throw [System.Configuration.ConfigurationException] "Printer driver ({0}) not successfully installed with 'Add-PrinterDriver'" -f $using:printerDriver.Name
}
}
TestScript = {
return ($true -eq (Get-PrinterDriver -Name $using:printerDriver.Name -ErrorAction SilentlyContinue))
}
DependsOn = "[Script]AddPrinterDriverToDriverStore$i"
}
$i++
}
Suggested solution
lines (16 sloc) 1.92 KB [ClassVersion("1.0.0.1"), FriendlyName("xRemoteFile")] class DSC_xRemoteFile : OMI_BaseResource { [Required, Description("Path under which downloaded or copied file should be accessible after operation.")] String DestinationPath; [Key, Description("URI of the file which should be downloaded. It must be a HTTP, HTTPS or FILE resource.")] String Uri; [Write, Description("User agent for the web request.")] String UserAgent; [Write, Description("Headers of the web request."), EmbeddedInstance("MSFT_KeyValuePair")] String Headers[]; [Write, Description("Specifies credential of a user which has permissions to send the request."), EmbeddedInstance("MSFT_Credential")] String Credential; [Write, Description("Determines whether the remote file should be re-downloaded if file in the DestinationPath was modified locally. The default value is true.")] Boolean MatchSource; [Write, Description("Specifies the algorithm used to calculate the checksum of the file."), ValueMap{"None","SHA1","SHA256","SHA384","SHA512","MACTripleDES","MD5","RIPEMD160"}, Values{"None","SHA1","SHA256","SHA384","SHA512","MACTripleDES","MD5","RIPEMD160"}] String ChecksumType; [Write, Description("Specifies the expected checksum value of downloaded file.")] String Checksum; [Write, Description("Specifies how long the request can be pending before it times out.")] Uint32 TimeoutSec; [Write, Description("Uses a proxy server for the request, rather than connecting directly to the Internet resource. Should be the URI of a network proxy server (e.g 'http://10.20.30.1').")] String Proxy; [Write, Description("Specifies a user account that has permission to use the proxy server that is specified by the Proxy parameter."), EmbeddedInstance("MSFT_Credential")] String ProxyCredential; [Read, Description("Returns whether the destination path exists on the machine."), ValueMap{"Present", "Absent"}, Values{"Present", "Absent"}] String Ensure; };
Operating system the target node is running
Server 2019
PowerShell version and build the target node is running
PSVersion 5.1.17763.2268
17763
xPSDesiredStateConfiguration version
v9.1.0
Happy to PR the change if it's agreed upon.
Hi @uw-dc - thank you for submitting this.
I suspect converting Uri to be the Key would break existing DSC configs. For example, when the same file needs to be copied to multiple locations. This is an edge case but could be easily managed by having both Uri and DestinationPath as keys. That should then support your scenario without breaking existing any existing configurations. What do you reckon?
Hello @PlagueHO ,
Thanks for the quick response. That sounds more than reasonable.
I'll go ahead and PR this change?
Hi @uw-dc - sure, would love the PR! If you run into any issues with the pipeline execution etc, please let me know as I've not had much time lately to work on this.