Powershell Azure

Apr 12th, 2019 - written by Kimserey with .

PowerShell is versatile command line shell which comes with a powerful scripting language. It is now even more available than before with the new PowerShell Core which makes it available on Linux. Even though the main commands in the scripting language have been around for ages, I always see myself having to re-learn how to use it every time I need to write a script. So today I will share examples that can we reused to quickly put together simple operations with PowerShell:

PowerShell

Starting first from the IDE, Visual Code is a great IDE to code PowerShell scripts coupled with the PowerShell extension. With this tool, we will have autocompletion and typesafety check.

Functions

PowerShell supports functions which is very useful to keep a script file structured. A function can take optionally take arguments and optionally return a result.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Get-BeginningOfYear {
    param ([int]$year, $month)

    Get-Date -Month $month -Day 1 -Year $year
    Get-Date -Month $month -Day 1 -Year ($year + 1)
    Get-Date -Month $month -Day 1 -Year ($year + 2)
}

Get-BeginningOfYear -month 1 -year 2017 |
Select-Object -Property Year |
ForEach-Object { Write-Host $_.Year }

# 2017
# 2018
# 2019

Here the function takes a typed parameter of int. If we were to call the function with a string Get-BeginningOfYear -month 1 -year "x", we would get the following error:

1
Get-BeginningOfYear : Cannot process argument transformation on parameter 'year'. Cannot convert value "x" to type "System.Int32". Error: "Input string was not in a correct format."

Another point to take note of is that the returns are implicit and the function will return an array of three dates which we can pipe to other cmdlet.

1
2
3
Get-Date -Month $month -Day 1 -Year $year
Get-Date -Month $month -Day 1 -Year ($year + 1)
Get-Date -Month $month -Day 1 -Year ($year + 2)

Official Function documentation

Hash Tables

Hash tables can be created using the @{} notation.

1
2
3
4
5
6
7
8
9
@{
    FirstVar = "var 1"
    SecondVar = 10
}

# Name                           Value
# ----                           -----
# SecondVar                      10
# FirstVar                       var 1

We can then access the values in it by making property calls

1
2
3
4
5
6
7
8
$someVar = @{
    FirstVar = "var 1"
    SecondVar = 10
}

Write-Host $someVar.FirstVar

# var 1

One thing to note is that interpolation of properties of hash tables can’t be specified by using inline formatting. Instead we need to use the -f format command.

1
2
3
4
5
6
7
8
9
10
11
12
13
$someVar = @{
    FirstVar = "var 1"
    SecondVar = 10
}

"Hello ${someVar}"
# Hello System.Collections.Hashtable

"Hello ${someVar.FirstVar} - world ${someVar.SecondVar}"
# Hello  - world   <== not working

"Hello {0} - world {1}" -f $someVar.FirstVar, $someVar.SecondVar
# Hello var 1 - world 10

ForEach-Object

We just used ForEach-Object which allow us to iterate over value lists.

1
2
3
4
5
1, 2, 3 | ForEach-Object { $_ }

# 1
# 2
# 3

We can also use ForEach-Object to map values.

1
2
3
4
5
1, 2, 3 | ForEach-Object { "My value: ${_}" }

# My value: 1
# My value: 2
# My value: 3

We can then assign this to a variable.

1
2
3
4
5
$myVal = 1, 2, 3 | ForEach-Object { "My value: ${_}" }

Write-Host $myVal

# My value: 1 My value: 2 My value: 3

Where-Object

Where-Object filters objects from an array based on a condition given.

1
2
3
4
5
6
7
8
9
10
11
function Get-BeginningOfYear2 {
    param ([int]$year, $month)

    for ($i = 0; $i -le 30; $i++) {
        Get-Date -Month $month -Day 1 -Year ($year + $i)
    }
}

Get-BeginningOfYear2 -month 1 -year 2017 |
Where-Object -Property Year -GE 2030 |
ForEach-Object { Write-Host $_ }

A property of the object can be selected to be compared against with -Property and operators like -GT, -LT, -GE, -LE and also -Match, -NotMatch for regex, and -Like, NotLike for wildcard characters filtering. The list is provided by intelisense when using Visual Code with PowerShell extension.

Select-Object

Select-Object selects a property of an object.

1
2
3
4
5
6
7
8
9
10
11
function Get-BeginningOfYear2 {
    param ([int]$year, $month)

    for ($i = 0; $i -le 5; $i++) {
        Get-Date -Month $month -Day 1 -Year ($year + $i)
    }
}

Get-BeginningOfYear2 -month 1 -year 2017 |
Select-Object -Property Year |
ForEach-Object { Write-Host "Year: $($_.Year)" }

Or can be used to select value at a certain index of an array.

1
2
3
Get-BeginningOfYear2 -month 1 -year 2017 |
Select-Object -Index 3 |
ForEach-Object { Write-Host $_.Year }

Or a range.

1
2
3
Get-BeginningOfYear2 -month 1 -year 2017 |
Select-Object -Skip 2 |
ForEach-Object { Write-Host $_.Year }

If Else

if, elseif and else conditions can be used together with a test condition.

1
2
3
4
5
6
7
8
9
10
11
$value = 10

