Powershell

As you may have heard already, my desktop is back to working condition. I decided to turn my hardware failure into a positive thing and take the time to upgrade my desktop. Not only did it get a new and shiny video card (nVidia GeForce GTX 760 – I kinda wanted the 780 but I would have to replace my PSU) but also a secondary hard drive on which I installed Windows 7. My rig was running Vista up until that point mostly due to the fact I was way to lazy to perform the upgrade. I figured that I would probably never get a better opportunity to do a clean OS install on this machine.

As a result the last week or so I have been running with a reduced set of tools on my machine. For example, the first few days I did not have Cygwin on board because I didn’t feel like going through the installation process which requires you to manually pick packages from a long list (that or install bare bones setup that typically doesn’t even include ssh). So as I was installing my regular programming environments (Ruby, Python, Node) I needed a working command line client. I typically use Cygwin for most things, and then cmd.exe when I’m in a pinch. The problem with cmd is that it is very, very limited in what it can do, and scripting in it is cumbersome. Bash can be quirky as a thousand fucks, but compared to cmd shell it is a walk in the park.

Windows does however have a proper shell these days. It is called Powershell and it was designed specifically to provide Windows admins with a Unix style experience. Let’s face it – while Cygwin is great for getting shit done it is not the best tool for system administration. That’s not necessarily the fault of Cygwin but rather design philosophy difference between different platforms. POSIX systems are designed around flat files. On any Unix-like systems config files are plain text, and so are log files. In fact even system and process info is accessible as flat files in the /proc directory (except Macs – Macs don’t proc). As a result most of the Unix tools used by admins evolved towards being great at parsing, processing and editing text files. On Windows on the other hand almost all admin-relevant data is encapsulated in some sort of object stores. Configuration is sealed in the Registry hives, system info is hiding in WMI store and logs are stored as Event Viewer blobs. All these constructs are proprietary Microsoft inventions and can’t be parsed using standard POSIX tools. As a result a Unix transplant system such as Cygwin can only be a second class citizen in the Windows universe.

Powershell was designed to be cmd.exe replacement that works with Windows rather than battling against it. It provides easy access to all the underlying data structures that are foreign to Unix tools, and much too complex for it’s simplistic, DOS derived predecessor. Not only that, but it also throws a bone to anyone coming from Unix background by aliasing a lot of its internal functions to classic short POSIX commands. So, for example, if you type in ls at the prompt it will work exactly as expected.

Let me give you a quick example just how neat Powershell can be sometimes. Whenever I build Win Boxen for work purposes, I like to change their Windows name to their vendor service tag / serial number because this helps a great deal with asset tracking automation and the like. How difficult is that to do in Powershell? Super easy. It can be done in 3 lines like this:

$serial = (gwmi Win32_SystemEnclosure).SerialNumber
(gwmi Win32_ComputerSystem).Rename($serial)
Restart-Computer -Force

So what is the problem with Powershell? How come people still bother cmd.exe batch files or VBS deployment scripts? How come Powershell did not become the one and only Windows shell?

Well, in its infinite wisdom Microsoft decided to cripple Powershell long before it could ever become popular. By default it starts with restricted execution policy. This means, no scripts of any kind will ever be allowed to run on your machine. When you double click a .ps1 file it throws an error. When you try to invoke a script you wrote yourself from the command line, it will puke half a page of errors at you before it crashes and burns.

  1. Design powerful new shell that can effortlessly hook into all the subsystems and data stores a Windows admin could ever need.
  2. Make all the commands have long-form aliases that are conducive to scripting (yielding clean and readable code) such as cd being just an alias of Set-Location.
  3. Install this shell on all modern Windows versions
  4. Register .ps1 as the executable Powershell script file type so that anyone can double-click these files to run scripts.
  5. Disable running of all scripts by default.

What the fuck?

Seriously, what the fuck happened there? What was the reasoning behind it? This is like creating an image editing software and disabling editing of images by default.

Actually, lets back up. Powershell is not new. It has existed since Windows XP days, back when Microsoft’s approach to system security was more or less “lol, buy a Mac”. They of course rescinded on that policy as soon as OSX came out and people figured out that Macs were actually a viable option. Today we exist in a world in which Windows actually ships with somewhat sane security setup. This was not the case in XP era when you couldn’t let a machine touch any networks until it was fully patched and bristling with at least 3 brands of security software. Back then the engineers saw the .ps1 file format and went great, yet another malware delivery vector. So they did the best thing they could: they plugged that hole before it became a serious security threat.

