diff --git a/.circleci/config.yml b/.circleci/config.yml index 29adb6e2..4882149f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,13 +2,13 @@ version: 2.1 parameters: buildReleaseType: description: "Semantic Version increment type of the release. Valid values: major, minor, patch" - default: "patch" + default: "minor" type: enum enum: ["major", "minor", "patch"] PublishToPSGallery: description: "When `true` and when run against Master branch, this workflow will publish the latest code to PSGallery" type: boolean - default: false + default: true ManualModuleVersion: description: "When `true` the pipeline will use the Module Version specified in JumpCloud Module JumpCloud.psd1 file" type: boolean diff --git a/Deploy/ADMU.ps1 b/Deploy/ADMU.ps1 index 19bf396b..645fd94e 100644 Binary files a/Deploy/ADMU.ps1 and b/Deploy/ADMU.ps1 differ diff --git a/ModuleChangelog.md b/ModuleChangelog.md index a9b4e5a8..4f2e0816 100644 --- a/ModuleChangelog.md +++ b/ModuleChangelog.md @@ -1,3 +1,13 @@ +## 2.5.0 + +Release Date: August 30, 2023 + +#### RELEASE NOTES + +``` +* The ADMU now checks for scheduled tasks before migration and attempts to disable any non-microsoft task. Scheduled tasks which load a user's registry into memory have been reported to have locked a user's registry into memory which will prevent the ADMU from functioning. This release of ADMU will attempt to disable any root level scheduled tasks and will re-enable these tasks after migration or if the ADMU fails to migrate. + - Only tasks that are in a "Ready" state will be disabled, currently running tasks are not stopped. +``` ## 2.4.3 Release Date: Aug 23,2023 diff --git a/jumpcloud-ADMU/Exe/gui_jcadmu.exe b/jumpcloud-ADMU/Exe/gui_jcadmu.exe index 33bc823e..e18aa1cf 100644 Binary files a/jumpcloud-ADMU/Exe/gui_jcadmu.exe and b/jumpcloud-ADMU/Exe/gui_jcadmu.exe differ diff --git a/jumpcloud-ADMU/Exe/uwp_jcadmu.exe b/jumpcloud-ADMU/Exe/uwp_jcadmu.exe index cd35c958..fbdb4843 100644 Binary files a/jumpcloud-ADMU/Exe/uwp_jcadmu.exe and b/jumpcloud-ADMU/Exe/uwp_jcadmu.exe differ diff --git a/jumpcloud-ADMU/JumpCloud.ADMU.nuspec b/jumpcloud-ADMU/JumpCloud.ADMU.nuspec index 904cef36..6ac27fff 100644 --- a/jumpcloud-ADMU/JumpCloud.ADMU.nuspec +++ b/jumpcloud-ADMU/JumpCloud.ADMU.nuspec @@ -2,7 +2,7 @@ JumpCloud.ADMU - 2.4.3 + 2.5.0.3282 Powershell Module to run JumpCloud Active Directory Migration Utility. JumpCloud Solutions Architect Team JumpCloud diff --git a/jumpcloud-ADMU/JumpCloud.ADMU.psd1 b/jumpcloud-ADMU/JumpCloud.ADMU.psd1 index 4acbb75b..6d71efa3 100644 --- a/jumpcloud-ADMU/JumpCloud.ADMU.psd1 +++ b/jumpcloud-ADMU/JumpCloud.ADMU.psd1 @@ -3,7 +3,7 @@ # # Generated by: JumpCloud Solutions Architect Team # -# Generated on: 8/23/2023 +# Generated on: 8/30/2023 # @{ @@ -12,13 +12,13 @@ RootModule = 'JumpCloud.ADMU.psm1' # Version number of this module. -ModuleVersion = '2.4.3' +ModuleVersion = '2.5.0' # Supported PSEditions # CompatiblePSEditions = @() # ID used to uniquely identify this module -GUID = 'd24fb2e2-a7d0-4228-afa4-45767980c10e' +GUID = 'd61a763b-2d1d-4b4d-ac21-0fb41d17a6ab' # Author of this module Author = 'JumpCloud Solutions Architect Team' diff --git a/jumpcloud-ADMU/Powershell/Form.ps1 b/jumpcloud-ADMU/Powershell/Form.ps1 index f58f76bd..94df474c 100644 --- a/jumpcloud-ADMU/Powershell/Form.ps1 +++ b/jumpcloud-ADMU/Powershell/Form.ps1 @@ -143,7 +143,7 @@ function show-mtpSelection { diff --git a/jumpcloud-ADMU/Powershell/Start-Migration.ps1 b/jumpcloud-ADMU/Powershell/Start-Migration.ps1 index af7e60d0..9506d679 100644 --- a/jumpcloud-ADMU/Powershell/Start-Migration.ps1 +++ b/jumpcloud-ADMU/Powershell/Start-Migration.ps1 @@ -1327,7 +1327,42 @@ function Test-DATFilePermission { } } } +function Set-ADMUScheduledTask { + # Param op "disable" or "enable" then -tasks (array of tasks) + param ( + [Parameter(Mandatory = $true)] + [ValidateSet("disable", "enable")] + [System.String] + $op, + [Parameter(Mandatory = $true)] + [System.Object[]] + $scheduledTasks + ) + # Switch op + switch ($op) { + "disable" { + try { + $scheduledTasks | ForEach-Object { + Write-ToLog -message:("Disabling Scheduled Task: $($_.TaskName)") + Disable-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath | Out-Null + } + } catch { + Write-ToLog -message:("Failed to disable Scheduled Tasks $($_.Exception.Message)") + } + } + "enable" { + try { + $scheduledTasks | ForEach-Object { + Write-ToLog -message("Enabling Scheduled Task: $($_.TaskName)") + Enable-ScheduledTask -TaskName $_.TaskName -TaskPath $_.TaskPath | Out-Null + } + } catch { + Write-ToLog -message("Could not enable Scheduled Task: $($_.TaskName)") -Level Warn + } + } + } + } #endregion Agent Install Helper Functions Function Start-Migration { [CmdletBinding(HelpURI = "https://github.com/TheJumpCloud/jumpcloud-ADMU/wiki/Start-Migration")] @@ -1350,7 +1385,7 @@ Function Start-Migration { Begin { Write-ToLog -Message:('####################################' + (get-date -format "dd-MMM-yyyy HH:mm") + '####################################') # Start script - $admuVersion = '2.4.3' + $admuVersion = '2.5.0' Write-ToLog -Message:('Running ADMU: ' + 'v' + $admuVersion) Write-ToLog -Message:('Script starting; Log file location: ' + $jcAdmuLogFile) Write-ToLog -Message:('Gathering system & profile information') @@ -1490,6 +1525,17 @@ Function Start-Migration { new-item -ItemType Directory -Force -Path $jcAdmuTempPath 2>&1 | Write-Verbose } Write-ToLog -Message:($localComputerName + ' is currently Domain joined to ' + $WmiComputerSystem.Domain + ' NetBiosName is ' + $netBiosName) + + # Get all schedule tasks that have State of "Ready" and not disabled and "Running" + $ScheduledTasks = Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "*\Microsoft\Windows*" -and $_.State -ne "Disabled" -and $_.state -ne "Running" } + # Disable tasks before migration + Write-ToLog -message:("Disabling Scheduled Tasks...") + # Check if $ScheduledTasks is not null + if ($ScheduledTasks) { + Set-ADMUScheduledTask -op "disable" -scheduledTasks $ScheduledTasks + } else { + Write-ToLog -message:("No Scheduled Tasks to disable") + } } Process { # Start Of Console Output @@ -2049,6 +2095,13 @@ Function Start-Migration { $admuTracker.leaveDomain.pass = $true } + # re-enable scheduled tasks if they were disabled + if ($ScheduledTasks) { + Set-ADMUScheduledTask -op "enable" -scheduledTasks $ScheduledTasks + } else { + Write-ToLog -Message:('No Scheduled Tasks to enable') + } + # Cleanup Folders Again Before Reboot Write-ToLog -Message:('Removing Temp Files & Folders.') try { @@ -2093,6 +2146,12 @@ Function Start-Migration { Write-ToLog -Message:("Could not remove the $JumpCloudUserName profile and user account") -Level Error } $FixedErrors += "$trackedStep" + # Create a list of scheduled tasks that are disabled + if ($ScheduledTasks) { + Set-ADMUScheduledTask -op "enable" -scheduledTasks $ScheduledTasks + } else { + Write-ToLog -Message:('No Scheduled Tasks to enable') + } } Default { @@ -2116,5 +2175,7 @@ Function Start-Migration { } throw "JumpCloud ADMU was unable to migrate $selectedUserName" } + + } } \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/Tests/Functions.Tests.ps1 b/jumpcloud-ADMU/Powershell/Tests/Functions.Tests.ps1 index def94c55..25a13f67 100644 --- a/jumpcloud-ADMU/Powershell/Tests/Functions.Tests.ps1 +++ b/jumpcloud-ADMU/Powershell/Tests/Functions.Tests.ps1 @@ -490,7 +490,33 @@ Describe 'Functions' { It 'Restart-ComputerWithDelay' { } } - + Context 'Test Set-ADMUScheduledTask'{ + BeforeAll { + $scheduledTasks = Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "*\Microsoft\Windows*" -and $_.State -ne "Disabled" -and $_.state -ne "Running" } + Set-ADMUScheduledTask -op "disable" -scheduledTasks $scheduledTasks + } + It 'Should disabled tasks'{ + # Disable tasks that are ready to run + $afterDisable = Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "*\Microsoft\Windows*" -and $_.State -eq "Disabled" } + # Compare $scheduledTasks and $afterDisable state should not be equal + $scheduledTasks | ForEach-Object { + $task = $_ + # Check that the task is disabled + $afterDisable | Where-Object { $_.TaskName -eq $task.TaskName -and $_.State -eq "Disabled" } | Should -Not -BeNullOrEmpty + } + } + It 'Should Enable tasks'{ + Set-ADMUScheduledTask -op "enable" -scheduledTasks $scheduledTasks + # Validate that the tasks are enabled + $afterEnable = Get-ScheduledTask | Where-Object { $_.TaskPath -notlike "*\Microsoft\Windows*" -and $_.State -eq "Ready" } + # Compare $scheduledTasks and $afterDisable state should not be equal + $scheduledTasks | ForEach-Object { + $task = $_ + # Check that the task is disabled + $afterEnable | Where-Object { $_.TaskName -eq $task.TaskName -and $_.State -eq "Ready" } | Should -Not -BeNullOrEmpty + } + } + } Context 'Validates that the Registry Hive Permissions are correct, given a username' { It 'Should return true when a users ntfs permissions are correct' { $datUserTrue = "ADMU_dat_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) diff --git a/jumpcloud-ADMU/Powershell/Tests/Migration.Tests.ps1 b/jumpcloud-ADMU/Powershell/Tests/Migration.Tests.ps1 index 98640129..9d029516 100644 --- a/jumpcloud-ADMU/Powershell/Tests/Migration.Tests.ps1 +++ b/jumpcloud-ADMU/Powershell/Tests/Migration.Tests.ps1 @@ -1,3 +1,19 @@ +function Enable-TestNameAsVariablePlugin { + & (get-module pester) { + $PluginParams = @{ + Name = "SaveTestNameToVariable" + EachTestSetupStart = { + $GLOBAL:TestName = $Context.Test.Name + } + EachTestTeardown = { + $GLOBAL:TestName = $null + } + } + $state.Plugin += New-PluginObject @PluginParams + } +} +# Add test name as variable +# https://github.com/pester/Pester/issues/1611 BeforeAll { # import build variables for test cases write-host "Importing Build Variables:" @@ -35,6 +51,11 @@ BeforeAll { Get-JCuser -username "ADMU_*" | Remove-JCuser -Force } Describe 'Migration Test Scenarios' { + Enable-TestNameAsVariablePlugin + BeforeEach { + Write-Host "---------------------------" + Write-Host "Begin Test: $testName`n" + } Context 'Start-Migration on local accounts (Test Functionallity)' { It "username extists for testing" { foreach ($user in $userTestingHash.Values) { @@ -44,6 +65,51 @@ Describe 'Migration Test Scenarios' { } } + It "Tests that a previously enabled Scheduled Task is enabled at the end of user migration" { + # Create a scheduled task + $action = New-ScheduledTaskAction -Execute "powershell.exe" + $trigger = New-ScheduledTaskTrigger -AtLogon + $settings = New-ScheduledTaskSettingsSet + $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings + Register-ScheduledTask "TestTask" -InputObject $task + + $Password = "Temp123!" + $user1 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + $user2 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + # Initialize a single user to migrate: + InitUser -UserName $user1 -Password $Password + # Migrate the initialized user to the second username + { Start-Migration -AutobindJCUser $false -JumpCloudUserName $user2 -SelectedUserName "$ENV:COMPUTERNAME\$user1" -TempPassword "$($Password)" } | Should -Not -Throw + + # Get the scheduled task + $task = Get-ScheduledTask -TaskName "TestTask" + # Task state should be ready + $task.State | Should -Be "Ready" + + + } + It "Tests that a previously disable Scheduled Task is disabled after migration" { + # Task should be enabled after migration + # Create a scheduled task + $action = New-ScheduledTaskAction -Execute "powershell.exe" + $trigger = New-ScheduledTaskTrigger -AtLogon + $settings = New-ScheduledTaskSettingsSet + $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings + Register-ScheduledTask "TestTaskDisabled" -InputObject $task + { Disable-ScheduledTask -TaskName "TestTaskDisabled" } | Should -Not -Throw + + $Password = "Temp123!" + $user1 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + $user2 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + # Initialize a single user to migrate: + InitUser -UserName $user1 -Password $Password + # Migrate the initialized user to the second username + { Start-Migration -AutobindJCUser $false -JumpCloudUserName $user2 -SelectedUserName "$ENV:COMPUTERNAME\$user1" -TempPassword "$($Password)" } | Should -Not -Throw + # Get the scheduled task + $task = Get-ScheduledTask -TaskName "TestTaskDisabled" + # Task state should still be disabled + $task.State | Should -Be "Disabled" + } It "Test Convert profile migration for Local users" { foreach ($user in $userTestingHash.Values) { # Remove log before testing @@ -94,51 +160,140 @@ Describe 'Migration Test Scenarios' { Remove-Item $logPath New-Item $logPath -Force -ItemType File } - # This test contains a job which will load the migration user's profile - # into memory and effectively break the migration process. This test - # simulates the case where a process is loaded 'during' migration. - foreach ($user in $JCReversionHash.Values) { - # Begin background job before Start-Migration - $waitJob = Start-Job -ScriptBlock:( { - [CmdletBinding()] - param ( - [Parameter()] - [string] - $UserName, - [Parameter()] - [string] - $Password, - [Parameter()] - [string] - $JCUserName - ) - $file = "C:\Users\$JCUserName" - # wait for the new user - while (!(Test-Path -Path $file -ErrorAction SilentlyContinue)) { + It "Tests that the tool can recover when the start migration script fails and that scheduled tasks are returned to their previous state" { + + # This test contains a job which will load the migration user's profile + # into memory and effectively break the migration process. This test + # simulates the case where a process is loaded 'during' migration. + foreach ($user in $JCReversionHash.Values) { + # Begin background job before Start-Migration + # define path to start migration for parallel job: + $pathToSM = "$PSScriptRoot\..\Start-Migration.ps1" + + Write-Host "$(Get-Date -UFormat "%D %r") - Start parallel job to wait for new user directory" + $waitJob = Start-Job -ScriptBlock:( { + [CmdletBinding()] + param ( + [Parameter()] + [string] + $UserName, + [Parameter()] + [string] + $Password, + [Parameter()] + [string] + $JCUserName + ) + $file = "C:\Users\$JCUserName" + # wait for the new user + do { + $date = Get-Date -UFormat "%D %r" + Start-Sleep -Seconds:(1) + } + Until ((Test-Path -Path $file -ErrorAction SilentlyContinue)) { + } + $date = Get-Date -UFormat "%D %r" + Write-Host "$date - Starting Process:" + # Start Process on the migration user to get the migration to fail + $credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @($UserName, (ConvertTo-SecureString -String $Password -AsPlainText -Force)) + # trigger PowerShell session + $process = Start-Process powershell.exe -Credential ($credentials) -WorkingDirectory "C:\windows\system32" -ArgumentList ('-WindowStyle Hidden') + # write out job complete, if the job completes we should see it in the ci logs + Write-Host "Job Completed: ID $($process.id)" + }) -ArgumentList:($($user.Username), $($user.password), $($user.JCUsername)) + # create the task before start-migration: + Write-Host "$(Get-Date -UFormat "%D %r") - Create scheduled task" + $waitTaskJob = Start-Job -ScriptBlock:( { + $action = New-ScheduledTaskAction -Execute "powershell.exe" + $trigger = New-ScheduledTaskTrigger -AtLogon + $settings = New-ScheduledTaskSettingsSet + $task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings + Register-ScheduledTask "TestTaskFail" -InputObject $task | Out-Null + $task = Get-ScheduledTask -TaskName "TestTaskFail" + do { + $task = Get-ScheduledTask -TaskName "TestTaskFail" + Start-Sleep -Seconds:(1) + + } + Until ($task.state -eq "Disabled") + if ($task.state -eq "Disabled") { + Write-Host "Task State: $($task.State)" + return $true + } else { + return $false + } + }) + # Begin job to kick off Start-Migration + + Write-Host "$(Get-Date -UFormat "%D %r") - Start parallel job for start-migration script" + write-host "`nRunning: Start-Migration -JumpCloudUserName $($user.JCUsername) -SelectedUserName $($user.username) -TempPassword $($user.password) Testing Reverse`n" + $waitStartMigrationJob = Start-Job -ScriptBlock:( { + param ( + [Parameter()] + [string] + $JCUSERNAME, + [Parameter()] + [string] + $SELECTEDCOMPUTERNAME, + [Parameter()] + [string] + $TEMPPASS, + [Parameter()] + [string] + $SMPath + ) $date = Get-Date -UFormat "%D %r" - Write-Host "$date - waiting for file:" - Start-Sleep -Seconds:(1) - } - $date = Get-Date -UFormat "%D %r" - Write-Host "$date - Starting Process:" - # Start Process on the migration user to get the migration to fail - $credentials = New-Object System.Management.Automation.PSCredential -ArgumentList @($UserName, (ConvertTo-SecureString -String $Password -AsPlainText -Force)) - # trigger PowerShell session - Start-Process powershell.exe -Credential ($credentials) -WorkingDirectory "C:\windows\system32" -ArgumentList ('-WindowStyle Hidden') - # write out job complete, if the job completes we should see it in the ci logs - Write-Host "Job Completed" - }) -ArgumentList:($($user.Username), ($($user.password)), $($user.JCUsername)) - # Begin job to kick off Start-Migration - write-host "`nRunning: Start-Migration -JumpCloudUserName $($user.JCUsername) -SelectedUserName $($user.username) -TempPassword $($user.password)`n" - { Start-Migration -JumpCloudAPIKey $env:JCApiKey -AutobindJCUser $false -JumpCloudUserName "$($user.JCUsername)" -SelectedUserName "$ENV:COMPUTERNAME\$($user.username)" -TempPassword "$($user.password)" -UpdateHomePath $user.UpdateHomePath } | Should -Throw - # Receive the wait-job to the ci logs - Write-Host "Job Details:" - Receive-Job -Job $waitJob -Keep - # The original user should exist - "C:\Users\$($user.username)" | Should -Exist - # NewUserInit should be reverted and the new user profile path should not exist - "C:\Users\$($user.JCUsername)" | Should -Not -Exist + Write-Host "$date - Starting Start migration:" + Write-Host "$date - path: $SMPath" + . $SMPath + if ($?) { + Write-Host "imported start migration" + } else { + write-host "failed to import start migration script" + } + Write-Host "Running Migration, $JCUSERNAME, $SELECTEDCOMPUTERNAME, $TEMPPASS" + try { + Start-Migration -AutobindJCUser $false -JumpCloudUserName "$($JCUSERNAME)" -SelectedUserName "$ENV:COMPUTERNAME\$($SELECTEDCOMPUTERNAME)" -TempPassword "$($TEMPPASS)" | Out-Null + } Catch { + write-host "Migration failed as expected" + } + $logContent = Get-Content -Tail 1 C:\Windows\Temp\Jcadmu.log + if ($logContent -match "The following migration steps were reverted to their original state: newUserInit") { + write-host "Start Migration Task Failed Sucessfully" + return $true + } else { + return $false + } + # $date = Get-Date -UFormat "%D %r" + # Write-Host "$date - Start migration complete" + }) -ArgumentList:($($user.JCUsername), $($user.username), $($user.password), $pathToSM) + Write-Host "$(Get-Date -UFormat "%D %r") - Start parallel job to wait for task to be disabled" + + + Write-Host "Job Details:" + # Wait for the job to start a new process + Wait-Job -Job $waitJob -Timeout 200 | Out-Null + Receive-Job -job $waitJob -Keep + # wait for the job to check when the task is disabled + Wait-Job -Job $waitTaskJob -Timeout 200 | Out-Null + $disabledTaskData = Receive-Job -job $waitTaskJob -Keep + # finally wait for the start migration script job to finish + Wait-Job -Job $waitStartMigrationJob | Out-Null + $SMData = Receive-Job -Job $waitStartMigrationJob -Keep + # start migration should return $true if the job completes and fails as expected (should be true) + $SMData | should -be $true + # the task should have been disabled during the start migration script (should be $true) + $disabledTaskData | should -be $true + # the migration user should exist + "C:\Users\$($user.username)" | Should -Exist + # NewUserInit should be reverted and the new user profile path should not exist + "C:\Users\$($user.JCUsername)" | Should -Not -Exist + # the task should be re-enabled (Ready) if the start-migration script failed + $task = Get-ScheduledTask -TaskName "TestTaskFail" + $task.State | Should -Be "Ready" + } } + } It "Account of a prior migration can be sucessfully migrated again and not overwrite registry backup files" { $Password = "Temp123!" @@ -272,106 +427,110 @@ Describe 'Migration Test Scenarios' { $logonUI.SelectedUserSID | Should -not -Be $UserSID } } -} - -Context 'Start-Migration Fails to Bind JumpCloud User to System and throws error' { - It 'user bound to system after migration' { - Write-Host "`nBegin Test: Start-Migration Fails to Bind JumpCloud User to System and throws error" - $Password = "Temp123!" - $user1 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) - $user2 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) - InitUser -UserName $user1 -Password $Password - write-host "`nRunning: Start-Migration -JumpCloudUserName $($user2) -SelectedUserName $($user1) -TempPassword $($Password)`n" - { Start-Migration -JumpCloudAPIKey $env:JCApiKey -AutobindJCUser $true -JumpCloudUserName "$($user2)" -SelectedUserName "$ENV:COMPUTERNAME\$($user1)" -TempPassword "$($Password)" } | Should -Throw - } -} -Context 'Start-Migration kicked off through JumpCloud agent' { - BeforeAll { - # test connection to Org - $Org = Get-JcSdkOrganization - Write-Host "Connected to Pester Org: $($Org.DisplayName)" - # Get System Key - $config = get-content 'C:\Program Files\JumpCloud\Plugins\Contrib\jcagent.conf' - $regex = 'systemKey\":\"(\w+)\"' - $systemKey = [regex]::Match($config, $regex).Groups[1].Value - Write-Host "Running Tests on SystemID: $systemKey" - # Connect-JCOnline - - # variables for test - $CommandBody = ' - . "C:\Users\circleci.$env:COMPUTERNAME\project\jumpcloud-ADMU\Powershell\Start-Migration.ps1" - # Trim env vars with hardcoded "" - $JCU = ${ENV:$JcUserName}.Trim([char]0x0022) - $SU = ${ENV:$SelectedUserName}.Trim([char]0x0022) - $PW = ${ENV:$TempPassword}.Trim([char]0x0022) - Start-Migration -JumpCloudUserName $JCU -SelectedUserName $ENV:COMPUTERNAME\$SU -TempPassword $PW - ' - $CommandTrigger = 'ADMU' - $CommandName = 'RemoteADMU' - # clear command results - $results = Get-JcSdkCommandResult - foreach ($result in $results) { - # Delete Command Results - Write-Host "Found Command Results: $($result.id) removing..." - remove-jcsdkcommandresult -id $result.id - } - # Clear previous commands matching the name - $RemoteADMUCommands = Get-JcSdkCommand | Where-Object { $_.name -eq $CommandName } - foreach ($result in $RemoteADMUCommands) { - # Delete Command Results - Write-Host "Found existing Command: $($result.id) removing..." - Remove-JcSdkCommand -id $result.id + Context 'Start-Migration Fails to Bind JumpCloud User to System and throws error' { + It 'user bound to system after migration' { + Write-Host "`nBegin Test: Start-Migration Fails to Bind JumpCloud User to System and throws error" + $Password = "Temp123!" + $user1 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + $user2 = "ADMU_" + -join ((65..90) + (97..122) | Get-Random -Count 5 | ForEach-Object { [char]$_ }) + InitUser -UserName $user1 -Password $Password + write-host "`nRunning: Start-Migration -JumpCloudUserName $($user2) -SelectedUserName $($user1) -TempPassword $($Password)`n" + { Start-Migration -JumpCloudAPIKey $env:JCApiKey -AutobindJCUser $true -JumpCloudUserName "$($user2)" -SelectedUserName "$ENV:COMPUTERNAME\$($user1)" -TempPassword "$($Password)" } | Should -Throw } - - # Create command & association to command - New-JcSdkCommand -Command $CommandBody -CommandType "windows" -Name $CommandName -Trigger $CommandTrigger -Shell powershell - $CommandID = (Get-JcSdkCommand | Where-Object { $_.Name -eq $CommandName }).Id - Write-Host "Setting CommandID: $CommandID associations" - Set-JcSdkCommandAssociation -CommandId $CommandID -Id $systemKey -Op add -Type system - } - It 'Test that system key exists' { - $systemKey | Should -Not -BeNullOrEmpty } - It 'Invoke ADMU from JumpCloud Command' { - # clear results - $results = Get-JcSdkCommandResult - foreach ($result in $results) { - # Delete Command Results - remove-jcsdkcommandresult -id $result.id + Context 'Start-Migration kicked off through JumpCloud agent' { + BeforeAll { + # test connection to Org + $Org = Get-JcSdkOrganization + Write-Host "Connected to Pester Org: $($Org.DisplayName)" + # Get System Key + $config = get-content 'C:\Program Files\JumpCloud\Plugins\Contrib\jcagent.conf' + $regex = 'systemKey\":\"(\w+)\"' + $systemKey = [regex]::Match($config, $regex).Groups[1].Value + Write-Host "Running Tests on SystemID: $systemKey" + # Connect-JCOnline + + # variables for test + $CommandBody = ' + . "C:\Users\circleci.$env:COMPUTERNAME\project\jumpcloud-ADMU\Powershell\Start-Migration.ps1" + # Trim env vars with hardcoded "" + $JCU = ${ENV:$JcUserName}.Trim([char]0x0022) + $SU = ${ENV:$SelectedUserName}.Trim([char]0x0022) + $PW = ${ENV:$TempPassword}.Trim([char]0x0022) + Start-Migration -JumpCloudUserName $JCU -SelectedUserName $ENV:COMPUTERNAME\$SU -TempPassword $PW + ' + $CommandTrigger = 'ADMU' + $CommandName = 'RemoteADMU' + # clear command results + $results = Get-JcSdkCommandResult + foreach ($result in $results) { + # Delete Command Results + Write-Host "Found Command Results: $($result.id) removing..." + remove-jcsdkcommandresult -id $result.id + } + # Clear previous commands matching the name + $RemoteADMUCommands = Get-JcSdkCommand | Where-Object { $_.name -eq $CommandName } + foreach ($result in $RemoteADMUCommands) { + # Delete Command Results + Write-Host "Found existing Command: $($result.id) removing..." + Remove-JcSdkCommand -id $result.id + } + + # Create command & association to command + New-JcSdkCommand -Command $CommandBody -CommandType "windows" -Name $CommandName -Trigger $CommandTrigger -Shell powershell + $CommandID = (Get-JcSdkCommand | Where-Object { $_.Name -eq $CommandName }).Id + Write-Host "Setting CommandID: $CommandID associations" + Set-JcSdkCommandAssociation -CommandId $CommandID -Id $systemKey -Op add -Type system + } + It 'Test that system key exists' { + $systemKey | Should -Not -BeNullOrEmpty } - # begin tests - foreach ($user in $JCCommandTestingHash.Values) { - write-host "Running: Start-Migration -JumpCloudUserName $($user.JCUsername) -SelectedUserName $($user.username) -TempPassword $($user.password)" - $headers = @{ - 'Accept' = "application/json" - 'x-api-key' = $env:JCApiKey + It 'Invoke ADMU from JumpCloud Command' { + # clear results + $results = Get-JcSdkCommandResult + foreach ($result in $results) { + # Delete Command Results + remove-jcsdkcommandresult -id $result.id + } + # begin tests + foreach ($user in $JCCommandTestingHash.Values) { + write-host "Running: Start-Migration -JumpCloudUserName $($user.JCUsername) -SelectedUserName $($user.username) -TempPassword $($user.password)" + $headers = @{ + 'Accept' = "application/json" + 'x-api-key' = $env:JCApiKey + } + $Form = @{ + '$JcUserName' = $user.JCUsername; + '$SelectedUserName' = $user.Username; + '$TempPassword' = $user.Password + } | ConvertTo-Json + Invoke-RestMethod -Method POST -Uri "https://console.jumpcloud.com/api/command/trigger/$($CommandTrigger)" -ContentType 'application/json' -Headers $headers -Body $Form + Write-Host "Invoke Command ADMU:" + $count = 0 + do { + $invokeResults = Get-JcSdkCommandResult + Write-Host "Waiting 5 seconds for system to receive command..." + $count += 1 + start-sleep 5 + } until (($invokeResults) -or ($count -eq 48)) + Write-Host "Command pushed to system, waiting on results" + $count = 0 + do { + $CommandResults = Get-JCCommandResult -CommandResultID $invokeResults.Id + Write-host "Waiting 5 seconds on results..." + $count += 1 + start-sleep 5 + } until ((($CommandResults.exitCode) -is [int]) -or ($count -eq 48)) + $CommandResults.exitCode | Should -Be 0 } - $Form = @{ - '$JcUserName' = $user.JCUsername; - '$SelectedUserName' = $user.Username; - '$TempPassword' = $user.Password - } | ConvertTo-Json - Invoke-RestMethod -Method POST -Uri "https://console.jumpcloud.com/api/command/trigger/$($CommandTrigger)" -ContentType 'application/json' -Headers $headers -Body $Form - Write-Host "Invoke Command ADMU:" - $count = 0 - do { - $invokeResults = Get-JcSdkCommandResult - Write-Host "Waiting 5 seconds for system to receive command..." - $count += 1 - start-sleep 5 - } until (($invokeResults) -or ($count -eq 48)) - Write-Host "Command pushed to system, waiting on results" - $count = 0 - do { - $CommandResults = Get-JCCommandResult -CommandResultID $invokeResults.Id - Write-host "Waiting 5 seconds on results..." - $count += 1 - start-sleep 5 - } until ((($CommandResults.exitCode) -is [int]) -or ($count -eq 48)) - $CommandResults.exitCode | Should -Be 0 } } + AfterEach { + Write-Host "`nEnd Test: $testName" + Write-Host "---------------------------`n" + } } + AfterAll { $systems = Get-JCsdkSystem $CIsystems = $systems | Where-Object { $_.displayname -match "packer" } diff --git a/jumpcloud-ADMU/Powershell/Tests/PSScriptAnalyzerSettings.psd1 b/jumpcloud-ADMU/Powershell/Tests/PSScriptAnalyzerSettings.psd1 index de8660ee..42c474ea 100644 --- a/jumpcloud-ADMU/Powershell/Tests/PSScriptAnalyzerSettings.psd1 +++ b/jumpcloud-ADMU/Powershell/Tests/PSScriptAnalyzerSettings.psd1 @@ -11,7 +11,7 @@ # Only diagnostic records of the specified severity will be generated. # Uncomment the following line if you only want Errors and Warnings but # not Information diagnostic records. - Severity = @('Error','Warning') + Severity = @('Error', 'Warning') # Analyze **only** the following rules. Use IncludeRules when you want # to invoke only a small subset of the default rules. @@ -32,15 +32,16 @@ # Note: if a rule is in both IncludeRules and ExcludeRules, the rule # will be excluded. ExcludeRules = @('PSUseShouldProcessForStateChangingFunctions', - 'PSAvoidUsingWriteHost', - 'PSAvoidUsingConvertToSecureStringWithPlainText' - 'PSAvoidUsingPlainTextForPassword', - 'PSAvoidUsingUsernameAndPasswordParams', - 'PSAvoidUsingWMICmdlet', - 'PSAvoidUsingInvokeExpression', - 'PSUseDeclaredVarsMoreThanAssignments', - 'PSUseBOMForUnicodeEncodedFile' - ) + 'PSAvoidUsingWriteHost', + 'PSAvoidUsingConvertToSecureStringWithPlainText' + 'PSAvoidUsingPlainTextForPassword', + 'PSAvoidUsingUsernameAndPasswordParams', + 'PSAvoidUsingWMICmdlet', + 'PSAvoidUsingInvokeExpression', + 'PSUseDeclaredVarsMoreThanAssignments', + 'PSUseBOMForUnicodeEncodedFile', + 'PSAvoidGlobalVars' + ) # You can use rule configuration to configure rules that support it: #Rules = @{