Practical PowerShell Scripting for DevOps - Part 1
Tags: powershell, devops, scripting
Published: 2021-03-27 08:12:51

Hello, friend. Welcome to Practical PowerShell Scripting for DevOps. In this series, I will attempt to teach to you how to write PowerShell scripts and solve some real-life DevOps challenges by writing scripts in PowerShell language. 

Scripting or Programming in general is a must have skill for anyone who want to seek a Cloud or DevOps career today. This series is geared towards giving some guidance on how to create your own PowerShell Scripts yourself.

## Who Is This Series For?

DevOps engineers are tasked to operate in cloud environments with great scale of resources. Often times, scripting is an easy way to automate tasks that can be programmatically achieved to save time and produce consistent results. 

If you are trying to learn Powershell for getting into a Cloud / DevOps career path and struggling to find some exercise challenges to solve. 

This is not a best practices or formal PowerShell education resource, I merely try to provide some made up DevOps/Cloud scenarios that can be used to create some Powershell scripts in an attempt to practice and learn scripting skills.

I suggest, using the Powershell reference documentation as much as possible. Spend time on terminal and play around with different cmdlets.
Your ultimate goal should be trying to learn a style for designing and creating a automation solution for any scenario. 

The tools you will learn and pickup along the way while solving these scenarios will accumulate and will widen your horizon in terms of what you can do with automation and scripting. 

## Problem

Imagine you are DevOps engineer for a project with multiple public web endpoints exposed to Internet. Your hosting provider is having an intermittent network outage on their load balancing infrastructure. You need to write a powershell script:

1- That accepts a list of urls to perform Http Ping Test (Http Get Request) from a configuration file
2- Performs a Http Get request test for given list of endpoints and return test results containing

  - Status Code Returned Http Response
  - Status Description For the Http Response
  - Response time of the request
  - Timestamp for the test result

  as a collection

3- Prints results to terminal in a human readable format

## Some Pointers For Writing Your Own Solution

### Some Test Endpoints You Can Use

Although you can use any public endpoint, I'm using https://httpbin.org status endpoint to randomly return me an HTTP status code for simulating a real testing scenario. When we hit a test url with multiple status codes aka Flaky Sites, we will receive different results.

if you use "https://httpbin.org/status/200,403,404,500" url you will randomly get a response code back, 200 being successful and other response codes simulating different types of failures.

you can use "https://httpbin.org/status/200" to only receive successful test results. These endpoints can be your starting point to use as a playground to curate your script.

### The Meat Of The Solution

Usually every script has a core functionality cmdlet you want to use, development usually starts with the results received from this core cmdlet. 

You can perform HTTP request in different ways in PowerShell, but most common ones are using the cmdlets:

`Invoke-RestMethod` and `Invoke-WebRequest` 

I suggest starting reading documentation for these cmdlets first, try to use them to perform some tests manually in terminal first, capturing the result and then curate a plan from there. 

### Input And Output For Your Solution

I've placed a requirement to prepare and consume the list of endpoints to tests from a file. You can use any format you like, row separated text file, a CSV file all are good. 

I've used and recommend JSON as it is the most richest and universal notation that can be used amongst a lot of programming languages.

This skill once learnt will come in handy for you in various scenarios where you will need to handle a set of data, you can't just store and handle as a command-line argument.

### Tasks You Need To Perform In Your Script

1- Get your Input
Try to figure out individual tasks you need perform first to get to a state you can actually do the work. 

In our case, you want to end up with your list of endpoint. To achieve this, we need to access our config file that holds our test endpoints.

Maybe have a parameter for accepting the file's path and then consume the content of this file. 

2- Have a function to perform your Test
It is good practice to create a function for repetitive code invocations, in our case we know that we will run test and produce a test result for multiple endpoints, hence it's a good idea to separate this part of our code to a function. 

3- At this point you have everything you need

Loop through your test endpoints, invoke your test function for each and capture the results. 

4- Print the result

Display the results, with way of your choosing.

## Final Tips

From this point below you will see my sample solution. Depending on your level and learning style, either take a look before or after you gave it your shot.

## My Sample Solution

### Folder Structure
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvs1f1qtbpgmzj4ge2up.png)

### Files

### GitHub

[Repository Url](https://github.com/MertSenel/devops-powershell/tree/main/scripts/local/practical-powershell-scripting-for-devops-part-1)

`Tests.json`

This is the list of endpoints I would like to test, stored in JSON format which can be produced and consumed with a variety of modern languages.
What we have here is an array of objects that has name and url keys for us to perform and label the tests.


```json
[{
        "name": "FlakyWebsite-#1",
        "url": "https://httpbin.org/status/200,403,404,500"
    },
    {
        "name": "StableWebsite-#1",
        "url": "https://httpbin.org/status/200"
    },
    {
        "name": "FlakyWebsite-#2",
        "url": "https://httpbin.org/status/200,403,404,500"
    },
    {
        "name": "StableWebsite-#2",
        "url": "https://httpbin.org/status/200"
    },
    {
        "name": "StableWebsite-#3",
        "url": "https://httpbin.org/status/200"
    },
    {
        "name": "StableWebsite-#4",
        "url": "https://httpbin.org/status/200"
    },
    {
        "name": "FlakyWebsite-#3",
        "url": "https://httpbin.org/status/200,403,404,500"
    },
    {
        "name": "FlakyWebsite-#4",
        "url": "https://httpbin.org/status/200,403,404,500"
    },
    {
        "name": "FaultyWebsite-#1",
        "url": "https://httpbin.org/status/500"
    }
]
```

Our Helper Function, create this file under path `./lib/New-HttpTestResult.ps1`

This script will perform an HTTP Ping Test with passed parameters and returns a Test Result Object

```powershell
function New-HttpTestResult {
    param (
        [Parameter(ValueFromPipeline=$true)]
        [PSCustomObject]
        $TestArgs
    )
    $ProgressPreference = 'SilentlyContinue'

    $Method = 'Get'

    $duration = Measure-Command {
        $Response = Invoke-WebRequest -Uri $TestArgs.url -Method $Method -SkipHttpErrorCheck
    }

    $result = [PSCustomObject]@{
        name = $TestArgs.name
        status_code = $Response.StatusCode.ToString()
        status_description = $Response.StatusDescription
        responsetime_ms = $duration.Milliseconds
        timestamp = (get-date).ToString('O')
    }

    return $result
}
```

`Test-HttpEndpoints.ps1`

Our Main script, we will invoke this script to run the tests and produce and returns a test results array

```powershell
[CmdletBinding()]
param (
    [Parameter(ValueFromPipeline = $true)]
    [string]
    $TestsFilePath =  '.\Tests.json'
)

# Convert JSON Config Files String value to a PowerShell Object
$TestsObj = Get-Content -Path $TestsFilePath | ConvertFrom-Json

# Import the Tester Function
. ./lib/New-HttpTestResult.ps1

# Loop through Test Objects and get the results as a collection
$TestResults = foreach ($Test in $TestsObj) { 
    New-HttpTestResult -TestArgs $Test 
}

$TestResults | Format-Table -AutoSize
```

### Sample Runs

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/htwlhr8v79uv2bwcav0z.png)

On each run, there is chance to get a different result back for flaky test endpoints. 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/emqghft54ayj1sv4bxmy.png)

## Conclusion

I hope this sample challenge helped you out learning powershell a bit and give it more context on how you can craft custom tools, in a short time frame.

You may share your solutions, via the comments if you wish me to review them or have any questions.

In Part 2 of this series, I am planning to go into Azure Cloud and I will share another challenge with usage of Az PowerShell module. 
Hope to see you there.