DSCResources/cFileAssoc/cFileAssoc.psm1
using namespace Microsoft.Win32 # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Get-TargetResource { [CmdletBinding()] [OutputType([System.Collections.Hashtable])] param ( [ValidateSet("Present", "Absent")] [string] $Ensure = 'Present', [Parameter(Mandatory = $true)] [string] $Extension, [Parameter()] [string] $FileType, [Parameter()] [string] $Command, [Parameter()] [string] $Icon ) Assert-PsDscRunAsUser $GetRes = @{ Ensure = $Ensure Extension = $Extension } $GetAssoc = Get-FileAssoc -Extension $Extension $GetRes.FileType = $GetAssoc.ProgId $GetRes.Command = $GetAssoc.Command $GetRes.Icon = $GetAssoc.Icon if ($GetRes.FileType) { $GetRes.Ensure = 'Present' } else { $GetRes.Ensure = 'Absent' } $GetRes } # end of Get-TargetResource # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Test-TargetResource { [CmdletBinding()] [OutputType([System.Boolean])] param ( [ValidateSet("Present", "Absent")] [string] $Ensure = 'Present', [Parameter(Mandatory = $true)] [string] $Extension, [Parameter()] [string] $FileType, [Parameter()] [string] $Command, [Parameter()] [string] $Icon ) Assert-PsDscRunAsUser $Ret = $true $CurrentState = Get-TargetResource -Ensure $Ensure -Extension $Extension if ($Ensure -ne $CurrentState.Ensure) { # Not match Ensure state Write-Verbose ('Not match Ensure state. your desired "{0}" but current "{1}"' -f $Ensure, $CurrentState.Ensure) $Ret = $Ret -and $false } if ($Ensure -eq 'Present') { if ($PSBoundParameters.FileType -and ($FileType -ne $CurrentState.FileType)) { # Not match associated FileType Write-Verbose ('Associated FileType is not match (Current:"{0}" / Desired:"{1}")' -f $CurrentState.FileType, $FileType) $Ret = $Ret -and $false } if ($PSBoundParameters.Command -and ($Command -ne $CurrentState.Command)) { # Not match associated command (optional) Write-Verbose ('Command attr is not match (Current:"{0}" / Desired:"{1}")' -f $CurrentState.Command, $Command) $Ret = $Ret -and $false } if ($PSBoundParameters.Icon -and ($Icon -ne $CurrentState.Icon)) { # Not match Icon (optional) Write-Verbose ('Icon attr is not match (Current:"{0}" / Desired:"{1}")' -f $CurrentState.Icon, $Icon) $Ret = $Ret -and $false } } return $Ret } # end of Test-TargetResource # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Set-TargetResource { [CmdletBinding()] param ( [ValidateSet("Present", "Absent")] [string] $Ensure = 'Present', [Parameter(Mandatory = $true)] [string] $Extension, [Parameter()] [string] $FileType, [Parameter()] [string] $Command, [Parameter()] [string] $Icon ) Assert-PsDscRunAsUser if ($Ensure -eq 'Absent') { #Remove association Write-Verbose ('Your desired state is "Absent". Start trying to remove file association of "{0}"' -f $Extension) Remove-FileAssoc -Extension $Extension } elseif ($Ensure -eq 'Present') { #Associate file type Write-Verbose ('Your desired state is "Present". Start trying to associate file type of "{0}"' -f $Extension) if (-not $PSBoundParameters.FileType) { Write-Error ('FileType is not specified.') return } $GetAssoc = Get-FileAssoc -Extension $Extension if ($FileType -ne $GetAssoc.FileType) { Write-Verbose ('Associate {0} to {1}' -f $Extension, $FileType) Set-FileAssoc -Extension $Extension -ProgId $FileType } if ($PSBoundParameters.Command -and ($Command -ne $GetAssoc.Command)) { $paramHash = @{ FileType = $FileType Command = $Command } if ($PSBoundParameters.Icon) { $paramHash.Icon = $Icon } } if ($PSBoundParameters.Icon -and ($Icon -ne $GetAssoc.Icon)) { if ($null -eq $paramHash) { $paramHash = @{ FileType = $FileType Command = $Command Icon = $Icon } } else { $paramHash.Icon = $Icon } } if ($paramHash.Command) { Write-Verbose ('Create FileType {0}' -f $FileType) New-FileType @paramHash } } } # end of Set-TargetResource # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Get-FileAssoc { [CmdletBinding()] Param( [Parameter()] [string] $Extension ) try { $SetUserFTA = Get-SetUserFTAPath -ErrorAction Stop } catch { Write-Error -Exception $_.Exception return } $Ret = @{ Extension = $Extension ProgId = $null Command = $null Icon = $null } $allUserFTAList = ConvertFrom-Csv (& $SetUserFTA get) -Header ('Extension', 'ProgId') if (-not $allUserFTAList) { Write-Error 'Failed to get user file type associations.' return } $fType = $allUserFTAList | Where-Object { $Extension -eq $_.Extension } | Select-Object -First 1 if ($fType.ProgId) { $Ret.ProgId = $fType.ProgId $GetCommand = & cmd.exe /c ("ftype {0} 2>null" -f $Ret.ProgId) foreach ($Line in $GetCommand) { if ($Line -match '=') { $Ret.Command = $Line.Split("=")[1].Trim() } } $RegKey = [Registry]::LocalMachine.OpenSubKey(("SOFTWARE\Classes\{0}\DefaultIcon" -f $Ret.ProgId)) if ($RegKey) { $Ret.Icon = $RegKey.GetValue($null, $null, [RegistryValueOptions]::DoNotExpandEnvironmentNames) $RegKey.Close() } } $Ret } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Set-FileAssoc { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string] $Extension, [Parameter(Mandatory = $true)] [string] $ProgId ) try { $SetUserFTA = Get-SetUserFTAPath -ErrorAction Stop } catch { Write-Error -Exception $_.Exception return } $result = & $SetUserFTA $Extension $ProgId if (-not $?) { Write-Error 'Unknown Exceptiopn' } elseif ($e = $result -match '^error:') { Write-Error ([string]$e) return } } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Remove-FileAssoc { [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [string] $Extension ) try { $SetUserFTA = Get-SetUserFTAPath -ErrorAction Stop } catch { Write-Error -Exception $_.Exception return } $result = & $SetUserFTA del $Extension if (-not $?) { Write-Error 'Unknown Exceptiopn' } elseif ($e = $result -match '^error:') { Write-Error ([string]$e) return } } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function New-FileType { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string] $FileType, [Parameter(Mandatory = $true)] [string] $Command, [Parameter()] [string] $Icon ) $SetCommand = & cmd.exe /c ("ftype {0}={1} 2>null" -f $FileType, $Command) if ($PSBoundParameters.ContainsKey('Icon')) { $Key = ("HKLM:\SOFTWARE\Classes\{0}\DefaultIcon" -f $FileType) if (-not (Test-Path -LiteralPath $Key)) { New-Item -Path $Key -Force | Out-Null } $RegKey = [Registry]::LocalMachine.OpenSubKey(("SOFTWARE\Classes\{0}\DefaultIcon" -f $FileType), $true) if ($RegKey) { $RegKey.SetValue("", $Icon, [RegistryValueKind]::ExpandString) $RegKey.Close() } } } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Get-SetUserFTAPath { [CmdletBinding()] [OutputType([string])] param() $private:exeHash = '791DC39F7BD059226364BB05CF5F8E1DD7CCFDAA33A1574F9DC821B2620991C2' $exe = Join-Path (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent) '\Libs\SetUserFTA\SetUserFTA.exe' if (-not (Test-Path -LiteralPath $exe)) { Write-Error 'SetUserFTA.exe is not found in the libs directory.' } elseif ($private:exeHash -ne (Get-FileHash -LiteralPath $exe).Hash) { Write-Error 'The Hash of SetUserFTA.exe is not valid.' } else { $exe } } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// function Assert-PsDscRunAsUser { [CmdletBinding()] param() if ('SYSTEM' -eq [Environment]::UserName) { Write-Error -Exception ([System.ArgumentException]::new('The PsDscRunAsCredential parameter is mandatory for this Resource.')) return } } # //////////////////////////////////////////////////////////////////////////////////////// # //////////////////////////////////////////////////////////////////////////////////////// Export-ModuleMember -Function *-TargetResource |