ScriptClub

Introduction

  • Why are you here?
  • What will we be covering?
  • What will we NOT be covering?
  • Why learn scripting?
  • Who am I?
  • Who are you?

What is Scripting?

  • List of commands in a simple text file
    • Commonly with specific extension (.ps1, .py, .js)
  • Generally read top to bottom (with caveats)
  • Commands are (ideally) 1 per line, or separated by semi-colons
  • Not compiled
  • Requires an interpreter to run

Common Scripting Languages

  • PowerShell
  • Python
  • Javascript
  • Bash
  • PHP
  • Many more

Running a script

  • Typically <interpreter> <scriptfile>
  • e.g. pwsh example.ps1, python example.py
  • From within the shell (PowerShell, Bash) you can just type the name of the file ./example.ps1 or ./example.sh

Getting Set Up

Exercise 1 - Install and run

  • Install both PowerShell & Python

  • Open pwsh

  • Type $PSVersionTable

  • Run python (*nix python3)

  • Look for version number

  • Type quit()

Printing

  • Most common way to get information from a script
  • PowerShell
    • Write-Host 'Hello World'
  • Python
    • print('Hello World')

Exercise 2

  • Write a script to print your name to the shell
  • PowerShell scripts are .ps1, Python .py

PowerShell

.\myscript.ps1 # Inside PowerShell
pwsh myscript.ps1 # From another shell (e.g. Bash)

Python

python3 myscript.py

Review

How did you do it?

Variables

  • Placeholders for values not yet set, or which could change
  • Often denoted with $ (PowerShell, Bash), but can be simple names (Python, Javascript)
  • Names are NOT surrounded by quotes
  • Some languages contain constants, or immutable variables (not common in PowerShell or Python)

Data types

  • String - Any unstructured text, enclosed in single or double quotes. Can contain spaces, punctuation, anything
  • Int/Float - Numbers, generally with some level of precision (int32, short, long). Signed or unsigned
  • Boolean - True/False
  • Array/List - Unsorted list of items
  • Hashtable/Dictionary/Object - Complex object with multiple key/value pairs
  • $null/None/nil - Literally nothing

Assigning Value

  • Use single = (not double)
  • Variable can be reassigned later (constants cannot)
  • Some languages enforce type, i.e. string variable cannot be assigned int (not PowerShell or Python)
  • Variable evaluation depends on its last assignment
# powershell
$MyVar = 1
Write-Host $MyVar
1
# python
my_var = 2
print(my_var)
2

Strings

  • Any length of characters. Can contain numbers, spaces, punctuation, line breaks etc.
  • Denoted by single or double quotes
    • Some languages treat strings differently depending on the quotes
  • No restriction on names, but style guides suggest certain formats
  • Quotes not shown when printing in all languages

Examples

PowerShell

$MyString = 'Hello there'
Write-Host $MyString
Hello there
Write-Host MyString
MyString

Python

my_string = 'Hello there'
print(my_string)
Hello there
print('my_string')
my_string

String Manipulation

  • All languages have methods to change strings
  • Commonly
    • SubString - Part of the string, starting or ending at an index
    • Replace - Replace one group of characters with another, or remove them (replace with nothing)
    • Split - Break a string into an Array/List based on a character (think query string parameters)
    • Upper/Lower - All caps, all lower case
    • Trim - Remove leading or trailing spaces
    • Concatenation / Interpolation - Joining or combining strings and variables

Numbers

  • Typically whole numbers (int) or decimals (float)
  • Range is set in bits
  • Signed (positive or negative) or unsigned (just positive)
  • NOT quoted

Examples

PowerShell

$Num = 6
$Num = 66.342
## NOT
$Num = '6' # This is a string

Python

num = 6
num = 123.456
## NOT
num = '6' # This is also a string

Boolean

  • Can only be true or false
  • Generally special words
    • Powershell $true or $false
    • Python True or False
  • Often shortened to bool

Examples

PowerShell

$MyBool = $true
$MyBool = $false
$MyBool = 'true' # NOT a boolean, just the word true

Python

my_bool = True
my_bool = False
my_bool = 'true' # NOT a boolean
my_bool = true # Will (probably) throw an error

Array / List

  • Unsorted list of items. Items can be any data type, including other Arrays/Lists
  • Items accessible by an index in square brackets, e.g. $MyArr[0] or my_list[6]
  • Array length can be obtained by $MyArr.length, len(my_list)
  • Index is an integer, ALWAYS starting at 0
  • Final index would be (length - 1), e.g. if array has 10 items last is $MyArr[9]
  • Negative indices count from end: -1 is last item, -2 is 2nd last and so on
  • Can select ranges, e.g. my_list[0:10], my_list[2:] or $MyArr[0..10]
  • Adding items to the end is easy. Removing or inserting is more difficult

Examples

PowerShell

# Create an empty array
$MyArr = @()
# Create an array with items
$MyArr = @(
    'one',
    'two',
    3
)
$MyArr = 'one', 'two', 3
# Can access specific items
$MyArr[1]
two
# Can add items to the list
$MyArr = $MyArr + 'four'
$MyArr
one
two
3
four

Python

# Create an empty array
my_arr = []
# Create an array with items
my_arr = [
    'one',
    'two',
    3
]
# Can access specific items
my_arr[1]
'two'
# Can add items to the list
my_arr.append('four')
my_arr
['one', 'two', 3, 'four']

Hashtable/Dictionary

  • Structured data object, generally used as key/value pairs
  • Similar to array, but index is a string rather than a number
  • Items can be any data type, including other Hashtables/Dictionaries
  • Hashtable/Dictionary members accessed by ['element']
  • Object members access by .element
  • Elements can be added and set by simply accessing the key
    • e.g. $MyHash['new'] = 'Something'

Examples

PowerShell

# Can declare empty hashtable
$MyHash = @{}
# Can declare with items
$MyHash = @{
    one = 1
    two = 2
    three = 3
}
$MyHash['one']
1
$MyHash['four'] = 4
$MyHash
Name                           Value
----                           -----
one                            1
three                          3
two                            2
four                           4

Python

# Can declare empty hashtable
my_dict = {}
# Can declare with items
my_dict = {
    'one': 1,
    'two': 2,
    'three': 3
}
my_dict
{'one': 1, 'two': 2, 'three': 3}
my_dict['four'] = 4
my_dict
{'one': 1, 'two': 2, 'three': 3, 'four': 4}

Objects

  • Most common datatype used in PowerShell
  • Also common in Javascript
  • Elements can be any data type, including other Objects
  • Elements accessed by .element, e.g. $MyObj.Name
  • Fiddly to create manually

$null/None/nil

  • Used to denote a variable with no value
  • Useful in clearing variables
  • Often we can compare a variable to $null or None to see if it has been assigned a value

