This blog post is part of my “Automating Power BI deployments” series, and you can find a list of the other posts here.

If you are going to use PowerShell to automate your deployments, it makes sense to have some understanding of how it works. You don’t have to be a PoSh expert (I certainly am not), but knowing the basics will make things easier down the line.

Development tools

PowerShell is a text-based scripting language, which means you can use Notepad (or any other text editor for that matter) to develop scripts. But that’s no fun and without some syntax highlighting, troubleshooting will be uhm…difficult.

Windows 10 comes with PowerShell preinstalled and you will have both the Windows PowerShell console and the Windows PowerShell ISE.

PowerShell Console (top) and ISE (bottom)

The console works great when you’re executing single commands, but not when you’re developing entire scripts. The ISE has a built-in console and has everything you need from a development perspective.

I have recently started using Visual Studio Code to develop my PowerShell scripts, and it works pretty well and feels like a more complete development tool. You would also need to use VS Code if you want to install and use PowerShell 7, because it doesn’t work with the ISE. I don’t want to get too lengthy here, so if you’re interested in using VS Code you can read all about it here.

Required permissions

Before you can start executing PoSh commands you will need to make sure that you have the appropriate permissions and execution context, which means the following:

  1. Editors or development tools (irrespective of what you use) will have to run as Administrator. I personally like to configure my desktop shortcuts so that I don’t have to worry about it every time.
  2. Even though PoSh is installed by default in Windows 10, its ability to execute commands are restricted by an execution policy. To be able to run commands and scripts, you’ll probably have to change the execution policy. You can use the commands below to see the current policies and change them. I prefer to change it for the entire machine.
#This will show you all the existing execution policies
Get-ExecutionPolicy -List

#If you want to set it for the machine
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine
 
#If you want to set it for the current process
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process 

#If you want to set it for current user only
Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser

Cmdlets

PowerShell works with cmdlets, which is nothing more than a command or function that performs a certain task. These cmdlets are grouped into modules or packages which you have to install and import in order to use.

Cmdlets have parameters (both mandatory and optional) you’ll have to provide for its execution, and finding the appropriate cmdlet and documentation around its parameters is the most time consuming task when developing scripts.

Some base modules are installed with Windows 10 by default, but for the purposes of this series we will use the Power BI Management module. You can execute the following commands to import and install this module:

#Install the module in order to use the cmdlets
Install-Module -Name MicrosoftPowerBIMgmt -AllowClobber

#Import the module into the current session
Import-Module MicrosoftPowerBIMgmt

The AllowClobber parameter is an interesting addition here and my first PoSh tip: It will force any preexisting modules with the same names to be replaced by the latest versions, an important step to remember if you want to make sure you’re always using the most recent version.

Even though it is possible to run different versions of PoSh modules side-by-side, it can be confusing and probably something you want to avoid if you’re just starting out.

.Net Constructs

PowerShell is built on top of the .Net framework which means that you can use C# constructs like while-loops or conditional statements, and you would recognize the syntax as almost identical.

The number one thing that may trip you up when using .Net constructs in PoSh are the comparison operators, which unlike C# is written in a parameter-like fashion. In the PoSh snippet below, we use -eq instead of the typical = operator.

if (value -eq comparison) {
    do some stuff
}

You can read more about comparison operators in PowerShell here.

Variables & Parameters

Variables are preceded by the $ sign and are not strongly typed, which means that you don’t have to specify the data type when declaring the variable. The following are all valid variable assignments:

$MyVariable1 = 1, 2, 3, 4, 5
$MyVariable2 = "one", "two", "three" 
$MyVariable3 = (Get-Date).DateTime

There are also “global” variables called automatic variables, used internally by PowerShell to store certain things. You can use these to get the current version of PoSh, or trap errors for example.

If you’re going to create a script that performs multiple tasks and you’d like to execute it as a unit, you will probably use parameters. Parameters are usually declared at the top of your script, and the following is an example of what that will look like:

param 
(    
    [Parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()] 
    [String]
    $MyParam1, 

    [Parameter(Mandatory=$false)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet("Yes","No")] 
    [String]
    $MyParam2 = "No"
)

In the code-segment above we’re creating two parameters ($MyParam1 and $MyParam2). $MyParam1 is mandatory, and we’re also validating that both variables are not null or empty when provided.

At first it may seem counter-intuitive to check an optional parameter ($MyParam2) for an empty or null value, but this is to ensure that it has a value when provided in the call to the script. If $MyParam2 has a value, we’re also limiting the possible values that can be provided to “Yes” or “No”, and it will default to “No” otherwise.

Comments

It goes without saying that comments are necessary to provide context, explain what a block of code (or command) is doing and/or logically separate different sections of a script.

Single-line comments are preceded by the # sign, and comment blocks (not shown below) are denoted by <# and #> to start and end the comment block.

#This is a single-line comment

Script Output

Most cmdlets will respond with some output, but this can get messy if you’re doing a lot in a single script or if you want to run multiple scripts at the same time. I prefer to suppress the default cmdlet responses and create my own custom response, and here’s how you can do it:

#This cmdlet will connect to a Power BI tenant, but suppress 
#the default output by using Out-Null in a pipeline
Connect-PowerBIServiceAccount | Out-Null 

#The following line will write text to the output window
Write-Output "Hello World!" 

#The following line will also write text to the output window, 
#but I can do some cool things with this cmdlet like changing 
#the color of the text 
Write-Host "An error has occurred!!" -ForegroundColor Red

Pipelines

PoSh pipelines provide a mechanism to condense code and potentially reduce the number of variables required. In a pipeline (denoted by the | sign), output from the first (left) cmdlet will be sent as input to the next cmdlet to the right.

Although a great feature, I’d recommend that you don’t jump into pipelines immediately if you’re just starting out with PowerShell. Get comfortable with everything else first and while you’re learning.

Error Handling

A PoSh script does not terminate its execution by default after an error…something I had to learn the hard way. Luckily you can enforce that with a few tweaks and some error handling, and this is how I like to do it:

#Setting the automatic (internal) variable here to force the script to stop 
#executing when an error occurs, and writing a custom error message to 
#the output screen...in yellow, because we can :-) 
$ErrorActionPreference = "Stop"
Write-Host "**NOTE: THIS SCRIPT WILL STOP EXECUTING AFTER THE FIRST ERROR**"`r`n -ForegroundColor Yellow  

#The following is a typical try-catch block to use for error-handling. I've 
#added a few custom messages to make reading easier. Just do it! 
try { 
    do some stuff
}
catch {
    Write-Host ""`n 
    Write-Host "An error has occurred!!" -ForegroundColor Red
    Write-Host "Error Line Number :   $($_.InvocationInfo.ScriptLineNumber)" -ForegroundColor Red
    Write-Host "Error Command     :   $($_.InvocationInfo.Line.Trim())" -ForegroundColor Red
    Write-Host "Error Message     :   $($_.Exception.Message)"`n -ForegroundColor Red 
    Write-Host "Terminating script execution..."`n 
}

Script Termination

You probably won’t need this while debugging your code or running a single script at a time, but I’ve seen some interesting errors from PoSh sessions that do not terminate completely…especially when you are executing the same script multiple times in succession.

To avoid this behavior, always add the exit command at the end of your script.





Want to see the PowerShell scripts for this series? Get it from my GitHub repo here.

Leave a Reply

%d bloggers like this: