Training and Invoke-Command

I’ve finally signed up for MS 50025A. I know that soon it will be outdated. But to be honest I really want to finally get some training in PS area. I know how valuable it can in opposite to reading articles/ books – I’ve seen it with VBS. It will be in June, so I should have TFM already. But I guess I will take course first and move on with the book soon after.

Today I finished my script for 4th event, PS, advanced. And my main problem was remoting – I know very little about it and had some serious problems with forcing it to use my variables when invoke-command some .NET class static method. I first tried:

Invoke-Command -Session $Session { [Environment]::GetEnvironmentVariable($Name, $Target) }

And got only error. So I search on similar problems found few suggestions, neither worked, got frustrated… ;)

Finally I came across this article: How to pass arguments for remote commands on Windows PowerShell BLog. And – man – it became so obvious when I read it. I decided that (param) statement is to much for my needs so all I need to do was:

Invoke-Command -Session $Session -Args $Name, $Target { [Environment]::GetEnvironmentVariable($Args[0], $Args[1]) }

Sweet and simple. Now to the next event of:

2010_scriptgames_badge1

Scripting Games: Advanced or Beginner? Or maybe both? :)

2010_scriptgames_badge1

OK, I’m really having fun while taking part in Scripting Games. Writing script with a purpose, even if this is something I would personally never use, helps a lot when you are trying to know new technology. I love writing cmdlet scripts, because it so simple to get parameters, write help, provide examples, and so long, and so forth, that it’s really hard to resist. There is one thing though I can’t understand.

I’ve decided to do “Advanced” scenarios, and I feel comfortable with it. However it’s not hard to notice that there are people out there who do both. I wonder what’s the point? Most of scenarios for beginner are those for advanced with some pieces cut off. In other words: if you created script for adv it’s only a matter of deleting few lines to create beg as well. Of course it does not harm me in any way, I’m glad that I got so many points already, but… why?

In my opinion it should be different: either scenarios should be written in a way that prevent such a easy-solution (write once, submit twice) or it should be clearly stated that one can only submit to each event with selected level.

So: I won’t do this cut & paste, I’m loosing few points here and there maybe, but I prefer to get rewarded for thinking rather than copy & paste. That’s too simple and has nothing to do with scripting games. ;)

Meanwhile: I’ve found so many cool things while writing the scripts for that challenges, consider this simple function:

function Get-CLICredential {
    $TempUser = Read-Host -Prompt "Please enter alternate username"
    $TempPass = Read-Host -AsSecureString -Prompt "Please enter password"
    $Global:TempCredentials = New-Object -TypeName Management.Automation.PSCredential -ArgumentList $TempUser, $TempPass
}

Don’t like GUI? Mouse is last thing you want to touch when playing in PowerShell? Need credentials here and there? Here it is. :)

You have to love RegExp! ;)

Second part of scripting games is out there and I’m still working with the first one. Well… I’m busy man after all, right? ;) I re-written whole script, mainly because I realized it will be impossible to use PSDrives in remote scenario, and I would really love to do “advance” version. Not that I’m all that advanced… It simply a matter of ambitions. ;)

Today I’ve read Don Jones post on his “PowerShell with purpose” Blog and I decided to go this route (script cmdlet looks so COOL! ;)

I’ve also wanted to add some logic to test registry path that user would provide and found out that besides “ValidateSet”, there is also “ValidatePattern” there waiting for me to grab it and do some cool stuff with it. But, to be honest, error message is not really obvious I’m afraid…:

D:ScriptsPowerShellUpdate-RemoteRegistry.ps1 : Cannot validate argument on parameter 'RegistryKey'. 
The argument "HKLM:Software*ScriptingGuys2010ScriptingGames" does not match the "^HK(LM|CU):\[^*?:<>|]+$" pattern. 
Supply an argument that matches "^HK(LM|CU):\[^*?:<>|]+$" and try the command again.
 
But it’s so ‘geeky’ that I can hardly resist it. And, after all, it won’t be ‘normal user’ who will use this cmdlet, right?
 
Anyway, I’m back where I assumed I was when writing previous post. I only hope that this time I’m not too optimistic and I really have to do only “ACL” part of the script… And with the .NET class it shouldn’t be that hard. I hope. ;)

