PowerShell Script Template

Important Update

As of September 2015, I have released version 2 of my PowerShell Script Template, which includes a number of improvements over this version, in particular the use of the new PSLogging PowerShell module that significantly improves the creation and management of log files. This version of the template is no longer supported or maintained, so please upgrade to the new version today!

Version 2 of the template is available here – PowerShell Script Template v2.


Recently I put up an article on how to easily create log files in PowerShell. If you want to read that post, then check out this link – PowerShell: How to easily create log files for your scripts.

Today’s article is the next step in the process of creating awesome PowerShell scripts.

So far we have a standard PowerShell Logging Function Library that we can use in all our scripts to easily and quickly make some awesome looking log files. Now wouldn’t it be awesome if we have a standard PowerShell script template that we can fill out and use everytime we need to create a new script.

Not only that, but wouldn’t it be awesome if it already dot sourced the logging function and it also already had all of the LoG-Start and Log-Finish stuff in it, including version control and standard error handling that integrates with the Loggging Function we created earlier?

PowerShell Script Template: Quick Intro

The good news is…. I have already made a Powershell script template that includes all of this and more! It is really, really easy to use and self-exaplanatory… so all you need to do is fill in the gaps, remove the comment sections and then enjoy!

PowerShell Script Template: Why use it?

This template provides the following benefits:

  • Easy to use;
  • Self-Exlpanatory with heaps of comments;
  • Standardises the look and feel of your scripts;
  • Ensures easy readability of your scripts and comments;
  • Provides your scripts will all the recommended meta information;
  • Integrates with my PowerShell Logging Function Library for easy to use script logging;
  • Provides a standard methodology for error handling;
  • Makes your life much simpler!

PowerShell Script Template: The Template

Ok here it is (there will be a download link at the bottom of this page if your don’t want to copy this into your own .ps1 file)

PowerShell Script Template: Small things to note

Ok like I said the template is pretty much self-explanatory, however there are a few things I would like to point out:

  • As mentioned previously, this template uses my PowerShell Logging Function Library. For more info click here to see the article.
  • $sScriptVersion – this is the variable that holds the current version of the script. This is written in the header of the log file
  • Under the functions section, simply remove the <# and #> to uncomment the function. You can then copy this as many times as you want to create all your other functions in the script
  • Uncomment each of the lines in the Execution section and of course replace the Script execution goes here line with each of the functions you want to call and in the order you want to call them

PowerShell Script Template: Download Link

If you don't want to copy the code above, then you can download the template from here: PowerShell Script Template

Do you need a PowerCLI Script Template?

If you are looking for a PowerCLI Script Template, then I have create one of those as well. You can get it from here >>> PowerCLI Script Template.

As per usual, if you get stuck on anything or are unsure about anything, please send me an email or better still use the comments below so that other people can benefit from it too. Also, if you have any new ideas or improvements let me know in the comments below.

I hope this helps some of you guys out and makes PowerShell scripting heaps more enjoyable.

Thanks everyone for the continued support.

Luca

