#region Function Manage-AppAssoc
function Manage-AppAssoc
{
<#
.Synopsis
Add or remove application associations in Windows 10 environment
.DESCRIPTION
Use a XML file and a local policy object to set default application associations (add, or remove associations)
.PARAMETER Action
Action mode : 'Add' or 'Remove'
.PARAMETER Source
Source XML file containing associations to add or remove
.PARAMETER Target
Target XML file on the local computer. If not specified, default file is 'C:\ProgramData\CompanyName\DefaultApps\DefaultApps.xml'
Which is the regular file to use in every case.
.EXAMPLE
Sample source file "$dirSupportFiles\MailtoAssoc.xml":
<?xml version="1.0" encoding="UTF-8"?>
<DefaultAssociations>
<Association Identifier=".eml" ProgId="Outlook.File.eml.14" ApplicationName="Microsoft Outlook" />
<Association Identifier="mailto" ProgId="Outlook.URL.mailto.14" ApplicationName="Microsoft Outlook" />
</DefaultAssociations>
.EXAMPLE
Add some application associations from MailtoAssoc.xml to the standard target file and (re-)set the local policy to this target file. Error exit if failed.
if ( -not ( Manage-AppAssoc -Action Add -Source "$dirSupportFiles\MailtoAssoc.xml" ) ) {
Exit-Script -ExitCode 70010 -Message 'Error trying to update application associations'
}
.INPUTS
None. This command does not accept pipeline input.
.OUTPUTS
$True if succeeded,
$False when an error occured: the error is already logged. The calling script may exit or not, depending on the importance of these associations.
#>
[CmdletBinding()]
[OutputType([bool])]
Param
(
[Parameter(Mandatory=$true)]
[ValidateSet("add","remove")]
[String] $Action,
[Parameter(Mandatory=$true)]
[ValidateNotNull()]
[String] $Source,
[Parameter(Mandatory=$false)]
[String] $Target = 'C:\ProgramData\CompanyName\DefaultApps\DefaultApps.xml'
)
Begin {
## Get the name of this function and write header
[string]${CmdletName} = $PSCmdlet.MyInvocation.MyCommand.Name
Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -CmdletBoundParameters $PSBoundParameters -Header
[xml]$sourcexml = $null
[xml]$targetxml = $null
}
Process {
try {
## Only for Win 10 and +
If ([int]$envOSVersionMajor -lt 10)
{
## Log a Warning
Write-Log -Message 'This function only works for windows 10.' -Source ${CmdletName} -Severity 2
return $false
}
Write-Log -Message "Action is [$Action]" -Source ${CmdletName}
Write-Log -Message "Source is [$Source]" -Source ${CmdletName}
Write-Log -Message "Target is [$Target]" -Source ${CmdletName}
## Read the source file
Write-Log -Message 'Reading Source file content ...' -Source ${CmdletName}
try {
[xml]$sourcexml = Get-Content $Source -ErrorAction Stop
}
catch {
Write-Log -Message "Error reading xml Source: [$Source] [$($_.Exception.Message)]" -Severity 3 -Source ${CmdletName}
return $false
}
if ( Test-Path $Target ) {
# Read the target file content
Write-Log -Message 'Reading target file content ...' -Source ${CmdletName}
try {
[xml]$targetxml = Get-Content $Target -ErrorAction Stop
}
catch {
Write-Log -Message "Error reading xml target file: [$Target] Error: [$($_.Exception.Message)]" -Severity 3 -Source ${CmdletName}
return $false
}
}
else {
# Create the target xml object. (The file will be created at the end of the function when changes are saved.)
Write-Log -Message 'Target file does not exist, creating an empty one ...' -Source ${CmdletName}
[xml]$targetxml = New-Object System.Xml.XmlDocument
[void]$targetxml.AppendChild($targetxml.CreateXmlDeclaration("1.0","UTF-8",$null))
[void]$targetxml.AppendChild($targetxml.CreateNode("element","DefaultAssociations",$null))
# Create the directory if needed
$target_directory = [System.IO.Path]::GetDirectoryName($Target)
if ( -not (Test-Path $target_directory -PathType Container) ) {
New-Item $target_directory -ItemType Directory -Force | out-null
}
}
# Parsing source file nodes
foreach($sourcenode in $sourcexml.SelectSingleNode("DefaultAssociations").SelectNodes("Association")) {
Write-Log -Message "Processing Association Identifier: $($sourcenode.Identifier), ProgID: $($sourcenode.ProgID), ApplicationName: $($sourcenode.ApplicationName)" -Source ${CmdletName}
# Test if source node exists in Destination file
$targetnode = $targetxml.DefaultAssociations.Association | Where {$_.Identifier -eq $sourcenode.Identifier}
if($targetnode) {
# The node exists: remove it. For the 'add' action it will be re-added with the source file content, to update it.
Write-Log -Message " + Association exists in target, removing node ..." -Source ${CmdletName}
[void]$targetxml.SelectSingleNode("DefaultAssociations").RemoveChild($targetnode)
}
if($Action -eq "add") {
# Add the new node to the target file
Write-Log -Message " + Adding node to target ..." -Source ${CmdletName}
[void]$targetxml.SelectSingleNode("DefaultAssociations").AppendChild($targetxml.ImportNode($sourcenode, $true))
}
}
# Save the target file and set the local policy, or remove them if there is no associations left
if($targetxml.SelectSingleNode("DefaultAssociations").SelectNodes("Association").Count -eq 0) {
# Remove the target file
if ( Test-Path $Target ) {
Write-Log -Message 'Target file has no content. Removing it ...' -Source ${CmdletName}
Remove-Item -Path $Target -Force
}
# Remove the local policy
Write-Log -Message 'Removing local policy...' -Source ${CmdletName}
$registryPath = "Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\System"
$Name = "DefaultAssociationsConfiguration"
Remove-ItemProperty -Path $registryPath -Name $name -Force | Out-Null
} else {
# Save the target file
Write-Log -Message "Saving target file ..." -Source ${CmdletName}
$targetxml.Save($Target)
# Set the Local Policy for this target file
Write-Log -Message 'Setting local policy' -Source ${CmdletName}
$registryPath = 'Registry::HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\System'
Set-RegistryKey -Key $registryPath -Name 'DefaultAssociationsConfiguration' -Value $Target -Type 'String' -ContinueOnError $false
}
Write-Log -Message 'OK function succeeded.' -Severity 1 -Source ${CmdletName}
return $True
}
catch {
Write-Log -Message ( 'Error function failed (error catched): ' + (& { $_ | Format-List * | Out-String }) ) -Severity 3 -Source ${CmdletName}
return $false
}
}
End {
Write-FunctionHeaderOrFooter -CmdletName ${CmdletName} -Footer
}
}
#endregion