Skip to content

Commit

Permalink
Sa 3041 retain default app protocol associations (#105)
Browse files Browse the repository at this point in the history
* pta/fta functions

* Added load/unload error check

---------

Co-authored-by: Joe Workman <54448601+jworkmanjc@users.noreply.github.com>
  • Loading branch information
kmaranionjc and jworkmanjc authored Feb 7, 2024
1 parent a50df00 commit bd50061
Show file tree
Hide file tree
Showing 8 changed files with 272 additions and 19 deletions.
Binary file modified Deploy/uwp_jcadmu.ps1
Binary file not shown.
15 changes: 15 additions & 0 deletions ModuleChangelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
## 2.6.0

Release Date: February 7, 2024

#### RELEASE NOTES

```
* Added a feature to migrate default applications (file associations) and protocol associations
```

#### Bug Fixes:
```
* Addressed a issue where a registry hive fails to load, the tool will now halt migration instead of continuing
* Fixed a bug with Module Changelog version test where release type number is not properly outputted
```
## 2.5.1

Release Date: December 18, 2023
Expand Down
2 changes: 1 addition & 1 deletion jumpcloud-ADMU/JumpCloud.ADMU.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
RootModule = 'JumpCloud.ADMU.psm1'

# Version number of this module.
ModuleVersion = '2.5.1'
ModuleVersion = '2.6.0'

# Supported PSEditions
# CompatiblePSEditions = @()
Expand Down
2 changes: 1 addition & 1 deletion jumpcloud-ADMU/Powershell/Form.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function show-mtpSelection {
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="JumpCloud ADMU 2.5.1"
Title="JumpCloud ADMU 2.6.0"
WindowStyle="SingleBorderWindow"
ResizeMode="NoResize"
Background="White" ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Visible" Width="1000" Height="520">
Expand Down
181 changes: 165 additions & 16 deletions jumpcloud-ADMU/Powershell/Start-Migration.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -401,23 +401,39 @@ function Set-UserRegistryLoadState {
[ValidatePattern("^S-\d-\d+-(\d+-){1,14}\d+$")]
[System.String]$UserSid
)
Begin {
$regQuery = REG QUERY HKU *>&1
}
process {
switch ($op) {
"Load" {

# Current loaded profiles before loading NTUSER.DAT.BAK and USRClass.dat.bak
Write-ToLog -Message:('Current Loaded Profiles Before Loading: ' + $regQuery)

Start-Sleep -Seconds 1
$lastModified = Get-Item -path "$ProfilePath\NTUSER.DAT.BAK" -Force | Select-Object -ExpandProperty LastWriteTime
Write-ToLog -Message:("Last Modified Time of $ProfilePath\NTUSER.DAT.BAK is $lastModified")
$results = REG LOAD HKU\$($UserSid)_admu "$ProfilePath\NTUSER.DAT.BAK" *>&1
# Get the last modified time of the NTUSER.DAT.BAK file
if ($?) {
Write-ToLog -Message:('Load Profile: ' + "$ProfilePath\NTUSER.DAT.BAK")
} else {
Write-ToLog -Message:('Could not load profile: ' + "$ProfilePath\NTUSER.DAT.BAK")
Throw "Could not load profile: $ProfilePath\NTUSER.DAT.BAK"
}

Start-Sleep -Seconds 1
$lastModified = Get-Item -path "$ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" -Force | Select-Object -ExpandProperty LastWriteTime
Write-ToLog -Message:("Last Modified Time of $ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak is $lastModified")
$results = REG LOAD HKU\"$($UserSid)_Classes_admu" "$ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" *>&1
# Get the last modified time of the USRClass.dat.bak file
if ($?) {
Write-ToLog -Message:('Load Profile: ' + "$ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak")
} else {
Write-ToLog -Message:('Could not load profile: ' + "$ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak")
Throw "Could not load profile: $ProfilePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak"
}


}
"Unload" {
[gc]::collect()
Expand Down Expand Up @@ -456,39 +472,42 @@ Function Test-UserRegistryLoadState {
# Tests to check that the reg items are not loaded
If ($results -match $UserSid) {
Write-ToLog "REG Keys are loaded, attempting to unload"
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid
try {
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid
}
catch {
Throw "Could Not Unload User Registry During Test-UserRegistryLoadState Unload Process"
}
}
}
process {
# Load New User Profile Registry Keys
try {
Set-UserRegistryLoadState -op "Load" -ProfilePath $ProfilePath -UserSid $UserSid
} catch {
Write-Error "Could Not Load"
Throw "Could Not Load User Registry During Test-UserRegistryLoadState Load Process"
}
# Load Selected User Profile Keys
# Unload "Selected" and "NewUser"
try {
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid
} catch {
Write-Error "Could Not Unload"
Write-Error "Could Not Unload User Registry During Test-UserRegistryLoadState Unload Process"
}
}
end {
$results = REG QUERY HKU *>&1
# Tests to check that the reg items are not loaded
If ($results -match $UserSid) {
Write-ToLog "REG Keys are loaded, attempting to unload"
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid
}
$results = REG QUERY HKU *>&1
# Tests to check that the reg items are not loaded
If ($results -match $UserSid) {
Write-ToLog "REG Keys are loaded at the end of testing, exiting..." -level Warn
throw "REG Keys are loaded at the end of testing, exiting..."
try {
Set-UserRegistryLoadState -op "Unload" -ProfilePath $ProfilePath -UserSid $UserSid
}
catch {
throw "Registry Keys are still loaded after Test-UserRegistryLoadState Testing Exiting..."
}
}
}

}


Expand Down Expand Up @@ -1368,6 +1387,85 @@ function Set-ADMUScheduledTask {
}
}
#endregion Agent Install Helper Functions


##### MIT License #####
# MIT License

# Copyright © 2022, Danysys
# Modified by JumpCloud

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# Get user file type associations
function Get-UserFileTypeAssociation {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, HelpMessage = 'The SID of the user to capture file type associations')]
[System.String]
$UserSid
)
$manifestList = @()
# Test path for file type associations
$pathRoot = "HKEY_USERS:\$($UserSid)_admu\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\"
if (Test-Path $pathRoot) {
$exts = Get-ChildItem $pathRoot*
foreach ($ext in $exts) {
$indivExtension = $ext.PSChildName
$progId = (Get-ItemProperty "$($pathRoot)\$indivExtension\UserChoice" -ErrorAction SilentlyContinue).ProgId
$manifestList += [PSCustomObject]@{
extension = $indivExtension
programId = $progId
}
}
}
return $manifestList
}