Naturally this ended up being only a half measure, because you can still easily trick people into running Powershell scripts by asking them to copy and paste code into the command line window. For example, this is how you install Chockolatey. Granted, this requires slightly more social engineering than just giving someone a script renamed to appear as porn.

Which is why, we don’t really have Powershell based viruses out there. Powershell was forever enshrined as the scripting language created by sysadmins and for sysadmins to do some admin stuff, but only if it does not involve users in any way. Why? Because to enable scripting you need to have admin privileges on the machine. Which is something sysadmins usually do, and end users they support do not. Which means that you can’t just write a shell script and give it to users, but you might be able to deploy them to deploy something across the domain.

If you are planning to use Powershell as a cmd.exe replacement, the first thing you need to do is to change the execution policy to enable scripting. To do that, you need to run Powershell as Admin and then execute the following command:

Set-ExecutionPolicy RemoteSigned

From that point on, scripts you write yourself or download from the internet will run as expected. The second thing you probably want to do is to create a profile. In Powershell, profiles work exactly the same way Unix profiles do. It is a script that gets automatically executed whenever you launch a new shell. The default path to your profile is kept in the $profile environment variable. That path is always there, but typically the file itself won’t exist unless you create it yourself. This can be easily done from the command line like this:

new-item -path $profile -itemtype file -force

At that point you can open it up in your favorite text editor like so:

notepad $profile

Substitute your preferred editor for notepad, but only if it is in the path. What goes in the profile? One thing you should probably consider putting there is a fancier shell prompt. Other things could for example be aliases. Here is mine:

function prompt
{
    # Check for Administrator elevation
    $w=[System.Security.Principal.WindowsIdentity]::GetCurrent()
    $p=new-object System.Security.Principal.WindowsPrincipal($w)
    $a=[System.Security.Principal.WindowsBuiltInRole]::Administrator
    $isAdmin=$p.IsInRole($a)

    if ($isAdmin) 
    {
        Write-Host "ADMIN" -NoNewLine -ForegroundColor White -BackgroundColor Red
        Write-Host " " -NoNewLine
    }

    Write-Host $ENV:USERNAME -NoNewLine -ForegroundColor Green
    Write-Host " @ " -NoNewLine
    Write-Host $ENV:COMPUTERNAME -NoNewLine -ForegroundColor Yellow
    Write-Host ": " -NoNewLine
    Write-Host $(get-location) -NoNewLine -ForegroundColor Magenta

    Write-Host " >" -NoNewLine
    return " "
}

set-alias gvim "C:\Program Files (x86)\Vim\vim74\gvim.exe"
function g { gvim --remote-silent $args }
function gd { gvim --servername DEV $args }

To change the command prompt you simply define a prompt function. The only caveat here is that it must return something that is non-zero and not an empty string. If you omit the return statement or return zero or blank string Powershell will simply append PS> to whatever was there. Other than that you can echo-out just about anything.

As you can see above, I’m using a unix-style prompt with my username, host name and path. If Powershell is called with elevated rights, there is also big, red, honking “ADMIN” tag up front to let me know I’m in the danger zone. Once you launch it, it looks more or less like this:

Powershell

My Powershell Prompt

And yes, my desktop is Gandalf, and my laptop is Balrog and they are usually on the same desk. I do realize I’m probably courting a disaster with this naming scheme.

Naturally, Powershell is not a replacement for good old Cygwin. For one, Cygwin provides me with native ssh and scp whereas with Powershell I have to use PuTTY and similar tools as proxies. Well, that and I don’t think I could ever wean myself from basic POSIX tools. Especially I tend to jump between Ubuntu, OSX and Win7 all the time.

Do you use Powershell on any Windows boxes? Do you have any nifty tips or tricks? What is in your Powershell profile right now? Let me know in the comments.

This entry was posted in Uncategorized. Bookmark the permalink.