Using variables

  • Variables can be converted to other types (sometimes). This is called ‘casting’
    • Powershell [string] $MyVar or [int] $MyVar
    • Python str(my_var) or int(my_var)
  • Types can be nested, e.g. an Array of Strings, or a Dict where its members are a String, a Number and a List

Exercise 1

  1. Write a script in PowerShell which does the following:
    • Creates an Array with 5 elements, mixing String and number types (try to mix Int and Float), but the last item should be the word ‘last’
    • Print the last item to the shell, using its index

Hint: The last item in a List/Array can be accessed with the index -1

Exercise 2

Write a script in Python which does the following:

  • Creates a Dict variable with 3 elements, where the 3rd is a List
  • Reassign the value of the first item
  • Add a fourth item with the value ‘last’
  • Print the Dict to the shell

Review

How did you do it?

Determining Type

  • Can use type(var_name) or $VarName.GetType()
  • Compare with isinstance(var_name, str) and $Var -is 'String'

Maths

  • All scripting languages support basic maths operations
    • + - * / pow, modulus etc.
  • Generally only + is supported for non-Number variables
  • + has different meanings depending on the data type
  • Adding Strings together is called ‘concatenation’
  • Also bitwise AND/OR for more complex operations

Examples

PowerShell

$a = 5
$b = 0.5
$a + $b
5.5
$a - $b
4.5
$a * $b
2.5

$a = '5'
$b = '0.5'
$a + $b
50.5

Python

a = '5'
b = '0.5'
a + b
'50.5'

a - b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'str' and 'str'

Further examples

PowerShell

$a = @(1,2,3,4)
$b = @(5,6,7,8)
$a + $b
1
2
3
4
5
6
7
8
## Result = Arrays are combined
$a = @{one = 1; two = 2}
$b = @{three = 3; four = 4}
$a + $b
Name                           Value
----                           -----
three                          3
two                            2
four                           4
one                            1
## Result = Hashtables are combined

Python

a = [1,2,3,4]
b = [5,6,7,8]
a + b
[1, 2, 3, 4, 5, 6, 7, 8]
## Result = Lists are combined
a = {'one':1, 'two':2}
b = {'three':3, 'four':4}
a + b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
## Result = ERROR!

Order of Precedence

  • Operators are NOT read left to right (necessarily)
  • Evaluate: 1 + 2 - 3 * 4 / 5
  • Options
    • Memorise order of precedence PER LANGUAGE (boo!)
    • Use parentheses (yay!)
  • Parentheses allow for much greater clarity and ease of understanding.
  • Can be nested, blocks are evaluated from the inside out
  • e.g. ((1 + 2) - (3 * 4)) / 5

Shorthands

  • ++ - Add 1 to variable, e.g. $i++ means add 1 to variable ‘i’
  • – - Subtract 1 from variable, e.g. var-- means subtract 1 from variable ‘var’
  • += - Variable equals itself plus what comes after the operator, e.g. $var += 10 means $var = $var + 10
  • -=, *=, \=, %= behave the same way
  • Watch out for the difference between $i++ and ++$i
    • $a = $b++ means assign $a the value of $b, THEN increase $b by 1
    • $a = ++$b means increase $b by 1, THEN assign $a the value of $b

Conditionals

  • Variables can be compared to conditionally execute code
  • IF (STATEMENT IS TRUE) THEN (DO SOMETHING)
  • ELSE and ELSE IF statements can be added for when your STATEMENT is not true
  • STATEMENT can be any logical combination of sub-clauses with AND, OR, NOT etc.

Comparison Operators

  • All languages allow you to compare variables with each other or with constants
  • Common comparisons: equals, not equals, greater than, less than, greater than or equal to, less than or equal to
  • Also commonly: element in array, element not in array
  • Result of ALL comparison operators is True or False

Comparison Operators

Comparison PowerShell Operator Python Operator
Equals -eq ==
Not Equals -ne !=
Greater than -gt >
Less than -lt <
Greater than or equal to -ge >=
Less than or equal to -le <=
In Array/List -in in
Not In Array/List -notin not in
Regex match -match

Combining Comparisons

  • Often you need to combine multiple conditions
  • Boolean AND or OR operators can be used to combine conditions
  • Good practice to put each condition in parentheses, though not always necessary
    • $var -gt 0 -or $var -lt 100 No parentheses necessary but ideally ($var -gt 0) -or ($var -lt 100)
    • $var -gt 0 -or $var -lt 100 -and $var -le 10 is ambiguous so parentheses are required
    • ($var -gt 0 -or $var -lt 100) -and $var -le 10
  • As with Maths parentheses are evaulated first, inside out

If syntax

PowerShell

if($var -eq $true){
    Write-Host 'var is true'
}
  • If clause in parentheses
  • Result enclosed in curly braces

Python

if var == True:
    print('var is true')
  • No parenthese around if clause (unless required), but must end in colon (:)
  • Result is indented (required)

If/Else Syntax

PowerShell

if($var -eq $true){
    Write-Host 'var is true'
}
else{
    Write-Host 'var is not true'
}
  • else keyword follows closing curly brace (generally on new line)
  • Result of if or else enclosed in curly braces. Indent is optional

Python

if var == True:
    print('var is true')
else:
    print('var is not true')
  • else keyword on new line beneath if result. NOT indented, but requires colon (:)
  • Result of if or else MUST be indented

Exercise 1

Write a Python script which performs the following:

  • A List of 5 numbers is created, with any values you wish (don’t need to generate random numbers)
  • If the 3rd item in the list is greater than 10, write the number to the shell
  • If not, write the statement ‘All Items:’, then the whole array to the shell

Change your list in such a way that you see what happens in both scenarios.

Exercise 2

Write a PowerShell script which performs the following:

  • An Array of Strings is created, as many as you wish
  • If the number of items in the array is less than 4, print ‘The first item is X’, replacing X with the first item
  • If not, add a new item to the List and print the whole list

Hints:

  • You can concatenate strings while printing, but it might be easier to create a new variable and print that
  • Within an If or Else block you can have as many lines/commands as you wish. Remember, though, that each command should be on its own line

Review

How did you do it?

Else If

  • Used to evaluate a 2nd (or more) If condition after the 1st has not been met
  • Can chain mutually exclusive scenarios
  • Commonly followed by else but not required

Syntax

PowerShell

if($var -eq 10){
    Write-Host 'var is 10'
}
elseif($var -gt 10){
    Write-Host 'var is greater than 10'
}
else{
    Write-Host 'var is less than 10'
}
  • elseif keyword behaves exactly as if does, but MUST follow if block
  • elseif statement does not need to compare same variable(s) as if. Can be completely different
  • Result contained within curly braces

