Tome's Land of IT

IT Notes from the Powertoe – Tome Tanasovski

Category Archives: Powerbits

Powerbits #10 – history |grep or hgrep in PowerShell

I’ve been working a bit more in Linux. Besides it inspiring me to write a lexical parser in python for PowerShell (don’t get your hopes up – it’s too much work, and I can’t do it), I keep getting little inspirations for cool projects. For example, someone needs to create tmux for PowerShell; I digress. This powerbit is from that inspiration. Recently, my laptop’s up and down arrows stopped working. Because of this, I have been relying on history |grep something in Linux so that I can quickly copy and paste that something back into the commandline. Context Swtich! Yesterday I was in PowerShell, and I got frustrated by the output of get-history (I really only want the commandline property) and the complexity it takes to filter for what I want using Where-Object.   As I was discussing this with a colleague, I said, let me just wrap this already and never worry about it again. So here it is, hgrep:

function hgrep {
    param(
        [Parameter(Mandatory=$false, Position=0)]
        [string]$Regex,
        [Parameter(Mandatory=$false)]
        [switch]$Full
    )
    $commands = get-history |?{$_.commandline -match $regex}
    if ($full) {
        $commands |ft *
    }
    else {
        foreach ($command in ($commands |select -ExpandProperty commandline)) {
            # This ensures that only the first line is shown of a multiline command
            if ($command -match '\r\n') {
                ($command -split '\r\n')[0] + " ..."
            }
            else {
                $command
            }
        }
    }
}

you can use it like this:

hgrep something

While, it’s bad practice to add the |ft command to the function, I find it useful so that I can use the -full switch to get everything I want in the format I want to see it:

11:17:22 PS D:\Dropbox\scripts> hgrep longfunction -full

   Id CommandLine                  ExecutionStatus StartExecutionTime              EndExecutionTime               
   -- -----------                  --------------- ------------------              ----------------               
   9 longfunction                        Completed 9/26/2013 11:00:00 AM           9/26/2013 11:14:41 AM

You can fork or clone this gist off of github here, or just run the following:

git clone https://gist.github.com/6715170.git

Powerbits #9 – Get the directory of the calling script or get the current working directory of a session

This is one of my most used snippits of code. I generally put this at the beginning of every script. Every time I need it, I always say I need to post this as a powerbit so that I can find it more easily. Well, my wait is finally over, and you get the pleasant side effect of learning my most used trick.

The following bit of code will populate $currdir. If the script is run via a .ps1 file, it will set $currdir to be the directory where the .ps1 file lives. This will allow you to right click on a file and say “run with powershelll”, and have it use the files in that directory. The problem with this is that it will notwork if you are developing in ISE or in a powershell.exe host window. In that scenario, you generally want $currdir set to the directory your shell is in. This snippit will do just that, i.e., if it is not run via .ps1 file, it will load from $pwd (with some regex to strip out the psprovider nonesense).

$currdir = ''
if ($MyInvocation.MyCommand.Path) {
    $currdir = Split-Path $MyInvocation.MyCommand.Path
} else {
    $currdir = $pwd -replace '^\S+::',''
}

You can fork or clone this gist off of github here, or just run the following:

git clone https://gist.github.com/6715996.git

Powerbits #8 – Opening a Hyper-V Console From PowerShell

I am right now Windows Server 8 and PowerShell 3 Beta obsessed.  I want to blog – I want to blog – I want to blog, but I’m trying to hold back a lot of it until we see how everything shakes out.  I’m running Serer 8 beta on my new Asus ultrabook.  Because of this, I’m also running a ton of Hyper-V VMs on my fancy type-1 hypervisor in order to play with the new features in PowerShell 3 such as PowerShell Web Access and disconnected PSSessions.  I love the autoload of the Hyper-V module when I do something like Get-VM, but I was really disappointed that there was no cmdlet to open up a console session for one of my VMs.  I mean, I have no interest in loading up a GUI for Hyper-V to do this.  A bit of quick research led me to vmconnect.exe.