if ($value -gt 5) {
    Write-Host "greater than 5"
}
elseif ($value -gt 3) {
    Write-Host "greater than 3"
}
else {
    Write-Host "None"
}

A widely used condition is to check if a directory exists:

1
2
3
4
5
if(![System.IO.Directory]::Exists("C:\Pcdrojects")){
    Write-Host "Does not Exits"
} else {
    Write-Host "Exits"
}

Or if a file exists with [System.IO.File]::Exists.

The full list of conditions is available on the documentation.

ConvertTo-Json

When we need to interact with other system, via HTTP for example, we often need to convert values to json before sending them. For that we can use ConvertTo-Json.

1
2
3
4
5
6
7
8
9
10
$someVar = @{
    FirstVar = "var 1"
    SecondVar = 10
}

ConvertTo-Json $someVar
# {
#   "SecondVar": 10,
#   "FirstVar": "var 1"
# }

We can also pipe it.

1
$someJsonVar = @{ FirstVar = "var 1"; SecondVar = 10 } | ConvertTo-Json

One thing to note is that arrays of single element are converted to Json object.

1
2
3
4
5
6
7
8
9
@(1, 2, 3) | ConvertTo-Json
# [
#   1,
#   2,
#   3
# ]

@(1) | ConvertTo-Json
# 1 <== and not [ 1 ]

If we need to keep the array format, we must NOT use piping.

1
2
3
4
ConvertTo-Json @(1)
# [
#  1
# ]

From Powershell 6 onward, ConvertTo-Json has an -AsArray command which would make the result an array.

1
2
3
4
ConvertTo-Json -AsArray 1
# [
#   1
# ]

Format-Table

For debugging purposes, we can use Format-Table and Format-List to enhance readiblity of the data.

Format-Table will format the result as a table.

1
2
3
4
5
6
7
> Get-BeginningOfYear2 -month 1 -year 2017 | Format-Table

DisplayHint Date                Day DayOfWeek DayOfYear Hour  Kind Millisecond Minute Month
----------- ----                --- --------- --------- ----  ---- ----------- ------ -----
   DateTime 01/01/2017 00:00:00   1    Sunday         1   10 Local         666     37     1
   DateTime 01/01/2018 00:00:00   1    Monday         1   10 Local         673     37     1
   DateTime 01/01/2019 00:00:00   1   Tuesday         1   10 Local         674     37     1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
> Get-BeginningOfYear2 -month 1 -year 2017 | Format-Table -GroupBy DayOfWeek

      DayOfWeek: Sunday

DisplayHint Date                Day DayOfWeek DayOfYear Hour  Kind Millisecond
----------- ----                --- --------- --------- ----  ---- -----------
   DateTime 01/01/2017 00:00:00   1    Sunday         1   10 Local         213


   DayOfWeek: Monday

DisplayHint Date                Day DayOfWeek DayOfYear Hour  Kind Millisecond
----------- ----                --- --------- --------- ----  ---- -----------
   DateTime 01/01/2018 00:00:00   1    Monday         1   10 Local         214


   DayOfWeek: Tuesday

DisplayHint Date                Day DayOfWeek DayOfYear Hour  Kind Millisecond
----------- ----                --- --------- --------- ----  ---- -----------
   DateTime 01/01/2019 00:00:00   1   Tuesday         1   10 Local         216

And Format-List would display it as a list.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
> Get-BeginningOfYear2 -month 1 -year 2017 | Format-List

DisplayHint : DateTime
Date        : 01/01/2017 00:00:00
Day         : 1
DayOfWeek   : Sunday
DayOfYear   : 1
Hour        : 10
Kind        : Local
Millisecond : 939
Minute      : 41
Month       : 1
Second      : 54
Ticks       : 636188641149394848
TimeOfDay   : 10:41:54.9394848
Year        : 2017
DateTime    : Sunday 1 January 2017 10:41:54

DisplayHint : DateTime
Date        : 01/01/2018 00:00:00
Day         : 1
DayOfWeek   : Monday
DayOfYear   : 1
Hour        : 10
Kind        : Local
Millisecond : 955
Minute      : 41
Month       : 1
Second      : 54
Ticks       : 636504001149551351
TimeOfDay   : 10:41:54.9551351
Year        : 2018
DateTime    : Monday 1 January 2018 10:41:54

DisplayHint : DateTime
Date        : 01/01/2019 00:00:00
Day         : 1
DayOfWeek   : Tuesday
DayOfYear   : 1
Hour        : 10
Kind        : Local
Millisecond : 966
Minute      : 41
Month       : 1
Second      : 54
Ticks       : 636819361149669959
TimeOfDay   : 10:41:54.9669959
Year        : 2019
DateTime    : Tuesday 1 January 2019 10:41:54

Conclusion

PowerShell is an extremely convenient tool with a whole set of functionalities that allows to write concise scripts. All major CI services, AppVeyor, GitLab, VSTS providing direct PowerShell support in their pipeline configuration, it is very handy to know some of the useful features of it. Today we saw how to implement functions in PowerShell, we then saw how to use cmdlets Where-Object and Select-Object to manipulate objects and arrays then we saw how If/Else conditions could be used and finally for debugging, we saw how the Format-x cmdlet could be used. Hope you liked this post, see you next time!

External Sources

Designed, built and maintained by Kimserey Lam.