RSS

Tag Archives: Script

#PowerShell – Getting started with #Pester unit tests to improve code quality

#PowerShell – Getting started with #Pester unit tests to improve code quality

On my list I have a couple of PowerShell related subjects I want/need to learn more about. These include, but are not limited to parallelization, GitHub, DSC, PowerShell Core, JEA, Plaster, DBAtools and DBAchecks.

I have been playing around a bit already with parallelization and Github and the next thing on my list was Pester.

Pester in general

For those that do not know it, the Pester Wiki describes Pester as follows:
“Pester provides a framework for running Unit Tests to execute and validate PowerShell commands”.

So basically you use it to run scripts/functions/code with a specific input and tell Pester what the expected output should be. Then the result of the test either passes or fails. This way you can verify if your code works as expected.

Instead of using it to test your code, you can also use it to test if a configuration is the way it should be/you want it to be. By example if you’ve created an Operating System deployment or configured DSC, you can check if the configuration has been applied (correctly) or not.

Another advantage of having Pester tests is that you can easily re-run tests without spending much time.

Reasons to re-run these tests could be if you run the code under other circumstances like by example with PowerShell Core instead of Windows PowerShell or on Windows Server 2019 instead of Windows Server 2012 R2.  A more common reason is when you change (parts of) your code that you can easily verify if your changes did not break anything. Keep in mind however that if you change your code, you might also need to change and/or add tests.

If you want to, you can use Pester as part of your Continuous Integration and Continuous Deployment (CI/CD) pipeline. But that’s beyond my knowledge and this post. If you want to know more about it, check Microsoft’s article ‘Building a Continuous Integration and Continuous Deployment pipeline with DSC’.

My plan for getting started with Pester

First, I started reading some articles about Pester, then I purchased ‘The Pester Book’ by Adam Bertram. I started reading the first couple of chapters, but didn’t finish it yet. It did provide me with the basics I needed to get started though. Instead of continuing to read the book, I decided to simply start using Pester with a simple real-life example and then build upon that.

The example I decided to start was a simple script I wrote in the past for a colleague to translate network addresses in the IP CIDR format to the IP subnet mask format. By example if the IP CIDR address was ‘192.168.1.1/24’ the output should be ‘192.168.1.1 255.255.255.0’. Basically it would take the input ‘192.168.1.1/24’ split it at the forward slash ( / ) and then use a switch statement to translate the CIDR notation (by example 24) to the subnet mask notation (by example 255.255.255.0).

To be honest I didn’t expect many advantages of using Pester for this specific script.

Actually getting started with Pester

Actually getting started with Pester consisted of the following steps that will be described in more detail:

  1. Installing Pester
  2. Creating a Pester tests file
  3. Coming up with tests
  4. Actually writing the PowerShell / Pester code for the tests you came up with
  5. Running the tests
  6. Analyzing the results, fixing code and re-running tests until all tests pass
  7. Checking code coverage, modifying code / tests, rerunning tests and checking code coverage

I have tried to include relevant parts of the code in my blog, but readability isn’t as good as I would like it to be. So it’s best if you simply download the files yourself from my GitHub.

1. Installing Pester

In my case I had already installed the latest version of Pester, but if you haven’t done that already you can do so by running PowerShell as administrator and running:

Install-Module -Name Pester -Force -SkipPublisherCheck

2. Creating a Pester tests file

My initial script full path was C:\Scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.ps1 and the initial version of it can be found on my GitHub page.

Then I used

New-Fixture -path C:\Scripts -Name Convert-IPCIDRNotationtoIPSubnetMaskNotation

to create a C:\Scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.tests.ps1 file that is linked to the initial script and that will contain the Pester tests for my script. The linking looks as follows:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path

$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace ‘\.Tests\.’, ‘.’

. $here\$sut

Sidenote: When using Test Driven Development (TDD) to create your code you first define your tests and then write your code. So if you create a new solution this way, you would also use New-Fixture and it would create both your empty script file and a linked Pester tests file.

3. Coming up with tests

Think about all possible scenarios and how you can test them. In my case the tests should at least test all possible input CIDR values ranging from 0 to 32 and if the subnet mask output would be what I expected.

4. Actually writing the PowerShell / Pester code for the tests you came up with

Pester tests have their own terminology and syntax. The Pester Book’ by Adam Bertram does a great job covering them in detail.

In this example in its simplest form you have  a ‘Describe‘ block containing a single test or a logical group of tests contained in ‘It‘ statements where you define the code that should be run and what the output should be. By example you could have a logical group of tests for testing input validation and you could have a logical group of tests for testing if the output is as expected. In

This would then by example look like this:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path

$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace ‘\.Tests\.’, ‘.’

. $here\$sut

Describe “Verify input validation working correctly” {

    It “Valid input (1.1.1.1/0) Should not throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1/0’ | Should Not Throw}

    }

    It “Invalid formatting (1.1.1.1-0 instead of 1.1.1.1/0) should throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1-0’ | Should Not Throw}

    }

    It “Invalid IP address (1.1.1.1.1) Should throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1.1/0’ | Should Not Throw}

    }

    It “Invalid IP address (1.1.1.256) Should throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1.1/0’ | Should Not Throw}

    }

    It “Invalid IP address (001.001.001.001) Should throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1.1/0’ | Should Not Throw}

    }

    It “Invalid CIDR (/33) Should Throw” {

        {Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR ‘1.1.1.1/33’ | Should Throw}

    }

}

Describe “Verify if output from Convert-IPCIDRNotationtoIPSubnetMaskNotation is correct” {

    $TestCases = @(

        @{InputValue = ‘192.168.1.1/0’; ExpectedIPCIDR = ‘192.168.1.1/0’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘0.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 0.0.0.0’}

        @{InputValue = ‘192.168.1.1/1’; ExpectedIPCIDR = ‘192.168.1.1/1’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘128.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 128.0.0.0’}

        @{InputValue = ‘192.168.1.1/2’; ExpectedIPCIDR = ‘192.168.1.1/2’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘192.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 192.0.0.0’}

        @{InputValue = ‘192.168.1.1/3’; ExpectedIPCIDR = ‘192.168.1.1/3’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘224.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 224.0.0.0’}

        @{InputValue = ‘192.168.1.1/4’; ExpectedIPCIDR = ‘192.168.1.1/4’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘240.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 240.0.0.0’}

        @{InputValue = ‘192.168.1.1/5’; ExpectedIPCIDR = ‘192.168.1.1/5’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘248.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 248.0.0.0’}

        @{InputValue = ‘192.168.1.1/6’; ExpectedIPCIDR = ‘192.168.1.1/6’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘252.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 252.0.0.0’}

        @{InputValue = ‘192.168.1.1/7’; ExpectedIPCIDR = ‘192.168.1.1/7’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘254.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 254.0.0.0’}

        @{InputValue = ‘192.168.1.1/8’; ExpectedIPCIDR = ‘192.168.1.1/8’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.0.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.0.0.0’}

        @{InputValue = ‘192.168.1.1/9’; ExpectedIPCIDR = ‘192.168.1.1/9’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.128.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.128.0.0’}

        @{InputValue = ‘192.168.1.1/10’; ExpectedIPCIDR = ‘192.168.1.1/10’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.192.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.192.0.0’}

        @{InputValue = ‘192.168.1.1/11’; ExpectedIPCIDR = ‘192.168.1.1/11’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.224.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.224.0.0’}

        @{InputValue = ‘192.168.1.1/12’; ExpectedIPCIDR = ‘192.168.1.1/12’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.240.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.240.0.0’}

        @{InputValue = ‘192.168.1.1/13’; ExpectedIPCIDR = ‘192.168.1.1/13’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.248.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.248.0.0’}

        @{InputValue = ‘192.168.1.1/14’; ExpectedIPCIDR = ‘192.168.1.1/14’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.252.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.252.0.0’}

        @{InputValue = ‘192.168.1.1/15’; ExpectedIPCIDR = ‘192.168.1.1/15’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.254.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.254.0.0’}

        @{InputValue = ‘192.168.1.1/16’; ExpectedIPCIDR = ‘192.168.1.1/16’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.0.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.0.0’}

        @{InputValue = ‘192.168.1.1/17’; ExpectedIPCIDR = ‘192.168.1.1/17’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.128.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.128.0’}

        @{InputValue = ‘192.168.1.1/18’; ExpectedIPCIDR = ‘192.168.1.1/18’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.192.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.192.0’}

        @{InputValue = ‘192.168.1.1/19’; ExpectedIPCIDR = ‘192.168.1.1/19’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.224.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.224.0’}

        @{InputValue = ‘192.168.1.1/20’; ExpectedIPCIDR = ‘192.168.1.1/20’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.240.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.240.0’}

        @{InputValue = ‘192.168.1.1/21’; ExpectedIPCIDR = ‘192.168.1.1/21’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.248.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.248.0’}

        @{InputValue = ‘192.168.1.1/22’; ExpectedIPCIDR = ‘192.168.1.1/22’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.252.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.252.0’}

        @{InputValue = ‘192.168.1.1/23’; ExpectedIPCIDR = ‘192.168.1.1/23’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.254.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.254.0’}

        @{InputValue = ‘192.168.1.1/24’; ExpectedIPCIDR = ‘192.168.1.1/24’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.0’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.0’}

        @{InputValue = ‘192.168.1.1/25’; ExpectedIPCIDR = ‘192.168.1.1/25’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.128’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.128’}

        @{InputValue = ‘192.168.1.1/26’; ExpectedIPCIDR = ‘192.168.1.1/26’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.192’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.192’}

        @{InputValue = ‘192.168.1.1/27’; ExpectedIPCIDR = ‘192.168.1.1/27’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.224’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.224’}

        @{InputValue = ‘192.168.1.1/28’; ExpectedIPCIDR = ‘192.168.1.1/28’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.240’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.240’}

        @{InputValue = ‘192.168.1.1/29’; ExpectedIPCIDR = ‘192.168.1.1/29’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.248’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.248’}

        @{InputValue = ‘192.168.1.1/30’; ExpectedIPCIDR = ‘192.168.1.1/30’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.252’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.252’}

        @{InputValue = ‘192.168.1.1/31’; ExpectedIPCIDR = ‘192.168.1.1/31’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.254’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.254’}

        @{InputValue = ‘192.168.1.1/32’; ExpectedIPCIDR = ‘192.168.1.1/32’; ExpectedIpAddress = ‘192.168.1.1’; ExpectedSubnetMask = ‘255.255.255.255’; ExpectedIpAndSubnetMask = ‘192.168.1.1 255.255.255.255’}

        )

    It “IPCIDR result for input <inputvalue> should be <ExpectedIPCIDR>” -TestCases $TestCases {

        param($InputValue, $ExpectedIpCIDR, $ExpectedIpAddress, $ExpectedSubnetMask, $ExpectedIpAndSubnetMask)

        (Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR $InputValue).IPCIDR | Should -Be $ExpectedIpCIDR

    }

    It “IPAddress result for input <inputvalue> should be <ExpectedIpAddress>” -TestCases $TestCases {

        param($InputValue, $ExpectedIpCIDR, $ExpectedIpAddress, $ExpectedSubnetMask, $ExpectedIpAndSubnetMask)

        (Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR $InputValue).IpAddress | Should -Be $ExpectedIpAddress

    }

    It “SubNetMask result for input <inputvalue> should be <ExpectedSubnetMask>” -TestCases $TestCases {

        param($InputValue, $ExpectedIpCIDR, $ExpectedIpAddress, $ExpectedSubnetMask, $ExpectedIpAndSubnetMask)

        (Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR $InputValue).SubnetMask | Should -Be $ExpectedSubnetMask

    }

    It “Ip and subnet mask result for input <inputvalue> should be <ExpectedIpAndSubnetMask>” -TestCases $TestCases {

        param($InputValue, $ExpectedIpCIDR, $ExpectedIpAddress, $ExpectedSubnetMask, $ExpectedIpAndSubnetMask)

        (Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR $InputValue).IpAndSubnetMask | Should -Be $ExpectedIpAndSubnetMask

    }

}

Sidenote: In the example above I used testcases to define multiple inputs to test, but you could also use standard PowerShell code like getting input from a CSV and using a Foreach. This would look like this:

$Tests = Import-Csv -path $here\Convert-IPCidrNotationToIpSubnetMaskNotation.Tests.Csv” -Delimiter ‘;’

Foreach ($Test in $Tests) #Determine for your own situation if you should use a foreach or Pester test cases

{

    Describe “Convert-IPCIDRNotationtoIPSubnetMaskNotation for $($Test.Input) {

        $Result = Convert-IPCIDRNotationtoIPSubnetMaskNotation -IPCIDR $Test.Input

        It “IPCIDR result should be $($Test.ExpectedIpCIDR) {

            $Result.IPCIDR | Should -Be $($Test.ExpectedIpCIDR)

        }

        It “IPAddress result should be $($Test.ExpectedIPAddress) {

            $Result.IpAddress | Should -Be $($Test.ExpectedIPAddress)

        }

        It “SubNetMask result should be $($Test.ExpectedSubnetMask) {

            $Result.SubNetMask | Should -Be $($Test.ExpectedSubnetMask)

        }

        It “Ip and subnet mask result should be $($Test.ExpectedIpAndSubnetMask) {

            $Result.IpAndSubNetMask | Should -Be $($Test.ExpectedIpAndSubnetMask)

        }

    }

}