Comments

  1. Hello, this is a great idea. I downloaded your script template and the Log Library. I did not make any changes to the log library. I only change the dot source and $sLogPath.

    For whatever reason the log is not working. Is there anything else I need to do in order to get this working by default?

    1. Hey Waylon,

      How are you going? Thanks for your comment. As long as you have the Log Function Library you should be ok with the Script Template. Are you getting any errors? If you are can you send a screenshot to admin@9to5IT.com and I will have a look for you. Hope this helps. Luca

  2. Hello Luca,
    This has been a huge help to me while starting out in PowerShell!

    I am also having this same issue that Waylon is having.
    It almost seems like it isn’t even entering the Log-Write Function for some reason.
    Any Thoughts?

    1. Hey Tony,

      Glad to here that this has helped you – that is awesome! Can you try the following, so that we can see what error or what the issue is. Can you please use the Microsoft PowerShell ISE which is a GUI for Windows PowerShell which is installed by default when you install PowerShell (or on Operating Systems that already have PowerShell installed). You can access the PowerShell ISE by either running powershell_ise.exe or on the Start Menu >> All Programs.

      Once you have the script open in PowerShell ISE can you select the first line of the script then press the F9 key which will set a breakpoint (which we will use in the debugger). Once you have done that press F5 (or the Green Arrow from the top menu) to get into the debugger. You will see you script halt at the line you set a breakpoint at and it will be highlighted in yellow. From here you can just simply press F11 to get the script to go to the next line. Continue doing this line by line until you get to the Log-Write line. This will then allow you to see if you get any errors or if the script actually executes the Log-Write function.

      Also, The entire Function has actually been commented out – so you will need to remove line 44 and line 72 otherwise the function will not even run. In addition, you will need to name your function on line 46. Line 46 should look something like this: Function MyFunction(){

      Finally once you have done that you will need to call your function in the script, otherwise it will not execute. You do this on Line 77. To do this, remove all the text from Line 77 and then just call your function like this: MyFunction().

      You can then essentially create as many functions as you like by copying the the standard function code between lines 46 and 70. If you have multiple functions you would then just call each one in order on Line 77 and beyond (one on each line).

      I hope this helps.

      Let me know how you go.

      Thanks
      Luca

  3. Hi Luca

    When I run the script it takes veeeeeeeeeery long until its done. To test the script I just added the line “mkdir c:\temp”. It should write an error, because the directory already exists. But it writes out just the default lines:

    ***************************************************************************************************
    Started processing at [03/13/2014 10:05:40].
    ***************************************************************************************************

    Running script version [1.0].

    ***************************************************************************************************

    ***************************************************************************************************
    Finished processing at [03/13/2014 10:07:27].
    ***************************************************************************************************

    Where do I have to put my Script line?

    Cheers
    Paul

    1. Hi Paul,

      To be able to use the template you need to remove the opening and closing tags for the multi line comments found on line 44 and 72 respectively. In addition, you will also need to name your function on line 46. Line 46 should look something like: Function MyFunction { where MyFunction is the name of your function.

      Finally you will also then need to call your function in order to execute it. To do this replace line 77 with your function name to call it. You may also want to remove the comments out from line 76 and line 78 so that the script executes the log-start and log-finish functions (although based on your output it seems like you have already done that).

      In your script if you would like to write a line to the log file, simply use log-write. If you want to write an error to the log file, then use log-error. The function has been set-up so that it automatically captures errors within the Try block, so the error handling should already been taken care of.

      Hope this helps

      Luca

  4. Luca – Awesome!!

    Thanks for sharing the template and the logging function. I have a ton of PowerShell code that I, curiously, didn’t think about formatting as I have my other programs/scripts that I’ve written in other languages.

    You’ve saved me a lot of time with the two items so Thanks again!
    JT

  5. Hi Luca!
    How can i use your logging function with my script, I want to use this when i copy files to network device,
    please any advice will be appriciate !
    Thanks in advance.
    SA

    1. Hi SA,

      All you need to do is remove the comments from line 48 and 76 and then just select an appropriate name for the function on line 50. You can then add all of you file copy script in between the Try section (line 59). If you want to to write a line to the log file all you need to do is call the Log-Write function, such as on line 54 of the PowerShell Script Template.

      You will then need to uncomment line 80 and line 82 and then finally call your function on line 81. This will cause each of the functions to execute.

      The only other thing to look out for is that you will need to first download and save my PowerShell Logging Function. You can get it from here. Once you have downloaded it, on line 34 of the script template, you will need to change the path to where you saved the logging function.

      Hope this helps
      Luca

  6. Do you have any examples of one of your scripts so I can look at it? I am writing a large script to update AD Attributes and I can’t seem to get your logging functions to write.

  7. First of all, thank you for your post on logging. It is such a clean system. I am having a problem with this logging though and I am hoping you can help me with it. My script runs fine when it is alone, here it is…

    $DT = Invoke-sqlcmd2
    -ServerInstance “\,” -Database DBAUtil
    -Query “Select server_name FROM ServerList” |
    foreach{
    Get-WmiObject Win32_LogicalDisk

    -ComputerName $_.server_name -filter "DriveType=3"
    -ErrorAction Continue |#SilentlyContinue |
    Select @{n='Date';e={get-date -Format "yyyy-MM-dd hh:mm:ss"}},

    `
    SystemName, DeviceID, VolumeName, `
    `
    @{n='Size';e={$([math]::round(($_.Size/1GB),2))}}, `
    @{n='UsedSpaceGB';e={$([Math]::round(($_.Size/1GB),2)) - ([math]::round(($_.FreeSpace/1GB),2))}},`
    @{n='FreeSpaceGB';e={$([math]::round(($_.FreeSpace/1GB),2))}}, `
    @{n='PercentUsed';e={100-$([Math]::round(($_.freespace/$_.size) * 100, 2))}}, `
    @{n='PercentFree';e={$([Math]::round(($_.freespace/$_.size) * 100, 2))}} } | out-datatable

    Write-DataTable -ServerInstance "<servername>\<instance>,<port>"
    -Database “DBAUtil” -TableName "ServerDiskSpace"
    -Data $DT

    This script makes a connection to a SQL Server, selects a list of servers from a table, then gathers disk information about those servers, then writes it to an in memory table. It uses the functions out-datatable, write-datatable, and invoke-sqlcmd2. The code above works even if there is a server in the list which is unreachable. It just doesn’t gather any data for the unreachable server, but it does store all the other server drive info inside the DT variable. Then we insert all the drive info into the target table listed in the write-data function. On my first attempt using your logging script/template it worked great, but I only had five servers in my server list and I was able to connect to all of them. When I added more servers to my server list, the process ran into a server which it could not access and it would not continue on. See code below…

    Set Error Action to Silently Continue

    $ErrorActionPreference = “SilentlyContinue”

    Dot Source required Function Libraries

    . D:\geoff\PowerShellFunctions\invoke-sqlcmd2.ps1 # this sources a more robust version of “invoke-sql” so we can run SQL commands
    . D:\geoff\PowerShellFunctions\out-datatable.ps1 # allows us to take the output of a powershell WMI call into an in memory table.
    . D:\geoff\PowerShellFunctions\write-datatable.ps1 # allows us to load the in memory table into the SQL table.
    . D:\geoff\PowerShellFunctions\LoggingFunction.ps1 # allows us to log

    ———————————————————-[Declarations]———————————————————-

    Script Version

    $sScriptVersion = “1.0”

    Log File Info

    $sLogPath = “D:\geoff\PowerShellLogs”
    $sLogName = “DiskSpace$(Get-Date -f _yyyy_MM_dd_hhmm).log”
    $sLogFile = Join-Path -Path $sLogPath -ChildPath $sLogName

    ———————————————————–[Functions]————————————————————

    ———————————— Get Drive Info For Each Server ————————————

    Function GSL_GDI_SIMT { #Get Server List _ Get Drive Info _ Store In Memory Table
    Param()

    Begin{
    Log-Write -LogPath $sLogFile -LineValue “Getting Server List, Pulling Drive Info…

    "

    }

    Process{
    Try{
    $DT =
    Invoke-sqlcmd2 -ServerInstance "<servername>\<instance>,<port>"
    -Database DBAUtil -Query "Select server_name FROM ServerList" |
    foreach{
    $a = $_.server_name
    Log-Write -LogPath $sLogFile -LineValue “Getting drive info for $a ”
    Get-WmiObject Win32_LogicalDisk -ComputerName $_.server_name
    -filter “DriveType=3” -ErrorAction Continue | #SilentlyContinue |
    Select @{n=’Date’;e={get-date -Format “yyyy-MM-dd hh:mm:ss”}}, `

    `
    SystemName, DeviceID, VolumeName, `
    `
    @{n='Size';e={$([math]::round(($_.Size/1GB),2))}}, `
    @{n='UsedSpaceGB';e={$([Math]::round(($_.Size/1GB),2)) - ([math]::round(($_.FreeSpace/1GB),2))}},`
    @{n='FreeSpaceGB';e={$([math]::round(($_.FreeSpace/1GB),2))}}, `
    @{n='PercentUsed';e={100-$([Math]::round(($_.freespace/$_.size) * 100, 2))}}, `
    @{n='PercentFree';e={$([Math]::round(($_.freespace/$_.size) * 100, 2))}} } | out-datatable

    RETURN $DT
    }

    Catch{
    Log-Error -LogPath $sLogFile -ErrorDesc $_.Exception -ExitGracefully $False
    #Break
    }

    }

    End{
    If($?){
    Log-Write -LogPath $sLogFile -LineValue “Completed Successfully.”
    Log-Write -LogPath $sLogFile -LineValue ” ”
    }
    }
    }

    ———————————— Insert Drive Info ————————————

    Function IDI { # Insert Drive Info
    Param($G_GSL_GDI_SIMT)

    Begin{
    Log-Write -LogPath $sLogFile -LineValue “Loading drive info into SQL table…”
    }

    Process{
    Try{
    Write-DataTable -ServerInstance "<servername>\<instance>,<port>"
    -Database “DBAUtil” -TableName "ServerDiskSpace"
    -Data $G_GSL_GDI_SIMT #$G_GetServerDriveInfo, since this is inside the function, i needed to use the global variable we created at the bottom of this script.

    }

    Catch{
    Log-Error -LogPath $sLogFile -ErrorDesc $_.Exception -ExitGracefully $False
    #Break
    }

    }

    End{
    If($?){
    Log-Write -LogPath $sLogFile -LineValue “Completed Successfully.”
    Log-Write -LogPath $sLogFile -LineValue ” ”
    }
    }
    }

    ———————————————————–[Execution]————————————————————

    Log-Start -LogPath $sLogPath -LogName $sLogName -ScriptVersion $sScriptVersion
    $G_GSL_GDI_SIMT = GSL_GDI_SIMT
    $G_IDI = IDI
    Log-Finish -LogPath $sLogFile

    For some reason I can’t figure out, once it hits the unreachable server, no data is stored inside the variable anymore. Here is the output of the log file… Any idea what I am doing wrong here?

    Started processing at [08/17/2015 09:12:03].

    Running script version [1.0].

    Getting Server List, Pulling Drive Info…

    Getting drive info for serverA
    Getting drive info for serverB
    Getting drive info for serverC
    Getting drive info for serverD
    Getting drive info for serverE
    Getting drive info for serverF
    Getting drive info for serverG
    Getting drive info for serverH
    Error: An error has occurred [System.UnauthorizedAccessException: Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
    at System.Management.ManagementScope.Initialize()
    at System.Management.ManagementObjectSearcher.Initialize()
    at System.Management.ManagementObjectSearcher.Get()
    at Microsoft.PowerShell.Commands.GetWmiObjectCommand.BeginProcessing()
    at System.Management.Automation.Cmdlet.DoBeginProcessing()
    at System.Management.Automation.CommandProcessorBase.DoBegin()].
    Completed Successfully.

    Loading drive info into SQL table…
    Completed Successfully.

    Finished processing at [08/17/2015 09:12:05].

    1. Hi Geoff,

      Sorry about the late reply… just didn’t get a chance to get to this earlier.

      Without doing any testing, and just from my past experience with PowerShell and reading your script I think you can attempt to fix this in one of two ways:

      • Do not comment out the Break in your Catch statement. I know I was trying to achieve something very similar to you once, and I had a look back in my code and my catch statement looked like this (and it worked…. i.e. continued on when it couldn’t connect to a server):

      Log-Error -LogPath $sLogFile -ErrorDesc $_.Exception -ExitGracefully $False
      $Global:sResult = "Failed"
      Break

      Ignore the second line of the catch statement, but pretty much it worked just like you need it to.

      • The other alternative if option 1 doesn’t work would be to create two separate function. One of the would be the gathering of the data (or the processing required for each server) and the other would simply enumerate all of the servers in the list and would call the processing function for each server. In this way, you would break out of the processing function but continue in the loop.

      Hope this helps!

      Let me know if solution 1 worked, because I am interested to see the results.

      Thanks
      Luca

      1. Thanks Luca! I tried solution 1, and I’m still getting the same error. Re your second solution, I thought I was already using two functions in that way?

        1. Hi Geoff,

          The other thing you can try which would be more helpful would be to use the debugger in the PowerShell ISE editor.

          What it allows you to do is set one or more breakpoints in your code. When you execute the script in the debugger, the ISE will stop at those breakpoints and allow you to check what is happening. You can then find out what is breaking it.

          I think this would help you the most to find the issue.

          If you need more info on using the debugger in the PowerShell ISE, have a look here >>> Using the PowerShell ISE Debugger.

          Hope this helps
          Luca

          1. Luca, thanks so much! I will check that out and let you know. Thanks again for this great powershell resource.

    1. Hi Pally,

      All you need to do is set the $sLogPath variable to whatever path you like (remote or local).

      Also, just letting you know, I have released version 2 of the PowerShell Logging solution, which gives you a number of improvements over this version. You get all the details on PowerShell Logging Functions version 2 here >> PowerShell – Easily Create Log Files version 2

  8. HI Luka,

    Thank you for sharing quality stuff. I got simple query. Please refer below query to add member to distribution group-
    $Owner = Read-Host “Type in Distribution List”
    $User = Read-Host “Type in user to Add”

    Add-DistributionGroupMember -Identity $Owner -Member $User
    Write-Host nn`n
    Write-Host “$User has been added to Distribution List $Owner”

    Now, I want to generate a log file every time the about script gets executed; I did R&D with template & Logging Function Library but just could not succeed.

    Would you able to help me integrating Logging inside script to ‘add member to DL’?

    1. Hi IT_Learner,

      What issue are you having exactly? Do you have any error messages appearing?

      If you are unsure on how to use the PowerShell Logging at all, then take a look at my PowerShell Script Template. It gives you an example and a template to follow on how to use logging.

      Also, just letting you know that version 2 of my PowerShell Logging solution has been released, you might want to use that as it is easier to use and integrate. My PowerShell Script Template includes the new version of PowerShell Logging called PSLogging.

      If you have any issues, let me know.

      Thanks
      Luca

  9. HI,

    Let me just add more details on what exactly I am looking at.
    Under testing environment I run following script which adds a user to Distribution Group.

    —–xxx———–xx——

    $Owner = Read-Host “Type in Distribution List”
    $User = Read-Host “Type in user to Add”

    Add-DistributionGroupMember -Identity $Owner -Member $User
    Write-Host nn`n
    Write-Host “$User has been added to Distribution List $Owner”

    —–xxx———–xx——

    Before making it run on production server I want to take some precautionary steps. Let’s say if tomorrow something goes wrong I will have to get in touch with server team to generate log and assess what went wrong; instead if I could integrate ‘Log’ function in the script, every time it will generate log of who executed it.

    I hope to clarify on my requirements, please advise.

    1. Hi IT_Learner,

      The best way to integrate logging into your script is to use the PowerShell Script Template combined with the PSLogging Module that I have developed. I have created a new version of the the PowerShell Script Template which you can access here >> PowerShell Script Template V2.

      There are detailed instructions on how to use it in the article. If you get stuck, let me know what you are struggling with in the comments.

      Hope this helps
      Luca

  10. @9to5IT, my Get-Help won’t work unless I put a newline between #requires -version 2 and <#.

    This seems to be consistent with PowerShell on Windows 10 as well as MacOS.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.