mkdir and it’s limitations

OK, so ScriptingGames are started. First task looks pretty cool. I’m not very familiar with working on a registry keys using PowerShell, so it’s great opportunity to start using it also for reg-related tasks. First problem I went into is mkdir behaviour within this PRProvider.

On filesystem it’s not very hard to create even longest path, just type:

 
mkdir c:temponetwothreefourfive

Everything (maybe except drive) might not be there, command will create full path. Not so in registry… So first thing I did was prepare something to check if given key exists and prepare all folders (keys) from top to bottom. I’m not sure if my way is the best, probably not. Has two main advantages: it works and it’s MINE! :D

So let’s assume $RegistryPath does not exist. What should I do?

Well, no I know, use –Force parameter…. ;) But I first tried harder way… :D
See below:

# $RegistryPath - path to create
# $Check-RegPath - function that returns 2 if path is invalid, 1 if it's valid but does not exist, 0 if it exists
 
$RegistryParent = $(Split-Path $RegistryPath)
$RegLeafs= $RegistryPath.Split("")
$i = $RegLeafs.Count - 1
do {
    $RegFlag = Check-RegPath $RegistryParent
    if ($RegFlag -eq 1) {
        # Time to move from top to bottom
        $RegistryParent = $(Split-Path $RegistryParent)
        $i--
    } elseif ($RegFlag -eq 0) {
        # Now it's time to move from bottom to top
        do {
            Write-Debug "$RegistryParent <= folder; $i <= index"
            $RegistryParent = Join-Path $RegistryParent -ChildPath $RegLeafs[$i]
            mkdir $RegistryParent
            $i++
        } while ($RegistryParent -ne $RegistryPath) 
        Write-Debug "Done!"
    }
} while ($RegFlag -ne 0)

Now what left is:

* take care about ACL (Yak! I’m admin, you see, I don’t really care! ;) )

* extend error/ information handling

Book, tab and the rest.

OK, I finally decided to buy myself present for the saint’s day. PowerShell 2.0 TFM. Paperback will arrive around May, or June… It could be quicker but I prefer to spend money on books rather than on delivery. ;)

Today I’ve also noticed that TAB is really annoying when it comes to tabbing thru hidden folders. Simply because it ignores them. I’ve googled a bit for it and found out that TabExpansion is nothing more than a function. Pretty long one… ;)

(get-command TabExpansion).definition.length 
10725

And I guess the reason why it’s not able to see my hidden folders is that it uses Resolve-Path, which has no –Force parameter (one that is necessary to get hidden folders while using Get-ChildItem). But I’m not 100% sure, after all I’m not the author of this function. ;) Sad if you really need to use hidden folders from time to time and they have spaces in names (Local Settings would be a good example).

There are 2 possible options now: try again to work with PowerTab (first time I’ve tried to use it it simply didn’t work out for me) or try different approach with folders/ files tab expansion by re-writing TabExpansion function. Yak. ;(

Meanwhile I got another one-liner to get total size of current folder and subfolders:

 
"Total size: {0:0,0.00} MB" -f ( (ls * -r -fo | measure length -s).sum / 1MB )

Now I know where/why all of my free disk space disappeared. ;)

$OneLiners

I’m really big fan of one liners. When I was playing with bash, cmd and other shells I liked the idea of doing something in one, single line. One problem though: you had to think all the time what comes out of the first command in the pipeline. Than do the same for the next. And again, and again… Quite frustrating and really difficult to develop. And once you did it – you’ve tried to launch it on a different OS version, where output looked slightly different and *BOOM* – unpredictable results.

It’s totally different story with PowerShell. Piped object is usually easy to track, modify, pushed to next pipe element. In fact the only thing that was better in text consoles (and I hope it’s feature that will be added in next PowerShell version) is && and || which I used a lot both in batch and cmd.

So, I wrote tones of one-liners in Powershell but after a while I got into problem: OK, I know I managed to solve this problem in a single line, but… Memoria fragilis est