Syntax

Python

if var == 10:
    print('var is 10')
elif var > 10:
    print('var is greater than 10')
else:
    print('var is less than 10')
  • elif (note spelling diff) behaves exactly as if does. MUST follow if block and is NOT indented
  • elif statement followed by colon (:)
  • elif statement can compare anything, as in PowerShell
  • Result MUST be indented

Gotchas

  • Comparisons always resolve to True or False, even if comparison checks if variable is NOT true
  • If - If - Else is NOT the same as If - Else If - Else

PowerShell

$MyVar = $false
if($MyVar -ne $true){
    ## Do something
}

Python

var = 10
if var >= 10:
    print('var is big')
elif var > 5:
    print('var is medium')
else:
    print('var is small')
var is big

var = 10
if var >= 10:
    print('var is big')
if var > 5:
    print('var is medium')
else:
    print('var is small')
var is big
var is medium

Gotchas

  • Else statement only applies to the If statement (or Else If) immediately prior.

PowerShell

$Var = 10
if($Var -gt 10){
    Write-Host 'Var is big'
}
if($Var -eq 10){
    Write-Host 'Var is 10'
}
else{
    Write-Host 'Var is small'
}
Var is small
  • The 1st if has no else so if it does not match, nothing happens
  • The 2nd if is evaluated and found to be true, so we execute that code block, even though there is an else below the 1st if

Exercise 3

Write a PowerShell script which performs the following:

  • Create a variable between 1 and 100
  • If the variable is between 50 and 75 and is even print 'Step 1 matched: ’ and the value to the shell (either on a new line or the same one)
  • Otherwise, if the variable is a multiple of 5, print 'Step 2 matched: ’ and the value to the shell
  • Finally if neither condition matched then print ‘Match not found’
  • Try multiple variable values to see what happens in each scenario

Hint: Finding if a number is multiple of 2 or 5 can be performed using the modulus % math operator

Switch / Case

  • Similar in function to if / else if / else
  • Typically cleaner
  • Switch statement common in PowerShell, only recently introduced to Python in 3.10

PowerShell

If/Else

$day = 3

if ( $day -eq 0 )     { $result = 'Sunday'    }
elseif ( $day -eq 1 ) { $result = 'Monday'    }
elseif ( $day -eq 2 ) { $result = 'Tuesday'   }
elseif ( $day -eq 3 ) { $result = 'Wednesday' }
elseif ( $day -eq 4 ) { $result = 'Thursday'  }
elseif ( $day -eq 5 ) { $result = 'Friday'    }
elseif ( $day -eq 6 ) { $result = 'Saturday'  }
else                  { $result = '?'         }
$result

PowerShell

Switch

$day = 3
switch ( $day )
{
    0 { $result = 'Sunday'    }
    1 { $result = 'Monday'    }
    2 { $result = 'Tuesday'   }
    3 { $result = 'Wednesday' }
    4 { $result = 'Thursday'  }
    5 { $result = 'Friday'    }
    6 { $result = 'Saturday'  }
    default { $result = '?'   }
}
$result

Python

If / Else

day = 3
if day == 0:
    result = "Sunday"
elif day == 1:
    result = "Monday"
elif day == 2:
    result = "Tuesday"
elif day == 3:
    result = "Wednesday"
elif day == 4:
    result = "Thursday"
elif day == 5:
    result = "Friday"
elif day == 6:
    result = "Saturday"
else:
    result = "?"
print(result)

Python

Match

day = 3
match (day):
    case 0:
        result = "Sunday"
    case 1:
        result = "Monday"
    case 2:
        result = "Tuesday"
    case 3:
        result = "Wednesday"
    case 4:
        result = "Thursday"
    case 5:
        result = "Friday"
    case 6:
        result = "Saturday"
    case _:
        result = "?"
print(result)

Looping

  • Allows multiple repeated operations, without repeating code
  • Keywords for and while
  • Typically iterates across an Array/List
  • continue can skip the rest of an interation
  • break can break out of the loop if required
  • Need to be careful of infinite loops

While

  • Simple condition is evaluated. If evaluation == true then code is executed
  • Evaluated condition should change per loop, otherwise the while will never end
  • If evaluation == false then script carries on
  • Syntax is the same as if, in terms of braces, colons & indent

PowerShell

$v = 0
while($v -lt 10){
    Write-Host $v
    $v++
}

Python

v = 0
while v < 10:
    print(v)
    v += 1

For

  • More commonly used than while
  • Multiple styles: commonly with an iterator or more modern in method

Iterator method (the old way)

  • Generally used if you need to know the position you are in the loop
  • Python doesn’t support it in any easy way
  • Definition in 3 parts
    • Iterator name and initial value
    • Condition to be evaluated each time
    • How to change the iterator

PowerShell

$MyArr = @(1,2,3,4,5)
for($i = 0; $i -lt $MyArr.count; $i++){
    Write-Host $MyArr[$i]
}

In Method (the better way)

  • New variable is defined which is updated each loop
  • Uses for keyword in Python, foreach keyword in PowerShell
  • Can’t update element in Python (can in Powershell)

PowerShell

$MyArr = @(1,2,3,4,5)
foreach($Item in $MyArr){
    Write-Host $Item
}

Python

my_arr = [1,2,3,4,5]
for item in my_arr:
    print(item)

Break and Continue

  • You may not wish to run every iteration of a loop
  • continue will skip the rest of the iteration and move on to the next one. Useful if you wish to skip specific conditions
  • break will stop the loop entirely. Useful if you have found what you are looking for

PowerShell

$MyArr = @(1,2,3,4,5)
foreach($Item in $MyArr){
    Write-Host $Item
    break
}

Python

my_arr = [1,2,3,4,5]
for item in my_arr:
    if item == 2:
        continue
    print(item)

Exercise 1

Write a Python script which performs the following:

  • Create a list of 8 strings containing names/fruit/anything. Make sure at least 2 of the strings are shorter than 5 characters, and at least 2 are longer than 5
  • Iterate through the list (using the In method) and print each item in ALL CAPS

Hint: You can convert a Python string to upper by using var.upper()

Exercise 2

Add to your script from the previous exercise the following:

  • If the string length is less than 5 characters, skip this iteration
  • Only print the first 5 strings

Hint: Maybe use a while loop?

Review

How did you do it?

Reading from files

  • Common to iterate across file contents
  • Typically txt, csv, json (also yaml)
  • Special setups for csv and json

Text Files

PowerShell

# As array
$Contents = Get-Content myfile.txt
$Contents.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
# As string
$Contents = Get-Content myfile.txt -Raw
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

Python

with open('myfile.txt', 'r') as f:
    contents = f.read()
