MyProfile.psm1
# References: # 1. Below are the list of predefined vars that can be used: # - $PSScriptRoot [System defined] The folder path for current scipt file, NOT the caller script to call this function . "$PSScriptRoot\Common.ps1" if (-not ("ddrr.PowerShell.MyProfileModule.MyProfile" -as [Type])) { Add-Type @" namespace ddrr.PowerShell.MyProfileModule { public class MyProfile { public string Name { get; set; } public int LoadOrder { get; set; } public string Path { get; set; } public bool IsRegistered { get; set; } public bool IsDefault { get; set; } public bool IsCustomized { get; set; } } } "@ } ############################################################################### # MyProfile private functions ############################################################################### <# .SYNOPSIS Check whether the specified path is a valid MyProfile. #> function Test-MyProfile { param( [Parameter(Position=0)] [string] $MyProfilePath ) # Check the path exists or not if ([string]::IsNullOrWhiteSpace($MyProfilePath)) { return $false } if (-NOT (Test-Path $MyProfilePath)) { return $false } # Check the manifest $manifestFilePath = Join-Path $MyProfilePath $MyProfileRelativeManifestPath if (-NOT(Test-Path $manifestFilePath)) { return $false } return $true } <# .SYNOPSIS Get the list of customized and default my profile paths. Higher priority prfile comes first in the list. #> function Find-CustomizedMyProfilePaths { return Get-EnvironmentListVariable -Name $MyProfileCustomizedListVarName | ? { Test-MyProfile $_ } | % { $_.TrimEnd('\') } | Sort-Object -Unique } function Find-DefaultMyProfilePaths { $ret = @() # Default 1: SynologyDrive $SynologyDriveMyProfilePath = "$($env:USERPROFILE)\SynologyDrive\MyProfile" if (Test-MyProfile $SynologyDriveMyProfilePath) { $ret += $SynologyDriveMyProfilePath } # Default 2: OneDrive - Personal $OneDriveMyProfilePath = "$($env:USERPROFILE)\OneDrive\MyProfile" if (Test-MyProfile $OneDriveMyProfilePath) { $ret += $OneDriveMyProfilePath } # Default 3: OneDrive - <Business> Get-ChildItem -Path $env:USERPROFILE -Filter "OneDrive - *" | % { $OneDriveBusinessMyProfilePath = "$($env:USERPROFILE)\$($_.Name)\MyProfile" if (Test-MyProfile $OneDriveBusinessMyProfilePath) { $ret += $OneDriveBusinessMyProfilePath } } return $ret } function Get-MyProfileManifest { param( [Parameter(Position=0)] [string] $MyProfilePath ) $manifestFilePath = Join-Path $MyProfilePath $MyProfileRelativeManifestPath return Import-PowerShellDataFile $manifestFilePath } ############################################################################### # MyProfile public functions ############################################################################### <# .SYNOPSIS Register MyProfile. #> function Register-MyProfiles { [CmdletBinding()] param() $profilePaths = @() $binPaths = @() $psModulesPaths = @() Get-MyProfile | % { # Register single MyProfile Write-ColorHost "Registering $($_.Name)" -Type Keynote # Register MyProfile path. This will support PowerShell Profile, System Profile, Cron $profilePaths += $_.Path # Register Bin path $binPaths += Join-Path $_.Path $MyProfileRelativeBinPath # Register PowerShell Modules $psModulesPaths += Join-Path $_.Path $MyProfileRelativePSMoudlesPath # Register Toolbox # Invoke PSProfile and PSModules for current MyProfile Invoke-MyProfile $_.Path -PSProfile -PSModules } # Invoke SysProfile for all MyProfile together Invoke-MyProfile -SysProfile # Save paths to environment variables Set-EnvironmentListVariable -Name $MyProfileRegisteredListVarName -ListValue $profilePaths -Target User Set-EnvironmentListVariable -Name $MyProfileBinPathVarName -ListValue $binPaths -Target User Add-EnvironmentListVariableValues -Name 'Path' -ValuesToAppend "%$MyProfileBinPathVarName%" -Target User Set-EnvironmentListVariable -Name $MyProfilePSModulesPathVarName -ListValue $psModulesPaths -Target User $currentUserPsModulesPath = Join-Path $([Environment]::GetFolderPath("MyDocuments")) "WindowsPowerShell\Modules" Add-EnvironmentListVariableValues -Name 'PSModulePath' -ValuesToAppend @($currentUserPsModulesPath,"%$MyProfilePSModulesPathVarName%") -Target User } function Invoke-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name, [switch] $SysProfile, [switch] $PSProfile, [switch] $PSModules ) if ((-NOT $SysProfile) -AND (-NOT $PSProfile) -AND (-NOT $PSModules)) { $SysProfile = $true $PSProfile = $true $PSModules = $true } $sysProfiles = @() Get-MyProfile $Name | % { if ($PSProfile) { $curPSProfilePath = Join-Path $_.Path $MyProfileRelativePSProfilePath # Note, use operator '.' here to invoke all MyPrfile's PowerShell profiles in the same scope of the MyProfile's PSProfile. # So, these PowerShell profiles can impact each other, and the MyProfile load order is important. . Invoke-ScriptInMyProfileModuleCallerScope $curPSProfilePath } if ($PSModules) { Import-MyModule -MyProfileName $_.Path -Force } # For SysProfile, it should not impact current PS scope, so they will not be invoked via operator '.' # But similar to PSProfiles, all SysProfiles should share the same scope. So they will be not invoked here. Instead, they will be invoked together later. # This is also consistent with the normal behavior when system startup: SysProfiles are invoked after all PSProfiles are invoked. if ($SysProfile) { $sysProfiles += Join-Path $_.Path $MyProfileRelativeSysProfilePath } } # NOT use operator '.' here to avoid all MyProfile's system profiles impact current PS scope. # But these SysProfiles will share the same scope, so the MyProfile load order is important. if ($sysProfiles.Count -gt 0){ Invoke-ScriptInMyProfileModuleCallerScope $sysProfiles } } function New-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path = '' ) if ([string]::IsNullOrWhiteSpace($Path)) { $Path = '.'} if (Test-Path $Path) { $Path = Resolve-Path $Path if ((Get-ChildItem $Path).Count -gt 0) { Write-ColorHost "The path for a new MyProfile must be a new path or an empty folder. Fail to create new MyProfile at '$Path'." -Type Error return } } else { New-Item -Path $Path -ItemType Directory | Out-Null } Copy-Item -Path "$MyProfileModuleTemplateRootPath\*" -Destination $Path Add-MyProfilePath -Path $Path Write-ColorHost "A new MyProfile has been created at '<Green>$Path</Green>'. You can update the <Green>$MyProfileRelativeManifestPath</Green> file to setup the details." -Type Highlight } function Get-MyProfile { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name ) $isPathMode = $false if ((-NOT [string]::IsNullOrWhiteSpace($Name)) -AND (Test-Path $Name)) { [array]$profilePathList = @($Name) $isPathMode = $true } else { [array]$registeredPathList = Get-EnvironmentListVariable -Name $MyProfileRegisteredListVarName [array]$customizedPathList = Find-CustomizedMyProfilePaths [array]$defaultPathList = Find-DefaultMyProfilePaths [array]$profilePathList = $customizedPathList + $defaultPathList + $registeredPathList | Sort-Object -Unique } [array]$profiles = $profilePathList | % { $curManifest = Get-MyProfileManifest $_ if ($isPathMode -OR ([string]::IsNullOrWhiteSpace($Name) -OR ($curManifest.Name -like $Name))) { $curProfile = New-Object ddrr.PowerShell.MyProfileModule.MyProfile $curProfile.Name = if ([string]::IsNullOrWhiteSpace($curManifest.Name)){ $_ } else { $curManifest.Name } $curProfile.LoadOrder = $curManifest.LoadOrder $curProfile.Path = $_ $curProfile.IsRegistered = $registeredPathList -contains $_ $curProfile.IsDefault = $defaultPathList -contains $_ $curProfile.IsCustomized = $customizedPathList -contains $_ $curProfile } } return $profiles | Sort-Object LoadOrder,Path } function Add-MyProfilePath { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path ) if (Test-MyProfile $Path) { $Path = (Resolve-Path $Path).Path.TrimEnd('\') Add-EnvironmentListVariableValues -Name $MyProfileCustomizedListVarName -Target User -ValuesToAppend $Path } else { Write-ColorHost "'Failed to add $Path' as it is not a valid MyProfile." -Type Error } } function Remove-MyProfilePath { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Path ) if (Test-Path $Path) { $PathWithSlash = Join-Path $Path '\' -Resolve } else { $PathWithSlash = if ($Path[-1] -eq '\') { $Path } else { "$Path\" } } $PathWithoutSlash = $PathWithSlash.TrimEnd('\') Remove-EnvironmentListVariableValues -Name $MyProfileCustomizedListVarName -Target User -ValuesToRemove @($PathWithSlash, $PathWithoutSlash) } function Import-MyModule { [CmdletBinding()] param( [Parameter(Position=0)] [string] $Name, [string] $MyProfileName, [switch] $Force ) if ((-NOT [string]::IsNullOrWhiteSpace($Name)) -AND (Test-Path $Name)) { try { Import-Module $Name -Global -Force:$Force -ErrorAction Stop if (-NOT [string]::IsNullOrWhiteSpace($MyProfileName)) { Write-ColorHost "[$MyProfileName] " -NoNewLine -Type Highlight } Write-ColorHost "Imported my module '$Name'" -Type Highlight } catch { Write-ColorHost "Failed to import my module '$Name'" -Type Error Write-Host $_ } } else { Get-MyProfile | ? { [string]::IsNullOrWhiteSpace($MyProfileName) -OR (($_.Name -like $MyProfileName) -OR ($_.Path -eq $MyProfileName)) } | % { $curMyProfile = $_ $psModulesPath = Join-Path $_.Path $MyProfileRelativePSMoudlesPath if (Test-Path $psModulesPath -PathType Container) { Get-ChildItem $psModulesPath -Directory | ? { [string]::IsNullOrWhiteSpace($Name) -OR ($_.Name -like $Name) } | % { Import-MyModule -Name (Join-Path $psModulesPath $_.Name) -MyProfileName $curMyProfile.Name -Force:$Force } } } } } ############################################################################### # Alias ############################################################################### Set-Alias -Name Start-MySysProfile -Value $MyProfileModuleSysProfilePath |