Cross-Platform Scripting
Tags: devops, tutorial, productivity, powershell
Published: 2020-07-13 06:46:04

In this months [DevOps Guide](https://justice.sh/blog/devops-getting-started/) we're getting into scripting. It's just the fastest way to simplify complex tasks. Before you continue, please make sure you are familiar with [command line basics](https://justice.sh/blog/devops/code/devops-command-line-basics/).

Just the other day, I wrote a small script to

1.  download multiple [swagger json files](https://swagger.io/specification/) from services
2.  download the [swagger-to-PDF](https://github.com/simonstewart/swagger-to-PDF) tool
3.  generate a single pdf with all api endpoints

Now developers don't even have to know which tool we use to generate our api documentation. They see the script, run it, and have the pdf file.  
Furthermore the tool just automatically identifies which API versions are available.

## Cross-Platform PowerShell

Why PowerShell? Well, it is preinstalled on Windows and easy to install on macOS and Linux.  
Installing most other scripting languages on Windows is complicated.  
My main motivation here is to keep the entrance barrier **as low as possible**. Enable everyone on your team.

If your team is already familiar with another cross-platform scripting solution (e.g. bash on windows via Cygwin), you already have all you need for our series on DevOps. In that case you can skip this one for the series.

## Installation

For this tutorial, we need PowerShell and Visual Studio Code.

### PowerShell

For installation steps, follow [this guide by microsoft for your operating system](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell?view=powershell-7).

On Windows, the installation of PowerShell 7 is optional. However, some advanced scripts will require PowerShell 7.

#### Extra Step for Windows

Before running PowerShell scripts on Windows, you must run `Set-ExecutionPolicy Unrestricted`.  
On macOS and Linux, Unrestricted is the default.  
For more information, see [microsoft's documentation on Execution Policies](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7).

### Visual Studio Code

Now let's download and install [Visual Studio Code from Microsoft](https://code.visualstudio.com/download).

Visual Studio Code offers coding assistance and even debugging functionality for PowerShell. So we will be using visual studio code as free cross-platform script editor within this tutorial.  
If you prefer a different editor, I still recommend using Visual Studio Code for this tutorial. After the tutorial, you should check if your preferred editor has the same debugging functionality.

## Start Scripting

After getting comfortable with moving around your file system, let's write your first script.  
Open Visual Studio Code and create a new file and save it with the .ps1 ending.  
This way Visual Studio Code will recognize it as PowerShell script and offer a play button in the top right.

Also I can really recommend reading [Microsoft's Guides to Debugging PowerShell with Visual Studio Code](https://devblogs.microsoft.com/scripting/debugging-powershell-script-in-visual-studio-code-part-1/) to learn how to use Breakpoints and Watches.

### She-Bang

As they use different default shells, the first line of your cross-platform script will tell macOS and Linux, that this is a PowerShell script.  
pwsh is the command name for PowerShell in macOS and linux.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>
</pre>

</div>

### Hello World

In PowerShell, you have different possibilities to write output.  
While Write-Host is okay to give the user information on the state of your script. However, Write-Object, Write-Error Write-Debug and Write-Verbose will be important for advanced scripts.

It's a good idea to be very verbose and always tell the script-user what's happening.  
So we can use `Write-Host "Copying files, please wait."` to display exactly this text to the console.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>

Write-Host <span style="color: #0000ff;">"Hello World."</span>
</pre>

</div>

### Variables

Variables in PowerShell are names with a prefixed $ sign.  
e.g. $count, $foldername, $date, etc.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>

$directory = Get-Location
Write-Host <span style="color: #0000ff;">"Current directory: $directory"</span>
</pre>

</div>

### Objects

The main advantage of PowerShell is its object orientation.  
Assuming that your script is called test.ps1, we can read this files properties:

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>
Set-StrictMode -Version Latest

$scriptfile = Get-Item test.ps1
Write-Host <span style="color: #0000ff;">"File name: $scriptfile.Name"</span>
Write-Host <span style="color: #0000ff;">"File extension: $scriptfile.Extension"</span>
Write-Host <span style="color: #0000ff;">"Creation Date: $scriptfile.CreationTimeUtc"</span>
</pre>

</div>

You can use Visual Studio Codes Watch function to examine your script's objects.

<figure class="wp-block-image size-large is-style-default">[![](https://dev-to-uploads.s3.amazonaws.com/i/g8xk9me817otbpldmb37.png)](https://brutalhack.com/wp-content/uploads/2020/05/Screenshot-2020-05-01-at-22.39.22.png)

<figcaption>We can use the **Watch** function to examine objects in scripts.</figcaption>

</figure>

As this would be a lot to cover, we will have a closer look at PowerShells object orientation later on. For now, just keep this fact in mind.

## Strict Mode

I always recommend enabling Strict Mode in all your scripts.  
This is basically your standard second line after the she-bang.  
It causes your scripts to abort when using uninitialized variables, non-existant properties etc.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>
Set-StrictMode -Version Latest</pre>

</div>

See [Microsofts documentation](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-7) for detailed information on strict mode.

## If Else

Microsoft has a [super documentation on if-else in PowerShell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_if?view=powershell-7), including many great examples.  
Therefore I only want to show one example here for completeness.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>
Set-StrictMode -Version Latest

$number = 2;

<span style="color: #000080; font-weight: bold;">if</span> ($number -gt 2) {
    Write-Host <span style="color: #0000ff;">"$number is greater than 2"</span>
}
elseif ($number -eq 2) {
    Write-Host <span style="color: #0000ff;">"$number is equal to 2"</span>
}
<span style="color: #000080; font-weight: bold;">else</span> {
    Write-Host (<span style="color: #0000ff;">"$number is less than 2"</span>)
}
</pre>

</div>

## Loops

Microsoft also has a super documentation on loops in PowerShell, so this example is also quite short.  
As PowerShell is object oriented, I tend to use ForEach normally.  
`For` or `While` Loops are quite rare in my scripts.

<div style="background: #ffffff; overflow: auto; width: auto; border: solid gray; border-width: .1em .1em .1em .8em; padding: .2em .6em;">

<pre style="margin: 0; line-height: 125%;"><span style="color: #008800; font-style: italic;">#! /usr/bin/env pwsh</span>
Set-StrictMode -Version Latest

Write-Host <span style="color: #0000ff;">"Listing files in current folder:"</span>
foreach ($file in Get-ChildItem)
{
  Write-Host <span style="color: #0000ff;">"- $file (created $($file.CreationTime))"</span>
}
</pre>

</div>

See more more great examples at Microsoft's documentation:

*   [For Loops in PowerShell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_for?view=powershell-7)
*   [ForEach Loops in PowerShell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_foreach?view=powershell-7)

## Further Reading

PowerShell is very popular, so you can google "_How to <DO ANYTHING> in PowerShell_".

The following are among some of my favourite resources:

*   [Official PowerShell Documentation](https://docs.microsoft.com/de-de/powershell/scripting/overview?view=powershell-7)
*   [Strongly Typed Variables in PowerShell](https://pscustomobject.github.io/powershell/coding%20habits/powershell%20best%20practice/PowerShell-Strongly-Typed-Variables/)
*   [Parameters in PowerShell Scripts](https://www.red-gate.com/simple-talk/sysadmin/powershell/how-to-use-parameters-in-powershell/)
*   [PowerShell Guru Best Practices](http://powershell-guru.com/)
*   [Arrays in PowerShell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_arrays?view=powershell-7)

## Next Steps

With this basic scripting knowledge, we are able to get started automating in the upcoming episodes of our [DevOps Guide](https://justice.sh/blog/devops-getting-started/).  
While most scripts will only run very few commands, these are a few commands that you and your colleagues save every day. And let's not forget the manual errors avoided by your scripts.

We can use them for builds, deployments, tests, project setup, anything!

If you have questions during your first steps in PowerShell, please contact me on [Discord](https://discordapp.com/invite/NekmPh), [Twitter](https://twitter.com/ZanvalDev) or [Facebook](https://www.facebook.com/zanval88).