type(contents)
<class 'str'>

Exercise 1

Write a PowerShell script to load the contents of the file products.txt and print each item with a prefix of 'ProductID: ’

Hint: Get-Content will automatically convert a file to an Array of strings, one per line. If you want to load a whole file as a single string use Get-Content -Raw

Review

How did you do it?

CSV Files

  • Common interchange format for data
  • Multiple items per line, with a header row to identify them
  • Will be loaded to Objects or Dicts, rather than a string
  • Attributes are named with the column header
  • Delimiter other than comma can be specified

PowerShell

$Rows = Import-CSV myfile.csv -Delimiter ',' # If using comma -Delimiter can be omitted
# Show the 3rd item
$Rows[2]
# Show the 'map' attribute of the 5th item
$Rows[4].map

Python

import csv # You need to use a Python module for this
with open('myfile.csv', 'r') as f:
    reader = csv.DictReader(f, delimiter=',') # If using comma delimiter=',' can be omitted
    # Cast to list
    rows = list(reader)

# Show the 3rd item
rows[2]
# Show the 'map' attribute of the 5th item
rows[4]['map']

Exercise 2

Write a Python script to load the contents of the file edgehostnames.csv and print the recordName attribute of all lines whose ‘securityType’ attribute is ‘STANDARD-TLS’

Hint: You will likely need to use a for loop and an if condition to achieve this

Review

How did you do it?

JSON

  • Strutured data, similar to Dictionary or Object data types
  • Will be loaded to Objects or Dicts, rather than a string

PowerShell

$JSON = Get-Content myfile.json -Raw
$Data = ConvertFrom-Json $JSON
# Show propertyName attribute
$Data.propertyName
myproperty
# Show 3rd child rule, 
$Data.rules.children[2].Name
my child rule

Python

import json # You need to use a Python module for this
with open('myfile.json', 'r') as f:
    data = json.load(f)
# Show propertyName attribute
data['propertyName']
'myproperty'
# Show 3rd child rule, 
data['rules']['children'][2]['name']
'my child rule'

Exercise 3

Write a PowerShell script to load the property.json file and print the origin hostname

Hint: The origin behaviour is the first item in the behavior Array/List, which is a member of the rules Object at the top of the json.

Hint: hostname is a member of the options Dictionary/Object inside the behaviour

Remember: Reading the json file in a text editor is NOT cheating!

Review

How did you do it?

Functions & Variable Scope

  • Allow you to re-use code and make scripts more efficient
  • Basic in/out structure
  • Variables within functions are specific to that function

Syntax - Definition

  • Typically a keyword: function in PowerShell, def in Python
  • The name of the function
  • Function parameters
  • Body of the function, enclosed in {} (Powershell) or indented (Python)
  • Typically contains return statement (not required)
  • Functions usually defined at the top of a script (best practice), but position is not essential
function add($var1, $var2){
    return $var1 + $var2
}
def add(var1, var2):
    return var1 + var2

Syntax - Usage

  • Once function is defined it can be called at any point by name
function add($var1, $var2){
    return $var1 + $var2
}
add 1 2
3
add 3 4
7
add 'hello ' 'there'
hello there
def add(var1, var2):
    return var1 + var2
add(1, 2)
3
add('hello ', 'there')
hello there

Exercise 1

Write a Python script with a function named TellMeWhy which prints the line ‘tell me why I don’t like…’ then on the next line the input to the function. Call this function at least twice with different days of the week.

Note: Your function doesn’t need a return statement in this case

Review

How did you do it?

When should you use functions?

  • Whenever code is repeated
  • When you want code to be reusable outside your script

Variable Scope

  • Variables defined inside a function are local to that function
  • Variables defined outside the function are not available to the function
    • Unless global
    • In python all variables outside functions are Global
  • Best practice to use parameters and return statements
  • Try to avoid overloading variable names

Examples

function MyNameIs(){
    $Name = 'Who?'
    Write-Host "My Name is $Name"
}

$Name = 'What?'
MyNameIs

# What will the result be?
function MyNameIs($MyName){
    Write-Host "My Name is $MyName"
}

$Name = 'Marshall'
MyNameIs -MyName $Name

Examples

function MyNameIs($MyName){
    $Name = $MyName
}

$Name = 'Jim'
MyNameIs -MyName 'Bob'
Write-Host $Name

# What will the result be?
function MyNameIs($MyName){
    return "My name is $MyName"
}

$Name = 'Jim'
$Name = MyNameIs -MyName 'Bob'
Write-Host $Name

# What will the result be?

Multiple Parameters

  • Positional parameters depend on the order in which they are supplied
  • Named parameters require explicit naming, but can be in any order
  • Rule of thumb: Positional parameters are fine for small functions, larger ones are easier to read with named
def param_order(a, b, c):
    print('The first param is {first}, the second is {second} and the third {third}'.format(first = a, second = b, third = c))

param_order('one', 'two', 'three')
The first param is one, the second is two and the third three
param_order('three', 'two', 'one')
The first param is three, the second is two and the third one
param_order(a = 'two', c = 'one', b = 'three')
The first param is two, the second is three and the third one

Named Parameters in PowerShell

  • Powershell can use simple notation like python, or a Param() block for more complex
  • Parameter requirement and type can be set, as well as many other options
  • Variables are cast to desired type, if possible (e.g. can cast int to string, not necessarily string to int)
function Param-Order{
    Param(
        $a,
        [string] $b,
        [Parameter(Mandatory=$true)] [string] $c
    )

    Write-Host "Param a = $a, param b = $b, param c = $c"
}

Param-Order -a 'one' -b 'two' -c 'three'
Param a = one, param b = two, param c = three

Param-Order -a 'one' -b 'two'
cmdlet Param-Order at command pipeline position 1
Supply values for the following parameters:
c:

Exercise 2

Write a PowerShell script which defines a function called combine. This function should take 3 parameters. Each parameter should be made part of a string of the form 'Parameter 1: ', then combined into an Array/List and returned. Your script should call this function at least twice, and print the results to the shell

Note: The Write-Host command must be outside the function

Review

How did you do it?

Recursive Functions

  • Functions which call themselves
  • Useful if you need to traverse data with an unknown structure
{
    "name": "top",
    "children": [
        {
            "name":"child",
            "children": [
                {
                    "name":"grandchild",
                    "children": [ ]
                }
            ]
        }
    ]
}
$Data = Get-Content myjson.json | ConvertFrom-Json -Depth 10
function recurse($Parent){
    foreach($child in $Parent.children){
        recurse($child)
    }

    Write-Host $Parent.name
}

recurse $Data.rules

Optional Exercise

Write a script in PowerShell which loads the file property.json and recurses through all rules under the top-level rules object, printing the name, comments (if any) and how many children are present in the rule.

Sorting & Filtering Data

  • Common use of scripting is managing large datasets to find only what you want
  • PowerShell Where-Object & Python list comprehension common
  • Can use looping and conditionals to keep things simple

For Loops and Conditionals

  • [Pro] Simplest method to filter data
  • [Pro] Largely the same between Python & PowerShell
  • [Con] More code required
results = []
for line in data:
    if line['record'] == 'something I want':
        results.append(line)
$Results = @()
foreach($Line in $Data){
    if($Line.record -eq 'something I want'){
        $Results += $Line
    }
}

PowerShell - Where-Object, Select-Object & Sort-Object

  • Commonly Where-Object used to filter objects by value
  • Where-Object aliases: where or even ?
  • For complex filters Where-Object uses $_ as a variable to represent each object that is being checked
  • Select-Object then used to select attributes you are interested in
  • Select-Object alias: select
  • Sort-Object sorts lists by specific object member
  • All commonly used via pipe - |

Piping

  • Common in shells
  • Output of one command becomes input of another
  • Can be avoided if desirable. Above functions can use -InputObject

Where-Object Syntax & Examples

Simple

  • <Object to be filtered> | <where|?|Where-Object>
$EdgeHostnames | Where-Object recordName -eq www.example.com
$EdgeHostnames | ? ttl -ne 21600

Complex

  • <Object to be filtered> | <where|?|Where-Object> { }
$EdgeHostnames | Where-Object { $_.recordName -eq www.example.com }
$EdgeHostnames | Where-Object { $_.recordName.contains('www') -or $_.dnsZone -eq 'edgesuite.net' }

Select-Object Syntax & Examples

  • Selected attributed will be displayed in the order supplied
  • <Object to be filtered> | Select <attribute1>, <attribute 2>
$EdgeHostnames | Select-Object recordName, EdgeHostnameID

Sort-Object

  • Basic object sorting
  • If input is a list of objects Sort-Object can use a specific property to sort
$MyList | Sort-Object
$MyList | Sort-Object -Descending
$MyList | Sort-Object -Property Name

Gotchas

  • Both Where-Object and Select-Object can return $null if nothing matches
  • If only a single item is matched PowerShell returns the object, NOT a single-item Array

Exercise 1

Write a powershell script which performs the following:

  • Loads the first 25 lines from edgehostnames.csv
  • Filters only those with a dnsZone of edgesuite.net
  • Selects the recordName, ttl and map attributes
  • Sorts the results by descending map
  • Writes the result to the shell

Use any filtering method you prefer

Review

How did you do it?

Python - List Comprehension, filter() & sort()

  • List comprehension trickier to understand but produces list of filtered results
  • filter() function takes input object and function to filter
  • sort() function sorts a list in-place and updates the variable

Note: sort() does not return the sorted list!

List Comprehension

  • Will produce a list of all matching List members
  • If no matching members empty list is returned
  • Syntax [ <element> for <element> in <List to filter> if <condition>]
results = [ member for member in data if member['something'] == 'something I want']

filter()

  • Takes as arguments a boolean function containing a filter condition, and a List to be filtered
  • Can use lambda in-line functions
def myFilter(argument):
    if argument > 1:
        return True
    else:
        return False

results = filter (myFilter, data)

More commonly…

results = filter( lambda a: a > 1, data)

Sort

  • Sorts in-place
  • Do not assign result to a variable
my_list = [5,3,2,4,1]
my_list.sort()
print(my_list)
[1,2,3,4,5]

Exercise 2

Write a Python script which performs the following:

  • Loads the file edgehostnames.csv
  • Filters the list for only those EHNs whose TTL is NOT equal to 21600
  • Add only the recordName attribute to a new list, and sort alphabetically
  • Print the new list to the shell

Use any filtering method you prefer

Inputs

  • Scripts typically contain parameters or other user input
  • Could hard-code in the code, but more flexible to allow user to supply

Reading Input from the shell

  • Powershell uses Read-Host, and you can supply a -Prompt param to help the user (best practice)
  • Python uses input(), and you can supply a string param to the function to help the user, e.g. input('Please input your name:')
  • Slightly different syntax in prompts. Powershell adds a colon and space
$InputVariable = Read-Host -Prompt 'What is your name?'
Write-Host $InputVariable
.\prompt.ps1
What is your name?: Stuart
Stuart
input_variable = input('What is your name?')
print(input_variable)
python prompt.py
What is your name?Stuart
Stuart

PowerShell Script Parameters

  • Behaves the same as named function parameters
  • Can be supplied in the script invocation (best practice) or any required params will force the shell to prompt the user
  • Parameters accessed by variable names as normal
param(
    [Parameter(Mandatory=$true)] [string] $Name
)

Write-Host "Hi, my name is $Name"
./myscript.ps1 -Name 'Stuart'
Hi, my name is Stuart
./myscript.ps1
cmdlet myscript.ps1 at command pipeline position 1
Supply values for the following parameters:
Name:

Default Values

  • Any non-mandatory parameter can have a default value
  • Useful if you want to assume values but allow users to override
param(
    [Parameter(Mandatory=$false)] [string] $Name = 'Slim Shady'
)

Write-Host "Hi, my name is $Name"
./myscript.ps1
Hi, my name is Slim Shady

Exercise 1

Write a PowerShell script with 4 parameters of various types, where the first 2 are mandatory but the 3rd and 4th are optional. Your script should prompt the user for a further piece of information, then print all 5 options (supplied or not) in some way.

Note: Execute your script using named parameters, rather than waiting for the script to prompt you (except for the deliberate prompt)

Review

How did you do it?

Python Parameters

  • Multiple methods to use
  • Most basic is sys.argv - List of script params, first is the name of the script
  • More common to use argparse
  • Could also use Click, but is more complex to implement (though a lot cooler)
import sys
print(str(sys.argv))
print(sys.argv[1])
python myscript.py arg1 arg2 arg3
['myscript.py', 'arg1', 'arg2','arg3']
'arg1'

argparse

import argparse

parser = argparse.ArgumentParser()

parser.add_argument('-s', '--simple', action='store', dest='simple_value',
                    help='Store a simple value')

parser.add_argument('-t', action='store_true', default=False,
                    dest='boolean_switch',
                    help='Set a switch to true')

parser.add_argument('--version', action='version', version='%(prog)s 1.0')

results = parser.parse_args()
print('simple_value     =', results.simple_value)
print('boolean_switch   =', results.boolean_switch)
$ python args.py -h

usage: arg.py [-h] [-s SIMPLE_VALUE] [-t] [--version]

options:
  -h, --help       show this help message and exit
  -s SIMPLE_VALUE  Store a simple value
  -t               Set a switch to true
  --version        show program's version number and exit