5. Running the tests

Even though you can simply run the C:\Scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.tests.ps1 file directly and run the tests, you should use Invoke-Pester because it gives your more options. In this simple example however, the differences aren’t that big but with Invoke-Pester it will show you how long it took to run all tests and how many tests had the result passed, failed, skipped, pending and inconclusive. So in this case you would run:

Invoke-Pester -Script ‘C:\scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.Tests.ps1’

6. Analyzing the results, fixing code and re-running tests until all tests pass

Before I mentioned that I didn’t expect many advantages of using Pester for this specific script, but boy was I wrong. Apparently I made typos in the switch statement and without Pester I would have probably never noticed them. It could have caused major issues If this had been used in production for by example creating firewall rules.

By fixing issues and re-running the tests I finally got rid of the errors in my code. If you are interested in what mistakes I made and what I corrected, take a look at GitHub.

7. Checking code coverage, modifying code / tests, rerunning tests and checking code coverage

The code coverage of your tests basically describes to what degree your tests cover all possible scenarios. So if I had only tested only half of the options in the switch statement, my code coverage could never be 100%. For more info, see the Pester Wiki about code coverage.

The cool thing about Pester is that if you specify the -CodeCoverage parameter with Invoke-Item, it will create a code coverage report for you. So you would run by example:

Invoke-Pester -Script ‘C:\scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.Tests.ps1’ -CodeCoverage ‘C:\scripts\Convert-IPCIDRNotationtoIPSubnetMaskNotation.ps1’

Then it would tell you what percentage is covered by your tests and it even tells you what part(s) you not have tested. I have to admit that I don’t know exactly how it works and if it’s able to detect everything so you have to be critical yourself as well.

In my case I had defined the default keyword in the switch statement to return an error if the CIDR value was not between 0 and 32. I however forgot to write a test for it. Instead of writing a test for it, I decided in this case it would be better to modify the existing script to do input validation and then write tests to test if the input validation was working correctly. After this, the code coverage was 100%

Sidenote: You don’t always have to have 100% code coverage for a script from the start. Often you have limited time and it is not feasible to write tests for scenarios that are unlikely to ever happen. This is something you have to decide yourself. You can always add/improve tests later on and each test you have created is better than having none at all.

Closing thoughts

Pester is a great tool that helps to improve your code, even for simple code. We are all human and we all make mistakes. Pester helps me finding these issues and therefore improves my code quality. It also gives me a better feeling and more confidence about the scripts I’ve written.

What I also really like, is that making changes to an existing scripts takes less time and is less likely to create issues that will not be noticed. This is because you can easily re-run the tests that you have already created. You should still keep in mind that changes to your code might require changes to existing tests or might require you to add tests. But using the -codecoverage parameter of Invoke-Pester also helps you with that.

I purposely chose this simple example just to get started, but Pester can do much more. I plan to read more about it in ‘The Pester Book’ by Adam Bertram and when I get a good use case I will blog about it.

If you have any thoughts about this article, please leave a reply as this motivates me to create more blog posts.

Advertisement
 
Leave a comment

Posted by on June 15, 2018 in ICT, Microsoft, Powershell

 

Tags: , , , , , , , , , , , , , , , ,

PowerShell – easily posting scripts to a website while maintaining syntax highlighting by copying as HTML

PowerShell – easily posting scripts to a website while maintaining syntax highlighting by copying as HTML

 

 

 

When sharing PowerShell scripts on the Internet there are a couple of options:

  1. Simply post it as plain text, which means syntax highlighting will not be included.
  2. Use available syntax highlighting for the platform you use. Sample platforms include:
    Microsoft ScriptCenter and a syntax highlighting example from a script of mine: https://gallery.technet.microsoft.com/scriptcenter/Determine-possible-values-eaf48782
    Wordpress : https://en.support.wordpress.com/code/posting-source-code/
  3. Copy code to HTML including syntax highlighting.

Option 2 and 3 are preferred, especially sharing to Microsoft ScriptCenter because others can then more easily find and use your scripts and maybe even improve them or give you tips on how to make better script.

