diff --git a/.github/workflows/admu-ci.yml b/.github/workflows/admu-ci.yml index d73711bb5..6f90718af 100644 --- a/.github/workflows/admu-ci.yml +++ b/.github/workflows/admu-ci.yml @@ -57,7 +57,7 @@ jobs: run: | # validate release type variables $env:RELEASE_TYPE | Should -BeIn @('major','minor','patch','manual') - Setup-Build-Dependancies: + Setup-Build-Dependencies: needs: ["Filter-Branch", "Check-PR-Labels", "Validate-Env-Variables"] runs-on: windows-latest timeout-minutes: 10 @@ -68,7 +68,7 @@ jobs: uses: actions/cache@v4 with: path: 'C:\Users\runneradmin\Documents\PowerShell\Modules\' - key: PS-Dependancies + key: PS-Dependencies - name: Install dependencies if: steps.cacher.outputs.cache-hit != 'true' shell: pwsh @@ -79,7 +79,7 @@ jobs: Write-Host ('[status]Installing package provider NuGet'); Install-PackageProvider -Name:('NuGet') -Scope:('CurrentUser') -Force } - # define dependancies for this ci workflow: + # define dependencies for this ci workflow: $PSDependencies = @{ 'PowerShellGet' = @{Repository = 'PSGallery'; RequiredVersion = '3.0.12-beta' } 'ps2exe' = @{Repository = 'PSGallery'; RequiredVersion = '1.0.13' } @@ -106,7 +106,7 @@ jobs: } } Build-Module: - needs: ["Setup-Build-Dependancies", "Check-PR-Labels"] + needs: ["Setup-Build-Dependencies", "Check-PR-Labels"] runs-on: windows-latest timeout-minutes: 10 steps: @@ -114,7 +114,7 @@ jobs: - uses: actions/cache@v4 with: path: 'C:\Users\runneradmin\Documents\PowerShell\Modules\' - key: PS-Dependancies + key: PS-Dependencies - name: Build ADMU Module shell: powershell env: @@ -122,18 +122,20 @@ jobs: run: | . "${{ github.workspace }}/Deploy/build.ps1" -ModuleVersionType $env:RELEASE_TYPE -ModuleName "JumpCloud.ADMU" - name: Upload Nuspec - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: jumpcloud-admu-build path: | ${{ github.workspace }}/Jumpcloud-ADMU/JumpCloud.ADMU.nuspec ${{ github.workspace }}/Jumpcloud-ADMU/Docs/*.md - ${{ github.workspace }}/Jumpcloud-ADMU/Exe/*.exe - ${{ github.workspace }}/Jumpcloud-ADMU/Powershell/Form.ps1 + ${{ github.workspace }}/Jumpcloud-ADMU/en-Us/JumpCloud.ADMU-help.xml + ${{ github.workspace }}/Jumpcloud-ADMU/PowerShell/Private/**/*.ps1 + ${{ github.workspace }}/Jumpcloud-ADMU/PowerShell/Public/**/*.ps1 ${{ github.workspace }}/Jumpcloud-ADMU/JumpCloud.ADMU.psd1 + ${{ github.workspace }}/Jumpcloud-ADMU/JumpCloud.ADMU.psm1 retention-days: 1 Test-Module: - needs: ["Setup-Build-Dependancies", "Check-PR-Labels", "Build-Module"] + needs: ["Setup-Build-Dependencies", "Check-PR-Labels", "Build-Module"] runs-on: windows-latest timeout-minutes: 75 strategy: @@ -143,13 +145,13 @@ jobs: steps: - uses: actions/checkout@v4 - name: Download artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: jumpcloud-admu-build - uses: actions/cache@v4 with: path: 'C:\Users\runneradmin\Documents\PowerShell\Modules\' - key: PS-Dependancies + key: PS-Dependencies - name: Test PWSH Module shell: powershell env: diff --git a/.github/workflows/admu-release.yml b/.github/workflows/admu-release.yml index 55153bf4c..b069f2553 100644 --- a/.github/workflows/admu-release.yml +++ b/.github/workflows/admu-release.yml @@ -54,7 +54,7 @@ jobs: } env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - Setup-Build-Dependancies: + Setup-Build-Dependencies: needs: ["Filter-Branch", "Check-PR-Labels"] runs-on: windows-latest timeout-minutes: 10 @@ -65,7 +65,7 @@ jobs: uses: actions/cache@v4 with: path: 'C:\Users\runneradmin\Documents\PowerShell\Modules\' - key: PS-Dependancies + key: PS-Dependencies - name: Install dependencies if: steps.cacher.outputs.cache-hit != 'true' shell: pwsh @@ -76,7 +76,7 @@ jobs: Write-Host ('[status]Installing package provider NuGet'); Install-PackageProvider -Name:('NuGet') -Scope:('CurrentUser') -Force } - # define dependancies for this ci workflow: + # define dependencies for this ci workflow: $PSDependencies = @{ 'PowerShellGet' = @{Repository = 'PSGallery'; RequiredVersion = '3.0.12-beta' } 'ps2exe' = @{Repository = 'PSGallery'; RequiredVersion = '1.0.13' } @@ -104,14 +104,14 @@ jobs: } Build-Sign-ADMU: runs-on: windows-latest - needs: ["Setup-Build-Dependancies", "Check-PR-Labels"] + needs: ["Setup-Build-Dependencies", "Check-PR-Labels"] environment: Production steps: - uses: actions/checkout@v4 - uses: actions/cache@v4 with: path: "/home/runner/.local/share/powershell/Modules/" - key: PS-Dependancies + key: PS-Dependencies - name: Build ADMU Module shell: powershell env: @@ -121,13 +121,10 @@ jobs: - name: Pack nuspec shell: pwsh run: | - # set psm1 to point to public/start-migartion - $psm1Path = "${{ github.workspace }}/jumpcloud-ADMU/JumpCloud.ADMU.psm1" - $psm1Content = Get-Content -path $psm1Path - $psm1Content = $psm1Content -replace "\\Powershell\\", "\Public\" - Set-Content -Value $psm1Content -Path $psm1Path -Force # Pack - nuget pack "${{ github.workspace }}/jumpcloud-ADMU/JumpCloud.ADMU.nuspec" + # NU5111 - ignore unrecognized ps1 files not named install/ uninstall or init + # NU5110 - ignore files outside tools folder + nuget pack "${{ github.workspace }}/jumpcloud-ADMU/JumpCloud.ADMU.nuspec" -Properties NoWarn=NU5111,NU5110 - name: Validate NuPkg File shell: pwsh run: | @@ -169,7 +166,7 @@ jobs: region: ${{ secrets.AWS_REGION }} version: ${{ env.RELEASE_VERSION }} - name: Upload Release Artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: jumpcloud-admu path: | @@ -182,7 +179,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Download ADMU artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: jumpcloud-admu - name: Build Draft Release @@ -211,7 +208,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Download ADMU artifact - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: jumpcloud-admu - name: Publish diff --git a/.gitignore b/.gitignore index b770aa51a..bac0f0555 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ test_results JumpCloud.ADMU.nuspec **/.nupkg -Deploy/ADMU.ps1 \ No newline at end of file +Deploy/ADMU.ps1 +Deploy/admuTemplate.ps1 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index bd7c65722..1b2a43a5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { + "spellright.language": ["en"], "spellright.documentTypes": [ "markdown", @@ -7,8 +8,25 @@ "yaml", "powershell" ], + // powershell settings "powershell.scriptAnalysis.settingsPath": "jumpcloud-ADMU/Powershell/Tests/PSScriptAnalyzerSettings.psd1", "powershell.codeFormatting.preset": "OTBS", "powershell.scriptAnalysis.enable": true, - "files.trimTrailingWhitespace": true + // file formatting & saving: + "prettier.enable": true, + "editor.formatOnSave": true, + "files.trimTrailingWhitespace": true, + "search.useIgnoreFiles": false, + // cSpell: + "cSpell.words": [ + "ADMU", + ], + "cSpell.language": "en", + "cSpell.enabled": true, + "cSpell.enabledFiletypes": [ + "latex", + "markdown", + "plaintext", + "powershell" + ] } diff --git a/Deploy/Build-Exe.ps1 b/Deploy/Build-Exe.ps1 deleted file mode 100644 index d0e50be19..000000000 --- a/Deploy/Build-Exe.ps1 +++ /dev/null @@ -1,119 +0,0 @@ -Write-Host "======= Begin Build-Exe =======" - -If (-not $ADMUGetConfig) { - . $PSScriptRoot\Get-Config.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) -} -# ChangeLog Variables -$FilePath_ModuleChangelog = "$FolderPath_ModuleRootPath/ModuleChangelog.md" -$ModuleChangelog = Get-Content -Path:($FilePath_ModuleChangelog) -$ModuleChangelogVersionRegex = "([0-9]+)\.([0-9]+)\.([0-9]+)" -$ModuleChangelogVersionMatch = ($ModuleChangelog | Select-Object -First 1) | Select-String -Pattern:($ModuleChangelogVersionRegex) -$ModuleChangelogVersion = $ModuleChangelogVersionMatch.Matches.Value -# Form.ps1 Variables -$FormPath = $FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Form.ps1' -$VersionFormRegex = [regex]'(?<=Title="JumpCloud ADMU )([0-9]+)\.([0-9]+)\.([0-9]+)' -$VersionMatchForm = Select-String -Path:($FormPath) -Pattern:($VersionFormRegex) -$FormVersion = $VersionMatchForm.Matches.Value -# .psd1 Variables -$PSD1Path = $FolderPath_ModuleRootPath + '\jumpcloud-ADMU\JumpCloud.ADMU.psd1' -$VersionPsd1Regex = [regex]"(?<=ModuleVersion\s*=\s*')(([0-9]+)\.([0-9]+)\.([0-9]+))" -$VersionMatchPsd1 = Select-String -Path:($PSD1Path) -Pattern:($VersionPsd1Regex) -$PSD1Version = $VersionMatchPsd1.Matches.Value - -# ADMU.ps1 variables -$year = Get-Date -Format "yyyy" -$Output = $FolderPath_ModuleRootPath + '\Deploy\ADMU.ps1' - -# Write out diagnostic information -Write-Host "[JumpCloud ADMU Build Configuration]" -Write-Host "Form Version: $FormVersion" -Write-Host "Psd1 Version: $PSD1Version" - -# Validate Version -$FormVersion | Should -Be $PSD1Version - -# Build ADMU.PS1 File: -# Clear existing file -If (Test-Path -Path:($Output)) { - Remove-Item -Path:($Output) -} - -# Get file contents -$StartJCADMU = (Get-Content -Path:($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Start-JCADMU.ps1') -Raw) -Replace ("`r", "") -$Functions = (Get-Content -Path:($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Start-Migration.ps1') -Raw) -Replace ("`r", "") -$ProgressForm = (Get-Content -Path:($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\ProgressForm.ps1') -Raw) -Replace ("`r", "") -$Form = (Get-Content -Path:($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Form.ps1') -Raw) -Replace ("`r", "") - -# TODO: Add Private functions to $NewContent. This code is commented out for later use. -# Get file content of /jumpcloud-ADMU/Powershell/Private -#$PrivateFolder = Get-ChildItem -Path:($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Private\') -# String manipulation -# Iterate through each file in the Private folder and append to $Functions -# ForEach ($File in $PrivateFolder) { -# $PrivateFunctions += (Get-Content -Path:($File.FullName) -Raw) -Replace ("`r", "") -# } -#$NewContent = $PrivateFunctions -$NewContent = $StartJCADMU -# Add Private functions to $NewContent - -$NewContent = $NewContent.Replace('# Get script path' + "`n", '') -$NewContent = $NewContent.Replace('$scriptPath = (Split-Path -Path:($MyInvocation.MyCommand.Path))' + "`n", '') -$NewContent = $NewContent.Replace('. ($scriptPath + ''\Start-Migration.ps1'')', $Functions) -$NewContent = $NewContent.Replace('. ($scriptPath + ''\ProgressForm.ps1'')', $ProgressForm) -$NewContent = $NewContent.Replace('$formResults = Invoke-Expression -Command:(''. "'' + $scriptPath + ''\Form.ps1"'')' + "`n", $Form) -$NewContent = $NewContent -replace('Return \$FormResults', '') -$NewContent = $NewContent + "`n" -$NewContent = $NewContent -split "`n" | ForEach-Object { If ($_.Trim()) { - $_ - } } -Write-Host "[Status] Building new ADMU.ps1 file from Start-JCADMU, Form, and, Start-Migration scripts" -$NewContent | Out-File -FilePath:($Output) -If (-not [System.String]::IsNullOrEmpty($NewContent)) { - $NewContent | Out-File -FilePath:($Output) -} Else { - Write-Error ('Build-Exe.ps1 failed. Transform process outputted an empty ADMU.ps1 file.') -} -# Get PSVersion Table PS2EXE can only run in pwsh shell -$PSVersion = $PSVersionTable -If ($PSVersion.PSEdition -eq "Core") { - Write-Warning "Building ADMU exe binary files requires PowerShell Non-Core edition and a windows host" -} else { - If (-Not (Get-InstalledModule -Name ps2exe -ErrorAction Ignore)) { - Install-Module -Name ps2exe -RequiredVersion '1.0.13' -force - } - Import-Module -Name ps2exe - If (-not [System.String]::IsNullOrEmpty($PSD1Version)) { - $guiOutputPath = ($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\exe\gui_jcadmu.exe') - Invoke-ps2exe -inputFile $Output -outputFile $guiOutputPath -title 'JumpCloud ADMU' -product 'JumpCloud ADMU' -description 'JumpCloud AD Migration Utility' -copyright "(c) $year" -version $Psd1Version -company 'JumpCloud' -requireAdmin -iconfile ($FolderPath_ModuleRootPath + '\Deploy\admu.ico') - $guiExeFile = Get-Item $guiOutputPath - $guiHash = (get-filehash -algorithm SHA256 -path $guiExeFile).Hash - Write-Host "==== GUI_JCADMU.EXE Build Status ====" - Write-Host "Version: $($guiExeFile.VersionInfo.FileVersionRaw)" - Write-Host "Build Date: $($guiExeFile.CreationTime)" - Write-Host "Size (bytes): $($guiExeFile.Length)" - Write-Host "SHA256 Hash: $guiHash" - Write-Host "gui_jcadmu.exe was generated successfully" - } Else { - Write-Error ('Unable to find version number in "' + $PSD1Path + '" using regex "' + $VersionPsd1Regex + '"') - throw "gui_jcadmu.exe was not generated" - } - $uwpPath = $FolderPath_ModuleRootPath + '\Deploy\uwp_jcadmu.ps1' - # Always generate a new UWP EXE - try { - $uwpOutputPath = ($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\exe\uwp_jcadmu.exe') - Invoke-ps2exe -inputFile ($uwpPath) -outputFile $uwpOutputPath -title 'JumpCloud ADMU UWP Fix' -product 'JumpCloud ADMU' -description 'JumpCloud AD Migration Utility UWP Fix Executable' -copyright "(c) $year" -version $Psd1Version -company 'JumpCloud' -iconfile ($FolderPath_ModuleRootPath + '\Deploy\admu.ico') - $uwpExeFile = Get-Item $uwpOutputPath - $uwpHash = (get-filehash -algorithm SHA256 -path $uwpExeFile).Hash - Write-Host "==== UWP_JCADMU.EXE Build Status ====" - Write-Host "Version: $($uwpExeFile.VersionInfo.FileVersionRaw)" - Write-Host "Build Date: $($uwpExeFile.CreationTime)" - Write-Host "Size (bytes): $($uwpExeFile.Length)" - Write-Host "SHA256 Hash: $uwpHash" - Write-Host "upw_jcadmu.exe was generated successfully" - } catch { - Write-Error ('Unable to find version number in "' + $PSD1Path + '" using regex "' + $VersionPsd1Regex + '"') - Throw "upw_jcadmu.exe was not generated" - } -} -Write-Host "======= End Build-Exe =======" - diff --git a/Deploy/Build-Module.ps1 b/Deploy/Build-Module.ps1 index c4aeaa244..dbbdd75b8 100644 --- a/Deploy/Build-Module.ps1 +++ b/Deploy/Build-Module.ps1 @@ -22,10 +22,10 @@ Write-Host ('[status]Check PowerShell Gallery for module version info') if ($ManualModuleVersion) { $ManualModuleVersionRetrieval = Get-Content -Path:($FilePath_psd1) | Where-Object { $_ -like '*ModuleVersion*' } $SemanticRegex = [Regex]"[0-9]+.[0-9]+.[0-9]+" - $SemeanticVersion = Select-String -InputObject $ManualModuleVersionRetrieval -pattern ($SemanticRegex) - $ModuleVersion = $SemeanticVersion[0].Matches.Value + $semanticVersion = Select-String -InputObject $ManualModuleVersionRetrieval -pattern ($SemanticRegex) + $ModuleVersion = $semanticVersion[0].Matches.Value } else { - $PSGalleryInfo = Get-PSGalleryModuleVersion -Name:($ModuleName) -ReleaseType:($RELEASETYPE) #('Major', 'Minor', 'Patch') + $PSGalleryInfo = Get-PSGalleryModuleVersion -Name:($ModuleName) -ReleaseType:($ModuleVersionType) #('Major', 'Minor', 'Patch') $ModuleVersion = $PSGalleryInfo.NextVersion } Write-Host ('[status]PowerShell Gallery Name:' + $PSGalleryInfo.Name + ';CurrentVersion:' + $PSGalleryInfo.Version + '; NextVersion:' + $ModuleVersion ) @@ -48,7 +48,7 @@ New-ModuleManifest -Path:($FilePath_psd1) ` -FunctionsToExport:($Functions_Public.BaseName | Sort-Object) ` -RootModule:((Get-Item -Path:($FilePath_psm1)).Name) ` -ModuleVersion:($ModuleVersion) ` - -Author:('JumpCloud Solutions Architect Team') ` + -Author:('JumpCloud Customer Tools Team') ` -CompanyName:('JumpCloud') ` -Copyright:('(c) JumpCloud. All rights reserved.') ` -Description:('Powershell Module to run JumpCloud Active Directory Migration Utility.') @@ -62,7 +62,7 @@ If ($ModuleChangelogVersion -ne $PSD1Version) { ($NewModuleChangelogRecord + ($ModuleChangelog | Out-String)).Trim() | Set-Content -Path:($FilePath_ModuleChangelog) -Force } else { # Get content between latest version and last - $ModuleChangelogContent = Get-Content -Path:($FilePath_ModuleChangelog) | Select -First 3 + $ModuleChangelogContent = Get-Content -Path:($FilePath_ModuleChangelog) | Select-Object -First 3 $ReleaseDateRegex = [regex]'(?<=Release Date:\s)(.*)' $ReleaseDateRegexMatch = $ModuleChangelogContent | Select-String -Pattern $ReleaseDateRegex $ReleaseDate = $ReleaseDateRegexMatch.Matches.Value diff --git a/Deploy/Build.ps1 b/Deploy/Build.ps1 index 79ffc263a..6ca26d9ef 100644 --- a/Deploy/Build.ps1 +++ b/Deploy/Build.ps1 @@ -1,11 +1,18 @@ +## +# This script will: +# Run Build-Module (update changelog & validate versions across required files) +# Run Build-Exe (windows systems only) +# Build-NuspecFromPSD1 +## [CmdletBinding()] param ( - [Parameter()] + [Parameter(Mandatory = $true)] + [ValidateSet("Major", "Minor", "Patch", "Manual")] [System.string] $ModuleVersionType, [Parameter()] [System.string] - $ModuleName + $ModuleName = "JumpCloud.ADMU" ) # Run Get-Config: @@ -17,10 +24,13 @@ if ($ModuleVersionType -eq 'manual') { } else { . $PSScriptRoot\Build-Module.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) } - -# Run Build-Exe -. $PSScriptRoot\Build-Exe.ps1 +# Create a new ADMU Template file in this directory for testing/ Building EXE (default hide debug pwsh window) +New-ADMUTemplate -ExportPath "$PSScriptRoot/admuTemplate.ps1" -hidePowerShellWindow $true +# Run Build-Exe On Windows Systems +if ($IsWindows) { + . $PSScriptRoot\New-ADMUExe.ps1 +} # Run Build-HelpFiles . $PSScriptRoot\Build-HelpFiles.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) # Run Build-NuspecFromPsd1 -. $PSScriptRoot\BuildNuspecFromPsd1.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) +. $PSScriptRoot\BuildNuspecFromPsd1.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) \ No newline at end of file diff --git a/Deploy/BuildNuspecFromPsd1.ps1 b/Deploy/BuildNuspecFromPsd1.ps1 index 76ff506b3..922313426 100644 --- a/Deploy/BuildNuspecFromPsd1.ps1 +++ b/Deploy/BuildNuspecFromPsd1.ps1 @@ -20,7 +20,8 @@ If (-not $ADMUGetConfig) { $nuspecFiles = @( @{src = ".\en-Us\JumpCloud.ADMU-help.xml"; target = "./" }, - @{src = ".\Powershell\Start-Migration.ps1"; target = "./Public" }, + @{src = ".\Powershell\Private\**\*.ps1"; target = "./Powershell/Private" }, + @{src = ".\Powershell\Public\**\*.ps1"; target = "./Powershell/Public" }, @{src = ".\Docs\*.md"; target = "./Docs" }, @{src = ".\JumpCloud.ADMU.psd1" }, @{src = ".\JumpCloud.ADMU.psm1" } diff --git a/Deploy/Functions/New-ADMUTemplate.ps1 b/Deploy/Functions/New-ADMUTemplate.ps1 new file mode 100644 index 000000000..08c9e4a44 --- /dev/null +++ b/Deploy/Functions/New-ADMUTemplate.ps1 @@ -0,0 +1,129 @@ +<# +.SYNOPSIS +This function will compile all the functions in this module into a single file so it can be built into an executable EXE. + +.DESCRIPTION +This function combines all the the private functions, forms, their assets and the public functions into a single file. + +.PARAMETER hidePowerShellWindow +This parameter optionally adds the code snippet to run the forms with or without the debug window. The default behavior is to hide these windows but when debugging it can be helpful to show these windows. + +#> +Function New-ADMUTemplate { + + [CmdletBinding()] + param ( + [Parameter( + HelpMessage = "When specified, this parameter will add or remove the code to hide the debug powershell window. By default this is set to `$true which will hide the powershell window when the code is executed." + )] + [bool] + $hidePowerShellWindow = $true, + [Parameter( + HelpMessage = "The path to export the file template." + )] + [System.String] + $ExportPath = "$PSScriptRoot/admuTemplate.ps1" + ) + begin { + # define empty string to build the template file + $templateString = "" + # Public Functions + $Public = @( Get-ChildItem -Path "$PSScriptRoot/../../jumpcloud-ADMU/Powershell/Public/*.ps1" -Recurse) + # Load all functions from private folders except for the forms and assets + $Private = @( Get-ChildItem -Path "$PSScriptRoot/../../jumpcloud-ADMU/Powershell/Private/*.ps1" -Recurse | Where-Object { ($_.fullname -notmatch "DisplayForms") -AND ($_.fullname -notmatch "DisplayAssets") } ) + } + process { + #define Run As Admin block + $adminString = @" +# Validate the user is an administrator +if (([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")) -eq `$false) { + Write-Host 'ADMU must be ran as an administrator.' + Read-Host -Prompt "Press Enter to exit" + exit +} +"@ + # add admin required string to template + $templateString += "$($adminString)" + [Environment]::NewLine + + # Define string for private functions + $PrivateFunctionsContent = "" + # add every private function to the new string + foreach ($item in $Private) { + $functionContent = Get-Content $item.FullName -Raw + $PrivateFunctionsContent += "$($functionContent)" + [Environment]::NewLine + } + + # Set the private region: + $privateFunctionsRegion = @" +## Region Private Functions ## +$PrivateFunctionsContent +## End Region Private Functions ## +"@ + # add private functions region to template + $templateString += $privateFunctionsRegion + [Environment]::NewLine + + # Define string for forms + $formsContent = "" + # Add Form Assets to template: + $Assets = @( Get-ChildItem -Path "$PSScriptRoot/../../jumpcloud-ADMU/Powershell/Private/DisplayAssets/*.ps1" -Recurse ) + foreach ($item in $Assets) { + $AssetContent = Get-Content $item.FullName -Raw + $formsContent += "$($AssetContent)" + [Environment]::NewLine + } + $Forms = @( Get-ChildItem -Path "$PSScriptRoot/../../jumpcloud-ADMU/Powershell/Private/DisplayForms/*.ps1" -Recurse ) + # Optionally hide debug window: + if ($hidePowerShellWindow) { + $hideRegion = @" +# Hides Powershell Window +`$ShowWindowAsync = Add-Type -MemberDefinition `@" + [DllImport("user32.dll")] + public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); +`"@ -Name "Win32ShowWindowAsync" -Namespace "Win32Functions" -PassThru +# PID of the current process +# Get PID of the current process +`$FormWindowPIDHandle = (Get-Process -Id `$pid).MainWindowHandle +`$ShowWindowAsync::ShowWindowAsync(`$FormWindowPIDHandle, 0) | Out-Null +# PID +"@ + $formsContent += $hideRegion + [Environment]::NewLine + [Environment]::NewLine + } + # add each form file to the form string: + foreach ($item in $Forms) { + $FormContent = Get-Content $item.FullName -Raw + $formsContent += "$($FormContent)" + [Environment]::NewLine + } + + # define forms region: + $formsRegion = @" +## Region Forms ## +$formsContent +## End Region Forms ## +"@ + + # add the forms region to the template + $templateString += $formsRegion + [Environment]::NewLine + + # add each public function to the template: + foreach ($item in $Public) { + $functionContent = Get-Content $item.FullName -Raw + $templateString += "$($functionContent)" + [Environment]::NewLine + } + + # Define executable region + # endRegion + $executableRegion = @" +`$formResults = Show-SelectionForm +If (`$formResults) { + Start-Migration -inputObject:(`$formResults) +} Else { + Write-Output ('Exiting ADMU process') +} +"@ + # add executable region to the template + $templateString += $executableRegion + [Environment]::NewLine + } + end { + # write out the file + $templateString | Out-File $ExportPath -Force + } +} diff --git a/Deploy/Get-Config.ps1 b/Deploy/Get-Config.ps1 index 3b8ed1e51..e74803bba 100644 --- a/Deploy/Get-Config.ps1 +++ b/Deploy/Get-Config.ps1 @@ -11,8 +11,8 @@ Write-Host "======= Begin Get-Config =======" $env:ModuleVersionType = $ModuleVersionType $env:MODULENAME = $ModuleName # Populate variables -$ModuleFolderName = "$PSScriptroot/../JumpCloud-ADMU/" -$DEPLOYFOLDER = "$PSScriptroot" +$ModuleFolderName = "$PSScriptRoot/../JumpCloud-ADMU/" +$DEPLOYFOLDER = "$PSScriptRoot" $RELEASETYPE = $ModuleVersionType $GitHubWikiUrl = 'https://github.com/TheJumpCloud/jumpcloud-ADMU/wiki/' $ScriptRoot = Switch ($env:DEPLOYFOLDER) { @@ -89,7 +89,8 @@ If (!(Get-PackageProvider -Name:('NuGet') -ListAvailable -ErrorAction:('Silently } # Get module function names -$Functions_Public = @(Get-ChildItem -Path "$ModuleFolderName/Powershell/Start-Migration.ps1") +$Functions_Public = @(Get-ChildItem -Path "$ModuleFolderName/Powershell/Public/" -Recurse) +$Functions_Private = @(Get-ChildItem -Path "$ModuleFolderName/Powershell/Private/" -Recurse) # Import module in development Write-Host ('Importing module: ' + $FilePath_psd1) diff --git a/Deploy/New-AdmuExe.ps1 b/Deploy/New-AdmuExe.ps1 new file mode 100644 index 000000000..425ff07f0 --- /dev/null +++ b/Deploy/New-AdmuExe.ps1 @@ -0,0 +1,103 @@ +[CmdletBinding()] +param ( + [Parameter()] + [System.String] + $ModuleName = "JumpCloud.ADMU" +) +# This script will build a new JumpCloud EXE File +Write-Host "======= Begin Build-Exe =======" + +If (-not $ADMUGetConfig) { + . $PSScriptRoot\Get-Config.ps1 -ModuleVersionType:($ModuleVersionType) -ModuleName:($ModuleName) +} +# ChangeLog Variables +$FilePath_ModuleChangelog = "$FolderPath_ModuleRootPath/ModuleChangelog.md" +$ModuleChangelog = Get-Content -Path:($FilePath_ModuleChangelog) +$ModuleChangelogVersionRegex = "([0-9]+)\.([0-9]+)\.([0-9]+)" +$ModuleChangelogVersionMatch = ($ModuleChangelog | Select-Object -First 1) | Select-String -Pattern:($ModuleChangelogVersionRegex) +$ModuleChangelogVersion = $ModuleChangelogVersionMatch.Matches.Value +# Form.ps1 Variables +$FormPath = $FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Private\DisplayForms\Form.ps1' +$VersionFormRegex = [regex]'(?<=Title="JumpCloud ADMU )([0-9]+)\.([0-9]+)\.([0-9]+)' +$VersionMatchForm = Select-String -Path:($FormPath) -Pattern:($VersionFormRegex) +$FormVersion = $VersionMatchForm.Matches.Value +# ProgressForm.ps1 Variables +$progressFormPath = $FolderPath_ModuleRootPath + '\jumpcloud-ADMU\Powershell\Private\DisplayForms\ProgressForm.ps1' +$versionProgressFormRegex = [regex]'(?<=Title="JumpCloud ADMU )([0-9]+)\.([0-9]+)\.([0-9]+)' +$versionMatchProgressForm = Select-String -Path:($progressFormPath) -Pattern:($versionProgressFormRegex) +$progressFormVersion = $versionMatchProgressForm.Matches.Value +# .psd1 Variables +$PSD1Path = $FolderPath_ModuleRootPath + '\jumpcloud-ADMU\JumpCloud.ADMU.psd1' +$VersionPsd1Regex = [regex]"(?<=ModuleVersion\s*=\s*')(([0-9]+)\.([0-9]+)\.([0-9]+))" +$VersionMatchPsd1 = Select-String -Path:($PSD1Path) -Pattern:($VersionPsd1Regex) +$PSD1Version = $VersionMatchPsd1.Matches.Value + +# ADMU.ps1 variables +$year = Get-Date -Format "yyyy" + +# Write out diagnostic information +Write-Host "[JumpCloud ADMU Build Configuration]" +Write-Host "Form Version: $FormVersion" +Write-Host "ProgressForm Version: $progressFormVersion" +Write-Host "Psd1 Version: $PSD1Version" + +# Validate Versions +$FormVersion | Should -Be $PSD1Version +$progressFormVersion | Should -Be $PSD1Version + +# Check for a template file: +If (-Not (Test-Path -Path:("$PSScriptRoot/admuTemplate.ps1"))) { + "A Template file does not exist, creating template file" + # Build ADMU.PS1 File: + . $PSScriptRoot\New-ADMUTemplate.ps1 + New-ADMUTemplate -ExportPath "$PSScriptRoot/admuTemplate.ps1" +} +if (-Not (Test-Path -Path:("$PSScriptRoot/admuTemplate.ps1"))) { + throw "A template file does not exist, an EXE can not be built." +} + +# TODO: double check but 1.0.15 should be good to run on core versions of PWSH +# Get PSVersion Table PS2EXE can only run in pwsh shell +# $PSVersion = $PSVersionTable +# If ($PSVersion.PSEdition -eq "Core") { +# Write-Warning "Building ADMU exe binary files requires PowerShell Non-Core edition and a windows host" +# } else { +If (-Not (Get-InstalledModule -Name ps2exe -ErrorAction Ignore)) { + Install-Module -Name ps2exe -RequiredVersion '1.0.13' -force +} +Import-Module -Name ps2exe +If (-not [System.String]::IsNullOrEmpty($PSD1Version)) { + $guiOutputPath = ($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\exe\gui_jcadmu.exe') + Invoke-ps2exe -inputFile "$PSScriptRoot/admuTemplate.ps1" -outputFile $guiOutputPath -title 'JumpCloud ADMU' -product 'JumpCloud ADMU' -description 'JumpCloud AD Migration Utility' -copyright "(c) $year" -version $Psd1Version -company 'JumpCloud' -requireAdmin -iconFile ($FolderPath_ModuleRootPath + '\Deploy\admu.ico') + $guiExeFile = Get-Item $guiOutputPath + $guiHash = (Get-FileHash -algorithm SHA256 -path $guiExeFile).Hash + Write-Host "==== GUI_JCADMU.EXE Build Status ====" + Write-Host "Version: $($guiExeFile.VersionInfo.FileVersionRaw)" + Write-Host "Build Date: $($guiExeFile.CreationTime)" + Write-Host "Size (bytes): $($guiExeFile.Length)" + Write-Host "SHA256 Hash: $guiHash" + Write-Host "gui_jcadmu.exe was generated successfully" +} Else { + Write-Error ('Unable to find version number in "' + $PSD1Path + '" using regex "' + $VersionPsd1Regex + '"') + throw "gui_jcadmu.exe was not generated" +} +$uwpPath = $FolderPath_ModuleRootPath + '\Deploy\uwp_jcadmu.ps1' +# Always generate a new UWP EXE +try { + $uwpOutputPath = ($FolderPath_ModuleRootPath + '\jumpcloud-ADMU\exe\uwp_jcadmu.exe') + Invoke-ps2exe -inputFile ($uwpPath) -outputFile $uwpOutputPath -title 'JumpCloud ADMU UWP Fix' -product 'JumpCloud ADMU' -description 'JumpCloud AD Migration Utility UWP Fix Executable' -copyright "(c) $year" -version $Psd1Version -company 'JumpCloud' -iconFile ($FolderPath_ModuleRootPath + '\Deploy\admu.ico') + $uwpExeFile = Get-Item $uwpOutputPath + $uwpHash = (Get-FileHash -algorithm SHA256 -path $uwpExeFile).Hash + Write-Host "==== UWP_JCADMU.EXE Build Status ====" + Write-Host "Version: $($uwpExeFile.VersionInfo.FileVersionRaw)" + Write-Host "Build Date: $($uwpExeFile.CreationTime)" + Write-Host "Size (bytes): $($uwpExeFile.Length)" + Write-Host "SHA256 Hash: $uwpHash" + Write-Host "upw_jcadmu.exe was generated successfully" +} catch { + Write-Error ('Unable to find version number in "' + $PSD1Path + '" using regex "' + $VersionPsd1Regex + '"') + Throw "upw_jcadmu.exe was not generated" +} +# } +Write-Host "======= End Build-Exe =======" + diff --git a/Deploy/TestSetup.ps1 b/Deploy/TestSetup.ps1 index a2486fcaf..c5ab46f17 100644 --- a/Deploy/TestSetup.ps1 +++ b/Deploy/TestSetup.ps1 @@ -11,7 +11,22 @@ process { #} # Load functions - . $PSScriptRoot\..\jumpcloud-ADMU\Powershell\Start-Migration.ps1 + $Private = @( Get-ChildItem -Path "$PSScriptRoot/../jumpcloud-ADMU/Powershell/Private/*.ps1" -Recurse) + Foreach ($Import in $Private) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } + } + $Private = @( Get-ChildItem -Path "$PSScriptRoot/../jumpcloud-ADMU/Powershell/Public/*.ps1" -Recurse) + Foreach ($Import in $Private) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } + } #USMT & VC Variables $jcAdmuTempPath = 'C:\Windows\Temp\JCADMU\' diff --git a/ModuleChangelog.md b/ModuleChangelog.md index 7a6a25a6a..c77146a27 100644 --- a/ModuleChangelog.md +++ b/ModuleChangelog.md @@ -1,31 +1,57 @@ +## 2.7.11 + +Release Date: January 28, 2025 + +#### RELEASE NOTES + +``` +This release addresses some code quality issues and a bug-fix for certain Windows 11 systems where migrated users could lost access to use Windows search post-migration. +``` + +#### IMPROVEMENTS: + +- Functions in this release are now broken up into individual files to de-clutter the Start-Migration script + +#### BUG FIXES: + +- Windows 11 systems with a specific build and Microsoft KB installed could lose access to the Windows Start search menu post-migration, this release applies the same fix in 2.4.3 (At the time then only affected Windows 10 systems) to Windows 11 systems. + ## 2.7.10 Release Date: January 3, 2025 #### RELEASE NOTES + ``` * This release prevents ADMU from migrating if one of the main user folders (Desktop, Downloads, Documents, Pictures, Music, Videos, Favorites) are redirected to network shared path ``` + #### Bug Fixes: + ``` * Fix issue when migrating a user with one of their main user folders are redirected to a network path. ADMU will now throw an error and prevent migration if any of the primary user folders (Desktop, Downloads, Documents, Pictures, Music, Videos, Favorites) are redirected to network shared path ``` + ## 2.7.9 Release Date: November 21, 2024 #### RELEASE NOTES + ``` * This release removes 40 char API key validation * When the migration fails during the account copy/merge processes, the tool would revert and remove the newly created account. We risk deleting user data once we do the account reversal in this step. To combat this, we have added a tracker to not remove the created account profile during account merge failure. * Remove unused .exe files ``` + #### Bug Fixes: + ``` * Fix progress form buttons disabled when JCAgent install fails * Fix issue with JCUsername that have a localUsername where progress form GUI get's stuck during migration when AutoBind is selected * Fix issue with MTP selection popups when migrating a user that belongs to an MTP ``` + ## 2.7.8 Release Date: October 14, 2024 @@ -50,9 +76,11 @@ Release Date: September 25, 2024 This release resolves an issue on Windows 10 systems where users were unable to use the search bar post-migration #### Bug Fixes: + ``` * Resolves an issue on Windows 10 systems where users were unable to use the search bar post-migration ``` + ## 2.7.6 Release Date: August 21, 2024 @@ -62,9 +90,11 @@ Release Date: August 21, 2024 This fixes an issue with disabled Migrate Button #### Bug Fixes: + ``` * Fixed an issue with ADMU UI "Migrate Profile" button where it remained disabled even though all the required fields were satisfied. ``` + ## 2.7.5 Release Date: August 28, 2024 diff --git a/jumpcloud-ADMU/Docs/Start-Migration.md b/jumpcloud-ADMU/Docs/Start-Migration.md index 48bfdd94c..7cbb3fb39 100644 --- a/jumpcloud-ADMU/Docs/Start-Migration.md +++ b/jumpcloud-ADMU/Docs/Start-Migration.md @@ -8,13 +8,11 @@ schema: 2.0.0 # Start-Migration ## SYNOPSIS - Starts the JumpCloud Active Directory Migration process. ## SYNTAX ### cmd - ``` Start-Migration -JumpCloudUserName -SelectedUserName -TempPassword [-LeaveDomain ] [-ForceReboot ] [-UpdateHomePath ] [-InstallJCAgent ] @@ -24,38 +22,43 @@ Start-Migration -JumpCloudUserName -SelectedUserName -TempPass ``` ### form - ``` Start-Migration [-inputObject ] [-ProgressAction ] [] ``` ## DESCRIPTION - -The Start-Migration function allows the starting of the JumpCloud Active Directory Migration Process. This utility can be used to convert domain bound user accounts into locally managed accounts ready to be taken over by JumpCloud. There are various options to run the utility depending on the administrators requirements. +The Start-Migration function allows the starting of the JumpCloud Active Directory Migration Process. +This utility can be used to convert domain bound user accounts into locally managed accounts ready to be taken over by JumpCloud. +There are various options to run the utility depending on the administrators requirements. ## EXAMPLES ### Example 1 - -```powershell +``` PS C:\> Start-Migration -SelectedUserName 'DOMAIN\bobfay' -JumpCloudUserName 'bob.fay' -TempPassword 'Temp123!Temp123!' -LeaveDomain $true -ForceReboot $true -InstallJCAgent $true -JumpCloudConnectKey 'ConnectKEY' -AutobindJCUser $true -JumpCloudAPIKey 'APIKEY' ``` -This example would run the `Start-Migration` function on a domain user `DOMAIN\bobfay` and create a new local user account `COMPUTERNAME\bob.fay`. Using a temporary password `Temp123!Temp123!`. The JumpCloud Agent would be installed. After migration the JumpCloud user `bob.fay` would be bound to the system. Finally, the system would leave the bound domain and reboot. +This example would run the \`Start-Migration\` function on a domain user \`DOMAIN\bobfay\` and create a new local user account \`COMPUTERNAME\bob.fay\`. +Using a temporary password \`Temp123!Temp123!\`. +The JumpCloud Agent would be installed. +After migration the JumpCloud user \`bob.fay\` would be bound to the system. +Finally, the system would leave the bound domain and reboot. ### Example 2 - -```powershell +``` PS C:\> Start-Migration -SelectedUserName 'DOMAIN\bobfay' -JumpCloudUserName 'bob.fay' -TempPassword 'Temp123!Temp123!' $false -ForceReboot $false -InstallJCAgent $false ``` -This example would run the `Start-Migration` function on a domain user `DOMAIN\jsmith` and create a new local user account `COMPUTERNAME\john.smith`. Using a temporary password `Temp123!Temp123!`, the system would remain bound to the current domain and not reboot. The JumpCloud Agent would not be installed. This would allow the administrator to run the converted account in parallel for testing. +This example would run the \`Start-Migration\` function on a domain user \`DOMAIN\jsmith\` and create a new local user account \`COMPUTERNAME\john.smith\`. +Using a temporary password \`Temp123!Temp123!\`, the system would remain bound to the current domain and not reboot. +The JumpCloud Agent would not be installed. +This would allow the administrator to run the converted account in parallel for testing. ## PARAMETERS ### -ForceReboot - -A boolean $true/$false value to force the system to reboot at the end of the migration process. A reboot is required when unbinding from a domain. +A boolean $true/$false value to force the system to reboot at the end of the migration process. +A reboot is required when unbinding from a domain. ```yaml Type: System.Boolean @@ -70,8 +73,9 @@ Accept wildcard characters: False ``` ### -InstallJCAgent - -A boolean $true/$false value to install the JumpCloud agent on the system. If this value is $true you will be required to also pass a `JumpCloudConnectKey` value. If the system remains on the domain, the JumpCloud agent will be installed but not connected until it leaves the domain. +A boolean $true/$false value to install the JumpCloud agent on the system. +If this value is $true you will be required to also pass a \`JumpCloudConnectKey\` value. +If the system remains on the domain, the JumpCloud agent will be installed but not connected until it leaves the domain. ```yaml Type: System.Boolean @@ -86,8 +90,9 @@ Accept wildcard characters: False ``` ### -JumpCloudConnectKey - -A string value that is required if `-InstallJCAgent` is $true. This connect key can be found in the JumpCloud console under add systems. It must be 24 chars and is different than an JumpCloud API key. +A string value that is required if \`-InstallJCAgent\` is $true. +This connect key can be found in the JumpCloud console under add systems. +It must be 24 chars and is different than an JumpCloud API key. ```yaml Type: System.String @@ -102,8 +107,9 @@ Accept wildcard characters: False ``` ### -JumpCloudUserName - -A string value that will be used for the new converted local account that can be bound to JumpCloud. This value must be unique on the system, if it is not unique an error will stop the migration. This value should match the JumpCloud Username value to allow takeover when a User is bound to a system within the JumpCloud console. +A string value that will be used for the new converted local account that can be bound to JumpCloud. +This value must be unique on the system, if it is not unique an error will stop the migration. +This value should match the JumpCloud Username value to allow takeover when a User is bound to a system within the JumpCloud console. ```yaml Type: System.String @@ -118,8 +124,9 @@ Accept wildcard characters: False ``` ### -LeaveDomain - -A boolean $true/$false value to force the system to leave currently bound domain, this is required for the JumpCloud Agent to operate. It can also be reversed by simply rejoining the system back to the domain. This will also work for AzureAD and will disconnect the AzureAD bind. +A boolean $true/$false value to force the system to leave currently bound domain, this is required for the JumpCloud Agent to operate. +It can also be reversed by simply rejoining the system back to the domain. +This will also work for AzureAD and will disconnect the AzureAD bind. ```yaml Type: System.Boolean @@ -134,8 +141,9 @@ Accept wildcard characters: False ``` ### -TempPassword - -A string value that is used to set the new local accounts password. When duplicating the user account a password must be set when created and this value is passed. Once the system is online in JumpCloud the password will be overwritten and synced with JumpCloud if the user is taken over. +A string value that is used to set the new local accounts password. +When duplicating the user account a password must be set when created and this value is passed. +Once the system is online in JumpCloud the password will be overwritten and synced with JumpCloud if the user is taken over. ```yaml Type: System.String @@ -150,8 +158,8 @@ Accept wildcard characters: False ``` ### -inputObject - -An PSObject can be passed to the function with the required values for the migration process. This is used when the GUI version of the tool is used and inputs to the XAML form are passed to this function. +An PSObject can be passed to the function with the required values for the migration process. +This is used when the GUI version of the tool is used and inputs to the XAML form are passed to this function. ```yaml Type: System.Object @@ -166,8 +174,10 @@ Accept wildcard characters: False ``` ### -SelectedUserName - -A string value for the DomainUserName that is used in the migration script. This value is verified to make sure the account exists on the system. If the Domain Account does not exist, the script will error and not continue. Either pass a username using the "Domain\username" syntax or a domain user SID. +A string value for the DomainUserName that is used in the migration script. +This value is verified to make sure the account exists on the system. +If the Domain Account does not exist, the script will error and not continue. +Either pass a username using the "Domain\username" syntax or a domain user SID. ```yaml Type: System.String @@ -182,8 +192,7 @@ Accept wildcard characters: False ``` ### -AutobindJCUser - -This parameter will bind the username specified in the `JumpCloudUserName` field to the current system after Migration. +This parameter will bind the username specified in the \`JumpCloudUserName\` field to the current system after Migration. ```yaml Type: System.Boolean @@ -198,8 +207,9 @@ Accept wildcard characters: False ``` ### -UpdateHomePath - -If set to $true, the ADMU will attempt to rename the selected username's homepath to the jumpcloud username. Note, this could break any applications that rely on a hard coded homepath. By default this is not set and will not rename the homepath. +If set to $true, the ADMU will attempt to rename the selected username's homepath to the jumpcloud username. +Note, this could break any applications that rely on a hard coded homepath. +By default this is not set and will not rename the homepath. ```yaml Type: System.Boolean @@ -214,8 +224,8 @@ Accept wildcard characters: False ``` ### -JumpCloudAPIKey - -The Read/Write API key of a JumpCloud Administrator. This parameter is required if the AutoBind JC User parameter is specified. +The Read/Write API key of a JumpCloud Administrator. +This parameter is required if the AutoBind JC User parameter is specified. ```yaml Type: System.String @@ -230,8 +240,8 @@ Accept wildcard characters: False ``` ### -JumpCloudOrgID - -The ID of the JumpCloud Organization you wish to connect to. This field is only required if an MTP Api Key is used in the JumpCloudApiKey Parameter +The ID of the JumpCloud Organization you wish to connect to. +This field is only required if an MTP Api Key is used in the JumpCloudApiKey Parameter ```yaml Type: System.String @@ -246,8 +256,9 @@ Accept wildcard characters: False ``` ### -BindAsAdmin - -Option to bind user as sudo administrator or not. This parameter is not required and will default to $false (User will not be bound as admin). Set to $true if you'd like to bind the JumpCloudUserName as administrator during migration. +Option to bind user as sudo administrator or not. +This parameter is not required and will default to $false (User will not be bound as admin). +Set to $true if you'd like to bind the JumpCloudUserName as administrator during migration. ```yaml Type: System.Boolean @@ -262,8 +273,9 @@ Accept wildcard characters: False ``` ### -SetDefaultWindowsUser - -Option to set the windows default login user to the migrated user post-migration. This parameter is not required and will default to $true (the next login window user will be the migrated user). Set to $false if you'd like to disable this functionality during migration. +Option to set the windows default login user to the migrated user post-migration. +This parameter is not required and will default to $true (the next login window user will be the migrated user). +Set to $false if you'd like to disable this functionality during migration. ```yaml Type: System.Boolean @@ -278,8 +290,8 @@ Accept wildcard characters: False ``` ### -AdminDebug - -Option to display detailed messages during migration. This parameter is optional, but if set to $true, the CLI will show verbose output during the migration process +Option to display detailed messages during migration. +This parameter is optional, but if set to $true, the CLI will show verbose output during the migration process ```yaml Type: System.Boolean @@ -294,8 +306,10 @@ Accept wildcard characters: False ``` ### -ValidateUserShellFolder - -Option to bypass User Shell Folder validation. When set to `$false`, the migration will not verify whether folders (Desktop, Downloads, Documents, Videos, Pictures, Music, Favorites) are redirected to another location, such as a network shared folder (e.g., `\\192.168.50.78\SharedFolder\USERNAME\Desktop`). Use this parameter with caution. After migration, the user may encounter a shared folder error and will need to provide account credentials to restore their shared folders +Option to bypass User Shell Folder validation. +When set to \`$false\`, the migration will not verify whether folders (Desktop, Downloads, Documents, Videos, Pictures, Music, Favorites) are redirected to another location, such as a network shared folder (e.g., \`\192.168.50.78\SharedFolder\USERNAME\Desktop\`). +Use this parameter with caution. +After migration, the user may encounter a shared folder error and will need to provide account credentials to restore their shared folders ```yaml Type: System.Boolean @@ -309,20 +323,33 @@ Accept pipeline input: False Accept wildcard characters: False ``` -### CommonParameters +### -ProgressAction +{{ Fill ProgressAction Description }} + +```yaml +Type: System.Management.Automation.ActionPreference +Parameter Sets: (All) +Aliases: proga +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). ## INPUTS ### None - ## OUTPUTS ### System.Object - ## NOTES ## RELATED LINKS [https://github.com/TheJumpCloud/jumpcloud-ADMU/wiki/Start-Migration](https://github.com/TheJumpCloud/jumpcloud-ADMU/wiki/Start-Migration) + diff --git a/jumpcloud-ADMU/JumpCloud.ADMU.psd1 b/jumpcloud-ADMU/JumpCloud.ADMU.psd1 index 48ff51b4f..5d49d0e99 100644 --- a/jumpcloud-ADMU/JumpCloud.ADMU.psd1 +++ b/jumpcloud-ADMU/JumpCloud.ADMU.psd1 @@ -1,133 +1,132 @@ # # Module manifest for module 'JumpCloud.ADMU' # -# Generated by: JumpCloud Solutions Architect Team +# Generated by: JumpCloud Customer Tools Team # -# Generated on: 7/21/2024 +# Generated on: 1/28/2025 # @{ - # Script module or binary module file associated with this manifest. - RootModule = 'JumpCloud.ADMU.psm1' +# Script module or binary module file associated with this manifest. +RootModule = 'JumpCloud.ADMU.psm1' - # Version number of this module. +# Version number of this module. +ModuleVersion = '2.7.11' - ModuleVersion = '2.7.10' +# Supported PSEditions +# CompatiblePSEditions = @() - # Supported PSEditions - # CompatiblePSEditions = @() +# ID used to uniquely identify this module +GUID = '76f57e3f-dae6-4e0a-864a-9960b8bfb4e8' - # ID used to uniquely identify this module - GUID = 'dc1c3ff9-b5dd-4f05-9f90-3da45b2ea28b' +# Author of this module +Author = 'JumpCloud Customer Tools Team' - # Author of this module - Author = 'JumpCloud Solutions Architect Team' +# Company or vendor of this module +CompanyName = 'JumpCloud' - # Company or vendor of this module - CompanyName = 'JumpCloud' +# Copyright statement for this module +Copyright = '(c) JumpCloud. All rights reserved.' - # Copyright statement for this module - Copyright = '(c) JumpCloud. All rights reserved.' +# Description of the functionality provided by this module +Description = 'Powershell Module to run JumpCloud Active Directory Migration Utility.' - # Description of the functionality provided by this module - Description = 'Powershell Module to run JumpCloud Active Directory Migration Utility.' +# Minimum version of the PowerShell engine required by this module +# PowerShellVersion = '' - # Minimum version of the PowerShell engine required by this module - # PowerShellVersion = '' +# Name of the PowerShell host required by this module +# PowerShellHostName = '' - # Name of the PowerShell host required by this module - # PowerShellHostName = '' +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' - # Minimum version of the PowerShell host required by this module - # PowerShellHostVersion = '' +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' - # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # DotNetFrameworkVersion = '' +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' - # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. - # ClrVersion = '' +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' - # Processor architecture (None, X86, Amd64) required by this module - # ProcessorArchitecture = '' +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() - # Modules that must be imported into the global environment prior to importing this module - # RequiredModules = @() +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() - # Assemblies that must be loaded prior to importing this module - # RequiredAssemblies = @() +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() - # Script files (.ps1) that are run in the caller's environment prior to importing this module. - # ScriptsToProcess = @() +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() - # Type files (.ps1xml) to be loaded when importing this module - # TypesToProcess = @() +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() - # Format files (.ps1xml) to be loaded when importing this module - # FormatsToProcess = @() +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() - # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess - # NestedModules = @() +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Start-Migration' - # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. - FunctionsToExport = 'Start-Migration' +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +CmdletsToExport = '*' - # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. - CmdletsToExport = '*' +# Variables to export from this module +VariablesToExport = '*' - # Variables to export from this module - VariablesToExport = '*' +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = '*' - # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. - AliasesToExport = '*' +# DSC resources to export from this module +# DscResourcesToExport = @() - # DSC resources to export from this module - # DscResourcesToExport = @() +# List of all modules packaged with this module +# ModuleList = @() - # List of all modules packaged with this module - # ModuleList = @() +# List of all files packaged with this module +# FileList = @() - # List of all files packaged with this module - # FileList = @() +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ - # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. - PrivateData = @{ + PSData = @{ - PSData = @{ + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() - # Tags applied to this module. These help with module discovery in online galleries. - # Tags = @() + # A URL to the license for this module. + # LicenseUri = '' - # A URL to the license for this module. - # LicenseUri = '' + # A URL to the main website for this project. + # ProjectUri = '' - # A URL to the main website for this project. - # ProjectUri = '' + # A URL to an icon representing this module. + # IconUri = '' - # A URL to an icon representing this module. - # IconUri = '' + # ReleaseNotes of this module + # ReleaseNotes = '' - # ReleaseNotes of this module - # ReleaseNotes = '' + # Prerelease string of this module + # Prerelease = '' - # Prerelease string of this module - # Prerelease = '' + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false - # Flag to indicate whether the module requires explicit user acceptance for install/update/save - # RequireLicenseAcceptance = $false + # External dependent modules of this module + # ExternalModuleDependencies = @() - # External dependent modules of this module - # ExternalModuleDependencies = @() + } # End of PSData hashtable - } # End of PSData hashtable +} # End of PrivateData hashtable - } # End of PrivateData hashtable +# HelpInfo URI of this module +# HelpInfoURI = '' - # HelpInfo URI of this module - # HelpInfoURI = '' - - # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. - # DefaultCommandPrefix = '' +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' } diff --git a/jumpcloud-ADMU/JumpCloud.ADMU.psm1 b/jumpcloud-ADMU/JumpCloud.ADMU.psm1 index fce4dcbf5..d122dc46c 100644 --- a/jumpcloud-ADMU/JumpCloud.ADMU.psm1 +++ b/jumpcloud-ADMU/JumpCloud.ADMU.psm1 @@ -1,5 +1,23 @@ -$Public = @(Get-ChildItem -Path "$PSScriptRoot\Powershell\Start-Migration.ps1") +# Load all functions from private folders +$Private = @( Get-ChildItem -Path "$PSScriptRoot/Powershell/Private/*.ps1" -Recurse) +Foreach ($Import in $Private) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } +} -. "$PSScriptRoot\Powershell\Start-Migration.ps1" +# Load all public functions: +$Public = @( Get-ChildItem -Path "$PSScriptRoot/Powershell/Public/*.ps1" -Recurse) +Foreach ($Import in $Public) { + Try { + . $Import.FullName + } Catch { + Write-Error -Message "Failed to import function $($Import.FullName): $_" + } +} + +# . "$PSScriptRoot\Powershell\Start-Migration.ps1" Export-ModuleMember -Function $Public.BaseName diff --git a/jumpcloud-ADMU/Powershell/Form.ps1 b/jumpcloud-ADMU/Powershell/Form.ps1 deleted file mode 100644 index 213a24420..000000000 --- a/jumpcloud-ADMU/Powershell/Form.ps1 +++ /dev/null @@ -1,901 +0,0 @@ -# Hides Powershell Window -$ShowWindowAsync = Add-Type -MemberDefinition @" - [DllImport("user32.dll")] - public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); -"@ -Name "Win32ShowWindowAsync" -Namespace "Win32Functions" -PassThru -# PID of the current process -# Get PID of the current process -$FormWindowPIDHandle = (Get-Process -Id $pid).MainWindowHandle -$ShowWindowAsync::ShowWindowAsync($FormWindowPIDHandle, 0) | Out-Null -# PID -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Loading ADMU GUI..' -# Base64 Encoded Strings of our Images -$JCLogoBase64 = "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" -$ErrorBase64 = "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFiSURBVHgBpVPNSsNAEJ4ZzwWv/lKfoPUN2oOl3lYKelAoPpkIelBQ4smih8YnMI8QqlQbL0JbT2bH2di0MXFD2n6w7OzMzscwMx/CP3hpqhoBtcWsySlHTmYPEL1Q67vtB8dJ52Dy8dZUZQY8k1ODfPgEur7WcfwM0eueqiJRVzyrUAQMn6x1ffPR8aZEphLN9JwmKR0fQunkKLKHF1cwvLzOkBHqXVMZmbeGOSpJQnJMK4xJvUZLQdzQBWD6GQ2HSCvbpzAIZvbgw0pGsNImZKxAAYTjcU6UZV0Qqrbw9/usCs4lgjLlRcMgQTTKJQJD5EMB6NGXPShbTwz8ZIuHg0SzE43PQKSDE111YRkwqWiz+82Drk1f6/c30d3fb9lo/I3O7U7UbAQ+NesOc1ciEhHx/nJMsKxop+M3DiNAKDBFGZBr/sYkfypKotdQiggVMlRkIvHC+nJcDfp8q+O46Zwfa3qRu77hWMMAAAAASUVORK5CYII=" -$ActiveBase64 = "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGKSURBVHgBlZRPTsJQEMbfTBvccgTcKYFQlxJM2hvUEygnID2CJ6jKAZQTgCcoorK1Cf5hZ4/A1oQ3Y98j1FKLbWfTvnn9fp3ON68gcqL15NtsGhfIbLOAxibLITOECPJh0fUmWQ2kF0eB3zBreCcAbPFPsBCR/JbO0vGiP6Bm4FtYM4I4UxflYgVC9rfVwW8lxmsFSAIjks5HzwtRrYyDSpWko46Avq6oPRu6bPK4jIoFR0x0HotVH61kQ0oHCcktC5FA+jOIqb+zpxwGwE6eKA+yPPUi1U9AY5wR2CiArXQOiEfv3cGhuuZBVD9jhxo7mniN2WqkoGt1XfQGl4L5qgiSwNrz2y/e3Uws3SaKIMwcIgl+zOTriEbQfPatMhAdqI8O3edsadjxfOgWQlRFxBM92a2Xm6DofO2FxGYoc3Sz1xjPBYuVqBrMK2WGutUg5QqxdCrBNpC+0iYgFcqlNcqT7DDugUzj6XY+U/8lyHuuPfNdMtEFFp3tmYL4BQQwhbUcvZ1506zmB49h1CYDMPPcAAAAAElFTkSuQmCC" -$BlueBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAElSURBVHgBbVE7TsNAEH2z+dQ+wnKCmIY6nICCIEIXdymIYk4AnCBBEETpVBQWSjgBpEM0+Ag+ghGiQIl3GH/BcUaa1erNe292ZglFuAsLGzMGmwGItCCRZACGh1lvXtAoPYcLjYZ5AbEuDZg8wHRTMfEUtycXCax2kolCzI4dud3kYhcjf5J1GD1NwOyiHq+StqT1B/GhEnK3yjNzGLOPzdqpkhM+bJW7FBGBFMPEEZpNXW+qOgrZNoqwxEXj4SwUu0GNT/yZCIKttl5e7WyZJbUPEfB1BWx9PaebA63wfwbmEPF61cC7H+KgtyeEbBbT/oHisdz61ecYB/f9NyqBc/9K0EvUQ54VO7g7Xaa6Smn4qNFsHclH2cmAst4A7e8lpk45yy8GxWbP/ZW8WwAAAABJRU5ErkJggg==" - -[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") -[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms") -# Add-Type -AssemblyName System.Windows.Forms -Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing, System.Drawing -function DecodeBase64Image { - param ( - [Parameter(Mandatory = $true)] - [String]$ImageBase64 - ) - # Parameter help description - $ObjBitmapImage = New-Object System.Windows.Media.Imaging.BitmapImage #Provides a specialized BitmapSource that is optimized for loading images using Extensible Application Markup Language (XAML). - $ObjBitmapImage.BeginInit() #Signals the start of the BitmapImage initialization. - $ObjBitmapImage.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64) #Creates a stream whose backing store is memory. - $ObjBitmapImage.EndInit() #Signals the end of the BitmapImage initialization. - $ObjBitmapImage.Freeze() #Makes the current object unmodifiable and sets its IsFrozen property to true. - $ObjBitmapImage -} -function show-mtpSelection { - [OutputType([object[]])] - [CmdletBinding()] - param ( - [Parameter()] - [System.Object] - $Orgs - ) - begin { - $Prompt = 'Please Select A JumpCloud MTP:' - $Title = 'MTP Organization Selection' - # define a data table to store org names/ org ids - $datatable = New-Object system.Data.DataTable - #Define Columns - $col1 = New-Object system.Data.DataColumn "Value", ([string]) - $col2 = New-Object system.Data.DataColumn "Text", ([string]) - #add columns to datatable - $datatable.columns.add($col1) - $datatable.columns.add($col2) - # Define Buttons: - $okButton = [System.Windows.Forms.Button]@{ - Location = '290,12' - Size = '60,22' - Text = 'OK' - DialogResult = [System.Windows.Forms.DialogResult]::OK - } - $cancelButton = [System.Windows.Forms.Button]@{ - Location = '290,40' - Size = '60,22' - Text = 'Cancel' - DialogResult = [System.Windows.Forms.DialogResult]::Cancel - } - # label for the form - $label = [System.Windows.Forms.Label]@{ - AutoSize = $true - Location = '10,10' - Size = '240,20' - MaximumSize = '250,0' - Text = $Prompt - } - $dynamicLabel = [System.Windows.Forms.Label]@{ - AutoSize = $true - Location = '10,30' - Size = '240,20' - MaximumSize = '250,0' - Text = '' - } - foreach ($org in $orgs) { - #Create a row - $name = New-Variable -Name "row_$($org._id)" - $name = $datarow1 = $datatable.NewRow() - #Enter data in the row - $name.Text = "$($org.DisplayName)" - $name.Value = "$($org._id)" - #Add the row to the datatable - $datatable.Rows.Add($name) - } - #create a combobox - $comboBox = [System.Windows.Forms.ComboBox]@{ - Location = '10,90' - AutoSize = $true - MaximumSize = '500,0' - # MaximumSize = '335,0' - DropDownStyle = "DropDownList" - } - $SelectBox = [System.Windows.Forms.Form]@{ - Text = $Title - Size = '369,159' - # Size = '369,159' - StartPosition = 'CenterScreen' - AcceptButton = $okButton - CancelButton = $cancelButton - FormBorderStyle = 'FixedDialog' - MinimizeBox = $false - MaximizeBox = $false - } - } - process { - #clear combo before we bind it - $combobox.Items.Clear() - - #bind combobox to datatable - $combobox.ValueMember = "Value" - $combobox.DisplayMember = "Text" - $combobox.Datasource = $datatable - - #add combobox to form - $SelectBox.Controls.Add($combobox) - - #show form - $SelectBox.Controls.AddRange(@($okButton, $cancelButton, $label, $dynamicLabel)) - $SelectBox.Topmost = $true - $SelectBox.Add_Shown({ $comboBox.Select() }) - - } - end { - $combobox.Add_SelectedIndexChanged({ - #output the selected value and text - $dynamicLabel.Text = "OrgName: $($combobox.SelectedItem['Text'])" - $dynamicLabel.Refresh(); - # write-host $combobox.SelectedItem["Value"] $combobox.SelectedItem["Text"] - }) - $result = $SelectBox.ShowDialog() - if ($result -eq [System.Windows.Forms.DialogResult]::OK) { - # return id of the org we selected - return $combobox.SelectedItem["Value"], $combobox.SelectedItem["Text"] - } else { - return $null - } - } -} -# Set source here. Take note in the XAML as to where the variable name was taken. - -#============================================================================================== -# XAML Code - Imported from Visual Studio WPF Application -#============================================================================================== -[void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework') -[xml]$XAML = @' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'@ - -# Read XAML -$reader = (New-Object System.Xml.XmlNodeReader $xaml) -Try { - $Form = [Windows.Markup.XamlReader]::Load($reader) -} Catch { - Write-Error "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."; - Exit; -} -#=========================================================================== -# Store Form Objects In PowerShell -#=========================================================================== -$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { - New-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force -} -$JCLogoImg.Source = DecodeBase64Image -ImageBase64 $JCLogoBase64 -$img_ckey_info.Source = DecodeBase64Image -ImageBase64 $BlueBase64 -$img_ckey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 -$img_apikey_info.Source = DecodeBase64Image -ImageBase64 $BlueBase64 -$img_apikey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 -$img_localaccount_info.Source = DecodeBase64Image -ImageBase64 $BlueBase64 -$img_localaccount_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 -$img_localaccount_password_info.Source = DecodeBase64Image -ImageBase64 $BlueBase64 -$img_localaccount_password_valid.Source = DecodeBase64Image -ImageBase64 $ActiveBase64 -# Define misc static variables - -$WmiComputerSystem = Get-WmiObject -Class:('Win32_ComputerSystem') -Write-progress -Activity 'Jumpcloud ADMU' -Status 'Loading Jumpcloud ADMU. Please Wait.. Checking AzureAD Status..' -PercentComplete 25 -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Checking AzureAD Status..' -if ($WmiComputerSystem.PartOfDomain) { - $WmiComputerDomain = Get-WmiObject -Class:('Win32_ntDomain') - $securechannelstatus = Test-ComputerSecureChannel - - $nbtstat = nbtstat -n - foreach ($line in $nbtStat) { - if ($line -match '^\s*([^<\s]+)\s*<00>\s*GROUP') { - $NetBiosName = $matches[1] - } - } - - if ([System.String]::IsNullOrEmpty($WmiComputerDomain[0].DnsForestName) -and $securechannelstatus -eq $false) { - $DomainName = 'Fix Secure Channel' - } else { - $DomainName = [string]$WmiComputerDomain.DnsForestName - } - $NetBiosName = [string]$NetBiosName -} elseif ($WmiComputerSystem.PartOfDomain -eq $false) { - $DomainName = 'N/A' - $NetBiosName = 'N/A' - $securechannelstatus = 'N/A' -} -if ((Get-CimInstance Win32_OperatingSystem).Version -match '10') { - $AzureADInfo = dsregcmd.exe /status - foreach ($line in $AzureADInfo) { - if ($line -match "AzureADJoined : ") { - $AzureADStatus = ($line.trimstart('AzureADJoined : ')) - } - if ($line -match "WorkplaceJoined : ") { - $Workplace_join = ($line.trimstart('WorkplaceJoined : ')) - } - if ($line -match "TenantName : ") { - $TenantName = ($line.trimstart('WorkplaceTenantName : ')) - } - if ($line -match "DomainJoined : ") { - $AzureDomainStatus = ($line.trimstart('DomainJoined : ')) - } - } -} else { - $AzureADStatus = 'N/A' - $Workplace_join = 'N/A' - $TenantName = 'N/A' -} - -$FormResults = [PSCustomObject]@{ } -Write-Progress -Activity 'Jumpcloud ADMU' -Status 'Loading Jumpcloud ADMU. Please Wait.. Verifying Local Accounts & Group Membership..' -PercentComplete 50 -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Verifying Local Accounts & Group Membership..' -Write-Progress -Activity 'Jumpcloud ADMU' -Status 'Loading Jumpcloud ADMU. Please Wait.. Getting C:\ & Local Profile Data..' -PercentComplete 70 -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Getting C:\ & Local Profile Data..' -# Get Valid SIDs from the Registry and build user object -$registyProfiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" -$profileList = @() -foreach ($profile in $registyProfiles) { - $profileList += Get-ItemProperty -Path $profile.PSPath | Select-Object PSChildName, ProfileImagePath -} -# List to store users -$users = @() -foreach ($listItem in $profileList) { - $sidPattern = "^S-\d-\d+-(\d+-){1,14}\d+$" - $isValidFormat = [regex]::IsMatch($($listItem.PSChildName), $sidPattern); - # Get Valid SIDs - if ($isValidFormat) { - # Populate Users List - $users += [PSCustomObject]@{ - Name = Convert-Sid $listItem.PSChildName - LocalPath = $listItem.ProfileImagePath - SID = $listItem.PSChildName - IsLocalAdmin = $null - LocalProfileSize = $null - Loaded = $null - RoamingConfigured = $null - LastLogin = $null - } - } -} -# Get Win32 Profiles to merge data with valid SIDs -$win32UserProfiles = Get-WmiObject -Class:('Win32_UserProfile') -Property * | Where-Object { $_.Special -eq $false } -$date_format = "yyyy-MM-dd HH:mm" -foreach ($user in $users) { - # Get Data from Win32Profile - foreach ($win32user in $win32UserProfiles) { - if ($($user.SID) -eq $($win32user.SID)) { - $user.RoamingConfigured = $win32user.RoamingConfigured - $user.Loaded = $win32user.Loaded - if ([string]::IsNullOrEmpty($($win32user.LastUseTime))) { - $user.LastLogin = "N/A" - } else { - $user.LastLogin = [System.Management.ManagementDateTimeConverter]::ToDateTime($($win32user.LastUseTime)).ToUniversalTime().ToSTring($date_format) - } - } - } - # Get Admin Status - try { - $admin = Get-LocalGroupMember -Member "$($user.SID)" -Name "Administrators" -EA SilentlyContinue - } catch { - $user = Get-LocalGroupMember -Member "$($user.SID)" -Name "Users" - } - if ($admin) { - $user.IsLocalAdmin = $true - } else { - $user.IsLocalAdmin = $false - } - # Get Profile Size - # $largeprofile = Get-ChildItem $($user.LocalPath) -Recurse -Force -ErrorAction SilentlyContinue | Measure-Object -Sum length | Select-Object -ExpandProperty Sum - # $largeprofile = [math]::Round($largeprofile / 1MB, 0) - # $user.LocalProfileSize = $largeprofile -} - -Write-Progress -Activity 'Jumpcloud ADMU' -Status 'Loading Jumpcloud ADMU. Please Wait.. Building Profile Group Box Query..' -PercentComplete 85 -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Building Profile Group Box Query..' - -$Profiles = $users | Select-Object SID, RoamingConfigured, Loaded, IsLocalAdmin, LocalPath, LocalProfileSize, LastLogin, @{Name = "UserName"; EXPRESSION = { $_.Name } } -Write-Progress -Activity 'Jumpcloud ADMU' -Status 'Loading Jumpcloud ADMU. Please Wait.. Done!' -PercentComplete 100 -Write-ToLog 'Loading Jumpcloud ADMU. Please Wait.. Done!' - -#load UI Labels - -#SystemInformation -$lbComputerName.Content = $WmiComputerSystem.Name - -#DomainInformation -$lbDomainName.Content = $DomainName -$lbNetBios.Content = $NetBiosName - -#AzureADInformation -$lbAzureAD_Joined.Content = $AzureADStatus -$lbTenantName.Content = $TenantName -Function Test-Button([object]$tbJumpCloudUserName, [object]$tbJumpCloudConnectKey, [object]$tbTempPassword, [object]$lvProfileList, [object]$tbJumpCloudAPIKey) { - If (![System.String]::IsNullOrEmpty($lvProfileList.SelectedItem.UserName)) { - If (!(Test-IsNotEmpty $tbJumpCloudUserName.Text) -and (Test-HasNoSpace $tbJumpCloudUserName.Text) -and (($($tbJumpCloudUserName.Text).length) -le 20) ` - -and ((Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password) -and (Test-HasNoSpace $tbJumpCloudConnectKey.Password) -and ($cb_installjcagent.IsChecked -eq $true))` - -and (!(Test-IsNotEmpty $tbJumpCloudAPIKey.Password) -and ($cb_autobindjcuser.IsChecked -eq $true))` - -and ((Test-CharLen -len 24 -testString $Env:selectedOrgID) -and (Test-HasNoSpace $Env:selectedOrgID) -and ($cb_autobindjcuser.IsChecked -eq $true))` - -and !(Test-IsNotEmpty $tbTempPassword.Text) -and (Test-HasNoSpace $tbTempPassword.Text)` - -and !(($($lvProfileList.selectedItem.Username) -split '\\')[0] -match $WmiComputerSystem.Name)` - -and !(Test-Localusername $tbJumpCloudUserName.Text)) { - $script:bMigrateProfile.Content = "Migrate Profile" - $script:bMigrateProfile.IsEnabled = $true - Return $true - } ElseIf (!(Test-IsNotEmpty $tbJumpCloudUserName.Text) -and (Test-HasNoSpace $tbJumpCloudUserName.Text) -and (($($tbJumpCloudUserName.Text).length) -le 20) ` - -and ((Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password) -and (Test-HasNoSpace $tbJumpCloudConnectKey.Password) -and ($cb_installjcagent.IsChecked -eq $true) -and ($cb_autobindjcuser.IsChecked -eq $false))` - -and !(Test-IsNotEmpty $tbTempPassword.Text) -and (Test-HasNoSpace $tbTempPassword.Text)` - -and !(Test-Localusername $tbJumpCloudUserName.Text)) { - $script:bMigrateProfile.Content = "Migrate Profile" - $script:bMigrateProfile.IsEnabled = $true - Return $true - } ElseIf (!(Test-IsNotEmpty $tbJumpCloudUserName.Text) -and (Test-HasNoSpace $tbJumpCloudUserName.Text) -and (($($tbJumpCloudUserName.Text).length) -le 20) ` - -and (!(Test-IsNotEmpty $tbJumpCloudAPIKey.Password) -and ($cb_autobindjcuser.IsChecked -eq $true) -and ($cb_installjcagent.IsChecked -eq $false))` - -and ((Test-CharLen -len 24 -testString $Env:selectedOrgID) -and (Test-HasNoSpace $Env:selectedOrgID) -and ($cb_autobindjcuser.IsChecked -eq $true) -and ($cb_installjcagent.IsChecked -eq $false))` - -and !(Test-IsNotEmpty $tbTempPassword.Text) -and (Test-HasNoSpace $tbTempPassword.Text)` - -and !(Test-Localusername $tbJumpCloudUserName.Text)) { - $script:bMigrateProfile.Content = "Migrate Profile" - $script:bMigrateProfile.IsEnabled = $true - Return $true - } Elseif (!(Test-IsNotEmpty $tbJumpCloudUserName.Text) -and (Test-HasNoSpace $tbJumpCloudUserName.Text) -and (($($tbJumpCloudUserName.Text).length) -le 20)` - -and ($cb_installjcagent.IsChecked -eq $false) -and ($cb_autobindjcuser.IsChecked -eq $false)` - -and !(Test-IsNotEmpty $tbTempPassword.Text) -and (Test-HasNoSpace $tbTempPassword.Text)` - -and !($lvProfileList.selectedItem.Username -match $WmiComputerSystem.Name)` - -and !(Test-Localusername $tbJumpCloudUserName.Text)) { - $script:bMigrateProfile.Content = "Migrate Profile" - $script:bMigrateProfile.IsEnabled = $true - Return $true - } Elseif ($lvProfileList.selectedItem.Username -eq 'UNKNOWN ACCOUNT') { - # Unmatched Profile, prevent migration - $script:bMigrateProfile.IsEnabled = $false - Return $false - } elseif (($($lvProfileList.selectedItem.Username) -split '\\')[0] -match $WmiComputerSystem.Name) { - $script:bMigrateProfile.IsEnabled = $false - Return $false - } Else { - $script:bMigrateProfile.Content = "Migrate Profile" - $script:bMigrateProfile.IsEnabled = $false - Return $false - } - } Else { - $script:bMigrateProfile.IsEnabled = $false - Return $false - } -} - -## Form changes & interactions - -# Install JCAgent checkbox -$script:InstallJCAgent = $false -$cb_installjcagent.Add_Checked( { Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) }) -$cb_installjcagent.Add_Checked( { $script:InstallJCAgent = $true }) -$cb_installjcagent.Add_Checked( { $tbJumpCloudConnectKey.IsEnabled = $true }) -$cb_installjcagent.Add_Checked( { $img_ckey_info.Visibility = 'Visible' }) -$cb_installjcagent.Add_Checked( { $img_ckey_valid.Visibility = 'Visible' }) -$cb_installjcagent.Add_Checked( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If (((Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password) -and (Test-HasNoSpace $tbJumpCloudConnectKey.Password)) -eq $false) { - #$tbJumpCloudConnectKey.Tooltip = "Connect Key Must be 40chars & Not Contain Spaces" - $tbJumpCloudConnectKey.Background = "#FFC6CBCF" - $tbJumpCloudConnectKey.BorderBrush = "#FFF90000" - } Else { - $tbJumpCloudConnectKey.Background = "white" - $tbJumpCloudConnectKey.Tooltip = $null - $tbJumpCloudConnectKey.FontWeight = "Normal" - $tbJumpCloudConnectKey.BorderBrush = "#FFC6CBCF" - } - - }) - -$cb_installjcagent.Add_UnChecked( { Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) }) -$cb_installjcagent.Add_Unchecked( { $script:InstallJCAgent = $false }) -$cb_installjcagent.Add_Unchecked( { $tbJumpCloudConnectKey.IsEnabled = $false }) -$cb_installjcagent.Add_Unchecked( { $img_ckey_info.Visibility = 'Hidden' }) -$cb_installjcagent.Add_Unchecked( { $img_ckey_valid.Visibility = 'Hidden' }) -$cb_installjcagent.Add_Unchecked( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If (((Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password) -and (Test-HasNoSpace $tbJumpCloudConnectKey.Password) -or ($cb_installjcagent.IsEnabled)) -eq $false) { - #$tbJumpCloudConnectKey.Tooltip = "Connect Key Must be 40chars & Not Contain Spaces" - $tbJumpCloudConnectKey.Background = "#FFC6CBCF" - $tbJumpCloudConnectKey.BorderBrush = "#FFF90000" - } Else { - $tbJumpCloudConnectKey.Background = "white" - $tbJumpCloudConnectKey.Tooltip = $null - $tbJumpCloudConnectKey.FontWeight = "Normal" - $tbJumpCloudConnectKey.BorderBrush = "#FFC6CBCF" - } - }) - - -# Autobind JC User checkbox -$script:AutobindJCUser = $false -$cb_autobindjcuser.Add_Checked( { Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) }) -$cb_autobindjcuser.Add_Checked( { $script:AutobindJCUser = $true }) -$cb_autobindjcuser.Add_Checked( { $tbJumpCloudAPIKey.IsEnabled = $true }) -$cb_autobindjcuser.Add_Checked( { $img_apikey_info.Visibility = 'Visible' }) -$cb_autobindjcuser.Add_Checked( { $img_apikey_valid.Visibility = 'Visible' }) -$cb_autobindjcuser.Add_Checked( { $cb_bindAsAdmin.IsEnabled = $true }) -$cb_bindAsAdmin.Add_Checked( { $script:BindAsAdmin = $true }) -$cb_autobindjcuser.Add_Checked( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If (Test-IsNotEmpty $tbJumpCloudAPIKey.Password ) { - #$tbJumpCloudAPIKey.Tooltip = "API Key Must be 40chars & Not Contain Spaces" - $tbJumpCloudAPIKey.Background = "#FFC6CBCF" - $tbJumpCloudAPIKey.BorderBrush = "#FFF90000" - } Else { - $tbJumpCloudAPIKey.Background = "white" - $tbJumpCloudAPIKey.Tooltip = $null - $tbJumpCloudAPIKey.FontWeight = "Normal" - $tbJumpCloudAPIKey.BorderBrush = "#FFC6CBCF" - } - }) - - -$cb_autobindjcuser.Add_UnChecked( { Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) }) -$cb_autobindjcuser.Add_Unchecked( { $script:AutobindJCUser = $false }) -$cb_autobindjcuser.Add_Unchecked( { $tbJumpCloudAPIKey.IsEnabled = $false }) -$cb_autobindjcuser.Add_Unchecked( { $img_apikey_info.Visibility = 'Hidden' }) -$cb_autobindjcuser.Add_Unchecked( { $img_apikey_valid.Visibility = 'Hidden' }) -$cb_autobindjcuser.Add_Unchecked( { $cb_bindAsAdmin.IsEnabled = $false }) -$cb_autobindjcuser.Add_Unchecked( { $cb_bindAsAdmin.IsChecked = $false }) -$cb_bindAsAdmin.Add_Unchecked( { $script:BindAsAdmin = $false }) -$cb_autobindjcuser.Add_Unchecked( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If ((!(Test-IsNotEmpty $tbJumpCloudAPIKey.Password) -or ($cb_autobindjcuser.IsEnabled)) -eq $false) { - #$tbJumpCloudAPIKey.Tooltip = "API Key Must be 40chars & Not Contain Spaces" - $tbJumpCloudAPIKey.Background = "#FFC6CBCF" - $tbJumpCloudAPIKey.BorderBrush = "#FFF90000" - } Else { - $tbJumpCloudAPIKey.Background = "white" - $tbJumpCloudAPIKey.Tooltip = $null - $tbJumpCloudAPIKey.FontWeight = "Normal" - $tbJumpCloudAPIKey.BorderBrush = "#FFC6CBCF" - } - }) - - -# Leave Domain checkbox -if (($AzureADStatus -eq 'Yes') -or ($AzureDomainStatus -eq 'Yes')) { - $script:cb_leavedomain.IsEnabled = $true -} else { - Write-ToLog "Device is not AzureAD Joined or Domain Joined. Leave Domain Checkbox Disabled." - $script:cb_leavedomain.IsEnabled = $false -} -$script:LeaveDomain = $false -$cb_leavedomain.Add_Checked( { $script:LeaveDomain = $true }) -$cb_leavedomain.Add_Unchecked( { $script:LeaveDomain = $false }) - -# Force Reboot checkbox -$script:ForceReboot = $false -$cb_forcereboot.Add_Checked( { $script:ForceReboot = $true }) -$cb_forcereboot.Add_Unchecked( { $script:ForceReboot = $false }) - -$hostname = $env:computername -$tbJumpCloudUserName.add_TextChanged( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If ((Test-IsNotEmpty $tbJumpCloudUserName.Text) -or (!(Test-HasNoSpace $tbJumpCloudUserName.Text)) -or (Test-Localusername $tbJumpCloudUserName.Text) -or (($tbJumpCloudUserName.Text).Length -gt 20) -or $tbJumpCloudUserName.Text -eq $hostname) { - $tbJumpCloudUserName.Background = "#FFC6CBCF" - $tbJumpCloudUserName.BorderBrush = "#FFF90000" - $img_localaccount_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - $img_localaccount_valid.ToolTip = "Local account username can't be empty, contain spaces, already exist on the local system or match the local computer name. Username must only be 20 characters long" - } Else { - $tbJumpCloudUserName.Background = "white" - $tbJumpCloudUserName.FontWeight = "Normal" - $tbJumpCloudUserName.BorderBrush = "#FFC6CBCF" - $img_localaccount_valid.Source = DecodeBase64Image -ImageBase64 $ActiveBase64 - $img_localaccount_valid.ToolTip = $null - } - if ($tbJumpCloudUserName.Text -eq $hostname) { - Write-ToLog "JumpCloud Username can not be the same as the hostname" - $script:bMigrateProfile.IsEnabled = $false - $img_localaccount_valid.ToolTip = "JumpCloud Username can not be the same as the hostname. Please change the username." - } - }) - -$tbJumpCloudConnectKey.Add_PasswordChanged( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If (((Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password) -and (Test-HasNoSpace $tbJumpCloudConnectKey.Password)) -eq $false) { - $tbJumpCloudConnectKey.Background = "#FFC6CBCF" - $tbJumpCloudConnectKey.BorderBrush = "#FFF90000" - $img_ckey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - $img_ckey_valid.ToolTip = "Connect Key must be 40chars & not contain spaces." - } Else { - $tbJumpCloudConnectKey.Background = "white" - $tbJumpCloudConnectKey.FontWeight = "Normal" - $tbJumpCloudConnectKey.BorderBrush = "#FFC6CBCF" - $img_ckey_valid.Source = DecodeBase64Image -ImageBase64 $ActiveBase64 - $img_ckey_valid.ToolTip = $null - } - }) - -$tbJumpCloudAPIKey.Add_PasswordChanged( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If (Test-IsNotEmpty $tbJumpCloudAPIKey.Password) { - $tbJumpCloudAPIKey.Background = "#FFC6CBCF" - $tbJumpCloudAPIKey.BorderBrush = "#FFF90000" - $img_apikey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - $img_apikey_valid.ToolTip = "Please enter a valid JumpCloud API Key" - - } Else { - # Get org name/ id - try { - $OrgSelection = Get-mtpOrganization -ApiKey $tbJumpCloudAPIKey.Password -inputType #-errorAction silentlycontinue - $lbl_orgName.Text = "$($OrgSelection[1])" - $Env:selectedOrgID = "$($OrgSelection[0])" - $tbJumpCloudAPIKey.Background = "white" - $tbJumpCloudAPIKey.Tooltip = $null - $tbJumpCloudAPIKey.FontWeight = "Normal" - $tbJumpCloudAPIKey.BorderBrush = "#FFC6CBCF" - $img_apikey_valid.Source = DecodeBase64Image -ImageBase64 $ActiveBase64 - $img_apikey_valid.ToolTip = $null - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - } catch { - $script:bMigrateProfile.IsEnabled = $false - $img_apikey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - $img_apikey_valid.ToolTip = "Please enter a valid JumpCloud API Key" - $OrgSelection = "" - $lbl_orgName.Text = "" - $img_apikey_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - Write-ToLog "MTP KEY MAY BE WRONG" - } - } - }) -$tbTempPassword.add_TextChanged( { - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - If ((!(Test-IsNotEmpty $tbTempPassword.Text) -and (Test-HasNoSpace $tbTempPassword.Text)) -eq $false) { - $tbTempPassword.Background = "#FFC6CBCF" - $tbTempPassword.BorderBrush = "#FFF90000" - $img_localaccount_password_valid.Source = DecodeBase64Image -ImageBase64 $ErrorBase64 - $img_localaccount_password_valid.ToolTip = "Local Account Temp Password should not be empty or contain spaces, it should also meet local password policy req. on the system." - } Else { - $tbTempPassword.Background = "white" - $tbTempPassword.Tooltip = $null - $tbTempPassword.FontWeight = "Normal" - $tbTempPassword.BorderBrush = "#FFC6CBCF" - $img_localaccount_password_valid.Source = DecodeBase64Image -ImageBase64 $ActiveBase64 - $img_localaccount_password_valid.ToolTip = $null - } - }) - -# Change button when profile selected -$lvProfileList.Add_SelectionChanged( { - $script:SelectedUserName = ($lvProfileList.SelectedItem.username) - New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS - Test-Button -tbJumpCloudUserName:($tbJumpCloudUserName) -tbJumpCloudConnectKey:($tbJumpCloudConnectKey) -tbTempPassword:($tbTempPassword) -lvProfileList:($lvProfileList) -tbJumpCloudAPIKey:($tbJumpCloudAPIKey) - try { - $SelectedUserSID = ((New-Object System.Security.Principal.NTAccount($script:SelectedUserName)).Translate( [System.Security.Principal.SecurityIdentifier]).Value) - } catch { - $SelectedUserSID = $script:SelectedUserName - } - $hku = ('HKU:\' + $SelectedUserSID) - if (Test-Path -Path $hku) { - $script:bMigrateProfile.IsEnabled = $false - $script:tbJumpCloudUserName.IsEnabled = $false - $script:tbTempPassword.IsEnabled = $false - } else { - $script:tbJumpCloudUserName.IsEnabled = $true - $script:tbTempPassword.IsEnabled = $true - } - }) - -$bMigrateProfile.Add_Click( { - if ($tbJumpCloudAPIKey.Password -And $tbJumpCloudUserName.Text -And $AutobindJCUser) { - # If text field is default/ not 40 chars - if (!(Test-CharLen -len 40 -testString $tbJumpCloudConnectKey.Password)) { - # Validate the the JumpCLoud Agent Conf File exists: - $keyResult = Test-JumpCloudSystemKey -WindowsDrive $(Get-WindowsDrive) - if (!$keyResult) { - # If we catch here, the system conf file does not exist. User is prompted to enter connect key; log below - Write-ToLog "The JumpCloud agent has not be registered on this system, to please specify a valid Connect Key to continue." - return - } - } else { - Write-ToLog "ConnectKey is populated, JumpCloud agent will be installed" - } - - $testResult, $JumpCloudUserId, $JCSystemUsername = Test-JumpCloudUsername -JumpCloudApiKey $tbJumpCloudAPIKey.Password -JumpCloudOrgID $Env:selectedOrgID -Username $tbJumpCloudUserName.Text -Prompt $true - if ($testResult) { - Write-ToLog "Matched $($tbJumpCloudUserName.Text) with user in the JumpCloud Console" - } else { - Write-ToLog "$($tbJumpCloudUserName.Text) not found in the JumpCloud console" - return - } - if ( -not [string]::isnullorempty($JCSystemUsername) ) { - # Regex to get the username from the domain\username string and compare it to JCSystemUsername - #Get all the local users - $registyProfiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" - $profileList = @() - foreach ($profile in $registyProfiles) { - $profileList += Get-ItemProperty -Path $profile.PSPath | Select-Object PSChildName, ProfileImagePath, @{ Name = "username"; Expression = { $sysUsername = Convert-Sid -sid $_.PSChildName; $sysUsername.Split('\')[1] } } - } - # If the JumpCloud found username was identified to exist locally, throw message - if ($JCSystemUsername -in $profileList.username) { - # Create a pop up that warns user then press ok to continue - Write-ToLog "JCSystemUsername $($JCSystemUsername) is the same as the another profile on this system" - $wshell = New-Object -ComObject Wscript.Shell - $message = "The JumpCloud User: $($tbJumpCloudUserName.Text) has a local account username of: $($jcsystemUserName). A local account already exists on this system with username: $($JCSystemUsername), please consider removing either the local account on this system or removing the local user account field from the JumpCloud user." - $var = $wshell.Popup("$message", 0, "JumpCloud SystemUsername and Local Computer Username Validation", 0) - # the user can not continue with migration at this stage - return - } - $wshell = New-Object -ComObject Wscript.Shell - $message = "The JumpCloud User: $($tbJumpCloudUserName.Text) has a local account username of: $($jcsystemUserName). After migration $($SelectedUserName) would be migrated and accessible with the local account username of: $($jcsystemUserName) Would you like to continue?" - $var = $wshell.Popup("$message", 0, "JumpCloud Local User Validation", 64 + 4) - # If user selects yes then migrate the local user profile to the JumpCloud User - - if ($var -eq 6) { - Write-ToLog -Message "User selected 'Yes', continuing with migration of $($SelectedUserName) to $($jcsystemUserName)" - } else { - Write-ToLog -Message "User selected 'No', returning to form" - return - } - } else { - Write-ToLog "User $($tbJumpCloudUserName.Text) does not have a local account on this system" - } - } - # Build FormResults object - Write-ToLog "Building Form Results" - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('InstallJCAgent') -Value:($InstallJCAgent) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('AutobindJCUser') -Value:($AutobindJCUser) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('BindAsAdmin') -Value:($BindAsAdmin) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('LeaveDomain') -Value:($LeaveDomain) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('ForceReboot') -Value:($ForceReboot) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('SelectedUserName') -Value:($SelectedUserName) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('JumpCloudUserName') -Value:($tbJumpCloudUserName.Text) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('TempPassword') -Value:($tbTempPassword.Text) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('JumpCloudConnectKey') -Value:($tbJumpCloudConnectKey.Password) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('JumpCloudAPIKey') -Value:($tbJumpCloudAPIKey.Password) - Add-Member -InputObject:($FormResults) -MemberType:('NoteProperty') -Name:('JumpCloudOrgID') -Value:($Env:selectedOrgID) - # Close form - $Form.Close() - }) - -$tbJumpCloudUserName.add_GotFocus( { - $tbJumpCloudUserName.Text = "" - }) - -$tbJumpCloudConnectKey.add_GotFocus( { - $tbJumpCloudConnectKey.Password = "" - }) - -$tbJumpCloudAPIKey.add_GotFocus( { - $tbJumpCloudAPIKey.Password = "" - }) - -# lbl_connectkey link - Mouse button event -$lbl_connectkey.Add_PreviewMouseDown( { [System.Diagnostics.Process]::start('https://console.jumpcloud.com/#/systems/new') }) - -# lbl_apikey link - Mouse button event -$lbl_apikey.Add_PreviewMouseDown( { [System.Diagnostics.Process]::start('https://support.jumpcloud.com/support/s/article/jumpcloud-apis1') }) - -# move window -$Form.Add_MouseLeftButtonDown( { - $Form.DragMove() - }) -$Form.Add_Closing({ - # exit and close form - $FormResults = $null - Return $FormResults - }) -# Put the list of profiles in the profile box -$Profiles | ForEach-Object { $lvProfileList.Items.Add($_) | Out-Null } -#=========================================================================== -# Shows the form & allow move -#=========================================================================== - -$Form.Showdialog() - -If ($bMigrateProfile.IsEnabled -eq $true) { - Return $FormResults -} \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/InvokePester.ps1 b/jumpcloud-ADMU/Powershell/InvokePester.ps1 index fdf9b8348..8a33fb1ef 100644 --- a/jumpcloud-ADMU/Powershell/InvokePester.ps1 +++ b/jumpcloud-ADMU/Powershell/InvokePester.ps1 @@ -5,9 +5,6 @@ param ( $ModuleVersionType ) $env:ModuleVersionType = $ModuleVersionType -# Load functions -. "$PSScriptRoot/Start-Migration.ps1" - # Import pester module $PesterInstalledVersion = Get-InstalledModule -Name Pester @@ -25,6 +22,7 @@ If ($env:CI) { $jobMatrixSet = @{ 0 = @{ 'filePath' = @( + "$PSScriptRoot/Tests/SelectionForm.Tests.ps1", "$PSScriptRoot/Tests/Functions.Tests.ps1", "$PSScriptRoot/Tests/Migration.Tests.ps1" ) diff --git a/jumpcloud-ADMU/Powershell/Private/Add-NativeMethod.ps1 b/jumpcloud-ADMU/Powershell/Private/Add-NativeMethod.ps1 new file mode 100644 index 000000000..4c780f488 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/Add-NativeMethod.ps1 @@ -0,0 +1,22 @@ +function Add-NativeMethod { + [CmdletBinding()] + [Alias()] + [OutputType([int])] + Param($typeName = 'NativeMethods') + + process { + $nativeMethodsCode = $script:nativeMethods | ForEach-Object { " + [DllImport(`"$($_.Dll)`")] + public static extern $($_.Signature); + " } + + Add-Type @" + using System; + using System.Text; + using System.Runtime.InteropServices; + public static class $typeName { + $nativeMethodsCode + } +"@ + } +} diff --git a/jumpcloud-ADMU/Powershell/Private/Backup-RegistryHive.ps1 b/jumpcloud-ADMU/Powershell/Private/Backup-RegistryHive.ps1 new file mode 100644 index 000000000..bb123c14d --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/Backup-RegistryHive.ps1 @@ -0,0 +1,41 @@ +Function Backup-RegistryHive { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [System.String] + $profileImagePath, + # Parameter help description + [Parameter(Mandatory = $true)] + [System.String] + $SID + ) + begin { + # get sid from PIP: + $domainUsername = Convert-SecurityIdentifier -Sid $SID + } + process { + try { + Copy-Item -Path "$profileImagePath\NTUSER.DAT" -Destination "$profileImagePath\NTUSER.DAT.BAK" -ErrorAction Stop + Copy-Item -Path "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat" -Destination "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" -ErrorAction Stop + } catch { + $processList = Get-ProcessByOwner -username $domainUsername + if ($processList) { + Show-ProcessListResult -ProcessList $processList -domainUsername $domainUsername + # $CloseResults = Close-ProcessByOwner -ProcessList $processList -force $ADMU_closeProcess + } + try { + Write-ToLog -Message("Initial backup was not successful, trying again...") + Write-ToLog $CloseResults + Start-Sleep 1 + # retry: + Copy-Item -Path "$profileImagePath\NTUSER.DAT" -Destination "$profileImagePath\NTUSER.DAT.BAK" -ErrorAction Stop + Copy-Item -Path "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat" -Destination "$profileImagePath\AppData\Local\Microsoft\Windows\UsrClass.dat.bak" -ErrorAction Stop + } catch { + Write-ToLog -Message("Could Not Backup Registry Hives in $($profileImagePath): Exiting...") + Write-AdmuErrorMessage -Error:("backup_error") + Write-ToLog -Message($_.Exception.Message) + throw "Could Not Backup Registry Hives in $($profileImagePath): Exiting..." + } + } + } +} \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/Private/Close-ProcessByOwner.ps1 b/jumpcloud-ADMU/Powershell/Private/Close-ProcessByOwner.ps1 new file mode 100644 index 000000000..80cab1c62 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/Close-ProcessByOwner.ps1 @@ -0,0 +1,57 @@ +function Close-ProcessByOwner { + [CmdletBinding()] + param ( + # Parameter help description + [Parameter(Mandatory = $true)] + [System.Object] + $ProcessList, + # force close processes + [Parameter()] + [bool] + $force + ) + + begin { + $resultList = New-Object System.Collections.ArrayList + } + + process { + switch ($force) { + $true { + foreach ($item in $ProcessList) { + Write-ToLog "Attempting to close processID: $($item.ProcessId)" + $tkStatus = taskkill /t /f /PID $item.ProcessId 2>&1 + $tkSuccess = if ($tkStatus -match "ERROR") { + $false + } else { + $true + } + $resultList.Add( + [PSCustomObject]@{ + ProcessName = $item.ProcessName + ProcessID = $item.ProcessId + Closed = $tkSuccess + } + ) | Out-Null + } + } + $false { + foreach ($item in $ProcessList) { + $resultList.Add( + [PSCustomObject]@{ + ProcessName = $item.ProcessName + ProcessID = $item.ProcessId + Closed = "NA" + } + ) | Out-Null + } + } + } + + # TODO: wait 1 -5 sec to ensure NTUser is closed + Start-Sleep 1 + } + end { + return $resultList + } +} diff --git a/jumpcloud-ADMU/Powershell/Private/Convert-SecurityIdentifier.ps1 b/jumpcloud-ADMU/Powershell/Private/Convert-SecurityIdentifier.ps1 new file mode 100644 index 000000000..75b51a980 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/Convert-SecurityIdentifier.ps1 @@ -0,0 +1,15 @@ +function Convert-SecurityIdentifier { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + $Sid + ) + process { + try { + (New-Object System.Security.Principal.SecurityIdentifier($Sid)).Translate( [System.Security.Principal.NTAccount]).Value + } catch { + return $Sid + } + } +} \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/Private/Convert-UserName.ps1 b/jumpcloud-ADMU/Powershell/Private/Convert-UserName.ps1 new file mode 100644 index 000000000..0feeb9688 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/Convert-UserName.ps1 @@ -0,0 +1,15 @@ +function Convert-UserName { + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + $user + ) + process { + try { + (New-Object System.Security.Principal.NTAccount($user)).Translate( [System.Security.Principal.SecurityIdentifier]).Value + } catch { + return $user + } + } +} \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/Private/DisplayAssets/Images.ps1 b/jumpcloud-ADMU/Powershell/Private/DisplayAssets/Images.ps1 new file mode 100644 index 000000000..df9282519 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/DisplayAssets/Images.ps1 @@ -0,0 +1,4 @@ +$JCLogoBase64 = "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" +$ErrorBase64 = "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAFiSURBVHgBpVPNSsNAEJ4ZzwWv/lKfoPUN2oOl3lYKelAoPpkIelBQ4smih8YnMI8QqlQbL0JbT2bH2di0MXFD2n6w7OzMzscwMx/CP3hpqhoBtcWsySlHTmYPEL1Q67vtB8dJ52Dy8dZUZQY8k1ODfPgEur7WcfwM0eueqiJRVzyrUAQMn6x1ffPR8aZEphLN9JwmKR0fQunkKLKHF1cwvLzOkBHqXVMZmbeGOSpJQnJMK4xJvUZLQdzQBWD6GQ2HSCvbpzAIZvbgw0pGsNImZKxAAYTjcU6UZV0Qqrbw9/usCs4lgjLlRcMgQTTKJQJD5EMB6NGXPShbTwz8ZIuHg0SzE43PQKSDE111YRkwqWiz+82Drk1f6/c30d3fb9lo/I3O7U7UbAQ+NesOc1ciEhHx/nJMsKxop+M3DiNAKDBFGZBr/sYkfypKotdQiggVMlRkIvHC+nJcDfp8q+O46Zwfa3qRu77hWMMAAAAASUVORK5CYII=" +$ActiveBase64 = "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGKSURBVHgBlZRPTsJQEMbfTBvccgTcKYFQlxJM2hvUEygnID2CJ6jKAZQTgCcoorK1Cf5hZ4/A1oQ3Y98j1FKLbWfTvnn9fp3ON68gcqL15NtsGhfIbLOAxibLITOECPJh0fUmWQ2kF0eB3zBreCcAbPFPsBCR/JbO0vGiP6Bm4FtYM4I4UxflYgVC9rfVwW8lxmsFSAIjks5HzwtRrYyDSpWko46Avq6oPRu6bPK4jIoFR0x0HotVH61kQ0oHCcktC5FA+jOIqb+zpxwGwE6eKA+yPPUi1U9AY5wR2CiArXQOiEfv3cGhuuZBVD9jhxo7mniN2WqkoGt1XfQGl4L5qgiSwNrz2y/e3Uws3SaKIMwcIgl+zOTriEbQfPatMhAdqI8O3edsadjxfOgWQlRFxBM92a2Xm6DofO2FxGYoc3Sz1xjPBYuVqBrMK2WGutUg5QqxdCrBNpC+0iYgFcqlNcqT7DDugUzj6XY+U/8lyHuuPfNdMtEFFp3tmYL4BQQwhbUcvZ1506zmB49h1CYDMPPcAAAAAElFTkSuQmCC" +$BlueBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAElSURBVHgBbVE7TsNAEH2z+dQ+wnKCmIY6nICCIEIXdymIYk4AnCBBEETpVBQWSjgBpEM0+Ag+ghGiQIl3GH/BcUaa1erNe292ZglFuAsLGzMGmwGItCCRZACGh1lvXtAoPYcLjYZ5AbEuDZg8wHRTMfEUtycXCax2kolCzI4dud3kYhcjf5J1GD1NwOyiHq+StqT1B/GhEnK3yjNzGLOPzdqpkhM+bJW7FBGBFMPEEZpNXW+qOgrZNoqwxEXj4SwUu0GNT/yZCIKttl5e7WyZJbUPEfB1BWx9PaebA63wfwbmEPF61cC7H+KgtyeEbBbT/oHisdz61ecYB/f9NyqBc/9K0EvUQ54VO7g7Xaa6Smn4qNFsHclH2cmAst4A7e8lpk45yy8GxWbP/ZW8WwAAAABJRU5ErkJggg==" \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/Private/DisplayForms/Form.ps1 b/jumpcloud-ADMU/Powershell/Private/DisplayForms/Form.ps1 new file mode 100644 index 000000000..6823302f9 --- /dev/null +++ b/jumpcloud-ADMU/Powershell/Private/DisplayForms/Form.ps1 @@ -0,0 +1,783 @@ +Function Show-SelectionForm { + + # Set source here. Take note in the XAML as to where the variable name was taken. + + #============================================================================================== + # XAML Code - Imported from Visual Studio WPF Application + #============================================================================================== + $types = @( + 'PresentationFramework', + 'PresentationCore', + 'System.Windows.Forms', + 'System.Drawing', + 'WindowsBase' + ) + foreach ($type in $types) { + if (-not ([System.Management.Automation.PSTypeName]$type).Type) { + [void][System.Reflection.Assembly]::LoadWithPartialName($type) + Add-Type -AssemblyName $type + } + } + # [void][System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework') + # [void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") + # [void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") + # # Add-Type -AssemblyName System.Windows.Forms + # Add-Type -AssemblyName PresentationFramework, PresentationCore, WindowsBase, System.Windows.Forms, System.Drawing + [xml]$XAML = @' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +'@ + + # Read XAML + $reader = (New-Object System.Xml.XmlNodeReader $xaml) + Try { + $Form = [Windows.Markup.XamlReader]::Load($reader) + } Catch { + Write-Error "Unable to load Windows.Markup.XamlReader. Some possible causes for this problem include: .NET Framework is missing PowerShell must be launched with PowerShell -sta, invalid XAML code was encountered."; + Exit; + } + #=========================================================================== + # Store Form Objects In PowerShell + #=========================================================================== + $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") | ForEach-Object { + New-Variable -Name $_.Name -Value $Form.FindName($_.Name) -Force + } + $JCLogoImg.Source = Get-ImageFromB64 -ImageBase64 $JCLogoBase64 + $img_connectKeyInfo.Source = Get-ImageFromB64 -ImageBase64 $BlueBase64 + $img_connectKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_apiKeyInfo.Source = Get-ImageFromB64 -ImageBase64 $BlueBase64 + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_localAccountInfo.Source = Get-ImageFromB64 -ImageBase64 $BlueBase64 + $img_localAccountValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_localAccountPasswordInfo.Source = Get-ImageFromB64 -ImageBase64 $BlueBase64 + $img_localAccountPasswordValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + # Define misc static variables + + $WmiComputerSystem = Get-WmiObject -Class:('Win32_ComputerSystem') + Write-progress -Activity 'JumpCloud ADMU' -Status 'Loading JumpCloud ADMU. Please Wait.. Checking AzureAD Status..' -PercentComplete 25 + Write-ToLog 'Loading JumpCloud ADMU. Please Wait.. Checking AzureAD Status..' + if ($WmiComputerSystem.PartOfDomain) { + $WmiComputerDomain = Get-WmiObject -Class:('Win32_ntDomain') + $secureChannelStatus = Test-ComputerSecureChannel + + $nbtstat = nbtstat -n + foreach ($line in $nbtStat) { + if ($line -match '^\s*([^<\s]+)\s*<00>\s*GROUP') { + $NetBiosName = $matches[1] + } + } + + if ([System.String]::IsNullOrEmpty($WmiComputerDomain[0].DnsForestName) -and $secureChannelStatus -eq $false) { + $DomainName = 'Fix Secure Channel' + } else { + $DomainName = [string]$WmiComputerDomain.DnsForestName + } + $NetBiosName = [string]$NetBiosName + } elseif ($WmiComputerSystem.PartOfDomain -eq $false) { + $DomainName = 'N/A' + $NetBiosName = 'N/A' + $secureChannelStatus = 'N/A' + } + if ((Get-CimInstance Win32_OperatingSystem).Version -match '10') { + $AzureADInfo = dsregcmd.exe /status + foreach ($line in $AzureADInfo) { + if ($line -match "AzureADJoined : ") { + $AzureADStatus = ($line.TrimStart('AzureADJoined : ')) + } + if ($line -match "WorkplaceJoined : ") { + $Workplace_join = ($line.TrimStart('WorkplaceJoined : ')) + } + if ($line -match "TenantName : ") { + $TenantName = ($line.TrimStart('WorkplaceTenantName : ')) + } + if ($line -match "DomainJoined : ") { + $AzureDomainStatus = ($line.TrimStart('DomainJoined : ')) + } + } + } else { + $AzureADStatus = 'N/A' + $Workplace_join = 'N/A' + $TenantName = 'N/A' + } + + # define return object: + $FormResults = [PSCustomObject]@{ + InstallJCAgent = $false + AutoBindJCUser = $false + BindAsAdmin = $false + LeaveDomain = $false + ForceReboot = $false + SelectedUserName = $null + JumpCloudUserName = $null + TempPassword = $null + JumpCloudConnectKey = $null + JumpCloudAPIKey = $null + JumpCloudOrgID = $null + } + Write-Progress -Activity 'JumpCloud ADMU' -Status 'Loading JumpCloud ADMU. Please Wait.. Verifying Local Accounts & Group Membership..' -PercentComplete 50 + Write-ToLog 'Loading JumpCloud ADMU. Please Wait.. Verifying Local Accounts & Group Membership..' + Write-Progress -Activity 'JumpCloud ADMU' -Status 'Loading JumpCloud ADMU. Please Wait.. Getting C:\ & Local Profile Data..' -PercentComplete 70 + Write-ToLog 'Loading JumpCloud ADMU. Please Wait.. Getting C:\ & Local Profile Data..' + # Get Valid SIDs from the Registry and build user object + $registryProfiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" + $profileList = @() + foreach ($profile in $registryProfiles) { + $profileList += Get-ItemProperty -Path $profile.PSPath | Select-Object PSChildName, ProfileImagePath + } + # List to store users + $users = @() + foreach ($listItem in $profileList) { + $sidPattern = "^S-\d-\d+-(\d+-){1,14}\d+$" + $isValidFormat = [regex]::IsMatch($($listItem.PSChildName), $sidPattern); + # Get Valid SIDs + if ($isValidFormat) { + # Populate Users List + $users += [PSCustomObject]@{ + Name = Convert-SecurityIdentifier $listItem.PSChildName + LocalPath = $listItem.ProfileImagePath + SID = $listItem.PSChildName + IsLocalAdmin = $null + LocalProfileSize = $null + Loaded = $null + RoamingConfigured = $null + LastLogin = $null + } + } + } + # Get Win32 Profiles to merge data with valid SIDs + $win32UserProfiles = Get-WmiObject -Class:('Win32_UserProfile') -Property * | Where-Object { $_.Special -eq $false } + # get localUsers (can contain users who have not logged in yet/ do not have a SID) + $nonSIDLocalUsers = Get-LocalUser + $date_format = "yyyy-MM-dd HH:mm" + foreach ($user in $users) { + # Get Data from Win32Profile + foreach ($win32user in $win32UserProfiles) { + if ($($user.SID) -eq $($win32user.SID)) { + $user.RoamingConfigured = $win32user.RoamingConfigured + $user.Loaded = $win32user.Loaded + if ([string]::IsNullOrEmpty($($win32user.LastUseTime))) { + $user.LastLogin = "N/A" + } else { + $user.LastLogin = [System.Management.ManagementDateTimeConverter]::ToDateTime($($win32user.LastUseTime)).ToUniversalTime().ToSTring($date_format) + } + } + } + # Get Admin Status + try { + $admin = Get-LocalGroupMember -Member "$($user.SID)" -Name "Administrators" -EA SilentlyContinue + } catch { + $user = Get-LocalGroupMember -Member "$($user.SID)" -Name "Users" + } + if ($admin) { + $user.IsLocalAdmin = $true + } else { + $user.IsLocalAdmin = $false + } + } + + Write-Progress -Activity 'JumpCloud ADMU' -Status 'Loading JumpCloud ADMU. Please Wait.. Building Profile Group Box Query..' -PercentComplete 85 + Write-ToLog 'Loading JumpCloud ADMU. Please Wait.. Building Profile Group Box Query..' + + $Profiles = $users | Select-Object SID, RoamingConfigured, Loaded, IsLocalAdmin, LocalPath, LocalProfileSize, LastLogin, @{Name = "UserName"; EXPRESSION = { $_.Name } } + Write-Progress -Activity 'JumpCloud ADMU' -Status 'Loading JumpCloud ADMU. Please Wait.. Done!' -PercentComplete 100 + Write-ToLog 'Loading JumpCloud ADMU. Please Wait.. Done!' + + #load UI Labels + + #SystemInformation + $lbComputerName.Content = $WmiComputerSystem.Name + + #DomainInformation + $lbDomainName.Content = $DomainName + $lbNetBios.Content = $NetBiosName + + #AzureADInformation + $lbAzureAD_Joined.Content = $AzureADStatus + $lbTenantName.Content = $TenantName + + ## Form changes & interactions + + # Install JCAgent checkbox + $cb_installJCAgent.Add_Checked( { Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) }) + # $cb_installJCAgent.Add_Checked( { $InstallJCAgent = $true }) + $cb_installJCAgent.Add_Checked( { $tb_JumpCloudConnectKey.IsEnabled = $true }) + $cb_installJCAgent.Add_Checked( { $img_connectKeyInfo.Visibility = 'Visible' }) + $cb_installJCAgent.Add_Checked( { $img_connectKeyValid.Visibility = 'Visible' }) + $cb_installJCAgent.Add_Checked( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If (((Test-CharLen -len 40 -testString $tb_JumpCloudConnectKey.Password) -and (Test-HasNoSpace $tb_JumpCloudConnectKey.Password)) -eq $false) { + #$tb_JumpCloudConnectKey.Tooltip = "Connect Key Must be 40chars & Not Contain Spaces" + $tb_JumpCloudConnectKey.Background = "#FFC6CBCF" + $tb_JumpCloudConnectKey.BorderBrush = "#FFF90000" + } Else { + $tb_JumpCloudConnectKey.Background = "white" + $tb_JumpCloudConnectKey.Tooltip = $null + $tb_JumpCloudConnectKey.FontWeight = "Normal" + $tb_JumpCloudConnectKey.BorderBrush = "#FFC6CBCF" + } + + }) + + $cb_installJCAgent.Add_UnChecked( { Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) }) + # $cb_installJCAgent.Add_Unchecked( { $InstallJCAgent = $false }) + $cb_installJCAgent.Add_Unchecked( { $tb_JumpCloudConnectKey.IsEnabled = $false }) + $cb_installJCAgent.Add_Unchecked( { $img_connectKeyInfo.Visibility = 'Hidden' }) + $cb_installJCAgent.Add_Unchecked( { $img_connectKeyValid.Visibility = 'Hidden' }) + $cb_installJCAgent.Add_Unchecked( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If (((Test-CharLen -len 40 -testString $tb_JumpCloudConnectKey.Password) -and (Test-HasNoSpace $tb_JumpCloudConnectKey.Password) -or ($cb_installJCAgent.IsEnabled)) -eq $false) { + #$tb_JumpCloudConnectKey.Tooltip = "Connect Key Must be 40chars & Not Contain Spaces" + $tb_JumpCloudConnectKey.Background = "#FFC6CBCF" + $tb_JumpCloudConnectKey.BorderBrush = "#FFF90000" + } Else { + $tb_JumpCloudConnectKey.Background = "white" + $tb_JumpCloudConnectKey.Tooltip = $null + $tb_JumpCloudConnectKey.FontWeight = "Normal" + $tb_JumpCloudConnectKey.BorderBrush = "#FFC6CBCF" + } + }) + + + # Autobind JC User checkbox + $cb_autobindJCUser.Add_Checked( { Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) }) + $cb_autobindJCUser.Add_Checked( { $tb_JumpCloudAPIKey.IsEnabled = $true }) + $cb_autobindJCUser.Add_Checked( { $img_apiKeyInfo.Visibility = 'Visible' }) + $cb_autobindJCUser.Add_Checked( { $img_apiKeyValid.Visibility = 'Visible' }) + $cb_autobindJCUser.Add_Checked( { $cb_bindAsAdmin.IsEnabled = $true }) + # $cb_bindAsAdmin.Add_Checked( { $BindAsAdmin = $true }) + $cb_autobindJCUser.Add_Checked( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If (Test-IsNotEmpty $tb_JumpCloudAPIKey.Password ) { + #$tb_JumpCloudAPIKey.Tooltip = "API Key Must be 40chars & Not Contain Spaces" + $tb_JumpCloudAPIKey.Background = "#FFC6CBCF" + $tb_JumpCloudAPIKey.BorderBrush = "#FFF90000" + } Else { + $tb_JumpCloudAPIKey.Background = "white" + $tb_JumpCloudAPIKey.Tooltip = $null + $tb_JumpCloudAPIKey.FontWeight = "Normal" + $tb_JumpCloudAPIKey.BorderBrush = "#FFC6CBCF" + } + }) + + + $cb_autobindJCUser.Add_UnChecked( { Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) }) + $cb_autobindJCUser.Add_Unchecked( { $tb_JumpCloudAPIKey.IsEnabled = $false }) + $cb_autobindJCUser.Add_Unchecked( { $img_apiKeyInfo.Visibility = 'Hidden' }) + $cb_autobindJCUser.Add_Unchecked( { $img_apiKeyValid.Visibility = 'Hidden' }) + $cb_autobindJCUser.Add_Unchecked( { $lbl_selectOrgName.Visibility = 'Hidden' }) + $cb_autobindJCUser.Add_Unchecked( { $cb_bindAsAdmin.IsEnabled = $false }) + $cb_autobindJCUser.Add_Unchecked( { $cb_bindAsAdmin.IsChecked = $false }) + # $cb_bindAsAdmin.Add_Unchecked( { $BindAsAdmin = $false }) + $cb_autobindJCUser.Add_Unchecked( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If ((!(Test-IsNotEmpty $tb_JumpCloudAPIKey.Password) -or ($cb_autobindJCUser.IsEnabled)) -eq $false) { + #$tb_JumpCloudAPIKey.Tooltip = "API Key Must be 40chars & Not Contain Spaces" + $tb_JumpCloudAPIKey.Background = "#FFC6CBCF" + $tb_JumpCloudAPIKey.BorderBrush = "#FFF90000" + } Else { + $tb_JumpCloudAPIKey.Background = "white" + $tb_JumpCloudAPIKey.Tooltip = $null + $tb_JumpCloudAPIKey.FontWeight = "Normal" + $tb_JumpCloudAPIKey.BorderBrush = "#FFC6CBCF" + } + }) + + + # Leave Domain checkbox + if (($AzureADStatus -eq 'Yes') -or ($AzureDomainStatus -eq 'Yes')) { + $cb_leaveDomain.IsEnabled = $true + } else { + Write-ToLog "Device is not AzureAD Joined or Domain Joined. Leave Domain Checkbox Disabled." + $cb_leaveDomain.IsEnabled = $false + } + + $tb_JumpCloudUserName.Add_TextChanged( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If ( + (Test-IsNotEmpty $tb_JumpCloudUserName.Text) -or ` + (!(Test-HasNoSpace $tb_JumpCloudUserName.Text)) -or ` + (Test-LocalUsername -username $tb_JumpCloudUserName.Text -win32UserProfiles $win32UserProfiles -localUserProfiles $nonSIDLocalUsers) -or ` + (($tb_JumpCloudUserName.Text).Length -gt 20) -or ` + (($tb_JumpCloudUserName.Text) -match $lbComputerName.Content)) { + $tb_JumpCloudUserName.Background = "#FFC6CBCF" + $tb_JumpCloudUserName.BorderBrush = "#FFF90000" + $img_localAccountValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_localAccountValid.ToolTip = "Local account username can not:`nBe empty or contain spaces.`nAlready exist on the local system.`nMatch the local computer name.`nContain more than 20 characters." + } Else { + $tb_JumpCloudUserName.Background = "white" + $tb_JumpCloudUserName.FontWeight = "Normal" + $tb_JumpCloudUserName.BorderBrush = "#FFC6CBCF" + $img_localAccountValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + $img_localAccountValid.ToolTip = $null + } + }) + + # Validate Connect Key + $tb_JumpCloudConnectKey.Add_PasswordChanged( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If (((Test-CharLen -len 40 -testString $tb_JumpCloudConnectKey.Password) -and (Test-HasNoSpace $tb_JumpCloudConnectKey.Password)) -eq $false) { + $tb_JumpCloudConnectKey.Background = "#FFC6CBCF" + $tb_JumpCloudConnectKey.BorderBrush = "#FFF90000" + $img_connectKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_connectKeyValid.ToolTip = "Connect Key must be 40chars & not contain spaces." + } Else { + $tb_JumpCloudConnectKey.Background = "white" + $tb_JumpCloudConnectKey.FontWeight = "Normal" + $tb_JumpCloudConnectKey.BorderBrush = "#FFC6CBCF" + $img_connectKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + $img_connectKeyValid.ToolTip = $null + } + }) + + # Validate API KEY + $tb_JumpCloudAPIKey.Add_PasswordChanged( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If (Test-IsNotEmpty $tb_JumpCloudAPIKey.Password) { + $tb_JumpCloudAPIKey.Background = "#FFC6CBCF" + $tb_JumpCloudAPIKey.BorderBrush = "#FFF90000" + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_apiKeyValid.ToolTip = "Please enter a valid JumpCloud API Key" + + } Else { + # Get org name/ id + try { + $OrgSelection, $mtpAdmin = Get-MtpOrganization -ApiKey $tb_JumpCloudAPIKey.Password -inputType + $lbl_orgName.Text = "$($OrgSelection[1])" + $script:selectedOrgID = "$($OrgSelection[0])" + if ($mtpAdmin) { + # only display this text label if a MTP admin entered their API key + $lbl_selectOrgName.Visibility = 'Visible' + } + $tb_JumpCloudAPIKey.Background = "white" + $tb_JumpCloudAPIKey.Tooltip = $null + $tb_JumpCloudAPIKey.FontWeight = "Normal" + $tb_JumpCloudAPIKey.BorderBrush = "#FFC6CBCF" + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + $img_apiKeyValid.ToolTip = $null + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -selectedOrgID($script:selectedOrgID) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + } catch { + $lbl_selectOrgName.Visibility = 'Hidden' + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_apiKeyValid.ToolTip = "Please enter a valid JumpCloud API Key" + $OrgSelection = "" + $lbl_orgName.Text = "" + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + Write-ToLog "MTP KEY MAY BE WRONG" + } + } + }) + + # Validate Temp Password + $tb_tempPassword.Add_TextChanged( { + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + If ((Test-IsNotEmpty $tb_tempPassword.Text) -or (-NOT (Test-HasNoSpace $tb_tempPassword.Text))) { + $tb_tempPassword.Background = "#FFC6CBCF" + $tb_tempPassword.BorderBrush = "#FFF90000" + $img_localAccountPasswordValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_localAccountPasswordValid.ToolTip = "Local Account Temp Password should:`nNot be empty or contain spaces.`n should also meet local password policy requirements on the system." + } Else { + $tb_tempPassword.Background = "white" + $tb_tempPassword.Tooltip = $null + $tb_tempPassword.FontWeight = "Normal" + $tb_tempPassword.BorderBrush = "#FFC6CBCF" + $img_localAccountPasswordValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + $img_localAccountPasswordValid.ToolTip = $null + } + }) + + # Change button when profile selected + $lvProfileList.Add_SelectionChanged( { + $SelectedUserName = $($lvProfileList.SelectedItem.username) + New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + try { + $SelectedUserSID = ((New-Object System.Security.Principal.NTAccount($SelectedUserName)).Translate( [System.Security.Principal.SecurityIdentifier]).Value) + } catch { + $SelectedUserSID = $SelectedUserName + } + $hku = ('HKU:\' + $SelectedUserSID) + if (Test-Path -Path $hku) { + $btn_migrateProfile.IsEnabled = $false + $tb_JumpCloudUserName.IsEnabled = $false + $tb_tempPassword.IsEnabled = $false + } else { + $tb_JumpCloudUserName.IsEnabled = $true + $tb_tempPassword.IsEnabled = $true + } + }) + $SelectedUserName = $($lvProfileList.SelectedItem.username) + + # Validate Migrate Profile & return $formResults + $btn_migrateProfile.Add_Click( { + if ($tb_JumpCloudAPIKey.Password -And $tb_JumpCloudUserName.Text -And $cb_autobindJCUser.IsChecked) { + # If text field is default/ not 40 chars + if (!(Test-CharLen -len 40 -testString $tb_JumpCloudConnectKey.Password)) { + # Validate the the JumpCLoud Agent Conf File exists: + $keyResult = Test-JumpCloudSystemKey -WindowsDrive $(Get-WindowsDrive) + if (!$keyResult) { + # If we catch here, the system conf file does not exist. User is prompted to enter connect key; log below + Write-ToLog "The JumpCloud agent has not be registered on this system, to please specify a valid Connect Key to continue." + return + } + } else { + Write-ToLog "ConnectKey is populated, JumpCloud agent will be installed" + } + + $testResult, $JumpCloudUserId, $JCSystemUsername = Test-JumpCloudUsername -JumpCloudApiKey $tb_JumpCloudAPIKey.Password -JumpCloudOrgID $script:selectedOrgID -Username $tb_JumpCloudUserName.Text -Prompt $true + if ($testResult) { + Write-ToLog "Matched $($tb_JumpCloudUserName.Text) with user in the JumpCloud Console" + } else { + Write-ToLog "$($tb_JumpCloudUserName.Text) not found in the JumpCloud console" + return + } + if ( -not [string]::IsNullOrEmpty($JCSystemUsername) ) { + # Regex to get the username from the domain\username string and compare it to JCSystemUsername + #Get all the local users + $registryProfiles = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" + $profileList = @() + foreach ($profile in $registryProfiles) { + $profileList += Get-ItemProperty -Path $profile.PSPath | Select-Object PSChildName, ProfileImagePath, @{ Name = "username"; Expression = { $sysUsername = Convert-SecurityIdentifier -sid $_.PSChildName; $sysUsername.Split('\')[1] } } + } + # If the JumpCloud found username was identified to exist locally, throw message + if ($JCSystemUsername -in $profileList.username) { + # Create a pop up that warns user then press ok to continue + Write-ToLog "JCSystemUsername $($JCSystemUsername) is the same as the another profile on this system" + $wshell = New-Object -ComObject Wscript.Shell + $message = "The JumpCloud User: $($tb_JumpCloudUserName.Text) has a local account username of: $($JCSystemUsername). A local account already exists on this system with username: $($JCSystemUsername), please consider removing either the local account on this system or removing the local user account field from the JumpCloud user." + $var = $wshell.Popup("$message", 0, "JumpCloud SystemUsername and Local Computer Username Validation", 0) + # the user can not continue with migration at this stage + return + } + $wshell = New-Object -ComObject Wscript.Shell + $message = "The JumpCloud User: $($tb_JumpCloudUserName.Text) has a local account username of: $($JCSystemUsername). After migration $($SelectedUserName) would be migrated and accessible with the local account username of: $($JCSystemUsername) Would you like to continue?" + $var = $wshell.Popup("$message", 0, "JumpCloud Local User Validation", 64 + 4) + # If user selects yes then migrate the local user profile to the JumpCloud User + + if ($var -eq 6) { + Write-ToLog -Message "User selected 'Yes', continuing with migration of $($SelectedUserName) to $($JCSystemUsername)" + } else { + Write-ToLog -Message "User selected 'No', returning to form" + return + } + } else { + Write-ToLog "User $($tb_JumpCloudUserName.Text) does not have a local account on this system" + } + } + # Build FormResults object + Write-ToLog "Building Form Results" + + if ([System.String]::IsNullOrEmpty($SelectedUserName)) { + # TODO: I've broken the conversion for the username here, need to figure out why this no longer works. + $SelectedUserName = $($lvProfileList.SelectedItem.username) + } + + # Set the options selected/ inputs to the $formResults object + $FormResults.InstallJCAgent = $cb_installJCAgent.IsChecked + $FormResults.AutoBindJCUser = $cb_autobindJCUser.IsChecked + $FormResults.BindAsAdmin = $cb_bindAsAdmin.IsChecked + $FormResults.LeaveDomain = $cb_leaveDomain.IsChecked + $FormResults.ForceReboot = $cb_forceReboot.IsChecked + $FormResults.SelectedUserName = $SelectedUserName + $FormResults.JumpCloudUserName = $tb_JumpCloudUserName.Text + $FormResults.TempPassword = $tb_tempPassword.Text + $FormResults.JumpCloudConnectKey = $tb_JumpCloudConnectKey.Password + $FormResults.JumpCloudAPIKey = $tb_JumpCloudAPIKey.Password + $FormResults.JumpCloudOrgID = $script:selectedOrgID + # Close form + $Form.Close() + }) + + # $tb_JumpCloudUserName.add_GotFocus( { + # $tb_JumpCloudUserName.Text = "" + # }) + + # $tb_JumpCloudConnectKey.add_GotFocus( { + # $tb_JumpCloudConnectKey.Password = "" + # }) + + # $tb_JumpCloudAPIKey.add_GotFocus( { + # $tb_JumpCloudAPIKey.Password = "" + # }) + + + + # lbl_connectKey link - Mouse button event + $lbl_connectKey.Add_PreviewMouseDown( { [System.Diagnostics.Process]::start('https://console.jumpcloud.com/#/systems/new') }) + + # lbl_apiKey link - Mouse button event + $lbl_apiKey.Add_PreviewMouseDown( { [System.Diagnostics.Process]::start('https://support.jumpcloud.com/support/s/article/jumpcloud-apis1') }) + + # Add controls for to select a different org + $lbl_selectOrgName.Add_PreviewMouseDown({ + try { + # Get MTP Organization returns, the org selected ($orgSelection) and whether the user is an MTP admin or not ($mtpAdmin) + $OrgSelection, $mtpAdmin = Get-MtpOrganization -ApiKey $tb_JumpCloudAPIKey.Password -inputType + $lbl_orgName.Text = "$($OrgSelection[1])" + $script:selectedOrgID = "$($OrgSelection[0])" + if ($mtpAdmin) { + # only display this text label if a MTP admin entered their API key + $lbl_selectOrgName.Visibility = 'Visible' + } + $tb_JumpCloudAPIKey.Background = "white" + $tb_JumpCloudAPIKey.Tooltip = $null + $tb_JumpCloudAPIKey.FontWeight = "Normal" + $tb_JumpCloudAPIKey.BorderBrush = "#FFC6CBCF" + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ActiveBase64 + $img_apiKeyValid.ToolTip = $null + Test-MigrationButton -tb_JumpCloudUserName:($tb_JumpCloudUserName) -tb_JumpCloudConnectKey:($tb_JumpCloudConnectKey) -tb_tempPassword:($tb_tempPassword) -lvProfileList:($lvProfileList) -tb_JumpCloudAPIKey:($tb_JumpCloudAPIKey) -selectedOrgID($script:selectedOrgID) -cb_installJCAgent:($cb_installJCAgent) -cb_autobindJCUser:($cb_autobindJCUser) + } catch { + $btn_migrateProfile.IsEnabled = $false + $lbl_selectOrgName.Visibility = 'Hidden' + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + $img_apiKeyValid.ToolTip = "Please enter a valid JumpCloud API Key" + $OrgSelection = "" + $lbl_orgName.Text = "" + $img_apiKeyValid.Source = Get-ImageFromB64 -ImageBase64 $ErrorBase64 + Write-ToLog "MTP KEY MAY BE WRONG" + } + }) + + # move window + $Form.Add_MouseLeftButtonDown( { + $Form.DragMove() + }) + # allow form to be clicked and remove focus from text fields + Function RefreshData { + $Test = "Testing" | Out-Gridview + } + $Form.Add_PreviewMouseDown({ + $grid1.Focus() + }) + + # exit and close form + $Form.Add_Closing({ + return + }) + # Put the list of profiles in the profile box + $Profiles | ForEach-Object { $lvProfileList.Items.Add($_) | Out-Null } + #=========================================================================== + # Shows the form & allow move + #=========================================================================== + + $Form.ShowDialog() + + # if the migrate button is enabled and it is clicked, send formResults to Start-Migration + If (($btn_migrateProfile.IsEnabled -eq $true) -AND $btn_migrateProfile.Add_Click) { + Return $FormResults + } +} \ No newline at end of file diff --git a/jumpcloud-ADMU/Powershell/ProgressForm.ps1 b/jumpcloud-ADMU/Powershell/Private/DisplayForms/ProgressForm.ps1 similarity index 62% rename from jumpcloud-ADMU/Powershell/ProgressForm.ps1 rename to jumpcloud-ADMU/Powershell/Private/DisplayForms/ProgressForm.ps1 index 53af628a8..1eff744c0 100644 --- a/jumpcloud-ADMU/Powershell/ProgressForm.ps1 +++ b/jumpcloud-ADMU/Powershell/Private/DisplayForms/ProgressForm.ps1 @@ -1,31 +1,16 @@ -$newJCLogoBase64 = "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" -function DecodeBase64Image { - param ( - [Parameter(Mandatory = $true)] - [String]$ImageBase64 - ) - # Parameter help description - $ObjBitmapImage = New-Object System.Windows.Media.Imaging.BitmapImage #Provides a specialized BitmapSource that is optimized for loading images using Extensible Application Markup Language (XAML). - $ObjBitmapImage.BeginInit() #Signals the start of the BitmapImage initialization. - $ObjBitmapImage.StreamSource = [System.IO.MemoryStream][System.Convert]::FromBase64String($ImageBase64) #Creates a stream whose backing store is memory. - $ObjBitmapImage.EndInit() #Signals the end of the BitmapImage initialization. - $ObjBitmapImage.Freeze() #Makes the current object unmodifiable and sets its IsFrozen property to true. - $ObjBitmapImage -} - function New-ProgressForm { - # Synchash the values + # syncHash the values [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms') | Out-Null - [System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | Out-Null + [System.Reflection.Assembly]::LoadWithPartialName('PresentationFramework') | Out-Null $syncHash = [hashtable]::Synchronized(@{ }) $newRunspace = [runspacefactory]::CreateRunspace() $syncHash.Runspace = $newRunspace $syncHash.PercentComplete = $PercentComplete $syncHash.StatusInput = '' $syncHash.LogText = @() - $synchash.logLevel = '' - $syncHash.base64JCLogo = DecodeBase64Image -ImageBase64 $newJCLogoBase64 - $synchash.closeWindow = $false + $syncHash.logLevel = '' + $syncHash.base64JCLogo = Get-ImageFromB64 -ImageBase64 $JCLogoBase64 + $syncHash.closeWindow = $false # Migration Details $syncHash.UsernameInput = '' @@ -37,7 +22,7 @@ function New-ProgressForm { @@ -123,10 +108,10 @@ function New-ProgressForm {