Exercise 2

Recreate your script from the previous exercise, except this time in Python using argparse and input()

Note: It’s probably better to get aruments from the script invocation than using input(), but it is still valid

Review

How did you do it?

PowerShell Comment-Based Help

  • Get-Help can be run on any function or script
  • Specific comments in code populate help output
  • Common keywords: SYNOPSIS, DESCRIPTION, PARAMETER, EXAMPLE, LINK

PowerShell Comment-Based Help

<#
.DESCRIPTION
This script adds 2 things together
.SYNOPSIS
Add 2 inputs
.EXAMPLE
> .\script.ps1 -Input1 1 -Input2 2
.PARAMETER Input1
The first input
.PARAMETER Input2
The second parameter
.LINK
http://scriptclub.edgesuite.net
#>
[CmdletBinding()]
param(
    [Parameter(Mandatory)]
    [int]
    $Input1,
    
    [Parameter()]
    [int]
    $Input2
)

# Print out 2 inputs added together
$Result = $Input1 + $Input2
return $Result

PowerShell Comment-Based Help

> Get-Help .\script.ps1

NAME
    C:\code\scriptclub\exercises\script.ps1

SYNOPSIS
    Add 2 inputs


SYNTAX
    C:\code\scriptclub\exercises\script.ps1 [-Input1] <Int32> [[-Input2] <Int32>] [<CommonParameters>]


DESCRIPTION
    This script adds 2 things together


RELATED LINKS
    http://scriptclub.edgesuite.net

REMARKS
    To see the examples, type: "Get-Help C:\code\scriptclub\exercises\script.ps1 -Examples"
    For more information, type: "Get-Help C:\code\scriptclub\exercises\script.ps1 -Detailed"
    For technical information, type: "Get-Help C:\code\scriptclub\exercises\script.ps1 -Full"
    For online help, type: "Get-Help C:\code\scriptclub\exercises\script.ps1 -Online"

Exercise 2

Rewrite your PowerShell script from exercise 1 and add comment-based help. Run Get-Help with the basic options, and also with -Detailed and -Full parameters.

Review

How did you do it?

Outputs

  • Scripts can typically output in 3 ways
    • Print to the shell (simple strings)
    • Return more complex objects
    • Write to files

Return

  • return statement in scripts behaves just as it does in functions
  • Only single return statement will apply, usually with single object
  • Return object can be complex, e.g. Array/List, Dictionary/Object etc.
  • Would need to be useful to the shell, so uncommon in Python
  • Any code after a return would never execute (editor may show this)
param(
    [string] $FirstElement,
    [string] $SecondElement
)

return @{ first = $FirstElement; second = $SecondElement }
$Result = combine.ps1 -FirstElement hello -SecondElement there
$Result
Name                           Value
----                           -----
first                          hello
second                         there
$Result.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

Exercise 1

Write a PowerShell script takes in 5 strings, sorts them alphabetically and returns them as an Array. Assign the result to a variable in your shell then print it. Do not print within the script.

Review

How did you do it?

Writing to files

  • Just as with importing, common to write to txt, csv and json files
  • External modules allow XLSX if required
  • Best file type depends on data structure

Text Files

  • PowerShell: Out-File, Set-Content or Add-Content
  • Python: use with open() and set mode to w (write) or a (append)
  • write() writes strings, writelines() writes lists
  • Python writelines() will NOT add line breaks to your file, but PowerShell Out-File will
$MyList = @(1,2,3,4,5)
$MyList | Out-File mylist.txt
my_string = 'To be, or not to be'
with open('mystring.txt', 'w') as f:
    f.write(my_string)
my_list = ['To','be,\n','or ','not ','to ','be']
with open('mylist.txt', 'w') as f:
    f.writelines(my_list)

CSV

  • Can convert Lists/Arrays of objects to CSV files
  • CSV files cannot have nested values, i.e. all values must be strings
  • Python: Need to write header row
  • PowerShell: Can use Export-CSV automatically
data_list = [] # List of Dicts
with open('mycsv.csv', 'w', encoding='utf8', newline='') as output_file:
    fc = csv.DictWriter(output_file, fieldnames=data_list[0].keys(),)
    fc.writeheader()
    fc.writerows(data_list)
$MyArray = @() # List of objects/hashtables
$MyArray | Export-CSV mycsv.csv

Exercise 1

Write a script in Python which reads the file edgehostnames.csv. Change any value and write the result to a new CSV file

Review

How did you do it?

JSON

  • Can export dictionary/hashtable/object
  • Python: Use json module. Watch out for the difference between dump() (write file) and dumps() convert to json string
  • Python: Use indent=2 or indent=4 to prettify output if required
  • PowerShell: Use ConvertTo-Json and pipe to Out-File or Set-Content. Likely need to set -Depth if your file is heavily nested
import json
my_dict = {} # Dictionary populated with data
with open('myjson.json', 'w') as f:
    json.dump(my_dict, f, indent=2)
$MyObject = @{} #Hashtable or Object with data
$MyObject | ConvertTo-Json -Depth 10 | Out-File myjson.json

Exercise 2

Write a script in PowerShell which loads the file property.json and converts it to a PowerShell object. Change the propertyName top-level attribute and write the resulting object to a new json file

Review

How did you do it?

Error Handling

  • Functions might fail, especially API calls
  • Unhandled exceptions will typically crash a script
  • Exception/Error handling allows you to cope when errors occur (which they will)

Try/Catch (PowerShell)

  • Code which might error should be held within a try block
  • PowerShell errors are created by throw statements
  • Errors are handled by catch blocks
  • try blocks must have a corresponding catch block
try{
    1 / 0
}
catch{
    Write-Host 'An error has occured. Nuts.'
}

Try/Catch (PowerShell)

  • Multiple lines or commands can exist within the same try, but if one throws an error, the rest will be skipped
  • The actual error thrown can be referenced by use of the $_
function MakeError($Fail){ if($fail){ throw 'Awww, shucks' } }
try{
    Write-Host 'Before the error'
    MakeError $true
    Write-Host 'After the error'
}
catch{
    Write-Host 'An error has occured'
    Write-Host $_
}

Throw

  • Errors can be raised manually if you wish
  • Uses the throw keyword
  • Errors can be complex objects, not just strings
  • PowerShell commands do not always throw errors, but you can force them to with -ErrorAction stop
$SomeVariable = Read-Host 'Enter a number greater than 0'
if($SomeVariable < 0){
    throw 'Variable is invalid'
}

Exercise 1

Write a function in PowerShell which generates a random number between 1 and 10 (use Get-Random). If the number is greater than 5, throw an error. Call your function in the script without try/catch, and finish with a Write-Host command to show you have reached the end. Note what happens when the script fails.