7 Responses to Powershell

  1. Grzechooo POLAND Opera Windows says:

    I’m not using Powershell much, but now you’ve got me interested. Do you know of any “tutorials” other than RTFM aka Get-Help?

    Reply  |  Quote
  2. Eric Google Chrome Windows says:

    Powershell is great. I add the cygwin\bin directory and vim to the path, and together the combination makes me not hate Windows at all.

    I also use it in combination with group policy as my main scripting language for supporting Windows P.C.’s.

    My only complaint is I haven’t been able to find a solarized color scheme. When I saw this post I was hoping you would have had a workable solution in your Powershell profile. But great post anyway, I’m glad to see other command line aficionados appreciating Windows.

    How to tell if Powershell Inventor Jeff Snover is driving in front of you.
    http://blogs.msdn.com/cfs-filesystemfile.ashx/__key/communityserver-co mponents-postattachments/00-02-28-75-22/PowerShellLicensePlate.jpg

    Reply  |  Quote
  3. Luke Maciak UNITED STATES Mozilla Firefox Windows Terminalist says:

    @ Grzechooo:

    Not really, sorry. I mean, it’s a shell so you kinda learn it the way you learn every shell – by using it. Instead of opening cmd, just use powershell, and whenever you need to do something fancy, google how to do it in it. That’s kinda how I learned Bash and how I cobbled together cmd.exe Batch scripts.

    Probably the best part of Powershell is that most of things return objects, so you typically don’t need to parse output to get the chunks you need. And if and when you do get a string, it is actual proper string object so you can do things like:

    "foobar".Substring(0,3)

    This alone makes it a 100% improvement over the string parsing in Batch files.

    @ Eric:

    Actually, I kinda cheated and used this registry hack. On my machine (win7pro) it turned cmd.exe to solarized like color scheme, but not the default Powershell console which remained blue. So I popped up the Properties section and just kinda copied the color codes over from the other one.

    It works ok, but the default windows console is not really 256 color terminal so you can’t even get close to the actual full range. If you want actual solarized support using 3rd party terminal like ConEmu is probably your best bet. :)

    Reply  |  Quote
  4. Jeffrey Snover[MSFT] Mozilla Windows says:

    $serial = (gwmi Win32_SystemEnclosure).SerialNumber
    (gwmi Win32_ComputerSystem).Rename($serial)
    Restart-Computer -Force

    Whoaa – I didn’t see that one coming.
    I love it!

    Jeffrey Snover [MSFT]
    Distinguished Engineer and Lead Architect for Windows Server and System Center

    Reply  |  Quote
  5. Jeffrey Snover[MSFT] Mozilla Windows says:

    > Seriously, what the fuck happened there? What was the reasoning behind it?

    Oh that. Good question. Simple answer:
    ILoveYou.vbs
    I’d like to be remembered after I die but not for being the guy that brought down the internet and destroyed Microsoft in the process.

    Now that we have a quite a few drama-free years behind us and the use of PowerShell has become a critical IT activity, we have re-evaluated a number of our initial security decisions for Windows Server (but not client – the % of client machines that use PS means we still want to be cautious).
    In WS2012 we turned remote management on by default.
    In WS2012/R2 we set the default execution policy to RemoteSigned.

    Apologizes for the PITA initial config but if we were not abundantly and overly cautious and something had gone wrong – the immune system reaction would have been swift and decisive and there would be no PowerShell and we’d all be flipping hamburgers. :-)

    Jeffrey Snover [MSFT]
    Distinguished Engineer and Lead Architect for Windows Server and System Center

    Reply  |  Quote
  6. Luke Maciak UNITED STATES Mozilla Firefox Windows Terminalist says:

    @ Jeffrey Snover[MSFT]:

    Heh, today I taught MS Engineer a new thing about Powershell. :P

    But yeah, after a few seconds of reflection I figured out that the decision was made to stave off use of Powershell as a virus delivery vector. Good to hear servers will now have it enabled by default.

    In the meantime, you can still pretty reliably use PS scripts on client side – you just need a batch file wrapper:

    @powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex script.ps1"

    Or you can execute a script from the web:

    @powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://example.com/script.ps1'))"

    Reply  |  Quote
  7. Pingback: A list of things you may possibly need, but maybe not (2013 edition) | Terminally Incoherent UNITED STATES WordPress

Leave a Reply

Your email address will not be published. Required fields are marked *