Start-PowerShellPoint

Working on my PowerShell for Developers presentation I’m doing at our upcoming Devscovery Conference in NYC (April 27-29) it was a no brainer to use Jeffrey Snover’s excellent Start-Demo script (much improved by Joel Bennett). If you’re a bad typist like I am having the demo script is a godsend when showing techniques in a tool like PowerShell. It’s also a great example of a PowerShell script for someone learning PowerShell.

As I worked out my demos, I still needed to have some PowerPoint slides to point out key concepts and things like URLs. As I was putting the slides together something was bothering me. Why was I using PowerPoint? I’m going to be a session on PowerShell. Shouldn’t I be using PowerShell? The first rule of the PowerShell club is to use PowerShell! That’s the second rule as well.

What we were missing was PowerShellPoint, but no longer. At the bottom of this article is the Start-PowerShell.ps1 script I wrote to do my presentation. I’m hoping that others doing PowerShell presentations will use it so PowerShell is the only tool you need to demonstrate PowerShell. So to demo PowerShellPoint, I’ll use PowerShellPoint itself. Just call me Captain Recursion!

Just like PowerPoint, I show that black screen. Press the “p” key to move back into the slides.

Below is the script. If you want to download the script and the demo presentation, click here.

Please do let me know if you find Start-PowerShellPoint useful and if you do extend it!

#requires -version 2

# (c) 2010 by John RobbinsWintellect – Do whatever you want to do with it
# as long as you give credit.

