en-US/about_PowerProfile.help.txt

TOPIC
    about_PowerProfile

SHORT DESCRIPTION
    A modern and cross-platform PowerShell profile, based on directories instead of pure files.

LONG DESCRIPTION
    PowerProfile uses a directory structure for user profiles instead of single script files. It extends the native
    capabilities of PowerShell to also support profiles for individual terminal applications.

EXAMPLES
    Installation on Windows can be done from the PowerShell Gallery as followed:

        PS C:\> Install-Module PowerProfile,PowerProfile.Commands -Scope CurrentUser

    Note that you should install both packages. Some interactive commands are outsourced into a dedicated module to avoid they
    are constantly loaded in every PS session for no particular reason.

    Now, manually import the module once to trigger the automatic setup procedure:

        PS> Import-Module PowerProfile

    This runs a precondition check and performs the required setup steps before loading the module. Essentially, the
    `profile.ps1` file in `$PROFILE.CurrentUserAllHosts` is generated to enable automatic load for new PowerShell sessions.
    Don't worry: Existing files will always be backed up as `.bak` files. Should you ever want to remove PowerProfile, the
    `Uninstall-PowerProfile` command has you covered.

    Differences on macOS and Linux
    ------------------------------

    Please note that the PowerProfile module must be installed in user scope and can not be used in global machine scope.
    On Windows machines, user modules are automatically installed to the `$HOME\Documents\PowerShell` folder which in turn
    also hosts PowerShell profile files.

    On macOS or Linux machines, the configuration/profile files are separated from local user installed modules. This is
    great in general and something we miss A LOT on Windows (see environment varibale `$env:IsProfileRedirected` mentioned below).

    Because of this, the initial installation and setup on macOS and Linux is slightly different as the designated module
    path is not part of `$env:PSModulePath` (yet):

        PS ~> $InstallPath = Join-Path (Split-Path $PROFILE) 'Modules'
        PS ~> New-Item -ItemType Directory -Force -Path $InstallPath
        PS ~> Save-Module -Name PowerProfile.Commands -Path $InstallPath
        PS ~> Save-Module -Name PowerProfile -Path $InstallPath
        PS ~> Import-Module $InstallPath/PowerProfile

    Should you be using the upcoming PowerShellGet V3 already, use the `Save-PSResource` command instead of the `Save-Module`
    command like this:

        PS ~> Save-PSResource -Name PowerProfile.Commands,PowerProfile -IncludeXML -Path $InstallPath

    That is, until you let PowerProfile take over, of course :-)

    About the PowerProfile runtime environment
    ------------------------------------------

    Enhancing the runtime environment is what PowerProfile does out of the box for you, no need to keep an eye on anything
    like this anymore. It transparently fixes PATH issues when PowerShell is used as login shell on Unix systems and has
    built-in support for the famous package manager Homebrew on macOS and Linux.

    These are environment variables specifically curated by PowerProfile for you:

        - `$env:IsElevated` (Set when `pwsh` was started with administrative/root privileges.)
        - `$env:IsProfileRedirected` (Set when the user profile is stored outside of the regular folder.)
        - `$env:SHELL` (Path to the current pwsh executable. Also fixes value when inherited from a parent Unix shell.)
        - `$env:SHLVL` (Unix shell levels are respected correctly to handle sub-shells appropriately. The built-in
                                              prompt uses this information to indicate the number of current parent shells.
                                              This functionality is also extended to Windows systems.)
        - `$env:PSLVL` (Similar to `$env:SHLVL` but only counts the parent PowerShell sessions. PowerProfile uses
                                              this information internally to skip certain parts of the profile that shall only be run at the
                                              beginning of the first PowerShell session.)
        - `$env:TERM_PROGRAM` (Set if the variable was not set by the terminal application)
        - `$env:PSHOST_PROGRAM` (Reformated `$PROFILE.CurrentUserCurrentHost` that PowerProfile uses internally to represent the
                                              current PowerShell host application.)
        - `$env:LC_TERMINAL` (`$env:TERM_PROGRAM` but in human readable format. Generated if not set by the terminal application.
                                              The PowerProfile profile directory is derived from this.)
        - `$env:LC_PSHOST` (Same as `$env:LC_TERMINAL` but for the PowerShell host application.)
        - `$env:COMPUTERNAME` (Generated from `$env:HOSTNAME` on Unix to support with platform compatibility)
        - `$env:HOSTNAME` (Generated from `$env:COMPUTERNAME` on Windows to support with platform compatibility)
        - `$env:PROCESSOR_ARCHITECTURE` (Generated on Unix to support with platform compatibility. PowerProfile conditional directories are
                                              derived from this.)
        - `$env:XDG_*` (Provides paths based on the XDG Base Directory Specification. PowerProfile respects these when
                                              storing local state information.)

    Also, these few PowerShell global variables can be very handy to use:

        - `$IsCommand` (Set when `pwsh` was started with `-Command` parameter.)
        - `$IsLogin` (Set when `pwsh` was started as login shell using the `-l` parameter.)
        - `$IsNoExit` (Set when `pwsh` was started with `-NoExit` parameter.)
        - `$IsNonInteractive` (Set when `pwsh` was started with `-NonInteractive` parameter or by running a PS1 script
                                      that does not use the file extension `.ps1` but a shebang like `#!/usr/bin/env pwsh` instead.)
        - `$PROFILEHOME` (A shortcut to navigate to the PowerShell user profile directory, similar to `$PROFILE`.)

    Obviously PowerProfile uses these variables internally to control what parts of the user profile shall be loaded in which situations.
    You can observe the variables in your current session like this:

        PS> Get-Variable | Where-Object -Property ModuleName -eq 'PowerProfile.Core' | Select-Object Name,Value,ModuleName

    How to begin using PowerProfile?
    ================================

    Okay... your brandnew PowerShell profile is entirely empty, so what do you do now?

    To create initial directories for you to put your startup script(s), let the `New-PowerProfile` command create the structure
    for your current machine, including the terminal application you are currently using:

        PS> New-PowerProfile
            Creating profile directories in: $HOME\Documents\PowerShell

            🗂 Profile
            🗂 Profile_powershell
            🗂 Profile_windowsterminal

    Here you can see that `Profile` and `Profile_powershell` were created as the equivalents to `$PROFILE.CurrentUserAllHosts`
    and `$PROFILE.AllUsersAllHosts`. Assuming we were running the new Windows Terminal, a third type of profile is generated that
    only loads when using Windows Terminal. The same applies when using iTerm2 or Apple Terminal on macOS.
    You can run the command as often as you like in every other terminal application you are using, even your IDE like for example
    Visual Studio Code. It would create yet another folder `Profile_vscode` that is only worked on in VSCode development sessions.

    The real power in PowerProfile comes into play when you start using two things:

    1. Conditional sub-directories
    ------------------------------

    Repeat the `New-PowerProfile` command but with parameters this time:

        PS> New-PowerProfile -AllConditionalDirectories
            Creating profile directories in: $HOME\Documents\PowerShell

            🗂 Profile
            ├──🏢 _Arch_AMD64
            ├──💻 _Machine_DESKTOP-XYZ
            ├──💾 _Platform_Windows
            │ └──🏢 _Arch_AMD64
            ├──🐚 _PSEdition_Core
            │ ├──🏢 _Arch_AMD64
            │ └──💻 _Machine_DESKTOP-XYZ
            └──🐚 _PSEdition_Desktop
                ├──🏢 _Arch_AMD64
                └──💻 _Machine_DESKTOP-XYZ

            🗂 Profile_powershell
            ├──🏢 _Arch_AMD64
            ├──💻 _Machine_DESKTOP-XYZ
            ├──💾 _Platform_Windows
            │ └──🏢 _Arch_AMD64
            ├──🐚 _PSEdition_Core
            │ ├──🏢 _Arch_AMD64
            │ └──💻 _Machine_DESKTOP-XYZ
            └──🐚 _PSEdition_Desktop
                ├──🏢 _Arch_AMD64
                └──💻 _Machine_DESKTOP-XYZ

            🗂 Profile_iterm2
            ├──🏢 _Arch_AMD64
            ├──💻 _Machine_DESKTOP-XYZ
            ├──💾 _Platform_Windows
            │ └──🏢 _Arch_AMD64
            ├──🐚 _PSEdition_Core
            │ ├──🏢 _Arch_AMD64
            │ └──💻 _Machine_DESKTOP-XYZ
            └──🐚 _PSEdition_Desktop
                ├──🏢 _Arch_AMD64
                └──💻 _Machine_DESKTOP-XYZ

    These are additional profile directories that are processed only based on conditions of your current machine. You can clearly identify
    them by the `_` prefix.
    Now with OneDrive installed and files on-demand enabled, your PowerShell profile on Windows is actually roaming to other PCs or also
    Macs and Linux machines. This allows you to use one single PS profile on all your devices. At the same time, you can be very selective
    what parts of your profile shall be loaded.
    Using a directory structure instead of a single profile file with complex nested if-elseif-then statements is a huge benefit in terms
    of transparency, portability, and maintainability.
    If you are done filling up the profiles directories with content, you may use the `Optimize-PowerProfile` command to cleanup directories
    that are not in use and therefore empty. Think of it as the natural opponent to the `New-PowerProfile` command.

    How does PowerProfile handle profile directories internally?
    ------------------------------------------------------------

    At load time, all profile directories that match to your current system environment are "merged" into a single, virtual directory with a
    fixed order preference. That means that all files from any of these directories come together and eventually can be handled as if they were
    in a single directory. This is called "layering".
    Files using the same name are replaced be a different variant that may exist later in the order. This happens before any code is read
    from files so it really is a very individual profile being generated, tailored for every machine and each terminal application.

    Indeed, PowerProfile on the fly generates up to three dynamic PowerShell modules. We can actually see this from the list of loaded
    modules, indicated by the prefix `PowerProfile-Dyn`:

            PS> Get-Module PowerProfile* | Select-Object -Property Name

            Name
            ----
            PowerProfile
            PowerProfile-Dyn.Functions
            PowerProfile-Dyn.Scripts
            PowerProfile-Dyn.Scripts.PowerShell
            PowerProfile-Dyn.Scripts.WindowsTerminal
            PowerProfile.Core
            PowerProfile.Core.Load

    As you can see from the output, there are three dynamic script modules named `PowerProfile-Dyn.Scripts*`.
    They "own" any variables that you created/used in your custom scripts:

            PS> Get-Variable | Where-Object -Property ModuleName -match '^PowerProfile-Dyn\..+' | Select-Object Name,Value,ModuleName

    But wait, there is a forth dynamic module `PowerProfile-Dyn.Functions`, what's that?
    This leads us to the second meaning of "power" in PowerProfile:

    2. Functional folders
    ---------------------

    Every profile directory can contain the following folders:

    - `Config`
    - `Functions`
    - `Modules`
    - `Scripts`

    A `Modules` folder is automatically added to `$env:PSModulePath` so any personal PS modules you wrote by yourself can be used easily.
    You could actually have the same module but in two different Conditional sub-directories and have only the variant loaded that matches
    the current runtime environment.
    `Script` folders are automatically added to the `$env:PATH` search path so scripts can be run from anywhere.
    Note that for performance reasons, `Modules` and `Scripts` folders are only considered when they are not empty at the beginning of a new
    PowerShell session.

    `Functions` folders are mostly self-explanatory: Save your `.ps1` function files that you require in your profile scripts or during your
    interactive terminal session (in case you don't want to write an entire PS module). You can (and should) follow best practices to use a
    single file per function, but generally that's up to you. Functions can have aliases set either inline by using the `[Alias()]` attribute,
    or separate with the `Set-Alias` command.
    Functions and aliases are loaded into a single dynamic PS module before any other profile scripts run. You can list the module content
    like this:

        PS> Get-Command -Type All -Module PowerProfile-Dyn.Functions

    The `Config` folder contains optional configuration files for PowerProfile.
    You can also create sub-folders for any 3rd-party configurations and use the `Get-PoProfileContent` command in your scripts to retrieve a
    list of config files that PowerProfile detected:

        PS> (Get-PoProfileContent).Config.Profile

        Name Value
        ---- -----
        PSModules {ABC.modules.psd1, XYZ.modules.psd1}
        Homebrew {Brewfile, Next.Brewfile, Another.Brewfile}

    Note that this example shows a structure that is used by the PowerProfile extention module `PowerProfile.Core.Extension`.

    Based on this flexibility, there are supplementary add-on modules for PowerProfile that can provide an entire pre-configured PowerShell profile,
    even ready-to-go for different user roles like "Microsoft 365 Admin" or "PowerShell Administrator".

    To find PowerProfile add-on modules, go to https://www.powershellgallery.com/profiles/PowerProfile or use the `Find-Module` command:

        PS> Find-Module -Tag PowerProfile.Addon

TROUBLESHOOTING NOTE:
    For issues and community discussion, visit the GitHub repository:
    https://github.com/PowerProfile/PowerProfile

SEE ALSO
  - https://PowerProfile.sh/

KEYWORDS
    Profile, PSProfile, PowerShellProfile, PoshProfile, Environment