Next, update your script to call your function inside a try/catch block such that an errors are handled gracefully, and finish with a Write-Host command to show you have made it to the end of the script.

Review

How did you do it?

Try/Except (Python)

  • Similar functionality to PowerShell
  • Risky code should be wrapped in try block
  • Errors can be caught by one or more except blocks, which typically create an error variable (except <type> as <variable>)
  • Only one exception will be used, depending on the error
import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except OSError as err:
    print("OS error:", err)
except ValueError:
    print("Could not convert data to an integer.")
except Exception as err:
    print(f"Unexpected {err=}, {type(err)=}")

Try/Except (Python)

  • Errors can be raised manually if you wish
  • Uses the raise function, which takes an error type an information string as argument
import re
some_variable = input('Enter a number greater than 0: ')
if not re.match('^\-?[\d]{1}$', some_variable):
    raise Exception('Only numbers are acceptable')
if int(some_variable) < 0:
    raise ValueError('Number must be positive')

print('Made it to the end')

Exercise 2

Write a function in Python which takes 2 inputs, and subtracts the 2nd from the 1st. Call your function without try/except and note what happens when you use variables which cannot be subtracted (e.g. string, List). Add a print() command at the end to show you have reached the end of the script.

Next, edit your script to raise an error if either input is not an integer, and wrap your function calls in try/except to see what happens when an error occurs.

Review

How did you do it?

Working with HTTP Requests

  • Very common for scripts to handle API calls
  • All languages can handle HTTP calls natively
  • Typically multiple ways to handle calls, depends on preference

Requests (Python)

  • Most common http library in python, but must be installed separately with pip
  • Super easy to use
  • Used under the hood in httpie
  • Support for all methods, custom headers/cookies etc.
  • Support for EdgeGrid authentication (big + for Akamai users)
import requests

r = requests.get('https://httpbin.org/get?hello=world')
print(r.text)

Requests (Python)

import requests

payload = {
    'hello': 'world'
}
r = requests.post('https://httpbin.org/post', json=payload)
print(r.text)
import requests

url = 'https://httpbin.org/headers'
headers = {
    'User-Agent': 'ScriptClub'
    'Mastery-Level': 'Expert'
}
response = requests.get(url, headers = headers)
data = response.json()
print(response.status_code)
print(data['headers']['Mastery-Level'])

Exercise 1

Write a script in Python which makes a call to https://httpbin.org/headers and includes a custom If-Modified-Since header and 3 request cookies. Use the json() function to convert your response, then print the names and values of each of the 3 cookies on their own line.

Hint: Cookies are sent in a single request header, and will be combined in the response, so you will need to split them to an array and loop through it.

Review

How did you do it?

Invoke-WebRequest and Invoke-RestMethod (PowerShell)

  • Built-in functions to handle HTTP calls
  • Invoke-WebRequest contains lots more information than Invoke-RestMethod, but Invoke-RestMethod probably the more common to use
  • Invoke-RestMethod Response automatically converted to Object from JSON (if applicable), but does not contain status code, or some other aspects
$1 = Invoke-WebRequest -Uri 'https://httpbin.org/get?hello=world'
$1

$2 = Invoke-RestMethod -Uri 'https://httpbin.org/get?hello=world'
$2

Invoke-WebRequest and Invoke-RestMethod (PowerShell)

  • Both functions support all (standard) methods
  • Support for custom headers
  • Invoke-RestMethod can assign response headers to a variable (PS 6+). Variable name needs to be provided WITHOUT $ prefix!
  • -Body is a String parameter, unlike json in Python requests (dict)
$Headers = @{
    'User-Agent' = 'ScriptClub'
    'Mastery-Level' = 'Expert'
}
$Payload = '{
    "hello": "world"
}'
$Response = Invoke-RestMethod -Uri 'https://httpbin.org/post' -Method Post -Headers $Headers -Body $Payload -ResponseHeadersVariable ResponseHeaders
$Response
$ResponseHeaders

Exercise 2

Convert your previous script from Exercise 1 into PowerShell. You may use either Invoke-RestMethod or Invoke-WebRequest as you see fit, but you must print the Server response header.

Review

How did you do it?

External Modules

  • Most languages come with the capability to install external modules
  • Best practice is to provide a list of external modules to users, or an install script
  • Most modules are held in public repos already configured in your language

Pip (Python)

  • pip or pip3 executable typically installed with your Python install
  • Can also be accessed (best practice) by typing python -m pip
  • Modules are installed from pypi.org, but custom repos are supported
  • Modules installed by typing python -m pip install <module name>
  • Specific versions of modules can be installed by typing python -m pip install <module name>==<version>
  • Multiple modules can be installed in a file, commonly called requirements.txt, and installed with python -m pip install -r requirements.txt
  • requirements.txt can contain just names, or also specific or minimum versions denoted by ==, >= etc.

Exercise 1

Look up the latest version of the following Python modules: requests, edgegrid-python & xlsxwriter. Create a requirements.txt file specifying these versions (either exactly or at a minimum) and install them onto your laptop.

If you already have one of these modules installed at its maximum version, pick a different one

Review

How did you do it?

Install-Module (PowerShell)

  • Similar in functionality to pip, external modules can be installed with a single command, Install-Module
  • Modules are installed from the PowerShell Gallery, a public store configured automatically
  • Version can be specified, either exactly or min/max. If not specified the latest is installed
  • Custom repos are easy to configure
  • Modules are installed to
    • Windows
      • ~/Documents/PowerShell/Modules (CurrentUser)
      • C:\Program Files\PowerShell\Modules (AllUsers)
    • MacOS/Linux
      • $HOME/.local/share/powershell/Modules (CurrentUser)
      • /usr/local/share/powershell/Modules (AllUsers)

Exercise 2

Create a text file or csv with the names and latest versions of the following PowerShell modules: AWSPowershell, AkamaiPowerShell and ImportExcel. Write a PowerShell script to load this file and install each of the modules.

You should run the following command first: Set-PSRepository -Name PSGallery -InstallationPolicy Trusted. This will prevent prompts during installation of the modules

Working with Akamai APIs

  • Most APIs use EdgeGrid auth, which is tricky
  • SDKs released for most major languages on github.com/akamai
    • Repos called AkamaiOpen-EdgeGrid-*
  • Python library compatible with requests (and derivatives)
  • PowerShell SDK released, but better to use full PowerShell Module

What is EdgeGrid?

  • Authentication schema based on SHMAC cryptographic signing
  • Can be done manually, but NOT recommended
  • Takes 3 elements of EdgeRC: client-token, access-token & client-secret and combines with:
    • Full URL (scheme, host, path, query)
    • For POST requests includes body (not PUT or PATCH)
    • Current time - OS clock must be within 30s of Akamai server
  • Produces signed authorization header

