PoshPAIG V1.6 released

I finally released PoshPAIG version 1.6 on Sunday. The link to it is here. I have 2 Guest Blogs with the Hey,Scripting Guy! blog (Thanks, Ed for letting me do this!) that will cover the new features. I will post the links to the guest blogs when they are published. As always, if you find any bugs or have feature requests, please post it in the Issue Tracker on the site tab.

Posted in Modules, powershell, WSUS | Tagged , , , | 1 Comment

PowerShell Binary Clock: Round 2

As you have seen in my previous post where I built a “proof of concept” binary clock (which I think was the first binary clock built using PowerShell, but have not been able to confirm yet), it worked as well as you would expect a binary clock to work, but I really wasn’t liking the look of it. I mean, it looks like a binary clock, but it really doesn’t look all that cool. The use of radial buttons and a weak background color doesn’t exactly scream out “Hey, place me on your desktop!”.

The first PowerShell binary clock.

Ryan Grant took out my call for others to build a binary clock and put out a nice version of a Sexagesimal binary clock using Show-UI that is great!

image

My latest update to my original Binary Clock makes it seem more like a WPF clock using some of better features that my other clock lacked.  I’ve removed the radial buttons in place of using ellipses to handle the on/off ‘lights’ to show when the binary clock ellipses are set to a 1 or 0. I also wanted to do away with the background and make only the ellipses visible. So with that, my clock looks like this:

image

The white background you see is actually the color of the background behind the clock, not the actual clock itself.

I configured the script to either allow for user input to determine the colors for both the on/off colors and also for the date stamp on the clock.

For instance, after you dot source the script, you can use the following code to set the colors to whatever you want.

.\Binary_Clock.ps1
Start-BinaryClock -OnColor Blue -OffColor Green -DateColor Silver

image

If you choose to not define the colors, randomly specified colors will be used instead.

Start-BinaryClock

image

Key inputs

I added some features to this clock such as being able to resize the clock by using the “r” key to being up the resizer in the bottom right hand corner. Another feature is to change the opacity using the ““ and “+” keys to make more transparent and less transparent. The “o” key toggles whether the clock remains at the top of all windows or not. Lastly, the “d” key shows or hides the date on the clock.

I use the following code to add the key inputs for the clock.

$Global:Window.Add_KeyDown({
    Switch ($_.Key) {
        {'Add','OemPlus' -contains $_} {
            If ($Window.Opacity -lt 1) {
                $Window.Opacity = $Window.Opacity + .1
                $Window.UpdateLayout()
                }            
            }
        {'Subtract','OemMinus' -contains $_} {
            If ($Window.Opacity -gt .2) {
                $Window.Opacity = $Window.Opacity - .1
                $Window.UpdateLayout()
                }             
            }
        "r" {
            If ($Window.ResizeMode -eq 'NoResize') {
                $Window.ResizeMode = 'CanResizeWithGrip'
                }      
            Else {
                $Window.ResizeMode = 'NoResize'             
                }       
            } 
        "d" {
            Switch ($MonthLabel.visibility) {
                'Collapsed' {$MonthLabel.visibility = 'Visible';$DayLabel.Visibility = 'Visible'}
                'Visible' {$MonthLabel.visibility = 'Collapsed ';$DayLabel.Visibility = 'Collapsed '}
                }
            }    
        "o" {
            If ($Window.TopMost) {
                $Window.TopMost = $False
                }
            Else {
                $Window.TopMost = $True
                }
            }     
        }
    })

Here is a example of the clock with the opacity turned down using the “-“ key:

image

Here is an example of the clock without the date using the “d” key:

image

Lastly, here is the clock with the re-size grip enabled using the “r” key:

image

Another thing about re-sizing the clock is that everything adjusts accordingly and will stretch in the direction that you drag using the re-sizer grip. A couple examples:

image

image

image

I was able to make this work by adding a TextBlock inside of a ViewBox. The viewbox properties include the stretchdirection and stretch properties that basically lets the text inside the textblock to stretch in any direction that you choose.  Here is the XAML code that allows me to stretch the text:

            <Viewbox VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch' Grid.Column = '12' Grid.RowSpan = '7'
            StretchDirection = 'Both' Stretch = 'Fill'>
                <TextBlock x:Name = 'DayLabel' VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch'
                FontWeight = 'Bold'> 
                    <TextBlock.LayoutTransform>
                        <RotateTransform Angle="90" />
                    </TextBlock.LayoutTransform>            
                </TextBlock>
            </Viewbox>
            <Viewbox VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch' Grid.Column = '13' Grid.RowSpan = '7'
            StretchDirection = 'Both' Stretch = 'Fill'>
                <TextBlock x:Name = 'MonthLabel' VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch'
                FontWeight = 'Bold'>
                    <TextBlock.LayoutTransform>
                        <RotateTransform Angle="90" />
                    </TextBlock.LayoutTransform>               
                </TextBlock>
            </Viewbox>

Mouse Inputs

You can use the left-button to drag and move the clock around and you can close the clock by right-clicking on the clock. Code for this action is below:

$Window.Add_MouseRightButtonUp({
    $This.close()
    })
$Window.Add_MouseLeftButtonDown({
    $This.DragMove()
    }) 

 

Code

Script Repository

PoshCode

Updated code thanks to a great find by Robert!

Check out his blog here too! Crazy smart guy!

Turns out I couldn’t use the [system.windows.media.brush] for the color parameters at the start of the script because they require the following assemblies be loaded first:

#Load Required Assemblies
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase

So instead, I updated it to be a [string] and now it works great!