Option 2 however requires that you have to accept whatever the platform provider has chosen with regards to syntax highlighting, which might differ between platforms. With option 3 you have more freedom with regards to the output and you are in control. Basically it means copying code to HTML including syntax highlighting. The solution I use mostly for this is the Windows PowerShell V3 ISE: Copy As HTML Add-On which adds a simple “Copy As HTML” option in the menu and which can also be used using the shortcut CTRL+SHIFT+C.

I don’t have experience with it, but you could take a look at the script that Lee Holmes from the PowerShell team provided: http://www.leeholmes.com/blog/2009/02/03/more-powershell-syntax-highlighting/
In the comments someone apparently also implemented it as a PowerGUI add-on.

For more PowerShell ISE Add-ons be sure to visit: http://social.technet.microsoft.com/wiki/contents/articles/2969.windows-powershell-ise-add-on-tools.aspx

 
Leave a comment

Posted by on March 14, 2016 in ICT, Microsoft, Powershell

 

Tags: , , , , , , , , , , ,

PowerShell – Using PowerShell 5 to automate the installation of your favorite Windows applications

In this blog post I will explain why to automate installations of Windows applications and how you can do this.

Why automate the installation of applications?

But first, why is this useful? Well this depends on your situation and there are probably many good reasons. For me though, it basically boils down to this:

  1. I often reinstall my computers with new (preview) versions of Windows operating systems and having to install applications each time is a waste of time. Also sometimes you forget to install some things.
  2. On a regular basis, friends and family either want me to install or upgrade their PC and I want to provide them with a standard set of programs that most people need/want without having to spend a lot of time on it. By example virus scanner, burning program, media player, codecs, etc.
  3. I want to update existing installed applications to the latest (and hopefully more secure and feature packed) versions.
  4. When installing applications, there are often checkboxes enabled to install other applications (you generally don’t want to install). Automated solutions using packages generally prevent these additional unwanted applications from installing.

Which tools to use to automate the installation of applications?

Before PowerShell 5 preview was released, I used both Ninite and Chocolatey to perform to automate installations. They both have their advantages as described on this wiki page.

The PowerShell 5 preview version of OneGet installs and searches software from Chocolatey repositories, but support of additional repositories will come in subsequent versions.

How to automate the installation of applications using PowerShell 5 preview?

To automate the installation of applications a couple of things are required:

  1. You need to determine which applications you want to install automatically.
  2. You need to determine the package name that Chocolatey uses for this application. Options include:
    -Using a browser to browse the Chocolatey packages
    -Using PowerShell and a part of the name of the application you’re looking for. By example if you’re looking for Irfanview, use:
    Find-Package -Name “fan”
  3. Store the package names to install somewhere (e.g. in a .txt file on OneDrive for easy access). My .txt file by example includes:
    AdobeReader
    Directx
    ffdshow
    Flashplayerplugin
    GoogleChrome
    Imgburn
    IrfanView
    Javaruntime
    Keepassx
    Mp3tag
    mpc-hc
    PDFCreator
    Silverlight
    TeamViewer
    Totalcommander
    Winrar
    greenshot
  4. Use the Install-Package cmdlet to install all the packages whose name is in the file from step 3.
    Install-Package -Name (Get-Content C:\OneDrive\AppsToInstall.txt) -Confirm:$False
  5. Wait for the programs to install

My opinion

It’s great to be able to use PowerShell to install my list of favorite applications similar to like I did with Chocolatey and I’m also looking forward to see what benefits the additional repositories will bring in the future.

I did encounter some errors however while trying to install some applications like Firefox and dotnet3.5. But since it’s still a preview, this will probably be fixed.

For regular users, I think they are better off sticking to by example Ninite because they’re often afraid of anything that involves a CLI.

Blog posts by other people about OneGet

Some other people have also blogged about the OneGet module and have gone in more technical detail, so be sure to take a look at their posts as well:

More information about PowerShell 5 Preview including a download link

Windows Management Framework v5 preview, includes also Desired State Configuration (DSC) improvements and NetworkSwitch commandlets to manage network switches that pass the Certified for Windows Program. For more information including a download link, you can read the initial blog post. : Windows Management Framework V5 Preview

 

 

 

Tags: , , , , , , , ,

LazyWinAdmin – a great powershell script for more efficient Windows management

The last couple of months I’ve been playing around a lot with PowerShell and I’ve also been trying to make some nice GUI versions for my script using PowerShell Studio 2012 from Sapien Technologies Inc. While looking for more information how to best use PowerShell Studio 2012 I came across LazyWinAdmin.