edgegrid-python

  • Installed as a pip package, python -m pip install edgegrid-python
  • Generates authentication headers on the fly, as part of a requests session object
  • Can read credentials from EdgeRC file, or provided as strings
import requests
from akamai.edgegrid import EdgeRc, EdgeGridAuth

session = requests.Session()

#Inline
session.auth = EdgeGridAuth(
    client_token='akab-fp5z3sp714x155hr-mv7hrcu7j4iohgl9',
    client_secret='tQi7/x5DbFoZG7Ojv62AQ2U0iBOEpzTlHn07KeYpyZs=',
    access_token='akab-rba8e9xa19gxbttd-rtyjm0rhcynqfslf'
)
hostname = 'akab-s1xcwas8oesoh43d-6rb2dj3zcusyr9zf.luna.akamaiapis.net'

## OR from EdgeRC
edgerc = EdgeRc('~/.edgerc')
section = 'default'
hostname = edgerc.get(section, 'host')
session.auth = EdgeGridAuth.from_edgerc(edgerc, section)

response = session.get('https://' + hostname + '/papi/v1/contracts')

Exercise 1

Install the edgegrid-python pip (if you don’t already have it). Write a Python script to list all groups in a given contract (any customer account will do). Filter the group to find a specific one, then with that groupId and contractId list all properties in that group. Print the names of all properties you find.

Hint: Finding the right API endpoint can be tricky sometimes. Feel free to ask if it is not immediately obvious which one to use

Review

How did you do it?

Akamai PowerShell

  • Custom module which can be installed from the PS Gallery, Install-Module AkamaiPowershell
  • API calls are made available by specific functions, e.g. Get-Property, New-AppSecConfiguration
  • Covers 860+ functions in ~ 40 different APIs
  • Function params can be listed from the shell or Intellisense
  • Functions have common params: -EdgeRCFile, -Section, -AccountSwitchKey

Akamai PowerShell

#PAPI
$Rules = Get-PropertyRuleTree -PropertyName MyProperty -PropertyVersion latest -AccountSwitchKey 1-6JHGX:1-8BYUX
$Rules.rules.behaviors.options.hostname = 'neworigin.example.com'
$Rules | Set-PropertyRuleTree -PropertyName MyProperty -ProeprtyVersion latest -AccountSwitchKey 1-6JHGX:1-8BYUX

#AppSec
Set-AppSecPolicyAttackGroup -ConfigName smacleod -VersionNumber latest -PolicyName ion.stuartmacleod.net -AttackGroupID SQL -Action deny -AccountSwitchKey 1-6JHGX:1-8BYUX

#EdgeWorkers
New-EdgeWorkerVersion -Name smacleod_ew1 -CodeDirectory . -AccountSwitchKey 1-6JHGX:1-8BYUX

Exercise 2

Install the AkamaiPowershell module (if you don’t already have it). Write a PowerShell script to list all cloudlet policies in a given account. Use that data to find a specific Edge Redirector policy, pull down the most recent version and print out the total number of rules it contains.

Hint: In PowerShell (as in the API) Shared cloudlet policies (v3) use different commands than regular (v2) ones. PowerShell also has a LOT of different commands, so feel free to ask for help in choosing the right command.

Style

  • Scripts could be written on a single line, but this is difficult to read
  • Make your script as easy to understand as possible, even if you are the only person who will use it
  • Comments, line breaks, indentation are all useful tools to make your script easier
  • Each language has a style guide you can read for tips
  • Modern editors (like VS Code) have plugins to do a lot of this for you

Comments

  • Text which has no effect and is ignored by the interpreter
  • Useful to explain the purpose of the whole script, down to individual lines
  • Commonly prefixed with # for single lines, but differs between languages
  • Multi-line quotes are supported in most languages
  • Keyboard shortcuts in some languages to do this
# This comment describes what this function is doing

<#
This is a
multi-line comment
#>

# So
# Is
# This
# Single comments use hash
"""
This is technically a multi-line string,
but if not assigned to a variable, Python will ignore it.
Just like a comment.
"""

Line breaking

  • Commands in most languages can be placed on a single line if separated by a semi colon (;)
  • Better to use multiple lines, as it is easier to read. Line count is irrelevant
  • Even use new lines if you only need a single character, such as a closing brace } (see indentation)
$Files = Get-ChildItem; foreach($File in $Files){ Write-Host $File.Name }; Write-Host 'Complete'
# Is the same as
$Files = Get-ChildItem
foreach($File in $Files){
    Write-Host $File.Name
}
Write-Host $Complete

Indentation

  • Most languages don’t enforce indentation (Python does)
  • Generally makes it much easier to see if conditions, loop definitions etc.
  • Best practice to use 2 or 4 spaces (be consistent) and not tabs
    • Tabs can behave differently on different systems, so what looks right on yours might look a mess elsewhere
  • Closing curly braces
if($Something -eq 10){
Write-Host 'Top'
foreach($Item in $MyArray){
Write-Host 'Middle'
while($Item -gt 5){
Write-Host 'Bottom'
}
}
}
if($Something -eq 10){
    Write-Host 'Top'
    foreach($Item in $MyArray){
        Write-Host 'Middle'
        while($Item -gt 5){
            Write-Host 'Bottom'
        }
    }
}

Help

Readme.md

  • Git repos should always have a readme
  • Doesn’t need to be complex, but should contain the following
    • Usage instructions - paramters etc.
    • Installation instructions - Which 3rd party modules are needed and how to install them
    • Examples - As many as possible
  • Is automatically displayed by most code sites: BitBucket, Github etc.

Assessment Time!

  • Write a script in EITHER Python OR Powershell to meet ONE of the challenges below
  • You can take as long as you need (within reason) and there is nothing wrong with asking for help, so long as what you produce remains your own work
  • Don’t cheat! Copying code snippets from slides/the web is fine
  • If the brief is unclear in some way, ASK. This is what you would do with customers (and customers are always cryptic) so if you are unsure, better to ask than waste your time potentially writing code you won’t use.

Options

(Helpful info below…)

General Tips:

  • Limit your account/group/input file to only 10-20 properties/configs, or your script execution time will be very long
  • Your API client will require access to the Property, AppSec & Reporting APIs for these exercises.
  • Scripts must be well laid-out, commented and indented properly, and easy to understand.
  • Do not hard code anything which would limit your script to only the account/group that you are testing it with
  • OPTIONALLY, include a README.md file to explain the script’s operation
  • Use of third party modules is perfectly fine, but won’t get you any extra credit. edgegrid-python or AkamaiPowershell are required