I was there! :)

2010 Scripting Games--I was there!

Yes, I was there. See here links to all my scripts:

Beginner (VBS): Evt 1, Evt 2, Evt 3, Evt 4, Evt 5, Evt 6,Evt 7, Evt 8, Evt 9, Evt 10

Advanced (PS): Evt 1, Evt 2, Evt 3, Evt 4, Evt 5, Evt 6, Evt 7, Evt 8, Evt 9, Evt 10

Or I would prefer to say: I’m still there. My mind is there still, and I really would like to have more events, more things to struggle with, more problems to solve with PowerShell. Why, you may ask. The main reason for that is simple: I have no chance to do the same in my organization, at least most of “run on my PC” stuff. No PowerShell on most of boxes out-there limits my PowerShell activity to things that can be done remotely in old-fashion way (gwmi –ComputerName, and stuff like that). So that’s why I’m still playing with my SG scripts and try to make them even better. Another thing is reading thru experts approach. I actually haven’t thought about making any of entries a real module… Now that’s something to look after if you would really like to make 5-star entry. I mean the one that would get 5-stars from any judge that would look at that. Complete and easy to Import- Remove-, as long of course as you don’t get overexcited and don’t do that for beginner events. 😉 But there are some ideas (ft within script? God NO!) that I don’t like at all. They are so… VBScript-way.

Yesterday I had interesting discussion on IRC channel. The main topic was: who is really master in Powershell and who is not. It was a dev-sysadmin front I would say. And I see clearly that sysadmin thinks different when he is using powershell than developer does. BUT – it’s also clear that it’s easier to communicate in that direction within powershell than within any other tool out-there. We (sysadmins) may see or do things differently than dev would, but there is pretty wide common ground where we can meet and discuss those differences in a way that both sides know what the other side is talking about. 🙂 Of course it does not mean that we agree on everything, we surely don’t (and I guess we shouldn’t) – but at least we can consider this second point of view when we are doing things in powershell. 🙂 That’s also beauty of that product. I think that is one of things (except for bank income) MS can be proud of. 🙂

Advertisement

Book arrived. :)

Finally I got my hand on PowerShell TFM. To be hones I haven’t expected it so soon. Hardly had time to read it though. Good news is that this week I will travel to work by bus/ subway and it means a LOT of time to spent on reading, one thing I can’t do if I (as usually) travel to work by car.

Second thing that SG are over. I mean in a write-scripts sense, because there is still a lot to come: experts articles, some articles by Scripting Guy Ed Wilson, and hopefully sooner than later – final standings. I’m looking forward to it, not that I expect some outcome (I probably ended in the middle of top ten, which is far more than I expected to), I’m just curious who won! 🙂

For me this whole SG stuff is very important learning experience. New things I thought I will never be able to learn. New ideas, new challenges I haven’t even though of before I had to in order to submit more than 2-stars script for advanced events. One script that I’m in a way proud of is the last one. It took me really a lot of time of banging head against desk in order to make all pieces to work well. It was a lot of effort for me to finally get everything in correct place, working both with ISE and standard console. But there it is:

<#
    .Synopsis
        Simple GUI app to move files away from desktop.
    .Description
        Tired of looking at all those files at your desktop? This script is here to help you!
        Not only will it move all files to different location that you choose.
        It will also create a link for you on your desktop!
        With no parameters given it will move everything but links.
        If you want to keep some other types - let the script know, simply add -Exclude extensions.
    .Notes
        Using registry to pass arguments between windows (yes, I'm sure there is easier way, simply could not find it...)
        Using windows.forms.messagebox as a simple UI.
        
    .Example
        Clear-Destkop.ps1 -Path C:temp -Exclude mp3, jpg
        Will move everything to c:temp and will not move any lnk, mp3 and jpg files.
    .Example
        Clear-Desktop.ps1 -Exclude txt
        Moves everything except .lnk and .txt to location that you select using GUI
    .Parameter Path
        Define path to folder that will be used to store files.
    .Parameter Exclude
        List of extensions that will be excluded from move operation
#>            
            
[CmdletBinding()]            
                
    param(            
    [parameter(HelpMessage='Provide a path to file(s)/ folder(s) that will be used to store files moved away from desktop')]            
    [string]$Path="C:TEMP",            
                
    [parameter(HelpMessage='Provide a list of extensions that will be excluded from operation')]            
    [string[]]$Exclude            
                
    )            
            
            
 if ([Threading.Thread]::CurrentThread.ApartmentState -eq 'STA') {            
  $isSTA = $true            
 } else {            
  $isSTA = $false            
 }            
             
 if (!$isSTA) {            
  Write-Host "Need STA mode or ISE to launch this script"            
  Throw "Use powershell.exe with -STA parameter or Ise to launch this script"            
 }            
             
 if (!(Get-Module WPK)) {            
  if (!(Get-Module WPK -ListAvailable)) {            
   Write-Host "Need WPK module (comes with PowerShellPack) to run"            
   Throw "Install PowerShell Pack first"            
  } else {            
   Import-Module WPK            
  }            
 }            
            
    #            
    # Just in case - fix Excludes first.            
    $RegPath = 'HKCU:SoftwareScriptingGuys2010ScriptingGames'            
    $__Exclude = @('.lnk')            
    foreach ($ext in @($Exclude)) {            
        $__Exclude += $($ext) -replace '^.*.|^(.*)$', '.$1'            
    }            
    # Get rid of all duplicates...            
    $__Exclude = @($__Exclude | Sort-Object -Unique)            
    #             
                
    if (Get-ItemProperty -Path $RegPath -Name 'Excludes' -ErrorAction 'SilentlyContinue') {            
        Set-ItemProperty -Path $RegPath -Name 'Excludes' -Value $__Exclude            
    } else {            
        New-ItemProperty -Path $RegPath -Name 'Excludes' -Value $__Exclude -PropertyType multistring            
    }            
    # And save a dest path in registry            
    Set-ItemProperty -Path $RegPath -Name 'Evt10Dest' -Value $Path            
                  
    # define some general-purpose functions (again - in global scope)            
    function Global:Show-UIDialog {            
        param([string]$MenuMessage, $MenuTitle)            
        [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null            
        $Answer = [Windows.Forms.MessageBox]::Show($MenuMessage,$MenuTitle,"YesNo","Question")            
        return $Answer.ToString()            
    }            
            
    Function Global:Show-UIMessage {            
        param([string]$Message, [string]$Title = 'Update-USBContents - Information')            
            [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null            
            [Windows.Forms.MessageBox]::Show($Message,$Title,"OK","Information") | Out-Null                 
    }            
               
                
             
    New-Window -Name "WPK" -Title "WPK Snippets" -Width 400 -Height 220 -Show -FontSize 14 -FontFamily Tahoma {            
        New-Grid -Rows 120, 56* {            
            $Text = "This program will clean up your desktop!"            
            $Text += "`nIf you like the idea - select path that you want to move your files and click YES!"            
            $Text += "`nIf not - just close the window. And suffer! :P"            
            $Text += "`nClick 'Folder' to select destination folder or manually enter path."            
            New-TextBlock -TextWrapping Wrap -Text $Text            
            New-Grid -Row 1 -Rows 28,28 -Columns 75,300* {            
                New-TextBlock -Text "Folder Path:" -Row 0 -Column 0 -Margin 3            
                New-TextBox -Name txtUserName -Row 0 -Column 1 -Margin 3 -On_Loaded {            
                    $RegPath = 'HKCU:SoftwareScriptingGuys2010ScriptingGames'            
                    if ($StartValue = $(Get-ItemProperty -Path $RegPath -Name 'Evt10Dest').Evt10Dest) {            
                        $this.Text = $StartValue            
                    }            
                }            
                New-Button -Content "Folder" -Name btnFolder -Row 1 -Column 0 -Margin 3 -On_Click {            
     [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null            
                    $obj = New-FolderBrowserDialog -RootFolder "MyComputer"            
                    $obj.ShowDialog()                   
                    $txtUserName = $Window | Get-ChildControl txtUserName            
                    $txtUserName.Text = $obj.SelectedPath             
                }            
                New-Button -Content "YES!" -Name btnGO -FontWeight Bold -Row 1 -Column 1 -Margin 3 -On_Click  {            
                    $txtUserName = $Window | Get-ChildControl txtUserName            
                    $Global:__Dest = $txtUserName.Text            
                    # Wy not use registry for that.... ;)            
                    # if even GLOBALS are missing in Script-Block? :(            
                    $RegPath = 'HKCU:SoftwareScriptingGuys2010ScriptingGames'            
                    if (!(Test-Path $RegPath )) {            
                        mkdir -Force $RegPath            
                    }            
                                
                    Set-ItemProperty -Path $RegPath -Name 'Evt10Dest' -Value $__Dest            
                                
                    New-Window -Name "FileMove" -DataBinding @{"Title" = "LastProgress.Activity"} -Width 600 -Height 200 -Show {            
                        New-Grid -Rows 100,100 {            
                            New-TextBlock -TextWrapping Wrap -Name "CopyFileName" -FontSize 16 -DataBinding @{"Text" = "LastProgress.StatusDescription"}            
                            New-ProgressBar -Row 1 -DataBinding @{"Value" = "LastProgress.PercentComplete"}            
                        }            
                    } -DataContext {            
                        Get-PowerShellDataSource -Script {            
                            $Dest = (Get-ItemProperty -Path 'HKCU:SoftwareScriptingGuys2010ScriptingGames' -Name 'Evt10Dest').Evt10Dest            
                            if (!(Test-Path $Dest)) {            
                                # No folder! No folder! Create one to move on!            
                                mkdir -Force $Dest            
                            }            
                            $Exclude = @($(Get-ItemProperty -Path 'HKCU:SoftwareScriptingGuys2010ScriptingGames' -Name 'Excludes').Excludes)            
                            $Desktop = [environment]::GetFolderPath('Desktop')            
                            $counter = 0            
                            $j = 1            
                            $TotalCount = Get-ChildItem -Recurse -Force $Desktop | ? { ! $_.PSIsContainter -and ($Exclude -notcontains $_.extension) } | Measure-Object | Select-Object -ExpandProperty count            
                            if ($TotalCount -gt 100) {            
                                $minFileCounter = $TotalCount/100            
                                $fix = 0            
                            } elseif ($TotalCount -gt 0) {            
                                [int32]$fix = 100/$TotalCount -1            
                                $minFileCounter = -1            
                            } else {            
                                trow "No files to process"            
                            }            
            
                            Get-ChildItem -Force -Recurse -Path $desktop |  ? { (! $_.PSIsContainter) -and ($Exclude -notcontains $_.extension) } | ForEach-Object {            
                                Try {            
                                    $sourcefile = $_.FullName            
                                    $destfile = $sourcefile.replace($Desktop, $Dest)            
                                    $destfileparent = $(Split-Path $destfile)            
                                    if (!(Test-Path $destfileparent)) { mkdir -Force $destfileparent}            
                                    Move-Item -Force -Path $sourcefile -Destination $destfileparent            
                  $counter++            
                  if ($counter -gt $j*$minFileCounter) {            
                                     $j++            
                                     $j += $fix            
                                 }            
                 } Catch {            
                  Show-UIMessage "ERROR: $_,Exception.Message"            
                 }            
                 Write-Progress "Progress: $j %" "Moving file: $sourcefile to $Destfileparent ..."  -PercentComplete $j            
                                            
                            }            
                            # Get rid off empty folders...            
                            $Folders = @()            
                            Get-ChildItem -Force -Recurse $Desktop | Where-Object { $_.PSIsContainer }  | ForEach-Object { $Folders += $_.FullName }            
                            $Folders = $Folders | Sort-Object -Descending -Unique            
                            Foreach ($folder in $folders) {            
                                if (!(Get-ChildItem -Force $folder)) {            
                                    # empty...            
                                    Remove-Item $folder -Force            
                                }            
                            }            
                            # And last but not least - create shortcut... :)            
                            $date = Get-Date -UFormat '%Y_%m_%d_%H_%M'            
                            $wsh = New-Object -ComObject Wscript.Shell            
                            $link = $wsh.CreateShortcut("$DesktopMoved Files_$date.lnk")            
                            $link.TargetPath = $Dest            
                            $link.WorkingDirectory = $Dest            
                            $link.Description = "Automaticaly created by Clear-Desktop"            
                            $link.Save()            
                                        
                                        
                Write-Progress "Done!" "Close this window!" -PercentComplete 100            
                                        
                        }            
                    }            
                }            
            
            }            
        }             
                    
    }            

 

Alll sytnax works thanks to tweaked a bit ‘Copy-ColorizedHTML from ISEPack. You can find script also on poshcode SG site, here: click

Blog_17_05_2010_1And that how it works: see? I’ve used piece of code here, and piece of code there to start with. But to make it work all together – man, that was a real challenge! And you know what? When I decided to let my wife know about SG and I’ve shown her standings after Wednesday I was sure she will be kind of angry: that I’ve wasted time I hardly have on some ‘games’. Well, it was opposite. She was… touched in a way, that I was able to get that high. She’s proud of me and that makes whole thing even better. But to add some bitter to this honey pot – you should read blog from (most likely) winner of whole SG contest this year. Some good point there about judging mainly that I have to agree with, seen here. After all – how can one care much about ‘stars’ if those stars are meaningless without feedback?

Long weekend.

To be honest I hate those long weekends. It’s fine if you have enough days off to travel somewhere, but if you stay at home it’s usually matter of weather, and you can rely on weather in May. This year it’s really bad here in Warsaw, so there is no way I can go out with children and have some fun – we have to stay at home.

I also noticed that after last week and Scripting Games fever I’m really in a mood of writing scripts all the time. I even went for beginner events yesterday to do something. To make it almost-a-challenge I’ve done all script in VBS. And it was not that bad. Maybe I will try to do advanced in VBS too, who knows?

Two things I would like to share today: first that jobs are not that bad as I thought. It took me a while to get familiar with them, but in the end (in my opinion) it went really fine.

Second: I’ve noticed that ISE is really nice tool (misses some functionality I like in editor from PowerGUI, but is adding some other that I could not find in latter). And you can extend it using some packs that are already out there. Such as part of PowerShellPack, IsePack module. Importing it is now in first line of my Microsoft.PowerShellISE_profile.ps1. One of features it adds is “Copy-ColoredAsHTML” ([ctrl] + [alt] + [shift] +c). I guess I will use it to add code to my posts. It’s really annoying with other tools that I’ve found. And with that I have to only tweak a HTML code a bit and I get something really readable and easy to follow.

So here is function I used to ask remote PCs about their video RAM (I left only part that is using jobs):

function Get-AdapterRAM {            
            <#
                .SYNOPSIS
                    Function, that retrieves info about video memory from worstations that were provided (pipe, parameter)
                .PARAMETER _computername
                    Name of the computer which will be tested.
                .PARAMETER AsJob
                    Switch to enable background jobs.
            #>            
            param(            
                [Parameter(Mandatory=$true)]            
                [string]$_computer,                            
                [switch]$AsJob            
            )            
            <#
                * define object to be return
                * test connection
                * go for it!
            #>            
            $obj = New-Object PSObject            
            $obj | Add-Member NoteProperty _computer $null            
            $obj | Add-Member NoteProperty value 0            
                        
            if (Test-Connection -Count 1 -Quiet -ComputerName $_computer) {            
                if ($AsJob) {            
                    Start-Job -ArgumentList $_computer, $obj {param([string]$computer, [PSobject]$obj)             
                        $obj._computer=$computer            
                        $obj.value = @($(Get-WMIObject -ErrorAction SilentlyContinue -ComputerName $computer -Query "SELECT AdapterRAM FROM Win32_VideoController"))[0].AdapterRAM            
                        $obj} | out-null            
                    Write-Verbose "Started job for computer`: $_computer"            
                }                
            } else {            
                Write-Verbose "$_computer is not accessible"            
                if ($AsJob) {            
                    ConvertTo-Object $_computer $null            
                }            
            }            
        }

And here how I get results back (this is at the end, there was also some conditional receiving in the middle of the PROCESS block, but I’m not sure if it actually worked)

while (Get-Job) {            
                Get-Job | ForEach-Object {            
                    if ($_.State -eq "Completed") {             
                        Receive-Job $_ | ConvertTo-Object; Remove-Job $_             
                    } else {            
                        Wait-job $_ | Out-Null            
                    }            
                }            
            }            

Simple, and maybe not the best practice – but again, if I do something I like to first figure out a way to do that by myself. I will look at “best practices” later, once I’m familiar with task/ technology used. I also created simple function that will create output objects for me. See:

function ConvertTo-Object {            
            <#
                .SYNOPSIS
                    Simple function to generate object with compname, videomemory, calculated memory and info if comp is ready for Windows7
                .PARAMETER _computername
                    Name of the computer to which object relates to.
                .PARAMETER Value
                    Computer's video memory size.
            #>            
            
            param(            
            [parameter(ValueFromPipelineByPropertyName=$true)]            
            [string]$_computer,             
                        
            [parameter(ValueFromPipelineByPropertyName=$true)]            
            [Uint32]$value            
            )            
                        
            $ReturnObj.ComputerName = $_computer            
            if ($value) {            
                    $ReturnObj.VideoRAM = $value             
                    $ReturnObj.VideoRAMCalc = ConvertTo-Bytes $value            
            } else {            
                $ReturnObj.VideoRAM = -1            
                $ReturnObj.VideoRAMCalc = 'unknown'            
            }            
            if ($value -ge 128MB) {            
                $ReturnObj.Win7Ready = $true            
            } else {            
                $ReturnObj.Win7Ready = $false            
            }            
            $ReturnObj            
                
        }

Pipe to it object that will have required properties (computername, value – that stands for total vidoe RAM size) and you will get something really nice. Of course you have to define ConvertTo-Bytes function. But this is too simple to blog about it. 😉

See how fine code is now? I had to only specify background, overflow and font size to make it more flexible and easy to use. But this is easier than spanning all the code manually…