So, without further ado, here’s a quick wrapper that will let you open up Hyper-V console sessions directly from PowerShell.  This is now permanently in my profile:

function Connect-VM {
	param(
		[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
		[String[]]$ComputerName
	)
	PROCESS {
		foreach ($name in $computername) {
			vmconnect localhost $name
		}
	}
}

Now, you can either do something like

Connect-VM server2

or

'server2','server3'|connect-vm

Finally, if you are running PowerShell 3.0, you can do the following:

(get-vm).name |Connect-VM

Powerbits #7 – Copying and Pasting a List of Computers into a PowerShell Script as a Collection

It’s quite common to receive an e-mail from someone that has a list of computers that need a script run against it – or perhaps a list of perfmon counters that need to be collected – or even a list of usernames that someone needs you to pull from AD in order to create a custom report about the user and his attributes.  There are a few ways to put this data into your scripts.  Probably the most common method I have seen is to put this data into a file and run Get-Content file.txt.  This works fine,  but I generally just need to do a bit of one-off scripting and want to bang it out quick. When that happens I throw the list into a here string and break it up with the -split operator:

$computers = @"
Computer1
Computer2
Computer3
Computer4
"@ -split '\r\n'

This creates a collection of strings where each line has its own string.

PS C:\> $computers.gettype()

IsPublic IsSerial Name                                     BaseType                                         
-------- -------- ----                                     --------                                         
True     True     String[]                                 System.Array

Now you can throw this collection into a foreach loop

foreach ($computer in $computers) {
     #Do something to $computer
}

or better yet you can throw it at a parameter that takes a collection like Invoke-Command

Invoke-Command -Computername $computers -Scriptblock {Get-Process}

Nice! I just spanned out Get-Process to the four computers in my list!

Powerbits #6 PowerShell Runas Function for UAC

I was just browsing Osin’s latest contribution to the community, pseventing plus, which allows you to add hotkey events to your PowerShell sessions.  I was looking at his samples and found the handy one to start notepad with elevated privileges to open your hosts file.  If you are unaware, there is a -Verb parameter with Start-Process that will accept “Runas” as an argument to start a new process with elevated privileges.  If you wanted to open your hosts file with notepad the command would be:

Start-Process -Verb Runas notepad c:\windows\system32\drivers\etc

This is already pretty well documented, but I realized that I was foolish because I never wrapped this in a function and put it in my profile.  so I did it, and it is now here for you as a powerbit.

function runas {
    param(
        [Parameter(Mandatory=$true,Position=0)]
        [string]$FilePath,
        [Parameter(Mandatory=$false,Position=1)]
        [string] $ArgumentList
    )
    if ($ArgumentList) {
        start -Verb runas -FilePath $FilePath -ArgumentList $ArgumentList
    } else {
        start -Verb runas $FilePath
    }
}

Add this to your profile and you can use it like this:

runas notepad c:\windows\system32\drivers\etc

Feel free to change the name from runas if you don’t like that name.  For some, the name is probably confusing if you were used to using runas from a command prompt. I’m used to typing ‘start -verb runas’ so it makes sense for me.

Powerbits #5 Installing an MSI via WinRM (Remoting) with PowerShell

I had to look this one up today so I thought it was time I added it to the site as a Powerbit.  There are many ways to execute an msi remotely with PowerShell.  I find myself needing to remember how exactly to do this via WinRM on occassion. This is the technique I use to push out an msi installation in my labs.

$script = {
    #do preinstall stuff
    $args = "-i c:\path\to\msi\file.msi /qn /norestart"
    [diagnostics.process]::start("msiexec.exe", $args).WaitForExit()
    #do follow up stuff
}
invoke-command -computername (gc computerlist.txt) -scriptblock $script

You can also use WMI or schtasks to execute an MSI remotely, but that’s so last decade 🙂

Powerbits #4 – Generating a Text File of a Specific Size

I was recently helping with a problem in a forum post.  On this occassion the person who posted the thread received an elegant text parsing solution that just didn’t scale.  In order to help him further I needed to be able to generate a similar file to the one he was using.  I needed a 50 MB text file with specific text repeated over and over.  So let’s look at today’s powerbit that does just that:

$file = "test.txt"
$size = 50MB
$writechunks = 10MB
$string = @"
abc 123
def 456
"@
"" |Out-File $file

while ((Get-ChildItem $file).length -lt $size) {
    $string*($writechunks/16/$string.length) |Out-File $file -Append
}

The file may not be exactly the size requested, but it’s close enough for my purposes.

Powerbits #3 – Adding a User to a Local Group

I’ve been negligent in this series. Maybe that means I’m just not looking things up that much. Well, no matter. Today I had to look this up so I thought I would post it.

Here’s how you add a user to a local group using Powershell:

$domain = "powertoe"
$username = "Toe"
$computername = "toepc"
$groupname = "Administrator" #of course

$objUser = [ADSI]("WinNT://$domain/$username")
$objGroup = [ADSI]("WinNT://$computername/$groupname")
$objGroup.PSBase.Invoke("Add",$objUser.PSBase.Path)

The funny thing is that the reason the script I had to look this up was to write a script that gave me one of those Powershell-is-so-damned-cool moments. I needed to set a user as admin on every computer connected to a vcenter instance:

$computers = Get-VM |%{$_.Name}
$script = {
    $objUser = [ADSI]("WinNT://Powertoe/toe")
    $objGroup = [ADSI]("WinNT://$($env:COMPUTERNAME)/Administrators")
    $objGroup.PSBase.Invoke("Add",$objUser.PSBase.Path)
}
Invoke-Command -ScriptBlock $script -ComputerName ($computers)

Did I just do that in a matter of seconds!!!?

Powerbits #2 – Read-Host -AsSecureString

Read-Host is a great little cmdlet that gives you a very simple way of getting user input into your script.  On occasion you may have sensitive information being input that you don’t want pesky over-the-shoulder guests to see.  The most obvious time is when you need to grab a password from the user of your script.  Read-Host has a nice way of hiding the information with the -AsSecureString parameter:

-AsSecureString changes the output of the cmdlet from a String to a SecureString.  Generally this will be fine because you will probably be passing the captured output to another cmdlet that expects a SecureString, but this may not always be the case.  Perhaps you have a database user account that you want to gather the password for and then create a connection string within your script.  In this case a SecureString is not what you want.  So how do you convert a SecureString to a String?  The answer is this Powerbit:

$password=read-host -assecurestring
$decodedpassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))

