Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Another PowerShell ToDo Tool

Posted on September 13, 2016

Recently a reader, Matt Penny, shared a tip in a comment on one of my articles. He had a short and simple PowerShell function that he used to insert ToDo commands into his Pester test scripts. Although you could easily use it for other PowerShell work. Of course, I am always on the look out for inspiration so I took Matt's idea and overworked it.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

Matt's original function was very simple, which is always a good thing.

function todo {
param ([string]$TodoText)
write-host -foregroundcolor DarkYellow ” [o] Todo: $TodoText”
}

Then in a script or function all he has to do is add lines that invoke this function. There is an assumption that this ToDo function is already loaded, perhaps as part of a profile script. Here's a demo function with several ToDo commands inserted. These indicate additional sections that need to be coded.

Function Demo {
[cmdletbinding()]
Param(

[Parameter(ValueFromPipeline)]
[int]$X = 1
)

Begin {
    Write-Verbose "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"  
    ToDo "verbose PSBoundparameter data goes here"
} #begin

Process {
    Write-Verbose "[PROCESS] Processing $X"
    ToDo "calculate square root of X" 
    ToDo "calculate X^X" 
   
    #write a custom object to the pipeline
    [pscustomobject]@{
        Value = $X
        Square = $X*$X
        SquareRoot = 0
        Power = 0
    }

    ToDo "add option to save output" 
} #process

End {
    ToDo "Display runtime"
    ToDo "Clean up environment" 

    Write-Verbose "[END    ] Ending: $($MyInvocation.Mycommand)"
} #end
}

When I run the command, I see the ToDo statements.

image

I like that. But I was curious to see what else I could do with it. Of course even if you have no need for my version of the function, I hope you'll learn something new.

I decided I wanted to have each ToDo numbered. I thought it also might be nice to color code ToDo entries to help me prioritize which to work on first. Finally, the current implementation doesn't work very well when running something in a pipelined expression. My Demo function takes pipeline input so if I test it with say 3 numbers I'll get 3 copies of the ToDo items, when all I really need is one.

Keeping a count is relatively simple. I can initialize a variable and then use the ++ operator to increment it. The challenge though is scope. When I call my version of the ToDo function, I get a new scope which disappears when the function exits. And I don't want to make my main script more difficult to write. So I will create a variable in the global scope and increment it. Note that best practice is to avoid referencing out of scope items, but there are always exceptions. This is one of them. By using the $global: prefix I'm telling PowerShell I know what I'm doing.

The other challenge was keeping track of what messages have already been displayed so I cut down on duplicates during a pipelined operation. Again, I resorted to a global variable  to keep track of message history. If the message has already been displayed don't do anything, otherwise show it and add it to the history variable.

if (-Not ($global:ToDoHistory -contains $ToDo)) {
        $global:ToDoCounter++
        #format the counter with 2 leading zeros.
        $msg = "[$($global:ToDoCounter.ToString("00#"))] TO-DO: $ToDo"

        Write-Host $msg -ForegroundColor $ForegroundColor
        $global:TodoHistory+=$ToDo
    }

To demonstrate here is a modified version of my demo function that invokes my version of the ToDo function. My version takes a parameter for the message and an optional parameter for the message color. The default is Cyan.

Function Demo {
[cmdletbinding()]
Param(

[Parameter(ValueFromPipeline)]
[int]$X = 1
)

Begin {
    Write-Verbose "[BEGIN  ] Starting: $($MyInvocation.Mycommand)"  
    ToDo "verbose PSBoundparameter data goes here" yellow
} #begin

Process {
    Write-Verbose "[PROCESS] Processing $X"
    ToDo "calculate square root of X" red
    ToDo "calculate X^X" 
   
    #write a custom object to the pipeline
    [pscustomobject]@{
        Value = $X
        Square = $X*$X
        SquareRoot = 0
        Power = 0
    }

    ToDo "add option to save output" green
} #process

End {
    ToDo "Display runtime"
    ToDo "Clean up environment" red

    <#
    force ToDo cleanup
    This is optional. If you don't, you'll manually need to clear
    $ToDoHistory and $ToDoCounter from the global scope before 
    running another script that uses ToDo.
    #>
    Remove-ToDoVariable

    Write-Verbose "[END    ] Ending: $($MyInvocation.Mycommand)"
} #end
}

As I complete ToDos I can delete the line from the function. The only thing extra I added was an additional function, Remove-ToDoVariable, in the End block to clean up the global variables. Otherwise, the next time I ran the function, the global variables would be re-used which wouldn't be helpful. You wouldn't have to take this step, you could always manually reset the variables before testing again. But now I get (perhaps) a more useful ToDo message.

image

And a pipelined test doesn't repeat anything.

image

I have put the entire set of functions on GitHub as a gist.

As I mentioned, you may not have much of a need for the function itself, but hopefully you picked up a tip or two. Always interested in hearing what you think.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

3 thoughts on “Another PowerShell ToDo Tool”

  1. Jeffery Hicks says:
    September 13, 2016 at 9:25 am

    By the way, you could combine this with my ToDo ISE snippet https://gist.github.com/jdhitsolutions/75a7216ab86110f7a2c1507d3b29499c

  2. Bevin Du Plessis says:
    September 13, 2016 at 5:25 pm

    If anyone is interested a small modification to add the script name and line number.

    if($null -ne $MyInvocation.ScriptName) {
    $msg = “[$($global:ToDoCounter.ToString(“00#”)) $($MyInvocation.ScriptName):$($MyInvocation.ScriptLineNumber)] TO-DO: $ToDo”
    } else {
    $msg = “[$($global:ToDoCounter.ToString(“00#”))] TO-DO: $ToDo”
    }

  3. Joel Bennett (@Jaykul) says:
    September 15, 2016 at 7:21 pm

    That’s a neat idea — I actually extended pester to use the tag system to mark tests as @wip or @todo … And print information, but not success/failure for them.I have to say, reading that, I was struck that it would be a good bullet point in a talk about how to know when you should write a module: when you need a persistent variable outside your script scope… You might need a module.I also thought of a way to have the number reset automatically: since you want it to reset when you run the script again, you basically want to reset it at the end of your pipeline– but not the pipeline it’s in, the whole outer pipeline that was kicked off from the prompt. For that, all you need is $MyInvocation.HistoryId … if you store that (in module or global scope) when you’re called, and compare against it each time, if it changes, reset your count…

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d