Function Start-BinaryClock {
<#
.SYNOPSIS
    This is a binary clock that lists the time in hours, minutes and seconds
    
.DESCRIPTION
    This is a binary clock that lists the time in hours, minutes and seconds.
    
    Key Input Tips:
    r: Toggles the resize mode of the clock so you can adjust the size.
    d: Toggles the date to hide/show
    o: Toggles whether the clock remains on top of windows or not.
    +: Increases the opacity of the clock so it is less transparent.
    -: Decreases the opacity of the clock so it appears more transparent.
    
    Right-Click to close.
    Use left mouse button to drag clock.
    
.PARAMETER OnColor
    Define the color used for the active time (1).
    
.PARAMETER OffColor
    Define the color used for the inactive time (0).
    
.PARAMETER RandomColor    
    Default parameter if manual colors are not used. Picks a random color for On and Off colors.

.NOTES  
    Name: BinaryClock.ps1
    Author: Boe Prox
    DateCreated: 07/05/2011
    Version 2.0 

.EXAMPLE
    Start-BinaryClock
    
Description
-----------
Starts the binary clock using randomly generated colors 

.EXAMPLE
    Start-BinaryClock -OnColor Red -OffColor Gold -DateColor Black
    
Description
-----------
Starts the binary clock using using specified colors.        
#>
[cmdletbinding(
    DefaultParameterSetName = 'RandomColor'
    )]
Param (
    [parameter(Mandatory = 'True',ParameterSetName = 'SetColor')]
    [String] $OnColor,
    [parameter(Mandatory = 'True',ParameterSetName = 'SetColor')]
    [String] $OffColor,
    [parameter(ParameterSetName = 'RandomColor')]
    [Switch]$RandomColor, 
    [parameter(Mandatory = 'True',ParameterSetName = 'SetColor')]
    [String] $DateColor   
    )
If ($PSCmdlet.ParameterSetName -eq 'RandomColor') {
    [switch]$RandomColor = $True
    }
   
$Global:rs = [RunspaceFactory]::CreateRunspace()
$rs.ApartmentState = “STA”
$rs.ThreadOptions = “ReuseThread”
$rs.Open() 
$psCmd = {Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase}.GetPowerShell() 
$rs.SessionStateProxy.SetVariable('OnColor',$OnColor)
$rs.SessionStateProxy.SetVariable('OffColor',$OffColor)
$rs.SessionStateProxy.SetVariable('RandomColor',$RandomColor)
$rs.SessionStateProxy.SetVariable('DateColor',$DateColor)
$psCmd.Runspace = $rs 
$psCmd.Invoke() 
$psCmd.Commands.Clear() 
$psCmd.AddScript({ 

#Load Required Assemblies
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase

##Colors
If ($RandomColor) {
    #On Color
    $OnColor = "#{0:X}" -f (Get-Random -min 1 -max 16777215)
    Try {
        [system.windows.media.brush]$OnColor | Out-Null
        }
    Catch {
        $OnColor = "White"
        }
    #Off Color
    $OffColor = "#{0:X}" -f (Get-Random -min 1 -max 16777215)
    Try {
        [system.windows.media.brush]$OffColor | Out-Null
        }
    Catch {
        $OffColor = "Black"
        }
    #DateColor Color
    $DateColor = "#{0:X}" -f (Get-Random -min 1 -max 16777215)
    Try {
        [system.windows.media.brush]$DateColor | Out-Null
        }
    Catch {
        $DateColor = "Black"
        }        
    }

[xml]$xaml = @"
<Window
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
    x:Name='Window' WindowStyle = 'None' WindowStartupLocation = 'CenterScreen' Width = '170' Height = '101' ShowInTaskbar = 'True' 
    ResizeMode = 'NoResize' Title = 'Clock' AllowsTransparency = 'True' Background = 'Transparent' Opacity = '1' Topmost = 'True'>
        <Grid x:Name = 'Grid' HorizontalAlignment="Stretch" ShowGridLines='false'  Background = 'Transparent'>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="2"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="5"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="2"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="5"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="2"/>
                <ColumnDefinition Width="*"/>                
                <ColumnDefinition Width="2"/>
                <ColumnDefinition Width="*" x:Name = 'DayColumn'/> 
                <ColumnDefinition Width="*" x:Name = 'MonthColumn'/> 
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '2'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '2'/>
                <RowDefinition Height = '*'/>                
                <RowDefinition Height = '2'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '2'/>
                <RowDefinition x:Name = 'timerow' Height = '0'/>
            </Grid.RowDefinitions>
            <Ellipse Grid.Row = '0' Grid.Column = '0' Fill = 'Transparent'/>
            <Ellipse Grid.Row = '2' Grid.Column = '0' Fill = 'Transparent' />
            <Ellipse x:Name = 'HourA0' Grid.Row = '6' Grid.Column = '0' Fill = 'Transparent' />
            <Ellipse x:Name = 'HourA1' Grid.Row = '4' Grid.Column = '0' Fill = 'Transparent' />    
            <Ellipse x:Name = 'HourB0' Grid.Row = '6' Grid.Column = '2' Fill = 'Transparent'/>
            <Ellipse x:Name = 'HourB1' Grid.Row = '4' Grid.Column = '2' Fill = 'Transparent' />
            <Ellipse x:Name = 'HourB2' Grid.Row = '2' Grid.Column = '2' Fill = 'Transparent' />
            <Ellipse x:Name = 'HourB3' Grid.Row = '0' Grid.Column = '2' Fill = 'Transparent' />
            <Ellipse Grid.Row = '0' Grid.Column = '4' Fill = 'Transparent'/>
            <Ellipse x:Name = 'MinuteA0' Grid.Row = '6' Grid.Column = '4' Fill = 'Transparent' />
            <Ellipse x:Name = 'MinuteA1' Grid.Row = '4' Grid.Column = '4' Fill = 'Transparent' />
            <Ellipse x:Name = 'MinuteA2' Grid.Row = '2' Grid.Column = '4' Fill = 'Transparent' /> 
            <Ellipse x:Name = 'MinuteB0' Grid.Row = '6' Grid.Column = '6' Fill = 'Transparent'/>
            <Ellipse x:Name = 'MinuteB1' Grid.Row = '4' Grid.Column = '6' Fill = 'Transparent' />
            <Ellipse x:Name = 'MinuteB2' Grid.Row = '2' Grid.Column = '6' Fill = 'Transparent' />
            <Ellipse x:Name = 'MinuteB3' Grid.Row = '0' Grid.Column = '6' Fill = 'Transparent' />  
            <Ellipse Grid.Row = '0' Grid.Column = '8' Fill = 'Transparent'/>
            <Ellipse x:Name = 'SecondA0' Grid.Row = '6' Grid.Column = '8' Fill = 'Transparent' />
            <Ellipse x:Name = 'SecondA1' Grid.Row = '4' Grid.Column = '8' Fill = 'Transparent' />
            <Ellipse x:Name = 'SecondA2' Grid.Row = '2' Grid.Column = '8' Fill = 'Transparent' />  
            <Ellipse x:Name = 'SecondB0' Grid.Row = '6' Grid.Column = '10' Fill = 'Transparent'/>
            <Ellipse x:Name = 'SecondB1' Grid.Row = '4' Grid.Column = '10' Fill = 'Transparent' />
            <Ellipse x:Name = 'SecondB2' Grid.Row = '2' Grid.Column = '10' Fill = 'Transparent' />
            <Ellipse x:Name = 'SecondB3' Grid.Row = '0' Grid.Column = '10' Fill = 'Transparent' />                                                                                  
            <Viewbox VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch' Grid.Column = '12' Grid.RowSpan = '7'
            StretchDirection = 'Both' Stretch = 'Fill'>
                <TextBlock x:Name = 'DayLabel' VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch'
                FontWeight = 'Bold'> 
                    <TextBlock.LayoutTransform>
                        <RotateTransform Angle="90" />
                    </TextBlock.LayoutTransform>            
                </TextBlock>
            </Viewbox>
            <Viewbox VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch' Grid.Column = '13' Grid.RowSpan = '7'
            StretchDirection = 'Both' Stretch = 'Fill'>
                <TextBlock x:Name = 'MonthLabel' VerticalAlignment = 'Stretch' HorizontalAlignment = 'Stretch'
                FontWeight = 'Bold'>
                    <TextBlock.LayoutTransform>
                        <RotateTransform Angle="90" />
                    </TextBlock.LayoutTransform>               
                </TextBlock>
            </Viewbox>
        </Grid>
</Window>
"@ 

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Global:Window=[Windows.Markup.XamlReader]::Load( $reader )
$Global:DayLabel = $Global:window.FindName("DayLabel")
$Global:MonthLabel = $Global:window.FindName("MonthLabel")
$Global:DayColumn = $Global:window.FindName("DayColumn")
$Global:MonthColumn = $Global:window.FindName("MonthColumn")
$Global:TimeRow = $Global:window.FindName("TimeRow")
$Global:Grid = $Global:window.FindName("Grid")

##Events 
#Key Events
$Global:Window.Add_KeyDown({
    Switch ($_.Key) {
        {'Add','OemPlus' -contains $_} {
            If ($Window.Opacity -lt 1) {
                $Window.Opacity = $Window.Opacity + .1
                $Window.UpdateLayout()
                }            
            }
        {'Subtract','OemMinus' -contains $_} {
            If ($Window.Opacity -gt .2) {
                $Window.Opacity = $Window.Opacity - .1
                $Window.UpdateLayout()
                }             
            }
        "r" {
            If ($Window.ResizeMode -eq 'NoResize') {
                $Window.ResizeMode = 'CanResizeWithGrip'
                }      
            Else {
                $Window.ResizeMode = 'NoResize'             
                }       
            } 
        "d" {
            Switch ($MonthLabel.visibility) {
                'Collapsed' {$MonthLabel.visibility = 'Visible';$DayLabel.Visibility = 'Visible'}
                'Visible' {$MonthLabel.visibility = 'Collapsed ';$DayLabel.Visibility = 'Collapsed '}
                }
            }    
        "o" {
            If ($Window.TopMost) {
                $Window.TopMost = $False
                }
            Else {
                $Window.TopMost = $True
                }
            }     
        }
    }) 
        
$Window.Add_MouseRightButtonUp({
    $This.close()
    })
$Window.Add_MouseLeftButtonDown({
    $This.DragMove()
    })  
         
$update = {
$DayLabel.Text = "$(((Get-Date).ToLongDateString() -split ',')[0] -split '')"
$DayLabel.Foreground = $DateColor
$MonthLabel.Text = Get-Date -u "%B %d %G"
$MonthLabel.Foreground = $DateColor
$hourA,$hourB = [string](Get-Date -f HH) -split "" | Where {$_}
$minuteA,$minuteB = [string](Get-Date -f mm) -split "" | Where {$_}
$secondA,$secondB = [string](Get-Date -f ss) -split "" | Where {$_}

$hourAdock = $grid.children | Where {$_.Name -like "hourA*"}
$minuteAdock = $grid.children | Where {$_.Name -like "minuteA*"}
$secondAdock = $grid.children | Where {$_.Name -like "secondA*"}
$hourBdock = $grid.children | Where {$_.Name -like "hourB*"}
$minuteBdock = $grid.children | Where {$_.Name -like "minuteB*"}
$secondBdock = $grid.children | Where {$_.Name -like "secondB*"}

#hourA
[array]$splittime = ([convert]::ToString($houra,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $hourAdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0

#hourB
[array]$splittime = ([convert]::ToString($hourb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $hourBdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0

#minuteA
[array]$splittime = ([convert]::ToString($minutea,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $minuteAdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0

#minuteB
[array]$splittime = ([convert]::ToString($minuteb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $minuteBdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0

#secondA
[array]$splittime = ([convert]::ToString($seconda,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $secondAdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0

#secondB
[array]$splittime = ([convert]::ToString($secondb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hdock in $secondBdock) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hdock.Fill = $OnColor
        }
    Else {
        $hdock.Fill = $OffColor
        }
    $i++
    }
$i = 0
}

$Global:Window.Add_KeyDown({
    If ($_.Key -eq "F5") {
        &$update 
        }
    })

#Timer Event
$Window.Add_SourceInitialized({
    #Create Timer object
    Write-Verbose "Creating timer object"
    $Global:timer = new-object System.Windows.Threading.DispatcherTimer 

    Write-Verbose "Adding interval to timer object"
    $timer.Interval = [TimeSpan]"0:0:.10"
    #Add event per tick
    Write-Verbose "Adding Tick Event to timer object"
    $timer.Add_Tick({
        &$update
        Write-Verbose "Updating Window"
        })
    #Start timer
    Write-Verbose "Starting Timer"
    $timer.Start()
    If (-NOT $timer.IsEnabled) {
        $Window.Close()
        }
    })

&$update
$window.Showdialog() | Out-Null             
}).BeginInvoke() | out-null
}
Posted in powershell, scripts | Tagged , , , | 1 Comment

Building a Binary Clock with PowerShell

I was busy surfing the web when I could have been working on some PowerShell scripts (or doing yard work outside) when I came across a binary clock. I don’t quite remember how I got to the page showing a binary clock, but regardless, it happened. Here is one page showing a binary clock.  This got me thinking about what it would take to write some code to create my very own binary clock using PowerShell. At first I thought about just making a “proof of concept” by just displaying a single instance of time in the console. But of course that is not very impressive, so I knew I would need to build out a WPF version of the clock.

If you want to know more about a binary clock, read this.

Now I know a lot of you will be saying “Boe, why don’t you use Show-UI since it is fricking amazing?”. My answer to that is , yes I know and will eventually make the leap over to it. But at this time I am not able to put a lot of time into using Show-UI as where I work restricts my ability to run modules and such downloaded from the internet without jumping through several hurdles That being said, I do have it downloaded and ready to run on my laptop and have plans to port my various GUI projects over to it when time permits.

Building the GUI

As I stated, I am not using Show-UI, so therefore I am working with XAML and PowerShell to build out my GUI and make the necessary control and event connections. Having built a few GUI’s now, I am more comfortable in writing the XAML and deciding what I will need to make it work the way that I want it to. For those of you unfamiliar with XAML, I suggest using your favorite search engine to learn more about it. It is a great way to build out your front end GUI! I decided on a Grid as it would make aligning everything much easier, especially since I can show the grid lines and see where I am at. I also used labels to display some other information that I will cover later on in the post.

image

Clock showing the grid lines.

One item I decided on was to use Radio Buttons to act as my on/off buttons for the binary clock. The issue that I first ran into was figuring out how to make sure that when one button was turned ‘on’, that the other buttons would not turn ‘off’. Turns out the answer was fairly simple: put each Radio Button into a group by itself so each button is independent of the other buttons when turned on or off.

As I started to put this together, I also had to find a way to convert a time into binary and then have it represented correctly on my clock. Luckily, I found the system.convert class to be my ally in converting a decimal value into Binary. By using the static method, ToString, I can easily perform the conversion of decimal values 7 and 9 into binary.

[convert]::ToString(7,2)

111

[convert]::ToString(9,2)

1001

Pretty simple, isn’t it?

Because I have the binary clock represented by 6 columns, I split the time out into 6 chunks by using –Split. That way, I can work with each value by itself and convert it into binary that can be used for the setting the clock to the correct time. Lets see an example o this by grabbing the hours:

$hourA,$hourB = [string](Get-Date -f HH) -split "" | Where {$_}

image

This trick allows me to store each piece of the array in its own variable rather than using the [0] and [1] method.

Another trick I use is to filter using Where-Object to only grab the parts of the array that actually contains the data. All of the extra empty space is removed.

Another gotcha that I ran into is that I first had to reverse the order of the binary array by using the [array]::Reverse() method. This allows you to supply an existing array into the method and reverse the order without having to save the results to a new variable! Very helpful stuff!

image

By reversing the array before using it for the GUI will help to ensure that my clock now behaves properly by managing the buttons in the correct fashion.

I configured a timer on the WPF window to run at every .1 second. I found timing issues and a lot of inconsistency if I made the value any higher. Sometimes a little trial and error can help out when working on these kind of items.

Extra Items

After finishing all of this, I decided to add a few little extra items just because I thought they would be useful to folks. The first item is a helper column that makes it easier to translate what each binary value means. This can be accomplished by hitting the ‘h’ key while the window for the clock is active.

The second item was to add a date on the top that can be hidden or visible using the ‘d’ key.

Lastly, if you want to see what the time is in a more “human” format, clicking the ‘t’ key will bring up a digital clock for you.

I chose to run the clock in a different runspace so the console would be free to use without having to stop the clock to run other commands or opening a second PowerShell console.

Conclusion

Hopefully everyone finds this to be a pretty cool application. While nothing too special, it was a lot of fun putting this together and working through a few roadblocks along the way. I have no doubt that there are better ways that I could have gone about building this clock, but I also encourage everyone that likes this to take the code and expand upon it. By expanding I mean make the code shorter and more efficient, port it over to Show-UI, make it look fancier and just have fun doing it!

Examples

Here are a few screenshots showing what the Binary clock looks like and also using the various keys.

binaryclock

Default clock

binaryclock_date

Clock showing the date using the ‘d’ key.

binaryclock_help

Clock showing the helper values using the ‘h’ key.

binaryclock_time

Clock showing the time in a “human readable” format using the ‘t’ key.

 

Code

Poshcode

Script Repository

<#
.SYNOPSIS
    This is a binary clock that lists the time in hours, minutes and seconds
    
.DESCRIPTION
    This is a binary clock that lists the time in hours, minutes and seconds. Also available is the ability to 
    display the time in a "human readable" format, display the date and display a helper display showoing how to read
    the binary numbers to determine the time.
    
    Tips:
    Use the "h" key show and hide the helper column to better understand what the binary values are.
    Use the "d" key to show and hide the current date.
    Use the "t" key to show the time in a more "human readable" format.

.NOTES  
    Name: BinaryClock/ps1
    Author: Boe Prox
    DateCreated: 07/05/2011
    Version 1.0 
#>
$rs = [RunspaceFactory]::CreateRunspace()
$rs.ApartmentState = “STA”
$rs.ThreadOptions = “ReuseThread”
$rs.Open() 
$psCmd = {Add-Type -AssemblyName PresentationCore,PresentationFramework,WindowsBase}.GetPowerShell() 
$psCmd.Runspace = $rs 
$psCmd.Invoke() 
$psCmd.Commands.Clear() 
$psCmd.AddScript({ 

#Load Required Assemblies
Add-Type –assemblyName PresentationFramework
Add-Type –assemblyName PresentationCore
Add-Type –assemblyName WindowsBase


[xml]$xaml = @"
<Window
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
    x:Name='Window' Title='Binary Clock' WindowStartupLocation = 'CenterScreen' Width = '205' Height = '196' ShowInTaskbar = 'True' ResizeMode = 'NoResize' >
        <Window.Background>
        <LinearGradientBrush StartPoint='0,0' EndPoint='0,1'>
            <LinearGradientBrush.GradientStops> <GradientStop Color='#C4CBD8' Offset='0' /> <GradientStop Color='#E6EAF5' Offset='0.2' /> 
            <GradientStop Color='#CFD7E2' Offset='0.9' /> <GradientStop Color='#C4CBD8' Offset='1' /> </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
    </Window.Background>
        <Grid x:Name = 'Grid1' HorizontalAlignment="Stretch" ShowGridLines='False'>
            <Grid.ColumnDefinitions>
                <ColumnDefinition x:Name = 'Column1' Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>                
                <ColumnDefinition x:Name = 'helpcolumn' Width="0"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition x:Name = 'daterow' Height = '0'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '*'/>
                <RowDefinition Height = '*'/>                
                <RowDefinition Height = '*'/>
                <RowDefinition x:Name = 'timerow' Height = '0'/>
            </Grid.RowDefinitions>
            <RadioButton x:Name = 'HourA0' IsChecked = 'False' GroupName = 'A' Grid.Row = '4' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourA1' IsChecked = 'False' GroupName = 'B' Grid.Row = '3' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB0' IsChecked = 'False' GroupName = 'C' Grid.Row = '4' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB1' IsChecked = 'False' GroupName = 'D' Grid.Row = '3' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'HourB2' IsChecked = 'False' GroupName = 'E' Grid.Row = '2' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'HourB3' IsChecked = 'False' GroupName = 'F' Grid.Row = '1' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'MinuteA0' IsChecked = 'False' GroupName = 'G' Grid.Row = '4' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteA1' IsChecked = 'False' GroupName = 'H' Grid.Row = '3' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'MinuteA2' IsChecked = 'False' GroupName = 'I' Grid.Row = '2' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteB0' IsChecked = 'False' GroupName = 'J' Grid.Row = '4' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />                                                
            <RadioButton x:Name = 'MinuteB1' IsChecked = 'False' GroupName = 'K' Grid.Row = '3' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'MinuteB2' IsChecked = 'False' GroupName = 'L' Grid.Row = '2' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />                         
            <RadioButton x:Name = 'MinuteB3' IsChecked = 'False' GroupName = 'M' Grid.Row = '1' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'SecondA0' IsChecked = 'False' GroupName = 'N' Grid.Row = '4' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' /> 
            <RadioButton x:Name = 'SecondA1' IsChecked = 'False' GroupName = 'O' Grid.Row = '3' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondA2' IsChecked = 'False' GroupName = 'P' Grid.Row = '2' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB0' IsChecked = 'False' GroupName = 'Q' Grid.Row = '4' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB1' IsChecked = 'False' GroupName = 'R' Grid.Row = '3' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB2' IsChecked = 'False' GroupName = 'S' Grid.Row = '2' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <RadioButton x:Name = 'SecondB3' IsChecked = 'False' GroupName = 'T' Grid.Row = '1' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '4' Grid.Column = '8' Content = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '3' Grid.Column = '8' Content = '2' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '2' Grid.Column = '8' Content = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '1' Grid.Column = '8' Content = '8' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'H1Label' Grid.Row = '5' Grid.Column = '0' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'H2Label' Grid.Row = '5' Grid.Column = '1' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '5' Grid.Column = '2' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' Content = ":" />
            <Label FontWeight = 'Bold' x:Name = 'M1Label' Grid.Row = '5' Grid.Column = '3' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'M2Label' Grid.Row = '5' Grid.Column = '4' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' Grid.Row = '5' Grid.Column = '5' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' Content = ":" />
            <Label FontWeight = 'Bold' x:Name = 'S1Label' Grid.Row = '5' Grid.Column = '6' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'S2Label' Grid.Row = '5' Grid.Column = '7' HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
            <Label FontWeight = 'Bold' x:Name = 'datelabel' Grid.Row = '0' Grid.Column = '1' Grid.ColumnSpan = "6" HorizontalAlignment = 'Center' VerticalAlignment = 'Center' />
        </Grid>
</Window>
"@ 

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Global:Window=[Windows.Markup.XamlReader]::Load( $reader )

$datelabel =  $Global:window.FindName("datelabel")
$H1Label =  $Global:window.FindName("H1Label")
$H2Label =  $Global:window.FindName("H2Label")
$M1Label =  $Global:window.FindName("M1Label")
$M2Label =  $Global:window.FindName("M2Label")
$S1Label =  $Global:window.FindName("S1Label")
$S2Label =  $Global:window.FindName("S2Label")
$timerow =  $Global:window.FindName("timerow")
$daterow =  $Global:window.FindName("daterow")
$helpcolumn =  $Global:window.FindName("helpcolumn")
$Global:Column1 = $Global:window.FindName("Column1")
$Global:Grid = $column1.parent

##Events
#Show helper column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "h") {
        Switch ($helpcolumn.width) {
            "*" {$helpcolumn.width = "0"}
            0 {$helpcolumn.width = "*"}
            }
        }
    }) 

#Show time column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "t") {
        Switch ($timerow.height) {
            "*" {$timerow.height = "0"}
            0 {$timerow.height = "*"}
            }
        }
    }) 
#Show date column   
$Global:Window.Add_KeyDown({
    If ($_.Key -eq "d") {
        Switch ($daterow.height) {
            "*" {$daterow.height = "0"}
            0 {$daterow.height = "*"}
            }
        }
    })     

$update = {
$datelabel.content = Get-Date -f D
$hourA,$hourB = [string](Get-Date -f HH) -split "" | Where {$_}
$minuteA,$minuteB = [string](Get-Date -f mm) -split "" | Where {$_}
$secondA,$secondB = [string](Get-Date -f ss) -split "" | Where {$_}

$hourAradio = $grid.children | Where {$_.Name -like "hourA*"}
$minuteAradio = $grid.children | Where {$_.Name -like "minuteA*"}
$secondAradio = $grid.children | Where {$_.Name -like "secondA*"}
$hourBradio = $grid.children | Where {$_.Name -like "hourB*"}
$minuteBradio = $grid.children | Where {$_.Name -like "minuteB*"}
$secondBradio = $grid.children | Where {$_.Name -like "secondB*"}

#hourA
$H1Label.content = $hourA
[array]$splittime = ([convert]::ToString($houra,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $hourAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#hourB
$H2Label.content = $hourB
[array]$splittime = ([convert]::ToString($hourb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $hourBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#minuteA
$M1Label.content = $minuteA
[array]$splittime = ([convert]::ToString($minutea,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $minuteAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#minuteB
$M2Label.content = $minuteB
[array]$splittime = ([convert]::ToString($minuteb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $minuteBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#secondA
$S1Label.content = $secondA
[array]$splittime = ([convert]::ToString($seconda,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $secondAradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0

#secondB
$S2Label.content = $secondB
[array]$splittime = ([convert]::ToString($secondb,2)) -split"" | Where {$_}
[array]::Reverse($splittime)
$i = 0
ForEach ($hradio in $secondBradio) {
    Write-Verbose "i: $($i)"
    Write-Verbose "split: $($splittime[$i])"
    If ($splittime[$i] -eq "1") {
        $hradio.Ischecked = $True
        }
    Else {
        $hradio.Ischecked = $False
        }
    $i++
    }
$i = 0
}

$Global:Window.Add_KeyDown({
    If ($_.Key -eq "F5") {
        &$update 
        }
    })

#Timer Event
$Window.Add_SourceInitialized({
    #Create Timer object
    Write-Verbose "Creating timer object"
    $Global:timer = new-object System.Windows.Threading.DispatcherTimer 

    Write-Verbose "Adding interval to timer object"
    $timer.Interval = [TimeSpan]"0:0:.10"
    #Add event per tick
    Write-Verbose "Adding Tick Event to timer object"
    $timer.Add_Tick({
        &$update
        Write-Verbose "Updating Window"
        })
    #Start timer
    Write-Verbose "Starting Timer"
    $timer.Start()
    If (-NOT $timer.IsEnabled) {
        $Window.Close()
        }
    })

&$update
$window.Showdialog() | Out-Null            
}).BeginInvoke() | out-null
Posted in powershell, scripts | Tagged , , , , | 3 Comments

Quick Hits: Translate Windows Error Codes

Sometimes while working in Windows you might see some logs or receive an error message that are just numbers. Something like a 5 or maybe a 32. Regardless, these are nothing short of confusing and make you have to Google (or Bing) to find out what exactly that number means. Or maybe you have a crazy curiosity to see what numbers translate to a windows error… Either way, there is a .NET namespace that can answer all of those questions. That namespace is ComponentModel.Win32Exception.

Lets look at the error code 5.

[ComponentModel.Win32Exception]5

image

How about error code 32…

[ComponentModel.Win32Exception]32

image

One more, I have this error in my windowsupdate.log that I don’t feel like looking up online:

[ComponentModel.Win32Exception]0x800700de

image

Pretty cool, isn’t it?

Ok, so this isn’t groundbreaking, but it is just another cool thing that you can do with PowerShell.

For even more fun, you can run this to find all sorts of error codes:

1..1000 | % {"$($_): $((([ComponentModel.Win32Exception]$_) -split ": ")[1])"} | more

image

This shows error codes 614-662 and would continue on to 1000.

Feel free to take it beyond 1000 and see what other error codes there are to see.

Posted in scripts | Tagged , , | 6 Comments

Working with Software Rollback Policies using PowerShell

One of the things I deal with a lot where I work when performing patch installations is that the number of .NET patches is just amazing! Obviously, these patches are a must to get installed, but sometimes they do not always install with the greatest of ease.  The most common error I see with installing a .NET patch mentions that the rollback policy must be enabled for the installation to continue.  This KB article provides more information about this as well.

The fix is to go to the registry and navigate to HKLM\Software\Policies\Microsoft\Windows\Installer and then locate the DisableRollback value name. Simply changing this value from a 1 to 0 will enable the rollback setting and the .NET patch will install without any issues.

Now this isn’t really a difficult task by any means, but it is tedious, especially if you have to do this against some 100+ systems. Can you imagine how long it would take and the tiresome clicking noise that you would have to put up with? That would drive me crazy! Enter PowerShell, the best tool for this job. Something that I say and am sure others say that if you have to do something more than once, automate it! So, with that, I put together a module of 3 commands to work with the rollback setting that can be run against one or more computers remotely so you can run the command once and move on with the installations across the network.

The biggest piece to this module that allows me to use it to make the changes lies in the Microsoft.Win32.RegistryKey namespace. The first thing we do is to create the object that makes the connection to the remote registry on a computer. For this, I look at the static methods that I can use to make this connection. The connection that stands out is the OpenRemoteBaseKey() method. This method takes two values: microsoft.win32.registryhive and the computername. Lets see what the microsoft.win32.registryhive properties are so we make sure we get the correct key.

[microsoft.win32.registryhive] | Get-Member -static -type Property

Name            MemberType
—-            ———-
ClassesRoot     Property
CurrentConfig   Property
CurrentUser     Property
DynData         Property
LocalMachine    Property
PerformanceData Property
Users           Property

Now we know what we can use with the static method to connect. We need the LocalMachine value so we can get to the DisableRollback value name.

Now I will connect to my server, DC1 and show the subkeys using the GetSubKeyNames() method from the object I created.

$computer = 'dc1'
$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer)
$registry.GetSubKeyNames()

image

Using this newly created object, you can then connect to other keys within the registry and view the data within the value names. For instance, I will continue on down to find the DisableRollback value on DC1.

$subkey = $registry.OpenSubKey("Software\Policies\Microsoft\Windows\Installer",$True)
$subkey.GetValue('DisableRollback')

image

I used two methods to accomplish task: OpenSubKey() and GetValue() which allows me to open the subkeys and then to open the value name and get the data from that value. The $True that I added at the end of the OpenSubKey() tells that I am opening this key as Read/Write vs. the default of Read Only. This is more important if you are going to modify the data in a value or creating a subkey.

Now with that, I can change the value, or create the value name with the value using the SetValue() method. Depending on whether the value exists, you can use either two or three values with the method. In the case of my module, I use three values to create the value using the defined value and to make it as a DWORD value.

First I need to find out the correct value to place to create the right kind of key.

[Microsoft.Win32.RegistryValueKind] | Get-Member -static -type Property

Name         MemberType
—-         ———-
Binary       Property
DWord        Property
ExpandString Property
MultiString  Property
QWord        Property
String       Property
Unknown      Property

$registry.SetValue("DisableRollback",0,"DWORD")
$registry.GetValue('DisableRollback')

image

So that is a little background on what you can do with PowerShell to make changes to a remote computer registry values. Hopefully Microsoft and the PowerShell team will come up with come cmdlets that do this without having to go through these hoops. But in the meantime, here is an excellent module by Shay Levi that fills in that gap.

The module that I put together is only specific for working with the rollback policy, but you can most definitely take the code and adjust it to your own ideas. Below are a couple of examples with screen shots of my module in action.

Import-Module .\RollbackPolicyModule.psm1 -Verbose
Get-RollbackPolicy -Computername DC1

image

Enable-RollbackPolicy -Computername DC1 -Passthru

image

Disable-RollbackPolicy -Computername DC1 -Passthru

image

There you have it, nothing groundbreaking by any means. But this also shows that it doesn’t matter what you decide to use PowerShell for, just as long as it helps you to accomplish exactly what you need to do, then that is all that counts.

 

Code

PoshCode

Script Repository

Function Get-RollbackPolicy {
<#  
.SYNOPSIS  
     Retieves the current rollback policy on a local or remote computer
         
.DESCRIPTION  
    Retieves the current rollback policy on a local or remote computer
    
.PARAMETER Computername
    The name of the computer or computers to perform the query against
               
.NOTES  
    Name: Get-RollbackPolicy
    Author: Boe Prox
    DateCreated: 06/24/2011
    Links: 

.EXAMPLE
Get-RollbackPolicy -Computername DC1

Rollback                                Computer                               
--------                                --------                               
Enabled                                 dc1     

Description
-----------
Performs a query against the server, DC1 and returns whether the rollback setting has been enabled 
or disabled.

.EXAMPLE
Get-RollbackPolicy -Computername DC1,server1,server2

Rollback                                Computer                               
--------                                --------                               
Enabled                                 dc1     
Disabled                                server1   
Enabled                                 server2   

Description
-----------
Performs a query against the server, DC1 and returns whether the rollback setting has been enabled 
or disabled.
#>
[cmdletbinding()]
Param (
    [parameter()]
    [string[]]$Computername
    )
Process {
    ForEach ($computer in $computername) {
        Try {
            #Make initial registry connection   
            Write-Verbose "Making registry connection to remote computer"     
            $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer)
            #Connect to the subkey where the rollback is located
            Write-Verbose "Making registry connection to 'Software' key" 
            If ($registry.GetSubkeyNames() -contains "Software") {
                $subkey = $registry.OpenSubKey("Software",$True)
                Write-Verbose "Making registry connection to 'Policies' key"
                If ($subkey.GetSubKeyNames() -contains "Policies") {
                    $subkey = $subkey.OpenSubKey("Policies",$True)
                    Write-Verbose "Making registry connection to 'Microsoft' key"
                    If ($subkey.GetSubKeyNames() -contains "Microsoft") {
                        $subkey = $subkey.OpenSubKey("Microsoft",$True)
                        Write-Verbose "Making registry connection to 'Windows' key"
                        If ($subkey.GetSubKeyNames() -contains "Windows") {
                            $subkey = $subkey.OpenSubKey("Windows",$True)
                            Write-Verbose "Making registry connection to 'Installer' key"
                            If ($subkey.GetSubKeyNames() -contains "Installer") {
                                $subkey = $subkey.OpenSubKey("Installer",$True)
                                Write-Verbose "Checking to see if 'DisableRollback' value name exists"
                                If ($subkey.GetValueNames() -contains "DisableRollback") {
                                    #Get the value of DisableRollback
                                    Write-Verbose "Retrieving value of 'DisableRollback'"
                                    $value = $subkey.GetValue('DisableRollback')
                                    Switch ($value) {
                                        0 {$hash = @{Computer = $computer;Rollback = "Enabled"}}
                                        1 {$hash = @{Computer = $computer;Rollback = "Disabled"}}
                                        Default {$hash = @{Computer = $computer;Rollback = "Unknown"}}
                                        }                                    
                                    }
                                Else {
                                    Write-Warning "$($computer): Missing 'DisableRollback' value name in registy!"
                                    $hash = @{Computer = $computer;Rollback = "Missing DisableRollback value name"}
                                    }
                                }
                            Else {
                                Write-Warning "$($computer): Missing 'Installer' key in registy!"
                                $hash = @{Computer = $computer;Rollback = "Missing Installer key"}
                                }
                            }
                        Else {
                            Write-Warning "$($computer): Missing 'Windows' key in registy!"
                            $hash = @{Computer = $computer;Rollback = "Missing Windows key"}
                            }
                        }
                    Else {
                        Write-Warning "$($computer): Missing 'Microsoft' key in registy!"
                        $hash = @{Computer = $computer;Rollback = "Missing Microsoft key"}
                        }
                    }
                Else {
                    Write-Warning "$($computer): Missing 'Policies' key in registy!"
                    $hash = @{Computer = $computer;Rollback = "Missing Policies key"}
                    }
                }
            Else {
                Write-Warning "$($computer): Missing 'Software' key in registy!"
                $hash = @{Computer = $computer;Rollback = "Missing Software key"}
                }
            }
        Catch {
            Write-Warning "$($computer): $($Error[0])"
            $hash = @{Computer = $computer;Rollback = ($error[0].exception.innerexception.message -split "`n")[0]}
            }
        Finally {
            $object = New-Object PSObject -Property $hash
            $object.PSTypeNames.Insert(0,'RollbackStatus')
            Write-Output $object        
            }
        }
    }
}

Function Disable-RollbackPolicy {
<#  
.SYNOPSIS  
     Disables the rollback policy on a local or remote computer
    
.DESCRIPTION  
    Disables the rollback policy on a local or remote computer
    
.PARAMETER Computername
    The name of the computer or computers to disable the rollback policy on

.PARAMETER Passthru
    Displays the returned object after disabling the policy.
    
.NOTES  
    Name: Disable-RollbackPolicy
    Author: Boe Prox
    DateCreated: 06/24/2011
    Links: 

.EXAMPLE
Disable-RollbackPolicy -Computername DC1 -Passthru

Rollback                                Computer                               
--------                                --------                               
Disabled                                DC1    

Description
-----------
This disables the rollback policy on DC1 and returns the object showing that the policy has 
been disabled.

.EXAMPLE
Disable-RollbackPolicy -Computername DC1,server1,server2 -Passthru

Rollback                                Computer                               
--------                                --------                               
Disabled                                DC1    
Disabled                                server1  
Disabled                                server2  

Description
-----------
This disables the rollback policy on DC1,server1 and server2 and returns the object showing that the policy has 
been disabled.
#>
[cmdletbinding(
    SupportsShouldProcess = 'True'
    )]
Param (
    [parameter()]
    [string[]]$Computername,
    [parameter()]
    [Switch]$Passthru
    )
Process {
    ForEach ($computer in $computername) {
        Try {
            #Make initial registry connection   
            Write-Verbose "Making registry connection to remote computer"     
            $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer)
            If ($PScmdlet.ShouldProcess("$computer","Disable Rollback")) {
                #Connect to the subkey where the rollback is located
                Write-Verbose "Making registry connection to 'Software' key" 
                If ($registry.GetSubkeyNames() -notcontains "Software") {
                    $registry.CreateSubKey("Software")
                    $subkey = $registry.OpenSubKey("Software",$True)
                    }
                Else {
                    $subkey = $registry.OpenSubKey("Software",$True)
                    }
                Write-Verbose "Making registry connection to 'Policies' key"
                If ($subkey.GetSubKeyNames() -notcontains "Policies") {
                    $subkey.CreateSubKey("Policies")
                    $subkey = $subkey.OpenSubKey("Policies",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Policies",$True)
                    }
                Write-Verbose "Making registry connection to 'Microsoft' key"
                If ($subkey.GetSubKeyNames() -notcontains "Microsoft") {
                    $subkey.CreateSubKey("Microsoft")
                    $subkey = $subkey.OpenSubKey("Microsoft",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Microsoft",$True)
                    }
                Write-Verbose "Making registry connection to 'Windows' key"
                If ($subkey.GetSubKeyNames() -notcontains "Windows") {
                    $subkey.CreateSubKey("Windows")
                    $subkey = $subkey.OpenSubKey("Windows",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Windows",$True)
                    }
                Write-Verbose "Making registry connection to 'Installer' key"
                If ($subkey.GetSubKeyNames() -notcontains "Installer") {
                    $subkey.CreateSubKey("Installer")
                    $subkey = $subkey.OpenSubKey("Installer",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Installer",$True)
                    }
                Write-Verbose "Checking to see if 'DisableRollback' value name exists"
                If ($subkey.GetValueNames() -notcontains "DisableRollback") {
                    Write-Verbose "Creating DisableRollback value name and setting to 1 (Disable)"
                    $subkey.SetValue("DisableRollback","1","DWord") 
                    }                                 
                Else {
                    Write-Verbose "Disabling Rollback"
                    $subkey.SetValue("DisableRollback","1","DWord") 
                    }
                If ($PSBoundParameters['Passthru']) {
                    Write-Verbose "Retrieving value of 'DisableRollback'"
                    $value = $subkey.GetValue('DisableRollback')
                    Switch ($value) {
                        0 {$hash = @{Computer = $computer;Rollback = "Enabled"}}
                        1 {$hash = @{Computer = $computer;Rollback = "Disabled"}}
                        Default {$hash = @{Computer = $computer;Rollback = "Unknown"}}
                        }                     
                    }                    
                }
            }
        Catch {
            Write-Warning "$($computer): $($Error[0])"
            }
        Finally {
            If ($PSBoundParameters['Passthru']) {        
                $object = New-Object PSObject -Property $hash
                $object.PSTypeNames.Insert(0,'RollbackStatus')
                Write-Output $object   
                }
            }            
        }
    }
}

Function Enable-RollbackPolicy {
<#  
.SYNOPSIS  
     Enables the rollback policy on a local or remote computer
    
.DESCRIPTION  
    Enables the rollback policy on a local or remote computer
    
.PARAMETER Computername
    Name of computer or computers to enable the rollback policy
    
.PARAMETER Passthru
    Displays the object after enabling the rollback policy on a computer
           
.NOTES  
    Name: Enable-RollbackPolicy
    Author: Boe Prox
    DateCreated: 06/24/2011
    Links: 

.EXAMPLE
Enable-RollbackPolicy -Computername DC1 -Passthru

Rollback                                Computer                               
--------                                --------                               
Enable                                DC1    

Description
-----------
This enables the rollback policy on DC1 and returns the object showing that the policy has 
been enabled.

.EXAMPLE
Enable-RollbackPolicy -Computername DC1,server1,server2 -Passthru

Rollback                                Computer                               
--------                                --------                               
Enable                                DC1    
Enable                                server1    
Enable                                server2    

Description
-----------
This enables the rollback policy on DC1,server1 and server2 and returns the object showing that the policy has 
been enabled.
#>
[cmdletbinding(
    SupportsShouldProcess = 'True'
    )]
Param (
    [parameter()]
    [string[]]$Computername,
    [parameter()]
    [Switch]$Passthru
    )
Process {
    ForEach ($computer in $computername) {
        Try {
            #Make initial registry connection   
            Write-Verbose "Making registry connection to remote computer"     
            $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer)
            If ($PScmdlet.ShouldProcess("$computer","Enable Rollback")) {
                #Connect to the subkey where the rollback is located
                Write-Verbose "Making registry connection to 'Software' key" 
                If ($registry.GetSubkeyNames() -notcontains "Software") {
                    $registry.CreateSubKey("Software")
                    $subkey = $registry.OpenSubKey("Software",$True)
                    }
                Else {
                    $subkey = $registry.OpenSubKey("Software",$True)
                    }
                Write-Verbose "Making registry connection to 'Policies' key"
                If ($subkey.GetSubKeyNames() -notcontains "Policies") {
                    $subkey.CreateSubKey("Policies")
                    $subkey = $subkey.OpenSubKey("Policies",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Policies",$True)
                    }
                Write-Verbose "Making registry connection to 'Microsoft' key"
                If ($subkey.GetSubKeyNames() -notcontains "Microsoft") {
                    $subkey.CreateSubKey("Microsoft")
                    $subkey = $subkey.OpenSubKey("Microsoft",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Microsoft",$True)
                    }
                Write-Verbose "Making registry connection to 'Windows' key"
                If ($subkey.GetSubKeyNames() -notcontains "Windows") {
                    $subkey.CreateSubKey("Windows")
                    $subkey = $subkey.OpenSubKey("Windows",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Windows",$True)
                    }
                Write-Verbose "Making registry connection to 'Installer' key"
                If ($subkey.GetSubKeyNames() -notcontains "Installer") {
                    $subkey.CreateSubKey("Installer")
                    $subkey = $subkey.OpenSubKey("Installer",$True)
                    }
                Else {
                    $subkey = $subkey.OpenSubKey("Installer",$True)
                    }
                Write-Verbose "Checking to see if 'DisableRollback' value name exists"
                If ($subkey.GetValueNames() -notcontains "DisableRollback") {
                    Write-Verbose "Creating DisableRollback value name and setting to 0 (Enable)"
                    $subkey.SetValue("DisableRollback","0","DWord") 
                    }                                 
                Else {
                    Write-Verbose "Enabling Rollback"
                    $subkey.SetValue("DisableRollback","0","DWord") 
                    }
                If ($PSBoundParameters['Passthru']) {
                    Write-Verbose "Retrieving value of 'DisableRollback'"
                    $value = $subkey.GetValue('DisableRollback')
                    Switch ($value) {
                        0 {$hash = @{Computer = $computer;Rollback = "Enabled"}}
                        1 {$hash = @{Computer = $computer;Rollback = "Disabled"}}
                        Default {$hash = @{Computer = $computer;Rollback = "Unknown"}}
                        }                     
                    }
                }
            }
        Catch {
            Write-Warning "$($computer): $($Error[0])"
            }
        Finally {
            If ($PSBoundParameters['Passthru']) {
                $object = New-Object PSObject -Property $hash
                $object.PSTypeNames.Insert(0,'RollbackStatus')
                Write-Output $object   
                }
            }
        }
    }
}

Export-ModuleMember -Function Get-RollbackPolicy,Enable-RollbackPolicy,Disable-RollbackPolicy
Posted in Modules, powershell | Tagged , , , | 1 Comment