# Get user protocol associations

function Get-ProtocolTypeAssociation{
[CmdletBinding()]
param (
[Parameter(Mandatory = $true, HelpMessage = 'The SID of the user to capture file type associations')]
[System.String]
$UserSid
)
$manifestList = @()

$pathRoot = "HKEY_USERS:\$($UserSid)_admu\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\"
if (Test-Path $pathRoot) {
Get-ChildItem $pathRoot* |
ForEach-Object {

$progId = (Get-ItemProperty "$($_.PSParentPath)\$($_.PSChildName)\UserChoice" -ErrorAction SilentlyContinue).ProgId
if ($progId) {
$manifestList += [PSCustomObject]@{
extension = $_.PSChildName
programId = $progId
}
}
}
}
return $manifestList
}
##### END MIT License #####

Function Start-Migration {
[CmdletBinding(HelpURI = "https://github.com/TheJumpCloud/jumpcloud-ADMU/wiki/Start-Migration")]
Param (
Expand All @@ -1389,7 +1487,7 @@ Function Start-Migration {
Begin {
Write-ToLog -Message:('####################################' + (get-date -format "dd-MMM-yyyy HH:mm") + '####################################')
# Start script
$admuVersion = '2.5.1'
$admuVersion = '2.6.0'
Write-ToLog -Message:('Running ADMU: ' + 'v' + $admuVersion)
Write-ToLog -Message:('Script starting; Log file location: ' + $jcAdmuLogFile)
Write-ToLog -Message:('Gathering system & profile information')
Expand Down Expand Up @@ -1641,7 +1739,7 @@ Function Start-Migration {
Test-UserRegistryLoadState -ProfilePath $newUserProfileImagePath -UserSid $newUserSid
Test-UserRegistryLoadState -ProfilePath $oldUserProfileImagePath -UserSid $SelectedUserSID
} catch {
Write-ToLog -Message:('could not load and unload registry of migration user, exiting') -level Warn
Write-ToLog -Message:('Could not load and unload registry of migration user during Test-UserRegistryLoadState, exiting') -level Warn
$admuTracker.testRegLoadUnload.fail = $true
break
}
Expand Down Expand Up @@ -1729,7 +1827,7 @@ Function Start-Migration {
break
}
# Validate file permissions on registry item
if ((Get-psdrive | select-object name) -notmatch "HKEY_USERS") {
if ("HKEY_USERS" -notin (Get-psdrive | select-object name).Name) {
Write-ToLog "Mounting HKEY_USERS to check USER UWP keys"
New-PSDrive -Name:("HKEY_USERS") -PSProvider:("Registry") -Root:("HKEY_USERS")
}
Expand Down Expand Up @@ -1783,8 +1881,31 @@ Function Start-Migration {
Set-ValueToKey -registryRoot Users -keyPath "$($newusersid)_admu\SOFTWARE\JCADMU" -Name "previousProfilePath" -value "$oldUserProfileImagePath" -regValueKind String
}
### End reg key check for new user
$path = $oldUserProfileImagePath + '\AppData\Local\JumpCloudADMU'
If (!(test-path $path)) {
New-Item -ItemType Directory -Force -Path $path
}

# SelectedUserSid
# Validate file permissions on registry item
if ("HKEY_USERS" -notin (Get-psdrive | select-object name).Name) {
Write-ToLog "Mounting HKEY_USERS to check USER UWP keys"
New-PSDrive -Name:("HKEY_USERS") -PSProvider:("Registry") -Root:("HKEY_USERS")
}


$fileTypeAssociations = Get-UserFileTypeAssociation -UserSid $SelectedUserSid
Write-ToLog -Message:('Found ' + $fileTypeAssociations.count + ' File Type Associations')
$fileTypeAssociations | Export-Csv -Path "$path\fileTypeAssociations.csv" -NoTypeInformation -Force

$protocolTypeAssociations = Get-ProtocolTypeAssociation -UserSid $SelectedUserSid
Write-ToLog -Message:('Found ' + $protocolTypeAssociations.count + ' Protocol Type Associations')
$protocolTypeAssociations | Export-Csv -Path "$path\protocolTypeAssociations.csv" -NoTypeInformation -Force


$regQuery = REG QUERY HKU *>&1
# Unload "Selected" and "NewUser"
Write-ToLog -Message:('Loaded profiles before unloading: ' + $regQuery)
Set-UserRegistryLoadState -op "Unload" -ProfilePath $newUserProfileImagePath -UserSid $NewUserSID
Set-UserRegistryLoadState -op "Unload" -ProfilePath $oldUserProfileImagePath -UserSid $SelectedUserSID

Expand All @@ -1807,6 +1928,30 @@ Function Start-Migration {
try {
Rename-Item -Path "$oldUserProfileImagePath\NTUSER.DAT" -NewName "$oldUserProfileImagePath\NTUSER_original_$renameDate.DAT" -Force -ErrorAction Stop
Rename-Item -Path "$oldUserProfileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat" -NewName "$oldUserProfileImagePath\AppData\Local\Microsoft\Windows\UsrClass_original_$renameDate.dat" -Force -ErrorAction Stop

# Validate the file have timestamps
$ntuserOriginal = Get-Item "$oldUserProfileImagePath\NTUSER_original_$renameDate.DAT" -Force
$usrClassOriginal = Get-Item "$oldUserProfileImagePath\AppData\Local\Microsoft\Windows\UsrClass_original_$renameDate.dat" -Force

# Get the name of the file
$ntuserOriginalName = $ntuserOriginal.Name
$usrClassOriginalName = $usrClassOriginal.Name

if ($ntuserOriginalName -match "ntuser_original_$($renameDate).DAT") {
Write-ToLog -Message:("Successfully renamed $ntuserOriginalName with timestamp $renameDate")
} else {
Write-ToLog -Message:("Failed to rename $ntuserOriginalName with timestamp $renameDate")
$admuTracker.renameOriginalFiles.fail = $true
break
}

if ($usrClassOriginalName -match "UsrClass_original_$($renameDate).dat") {
Write-ToLog -Message:("Successfully renamed $usrClassOriginalName with timestamp $renameDate")
} else {
Write-ToLog -Message:("Failed to rename $usrClassOriginalName with timestamp $renameDate")
$admuTracker.renameOriginalFiles.fail = $true
break
}
} catch {
Write-ToLog -Message("Could not rename original registry files for backup purposes: Exiting...")
Write-ToLog -Message($_.Exception.Message)
Expand All @@ -1819,6 +1964,9 @@ Function Start-Migration {
try {
Rename-Item -Path "$oldUserProfileImagePath\NTUSER.DAT.BAK" -NewName "$oldUserProfileImagePath\NTUSER.DAT" -Force -ErrorAction Stop
Rename-Item -Path "$oldUserProfileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" -NewName "$oldUserProfileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat" -Force -ErrorAction Stop



} catch {
Write-ToLog -Message("Could not rename backup registry files to a system recognizable name: Exiting...")
Write-ToLog -Message($_.Exception.Message)
Expand Down Expand Up @@ -1966,6 +2114,7 @@ Function Start-Migration {

Write-ToLog -Message:('Updating UWP Apps for new user')
$newUserProfileImagePath = Get-ItemPropertyValue -Path ('HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\' + $newusersid) -Name 'ProfileImagePath'

$path = $newUserProfileImagePath + '\AppData\Local\JumpCloudADMU'
If (!(test-path $path)) {
New-Item -ItemType Directory -Force -Path $path
Expand Down
5 changes: 4 additions & 1 deletion jumpcloud-ADMU/Powershell/Tests/Build.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,12 @@ Describe "Module Validation Tests" {
It 'Module Changlog Version should be correct' {
$ModuleChangelogVersionRegex = "([0-9]+)\.([0-9]+)\.([0-9]+)"
$ModuleChangelogVersionMatch = ($ModuleChangelogContent | Select-Object -First 1) | Select-String -Pattern:($ModuleChangelogVersionRegex)
Write-Host "Module Changelog Content: $ModuleChangelogVersionMatch"
$ModuleChangelogVersion = $ModuleChangelogVersionMatch.Matches.Value
Write-Host "Module Changelog Version: $ModuleChangelogVersion"
# Compare
([version]$ModuleChangelogVersion).$($env:ModuleVersionType) | Should -Be ($lastestModule.version.$($env:ModuleVersionType) + 1)
$latestVersion = [version]$lastestModule.version
([version]$ModuleChangelogVersion).$($env:ModuleVersionType) | Should -Be ($latestVersion.$($env:ModuleVersionType) + 1)

}
It 'Module Changelog should not contain placeholder values' {
Expand Down
23 changes: 23 additions & 0 deletions jumpcloud-ADMU/Powershell/Tests/Functions.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ Describe 'Functions' {
Test-RegistryValueMatch -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' -Value 'Public' -stringmatch 'Private' | Should -Be $false
}
}
Context 'Set-PTA/FTA Test'{
BeforeAll{
# Import /Deploy/uwp_jcadmu.ps1 and use the function Set-FTA
. $PSScriptRoot\..\..\..\Deploy\uwp_jcadmu.ps1
}
It 'Set-FTA should be changed after migration'{
$protocol = "http"
$fileType = ".txt"

Set-FTA "wordpad" $fileType
Set-PTA -ProgId "notepad" -Protocol $protocol

$fta = Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\$($fileType)\UserChoice"
$pta = Get-ItemProperty "HKCU:\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\$($protocol)\UserChoice"
# Write out the contents of the FTA and PTA
Write-Host "FTA: $($fta)"
Write-Host "PTA: $($pta)"
# Check if programId is wordpad
$fta.ProgId | Should -Contain "wordpad"
$pta.ProgId | Should -Contain "notepad"

}
}
Context 'Test-JumpCloudUsername Function' {
It 'Valid Username Returns True' {
# Get the first user
Expand Down
Loading

0 comments on commit bd50061

Please sign in to comment.