Powerbits #1

I thought I would start a new feature.  I have a document I keep in dropbox that keeps my little Powershell notes.  It has quick and dirty tips.  Kind of a Powershell Cookbook of my own workings.  I thought I would share them since they are so useful to me (with the added benefit that I can search my site for them when I need them).

Powerbit #1 comes from a forum posting as most of my ideas seem to come from that revolve around Powershell.  The question posed is how can you run Powershell from a command line without having a powershell window pop up.  This is especially key for scheduled tasks to ensure that little windows don’t litter your console.  Fortunately my little Powerbits cheat sheet had an answer.

Let’s first look at Powershell.exe -? to see what it has to offer:

There are two parameters that look like they will solve our problem.

-NoLogo
    Hides the copyright banner at startup.
-WindowStyle
    Sets the window style to Normal, Minimized, Maximized or Hidden.

Unfortunately when you try the following from start->run you’ll see that it does pop open a window for a brief moment:

powershell.exe -nologo -windowstyle hidden -command "dir c:\users\ttanasovski\scripts\*.*|out-file c:\users\ttanasovski\scripts\test.txt"

There are two solutions to this problem.  One is to load up visual studio and compile an executable to launch the Powershell command or script.  The other is to wrap the command in a vbs script so that you can use the Run method of WSH:

Dim shell,command
command = "powershell.exe -nologo -command ""dir c:\users\ttanasovski\scripts\*.* |out-file c:\users\ttanasovski\scripts\test.txt"""
Set shell = CreateObject("WScript.Shell")
shell.Run command,0

Problem solved.  Thank you Powerbits sheet!