xPSDesiredStateConfiguration icon indicating copy to clipboard operation
xPSDesiredStateConfiguration copied to clipboard

xRemoteFile: Key should be URI rather than remote destination?

Open uw-dc opened this issue 3 years ago • 4 comments

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

uw-dc avatar Apr 04 '22 16:04 uw-dc

Happy to PR the change if it's agreed upon.

uw-dc avatar Apr 04 '22 16:04 uw-dc

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?

PlagueHO avatar Apr 04 '22 20:04 PlagueHO

Hello @PlagueHO ,

Thanks for the quick response. That sounds more than reasonable.

I'll go ahead and PR this change?

uw-dc avatar Apr 05 '22 08:04 uw-dc

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.

PlagueHO avatar Apr 07 '22 20:04 PlagueHO