<#.SYNOPSIS
PowerShellPoint is the only way to do a presentation on PowerShell. All
PowerShell all the time!
.DESCRIPTION
If you’re doing a presentation on using PowerShell, there’s Jeffrey Snover’s
excellent Start-Demo, (updated by Joel Bennett (http://poshcode.org/705)) for
running the actual commands. However, to show discussion and bullet points,
everyone switches to PowerPoint. That’s crazy! EVERYTHING should be done in
PowerShell when presenting PowerShell. Hence, PowerShellPoint!

To create your “slides” the format is as follows:
Slide titles start with an exclamation point.
Comment (#) are ignored.
The slide points respect any white space and blank lines you have.
All titles and slide points are indented one character.

Here’s an example slide file:
——
# A comment line that’s ignored.
!First Title
Point 1
Sub Point A
Point 2
Sub Point B
!Second Title
Point 3
Sub Point C
Point 4
Sub Point D
!Third Title
Point 5
Sub Point E
——

The script will validate that no slides contain more points than can be
displayed or individual points will wrap.

The default is to switch the window to 80 x 25 but you can specify the window size
as parameters to the script.

The script properly saves and restores the original screen size and buffer on
exit.

When presenting with PowerShellPoint, use the ‘h’ command to get help.

.PARAMETER File
The file that contains your slides. Defaults to .Slides.txt.
.PARAMETER Width
The width in characters to make the screen and buffer. Defaults to 80.
.PARAMETER Height
The height in characters to make the screen and bugger. Defaults to 25.
#>

param( [string]$File = “.Slides.txt”,
       [int]$Width = 80,
       [int]$Height = 25)

Set-StrictMode –version Latest

$scriptVersion = “1.0”

# Constants you may want to change.
# The foreground and background colors for the title and footer text.
$titleForeground = “Yellow”
$titleBackground = “Black”
# Slide points foreground and background.
$textBackGround = $Host.UI.RawUI.BackgroundColor
$textForeGround = $Host.UI.RawUI.ForegroundColor

# A function for reading in a character swiped from Jaykul’s
# excellect Start-Demo 3.3.3.
function Read-Char()
{
    $inChar=$Host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyUp”)
    # loop until they press a character, so Shift or Ctrl, etc don’t terminate us
    while($inChar.Character -eq 0)
{
$inChar=$Host.UI.RawUI.ReadKey(“NoEcho,IncludeKeyUp”)
}
    return $inChar.Character
}

function ProcessSlides($inputFile)
{
$rawLines = Get-Content $inputFile

    # Contains the actual slides. The key is the slide number and the value are the
    # text lines.
    $slides = @{}

# The slide number I’m processing.
    $slideNumber = 0
    [string[]]$lines = $null

    # Process the raw text by reading it into the hash table.
    for ($i = 0 ; $i -lt $rawLines.Count ; $i++ )
{
# Skip any comment lines.
        if ($rawLines[$i].Trim(” “).StartsWith(“#”))
{
continue
        }

        # Lines starting with “!” are a title.
        if ($rawLines[$i].StartsWith(“!”))
{
if ($lines -ne $null)
{
$slides.Add($slideNumber,$lines)
$lines = $null
}
$slideNumber++
            if ($rawLines[$i].Substring(1).Length -eq 0)
{
throw “You have an empty title slide”
            }
$lines += $rawLines[$i].Substring(1)
}
else
        {
if ($slideNumber -eq 0)
{
throw “The first line must be a title slide starting with !”
            }

            # Make sure the line won’t wrap.
            if ($rawLines[$i].Length -gt ($Width 1))
{
Write-Warning “Slide line: $rawLines[$i] is too wide for the screen” -WarningAction Inquire
            }

            $lines += $rawLines[$i]

            # Check to see if this slide is bigger than the height
            if ($lines.Length -gt ($Height 4))
{
$title = $lines[0]
                Write-Warning “Slide $title is too long for the screen” -WarningAction Inquire
            }
}
}

# Add the last slide.
    $slides.Add($slideNumber,$lines)

    # Do some basic sanity checks on the slides.
    if ($slides.Keys.Count -eq 0)
{
throw “Input file ‘$File’ does not look properly formatted.”
    }
return $slides
}

function Draw-Title($title)
{
$cursorPos = $Host.UI.RawUI.CursorPosition
$cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    Write-Host -NoNewline -back $titleBackground -fore $titleForeground $(” “ $Width)
Write-Host -NoNewline -back $titleBackground -fore $titleForeground ” “ $title $(” ”
($Width $title.Length 3))
Write-Host -NoNewline -back $titleBackground -fore $titleForeground $(” “ * $Width)
Write-Host
}

function Draw-SlideText($lines)
{
$cursorPos = $Host.UI.RawUI.CursorPosition
$cursorPos.x = 0
    $cursorPos.y = 4
    $Host.UI.RawUI.CursorPosition = $cursorPos

    for ($i = 1 ; $i -lt $lines.Count ; $i++ )    {        

Write-Host ” “ $lines[$i]
    }}

function Draw-Footer($slideNumber,$slideCount)
{
$cursorPos = $Host.UI.RawUI.CursorPosition
$cursorPos.y = $Height 1
    $cursorPos.x = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $footer = “$slideNumber of $slideCount”
    Write-Host -NoNewline -back $titleBackground -fore $titleForeground “$(” ” * ($Width – $footer.Length – 2)) $footer”
}

function Draw-BackScreen($message)
{
$cursorPos = $Host.UI.RawUI.CursorPosition
$cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $spaces = $(” “ $Width)
for ($i = 0 ; $i -lt $Height ; $i++)
{
Write-Host -NoNewline -BackgroundColor black -ForegroundColor yellow $spaces
    }

    $cursorPos.x = ($Width / 2) ($message.Length / 2)
$cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    Write-Host -NoNewline -BackgroundColor black -ForegroundColor White $message
}

function Show-UsageHelp
{
$help = @”
PowerShellPoint Help $scriptVersion – John Robbins – john@training.atmosera.com

Key             Action
—             ——
‘n’, ‘<space>’  Next slide
‘p’             Previous slide
‘s’             Shell out to PowerShell
‘h’, ‘?’        This help
‘q’             Quit

Press any key now to return to the current slide.
“@
$cursorPos = $Host.UI.RawUI.CursorPosition
$cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    $spaces = $(” ”
$Width)
for ($i = 0 ; $i -lt $Height ; $i++)
{
        Write-Host -NoNewline -BackgroundColor black -ForegroundColor yellow $spaces
    }

    $cursorPos.x = 0
    $cursorPos.y = 0
    $Host.UI.RawUI.CursorPosition = $cursorPos

    Write-Host -NoNewline -BackgroundColor black -ForegroundColor White $help
}

function main
{
# Save off the original window data.
    $originalWindowSize = $Host.UI.RawUI.WindowSize
$originalBufferSize = $Host.UI.RawUI.BufferSize
$originalTitle     = $Host.UI.RawUI.WindowTitle
$originalBackground = $Host.UI.RawUI.BackgroundColor
$originalForeground = $Host.UI.RawUI.ForegroundColor

    # Make sure the file exists. If not, give the user a chance to
    # enter it.
    $File = Resolve-Path $File
    while(-not(Test-Path $File))
{
$File = Read-Host “Please enter the path of your slides file (Crtl+C to cancel)”
        $File = Resolve-Path $File
    }

    try
    {
# Set the new window and buffer sizes to be the same so
        # there are no scroll bars.
        $scriptWindowSize = $originalWindowSize
        $scriptWindowSize.Width = $Width
        $scriptWindowSize.Height = $Height
        $scriptBufferSize = $scriptWindowSize

        $Host.UI.RawUI.BackgroundColor = $textBackGround
        $Host.UI.RawUI.ForegroundColor = $textForeGround


        # Set the title.
        $Host.UI.RawUI.WindowTitle = “PowerShellPoint”

        # Read in the file and build the slides hash.
        $slides = ProcessSlides($File)
        
# The slides are good to go so now resize the window.
        $Host.UI.RawUI.WindowSize = $scriptWindowSize
        $Host.UI.RawUI.BufferSize = $scriptBufferSize

        # Keeps track of the slide we are on.
        [int]$currentSlideNumber = 1
        # The flag to break out of displaying slides.
        [boolean]$keepShowing = $true
        # Flag to avoid redrawing the screen for unknown keypresses.
        [boolean]$redrawScreen = $true

        do
        {            
if ($redrawScreen -eq $true)            {                

Clear-Host

                # Grab the current slide.
                $slideData = $slides.$currentSlideNumber

                Draw-Title $slideData[0]
                Draw-SlideText $slideData
                Draw-Footer $currentSlideNumber $slides.Keys.Count            }

            
$char = Read-Char

            switch -regex ($char)            {                

# Next slide processing.
                “[ ]|n”
                {                    
$redrawScreen = $true
                    $currentSlideNumber++

                    if ($currentSlideNumber -eq ($slides.Keys.Count + 1))                    {                        

# Pretend you’re PowerPoint and show the black screen
                        Draw-BackScreen “End of slide show”
                        $ch = Read-Char
                        if ($ch -eq “p”)                        {                            

$currentSlideNumber                            }                        

else
                        {                            
$keepShowing = $false
                        }                    }                }                

# Previous slide processing.
                “p”
                {                    
$redrawScreen = $true
                    $currentSlideNumber

                    if($currentSlideNumber -eq 0)                    {                        

$currentSlideNumber = 1
                    }                }                

# Quit processing.
                “q”
                {                    
$keepShowing = $false
                }                

“s”
                {                    
Clear-Host
                    Write-Host -ForegroundColor $titleForeground -BackgroundColor $titleBackground “Suspending PowerShellPoint – type ‘Exit’ to resume”

                   $Host.EnterNestedPrompt()
}
# Help processing.
                “h|?”
                {
Show-UsageHelp
                    $redrawScreen = $true
                    Read-Char
                }
# All other keys fall here.
                default
                {
$redrawScreen = $false
                }
}
} while ($keepShowing)

# The script has finished cleanly so clear the screen.
        $Host.UI.RawUI.BackgroundColor = $originalBackground
        $Host.UI.RawUI.ForegroundColor = $originalForeground
        Clear-Host
    }
finally
    {
# I learned something here. You have to set the buffer size before
        # you set the window size or the window won’t resize.
        $Host.UI.RawUI.BufferSize = $originalBufferSize
        $Host.UI.RawUI.WindowSize = $originalWindowSize
        $Host.UI.RawUI.WindowTitle = $originalTitle
        $Host.UI.RawUI.BackgroundColor = $originalBackground
        $Host.UI.RawUI.ForegroundColor = $originalForeground
    }
}

. main

Secure from design through deployment.

Our business is built on ensuring customer environments are secure from design through deployment and to provide solutions which are realistically implementable. We provide managed Information Security (InfoSec) and compliance options across the entire computing stack, from connectivity to applications, with stringent physical and logical security controls.

Define your objectives.

We partner with your team to define the right security profile for your business needs.

Build your foundation.

Together we develop comprehensive best practices encompassing all aspects of security: people, process and technology.

Execute your roadmap.

We collaborate to build and evolve an information security program that bolsters your security profile.

More than technology

All Atmosera customers benefit from processes, policies, training and 24x7x365 technical resources.

Our customers have the peace of mind of knowing an industry expert has performed a thorough risk assessment, identified a remediation plan and provided ongoing audit support to ensure customers stay secure.

Best of all, we understands this level of service is — and will continue to be — required year after year.

We always implement networks which
deliver better security and usability.

All our deployments take into consideration the end-to-end solution and we secure all aspects of the network and connectivity.

  • Defense in Depth – Our approach offers a flexible and customizable set of capabilities based on enterprise-grade standards for performance, availability, and response.
  • WANs, MANs, LANs, and VPNs – We provide comprehensive networking and high-speed connectivity options from Wide Area Networks (WAN), Metropolitan Area Networks (MAN) and Local Area Networks (LAN), as well as managed Virtual Private Networks (VPNs) and firewalls.
  • Stay current – We can be counted on to deliver proactive testing and the most secure network protocols.
  • Reduce vulnerabilities – We help your team leverage advanced security features coupled with relentless vigilance from trained experts.
Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X