LazyWinAdmin is a great tool created by Francois-Xavier C that will save you a lot of time. It provides you with most of the commonly used tools and commands in a very structured and easy to access way. In the picture below you can see what I mean.

For more info on the tool, take a look at the website and be sure to download and test it yourself.

If you want to take a look at other useful tools I’ve found in the past, take a look at my applications list.

 
 

Tags: , , , , , , , , , , , ,

PowerShell – Reverse strings and reverse file names

With PowerShell it is relatively easy to reverse a string:
$str = “gnirtsdesrever”
$str[-1..-($str.length)] -join “”

Some of you might ask: Why would anyone want to do this ? Well, since the start of the year Usenet DMCA takedowns have begun to occur automatically, very quickly and very often. This means many movies and TV series on Usenet / News Groups are being taken offline very quickly.

Counter measures to prevent takedowns by uploaders include:

  • The use of encrypted links to NZB files.
  • The use file names that make it harder to find copyrighted files.

As you can probably guess, some just reverse the file names. The PowerShell script I’ve created will automate reversing the file names for files in the specified folder that match the specified file name filter.

I hope it is useful for you as well.

PS: Someone else on usenet had the same idea as me and created a VBS that will add a right-click menu option to explorer. I’ve added this in the same location as my PowerShell script as well.

 

Tags: , , , , , , ,

PowerShell – Cleaning folders containing temporary files

At the companies I’ve worked for , disk space on the system disk ran out on a regular basis for some systems and caused a lot of monitor calls. These kind of boring repetitive tasks are always the ones you want to automate.

So when I came across the great blog post “Weekend Scripter: Use PowerShell to Clean Out Temp Folders” on the “Hey, Scripting Guy! Blog”, I wanted to post it on my own blog as well. The blog post also describes the process of creating the script very well, which is very convenient for those who are relatively new to PowerShell to scripting and want to learn how to do it better.

If you want to take it even a step further, you could automate it even more. By example by automatically running the clean script when an event is generated that the disk is running out of space. You can configure this using by example:

Or you could use PowerShell remoting to do it on many systems at the same time. This could come in handy by example when after WSUS patching the content in “C:\Windows\SoftwareDistribution\Download” is not deleted automatically.

 

Tags: , , , , , , , , , , , , ,

VMware – PowerShell script to get (storage)vmotion history

Even though I’ve always liked VMWare vSphere, I thought the events and informational messages weren’t always as helpful as they could be.

One of the common things I want to know are the vmotions and storage vmotions that have taken place. Luc Dekens has created a great script that shows the (s)vmotions that have taken place including details about them.

Be sure to take a look at his website for the PowerShell / PowerCLI script including a detailed explanation:
http://www.lucd.info/2013/03/31/get-the-vmotionsvmotion-history/

 
 

Tags: , , , , , , , , ,

Microsoft – Run programs and scripts under the local system user context

In the past I’ve had numerous occasions where I wanted to run a program or system to run under the local system user context. Most of them involved customers and application administrators that wanted to have a service or scheduled task configured. They often weren’t able to tell me if it was sufficient to run the service or scheduled task as local system or that a special service account would have to be created.

The issue I always ran into when I wanted to test this, is that you had to provide a password that you didn’t know if you wanted to use “Run As” with the local system account.

Recently I ran into the “Run As System” application. It enables you to start a program or run command and script under a local system account. It is UAC compatible, but it requires administrative privileges.

Another use of the “Run As System” application is if you want to access files or folders that are normally not accessible for users. This can come in handy when troubleshooting Offline Files. The tool might not work correctly with Windows Explorer though, so you should consider using other tools like Total Commander.

Other methods to run something as system can be found here.

For more tools and applications, take a look at my website: http://bjornhouben-web.sharepoint.com/Lists/Applications/Summary.aspx

 

Tags: , , , , , , , , , , , , , , , ,

PowerShell – Advanced Function Template

I’m currently reading the great book “Learn PowerShell In A Month Of Lunches“.As it turns out there is a lot I can still improve on (which I already knew ofcourse).

Part of the book covers properly creating an advanced function. I’ve taken these sample scripts and added additional comments so I can more easily use it for future functions. You can find it here.

 
Leave a comment

Posted by on March 4, 2013 in Uncategorized

 

Tags: , , , , , ,

 
%d bloggers like this: