This continues a short series of posts about getting started with PowerShell … with a few tips about things you can do to keep your PowerShell profile safe and organized. Your “profile” is the script that is automatically loaded when you start up PowerShell. Really, I should say that your profile is the set of scripts which are loaded by default when you start up PowerShell. “By default” because you can always skip loading them by passing the -NoProfile switch to PowerShell.exe, and a set because PowerShell does, in fact, attempt to load at least four scripts when you run it:

PowerShell loads “machine” profile scripts (which are located in the PowerShell folder) and “user” profile scripts (located in your Documents\WindowsPowerShell folder). But there’s a little more to it than that: PowerShell is a scripting engine which can be hosted inside any app, PowerShell.exe is a DOS-style console which is the default host. There are several third-party hosts available such as PowerShell Plus and PowerGUI and several open source hosts such as BgShell and PoshConsole … in order to support this ecosystem of hosts, the default PowerShell behavior is to load a host-specific profile script (for both the machine settings and the local-user settings). Not all hosts will do that, but anyway … the default host loads Microsoft.PowerShell_profile.ps1 and Profile.ps1 from both the user and machine locations.

By default, none of those profiles actually exist. Once you’ve installed everything as in Part 1, you should have a Profile.ps1 file provided by PSCX. This profile defines a whole bunch of values that are used by various PSCX cmdlets and scripts, so you may want to change some of it, but you should be careful about just deleting things until you’re well acquainted with the PSCX cmdlets. Over time, I’ve added settings to my profile for other snapins as well, and there gets to be a lot of noise in there that’s specific to different snapins, so instead of just leaving all of that in my main profile, I rename the Profile.ps1 file provided by PSCX and then dot-source it from a new blank profile script.

In fact, and I found that I started collecting a lot of scripts in my WindowsPowerShell folder so I created a sub-folder for them, and I automatically load everything that’s in that folder, so I don’t have to manually dot-source things when I add a new snapin profile.

Authenticode Signing your auto-load scripts

In order to make sure that automatically loading scripts doesn’t become a way for people to attack my computer, I made a decision awhile ago that I would only auto-load signed scripts. The how and why of this is a bit much to get into, and I wrote about Generating Windows Authenticode Code-Signing Certificates with OpenSSL a while back, so you can read that if you want more details, I want to review the simplest steps.

[new] Edit: I should take a moment here to point out, in case you’re really new to PowerShell, that the first thing you’ll have to do is fix the error “File … cannot be loaded because the execution of scripts is disabled on this system.” I originally was just assuming you would have figured that out — it tells you to read the about_signing help, so you can just execute get-help about_signing and it explains in there what you need to do. In case you haven’t gotten that far, before you can execute the New-CodeSigningCert you’re going to need to run PowerShell as administrator (“elevated”) and execute this command: Set-ExecutionPolicy RemoteSigned … that will let you execute most scripts that you have saved locally without having signed them. You may want to tighten that up later and set it to “AllSigned,” but first you have to be able to sign scripts.

Generating a code-signing certificate

[new] Edit: I’ve simplified this a bit, and modified the script to import the certificate to the Current User certificate store if you aren’t running in an elevated console (in Vista) — which should work fine if you’re the only user on your machine that needs to run the scripts — if you need them in the Local Machine store, you need to be running as administrator. The script will let you know which it does, so you’ll know, one way or another.

Step 1. Download my PoshCerts package and unpack it in your WindowsPowerShell folder — lets say in, WindowsPowerShell\PoshCerts\bin.

Step 2. Run the New-CodeSigningCert script to generate into the Certs folder. So if you’re in the WindowsPowerShell directory, you can just run it and you’ll be prompted for a few pieces of information that the certificates require, plus two passwords — and all of the keys will be generated into the Certs folder as files, and imported to your local store. Make sure you make a note of those passwords.


cd (Split-Path $Profile)\PoshCerts\
.\bin\New-CodeSigningCert.ps1 $pwd "[Your Name]" "[Your Email]" -ImportAll
 

[new] Edit: I came up with a Step 3. Under most circumstances (that is, unless you were planning on running a corporate Certificate Authority), you’ll have no further use for the CA certificate, If you want to make sure that nobody can abuse your root CA … you can and should just delete the private certificates, like so: Remove-Item *Root-CA.* -exclude *.crt

Signing Scripts

You can now sign scripts at will using the pfx file generated in step 2 with the Get-PfxCertificate cmdlet, or the Cert: provider (you have use -ImportAll when you call New-CodeSigningCert, not just -Import — that causes the import of your code-signing certificate to the “My” store … then you can do Get-ChildItem "Cert:\CurrentUser\My\$thumbprint" where $thumbprint is your certificate’s thumbprint).

Let’s rename our Profile.ps1 script and then sign it. We’ll create create a folder called “AutoModules” to stick your PSCX profile (and any future such scripts) in. If you’re still on PowerShell 1, you’ll just put the .ps1 files into that folder, and we’ll dot-source them.


cd (Split-Path $Profile)
mkdir AutoModules
# Sign it (you'll reuse this exact code in a moment)
Set-AuthenticodeSignature -Cert (
    # You'll be prompted for your code-signing cert password here:
    Get-PfxCertificate .\PoshCerts\*Code-Signing.pfx
    ) .\Profile.ps1
# Now move it (the signature doesn't care about the file name).
mv .\Profile.ps1 .\AutoModules\PSCX.ps1

 

[new] Edit: The simplest

Checking for valid signatures

Once you’ve got that set up, we need to create the new profile script and modify it to load not just the PSCX module, but any other signed scripts we put in there. Paste this into your new Profile.ps1, and don’t forget to sign it.


$ProfileDir = Split-Path $MyInvocation.MyCommand.Path

## If we're on version two, we need to set the PsPackagePath (it doesn't hurt anything anyway)
$ENV:PSPACKAGEPATH = "$ProfileDir\AutoModules"

## Now, preload any script in AutoModules that is signed ...
##### Problem 1:  There's a bug which prevents signing or checking psm1 files
##### I'll show you how to work around that in Part 3
##### For now you don't have any, so this is quite simple:
Get-ChildItem $ProfileDir\AutoModules\* -include *.ps1 -recurse |
   Get-AuthenticodeSignature |
   Where-Object {$_.Status -eq "Valid"} |
   Get-ChildItem | &{ PROCESS {
      switch -regex ($_) {
         ".*\.ps1$"    { . $_.FullName; Write-Verbose "Loaded $_" }
         ".*\.ps1xml$" { Update-TypeData -PrependPath $_.FullName  }
         default        { Write-Verbose "Not sure what to do with: $($_.Name)" }
      }
   } }
 

If you’re testing PowerShell 2, you’ll want to treat these scripts as Modules … SnapIns themselves can be loaded as modules in PowerShell 2, but so can any script which needs to be dot-sourced. In this case, the easiest thing is to create a subfolder for each one and then use Add-Module with the folder name. There are some problems with that, which I’ll go into in Part 3.