The Profile and Setting It Up

The Profile and Setting It Up
iwr https://lksz.me/pwsz | iex
Setting up should always be this easy - read the article below for all the details

This one took my some time to complete. Much more than I thought it will. But I gotta say, I'm rather proud of this one.

The Problem

The problems I wanted to solve was setting up the $PROFILE file:

  • The $PROFILE file needs to do some work to support the environment I've created.
  • I want to have only the Scripts directory in the git repo, which means the $PROFILE file will not be pulled when I clone the repo.
  • With the above stated, this means setting up my environment requires multiple steps:
    1. If the Scripts dir doesn't exists create it.
    2. git clone the repo into the Scripts dir.
    3. Create the $PROFILE file and paste in the code*.
  • * Currently, The relevant $PROFILE code is in the git repo's README.md
  • Editing the $PROFILE file isn't synced into the git repo.
  • $PROFILE can be in one of two contexts, CurrentUser or AllUsers (this isn't even counting the different CurrentHost and AllHosts contexts), making syncing even more troublesome.
  • Even if $PROFILE would be pulled from the git repo, doing it blindly can be bad, as there might be a need for some unique system/user specific code.

Coming up with a plan

I wanted to come up with a solution that would simplify the entire setup process and solve the problems listed above.

I was determined to create a Setup-Profile script that will address those problems, it would have the following goals:

  • The script file will contain the code for the $PROFILE file. This way, the code is saved and managed in git. From this point forward, this code will be named common-profile-code
  • Ability to remove/refresh the common-profile-code easily. If there is code before or after the common code, only affect the common code block.
  • Ability to work in either contexts (CurrentUser or AllUsers).

Seems simple enough.

Wait, there's more...

While starting to code, I got most of this done, mostly based on some older code I wrote, and then I thought of one more concept.

I got the notion to transform the script so it would allow an easy setup of the Scripts dir environment. The idea is to be able to pull its code using Invoke-WebRequest and feeding it into Invoke-Expression. This will allow me to construct a one-liner setup call (like the one at the top of this post), that will do all the setup needed to get the environment up and running.

This threw me into a spin, because this introduced one more complications:

  • The script must be totally self-sufficient, and cannot assume anything about the environment.

The reason this is so important is because the script I already came up with already relied on a few of the function-scripts I've created.

A Lesson Learned

This is probably a great lesson to pause and contemplate upon:

It's important to form a philosophy to guide you, but it's even more important to identify when the rules set by will not serve you and exceptions will need to be made.

This Setup-Profile script is going to change some things around.

It is going to contain some functions that will be loaded into memory and not stored in the Scripts dir like I intended originally.

One important philosophy I do want to stick to, is:

Whenever possible, do not duplicate reusable code.

The Details

I'll start by laying out the different components of the script:

  • ProfileCode_common function: Shared code between the Setup-Profile script code and the actual $PROFILE. The code is completely self-sufficient and includes several common functions that will become an integral part of the run-time environment.
  • ProfileCode_pre_common and ProfileCode_post_common functions: Each containing code that should be written into the common-profile-code but not necessarily run in-line whlie the script runs.
  • Setup-Profile script, containing the above mentioned functions, and used to setup the $PROFIEL as well as it's supporting environment. The script has the following features:
    • Can run without any dependency on any other script
    • Decides on the prefered $PROFILE context based on the following:
      • If running as admin, chooses the AllUsers context, otherwise write to CurrentUser context.
      • If passed the -sudo switch, will use sudo to acheive 'running as admin' status, will use sudo only on global resources.
      • If code exists, will replace it, otherwise will insert the code at the beginning of the $PROFILE file.
      • Can be run with -RemoveOnly to only remove existing common-profile-code.
      • Respect existing content, and ask permission before destroying exsiting work.

The following are functions embedded within ProfileCode_common. These will always be part of the loaded profile:

  • 'Get-PowerShellPath' - Identifies the path of the shell
  • 'Export-FunctionSource' - Exports the source code of functions, either with the function name header, or without.
  • `Test-IsAdmin' - return $True when running 'as admin'
  • 'ConvertTo-Base64' - outputs a Base64 version of it's input string
  • 'Invoke-ExpressionEx' - An extention for Invoke-Expression, one that will allow runing PowerShell code, even functions, with sudo (or without).

How to Use the Setup-Profile Script

The script syntax is as follows:

Setup-Profile [-sudo] [-RemoveOnly] [-GitClone [-GitURL <string>]] [-Force]  [-ShowSkipped] [-WhatIf] [-Confirm] [<CommonParameters>]
Setup-Profile syntax

Probably, the most common invocation, in an environment already setup, where you would like to update existing $PROFILE would be:

Setup-Profile [-sudo] -Force

The -Force will be required here to overwrite an already existing $PROFILE file.

Where this script really shines though, is in it's direct-from-web invocation:

Invoke-WebRequest https://code.lksz.me/lksz/PowerShell_Scripts/raw/Setup-Profile.ps1 | Invoke-Expression 

or the shorthand version (also the first line of this post)

iwr https://lksz.me/pwsz | iex

This will grab the code from the https://code.lksz.me/lksz/PowerShell_Scripts/raw/Setup-Profile.ps1 URL, pass it through Invoke-Expression.

One final trick this script does, is loading the entire script into memory, allowing to construct a one-liner that not only loads the script from the web it also allows to execute it with parameters:

$SetupFromWeb=1; iwr https://lksz.me/pwsz | iex; _setup -sudo -GitClone
Clone the PowerShell_Scripts git repo and setup the $PROFILE to utilize it

* Source Code

Link to latest version At the time of writing
Edit-MyProfiles commit ae07e06c27
Edit-MyScript commit ae07e06c27
Edit-TextFile commit ae07e06c27
Setup-Profile commit ae07e06c27