I’ve decided one day, that if I create one-liner it would be good to have it stored somewhere. One thing led to another and I end up with $OneLiners array that contains few objects with three properties: name, command and description. It’s all done via Export/Import-Csv, but because comma would be dangerous in this case I’ve decided to separate values with “#” sign. I also put everything in .ps1 file, so any editor used that supports syntax highlighting would show command properly. And, of course, I’ve included function to do it directly from PowerShell, but it’s so simple that it’s not worth mentioning. ;) Anyway, file format is something like:

Command#Name#Description
gqadu -SearchRoot $LDAP_WAR | Select SAMAccountName, Name | % { $groups = @(Get-QADMemberOf $_.SAMAccountName | select Name | % { $_.Name.ToString() } ); if ( ( $groups -contains "SmartCard_No_NovellClient") -and ($groups -contains "+Warsaw-CRA") ) { $_.Name } }#AutoNovell#List all CRA-s that are automatically logged on Novell
gqadu -SearchRoot $LDAP_WAR | select Name, PasswordExpires | ? { ($_.PasswordExpires -lt $(Get-Date).AddDays(7) ) -and ($_.PasswordExpires -gt $(Get-Date).AddDays(-7) ) }#SoonExpire#Get list of users whos password expired/ will expire soon
Get-QADGroup -SearchRoot $LDAP_WAR | ? { $_.Name -match "L3$" } | % { $group = $_.Name; $groupMember = "" | select Name, SAM, L3; Get-QADGroupMember $group | % { $groupMember.SAM = $_.SAMAccountName; $groupMember.Name = $_.Name; $groupMember } }#L3User#List users that are members of L3 group

(gqadu is alias that I use for Get-QADUser).

OK, but how would I launch this line? In fact it’s quite simple. I’ve just added Import to my $profile. And use Invoke-Expression (or iex, to make things quicker to write):

$OneLiners = @(Import-Csv -Delimiter '#' "C:TMPPowershellOneLiners.ps1")
Invoke-Expression $OneLiners[0].command

Today I’ve tried to move a bit closer to the thing I would like to accomplish – why use property as a parameter to command, why not add a method to an object and do it directly. So I did something like that:

foreach ($line in $OneLiners) 
{ 
    $line | Add-Member ScriptMethod Run {Invoke-Expression $this.command }
}

Yuppi! It worked! Well… it for some reason ‘killed’ pipe (all results came out once everything was collected), and for some reason one of one-liners misbehaved when launched via $OneLiners[x].Run() – but besides that… ;)

Now I need to find out why is it so. For now – I have no clue. :(

First entry. ;)

I’ve tried to start blogging several times in the past. Always failed. Mainly because I don’t really like to write to everybody out there – this way it seems like writing to nobody. Nobody reads, nobody cares, nobody notices. ;) I hope this time it will be different. at least on my side. It may happen that again: nobody will ever read any of my blog-posts. But I decided that this time I will try to be more technical. Which means that it may happen, that my blog posts will help others solve their problems. Or help them realize that they do have a problem that they haven’t noticed before. ;)

I’m IT person so I will focus on IT problems that I come across. I’m also script maniac, been writing scripts for years now. Mainly in the windows environment. I’ve been using cmd for many, many years. Until I’ve wanted to check batteries status within our location. 100+ laptops… Googled it, as usually. And got some simple query using something named “PowerShell”. I knew there was WMI out there, I knew there was VBS too. But I simply liked the idea of interactive shell. And here it was: WMI, VBS and shell in one. “One to rule them all”, came across my mind. And now I’m trying really hard to become Lord of the Rings. ;)

Meanwhile I had chance to participate in VBS + WMI training and I really enjoyed both the training and writing scripts in VBS. But life was never same again… ;) I’m PowerShell addicted now, and write both VBS scripts (in areas where it would be possible to share it with others in my organization) and PowerShell scripts/ one-liners – for myself. :) I also like to do things that can be done GUI-way in PowerShell directly, just because it’s quicker and more consistent than to click my way thru GUI interface.

Why in English? Mainly because I use EN_en at work. If I do something in IT area I usually think in English. ;) So even though my English is not as good as I would like it to be, I don’t see any other option.

This is first post. Hopefully not the last one. ;)