Menu

PowerShell Notes

PowerShell Notes

0. Introduction

Build on the .NET framework, Windows PowerShell is a task-based command-line shell and scripting language; it is designed specifically for system administrators and power users, to rapidly automate the administration of multiple operating systems (Linux, macOS, Unix, and Windows) and the processes related to the application that run on those operating systems.

PowerShell is an object-oriented automation engine and scripting language with an interactive command-line shell that Microsoft developed to help IT Professionals configure systems and automate administrative tasks.

PowerShell is open-source.

PowerShell can be considered as a “glue” that ties most of Microsoft applications together.

Virtually all the server products Microsoft is producing right now can be managed through PowerShell. From an administrative standpoint, this means that if you become proficient in PowerShell, you will have the skill set necessary for managing most of Microsoft’s newer products.

PowerShell is Object-Based
This gives us incredible flexibility. Filter, sort, measure, group, compare or take other actions on objects as they pass through the pipeline. Work with properties and methods rather than raw text.

Why PowerShell?

  • Consistency
    A scripted solution will run the exact same script every time. No risk of typos, forgetting to complete the task, or doing the task incorrectly.
  • Audit trail
    There are many tasks where having an audit trail would be helpful, perhaps including what task was performed, important results, errors that occured, when the task ran, who ran it, and so forth.
  • Change of pace
    From scripting you can achieve a task significantly faster than GUI.

PowerShell is easy to adopt, learn and use because it does not require a background in programming.

Purpose of PowerShell

  • Improvement Management
  • Improved Automation
  • Manage real-time
  • Manage large scale envrionments

1. Execution Policy

It’s important to know what we can do in our system. Can we just run any script and brick our system? Or we can only execute commands that are safe?

Windows PowerShell execution policies let you determine the conditions under which Windows PowerShell loads configuration files and run scripts.

You can set an execution policy for the local computer, for the current user, or for a particular session. You can also use a Group Policy setting to set execution policy for computers and users.

Different execution policies

AllSigned

Requires that all scripts and configuration files be signed by a trusted publisher, including scripts that you write on the local computer. Prompts you before running scripts from publishers that you have not yet classified as trusted or untrusted.

RemoteSigned

Does not require digital signatures on scripts that you have written on the local computer (not downloaded from the internet)

Scripting languages like VBScript allows every script to run which is signed, no matter if you trust the authority or not. Even it’s a fake forged signature, it will trust it and get executed. Whereas in PowerShell, only if the signature is from a trusted authority, only then it will work.

Unrestricted

Unsigned scripts can run. Warns the user before running scripts and configuration files that are downloaded from the Internet.

Restricted

Permits individual commands, but will not run scripts.

Bypass:

Nothing is blocked and there are no warnings or prompts. (Unsecure)

To see our current execution policy, use:

Get-ExecutionPolicy

To change the policy

Set-ExecutionPolicy RemoteSigned
Set-ExecutionPolicy RemoteSigned -Force# for avoiding a prompt in ISE

You will have run powershell as an administrator and then run the above command.

If you worried about this command and you just want to check what this command will do then run the following:

Set-ExecutionPolicy RemoteSigned -WhatIf

WhatIf and Force are application on all commands, in PowerShell.

In production environments, Execution Policy can be imposed by a Group Policy. We will later understand Group policy but for now, you can think of it as a document, where you write rules or policies and impose them on the machines in a domain automatically.

Execution policy is one major aspect of PowerShell, there are more.

2. PowerShell Security Features

Sample .bat file

@echo off
echo "Spyware and Ransomware code initialized"
echo "All drives are getting encrypted"
echo "This system is not compromised"

pause

1. Association with notepad & not powershell.exe by default You can directly click this .bat script and it will run. Powershell scripts don’t directly run on a double click. They are associated by default to Notepad.

2. Execution policy restrictions Using this we can declare how we want PowerShell scripts to be executed in out system

3. Have to type the explicit path of the script in order to execute it Also, when you are running a script, just the name of the script is not enough, you have to type in the entire path. So you can be protected from malicious scripts in Unix systems where cd can be a directly writtem. Explain this.

4. Can require script to be signed else will not execute it

5. Can further add restrictions that script should be signed by your trustworthy certificate provider only.

3. Get-Help & Get-Command

Get-Help commands provides help about every command available in PowerShell. It provides much more than just the syntax and brief description of the command. This is one of the most useful commands.

All PowerShell commands are named in a fixed pattern, VERB-NOUN. So for example Get-Command will give you the list of commands.

So say you want to search commands that have “service” in their name. Then use

Get-Command-Name'*service*'

The * is a wildcard character, just means ignore characters after or before it.

To just get the first or last 3 results, you can pipe another command to this.

Get-Command-Name'*service*' | Select-Object-First3Get-Command-Name'*service*' | Select-Object-Last3

If you want to see help on a particular command, then use:

Get-HelpGet-Service

You will a summary of the help here. If you want to more details, with example usage of the commands etc., then use:

Get-HelpGet-Service-Full

Reading this much documentation in the terminal, is not a good user experience. Use this to open the documentation in the browser:

Get-HelpGet-Service-Online

One of the best ways of reading help documentation is opening it in a dedicated window. You also get search functionality. Use the following:

Get-HelpGet-Service-ShowWindow

The above was all about getting help for a command. But what if you want to get help for a concept like a for-loop or something like ISE. Then you can use the following:

Get-Help"about*"

The above will give you a list of all the help topics, with about in it. Once you find what you are looking for, you can get get help about it:

Get-Help"about_Arrays"-ShowWindow

Some other commands

Update-Help

Use this to update the help documentation.

To save help files in a particular path, use the following:

Save-Help C:\Users\rst\Documents\Training\PowerShell\scripts\test

4. Lab – Important Commands

1. Comment

# I am single line comment. I will be ignored by PowerShell<#
I am a multiline
comment. I will
be ignored
#>

2. Get the location of the current directory

Get-Location

3. To change directory

Set-Location C:\Users\rst\Downloads

4. To go back

Set-Location ..

5. To get current date

Get-Date

6. List all files and folders at a location

Get-ChildItem

7. Search recursively inside the sub folders

Get-ChildItem-Recurse

8. Get the list of items by path

Get-ChildItem-Path C:\Users\rst\Downloads

9. Print on the console

Write-Output"Print me on the console"

10. Clear your screen

Clear-Host

11. Copy a file

Copy-Item-Path ..\Notes.md -Destination .

12. Copy everything from source path to destination path

Copy-Item-Path Test_Folder1\* -Destination Test_Folder2\ -Recurse

13. Copy everything from source path to destination path except the files inside subfolders

Copy-Item Test_Folder1\* -Destination Test_Folder2

14. Move item from source to destination

Move-Item sample.bat -Destinationtest-folder

15. Move item from source to destination and rename

Move-Item sample.bat -Destinationtest-folder\sample-bat.bat

16. Rename file or folder

Rename-Item sample.bat -NewName thisisnewname.bat
# orRename-Item .\thisisnewname.bat -NewName"sample hello.bat"Rename-Item .\test-folder-NewName test_folder

17. Delete an item

Remove-Item-Path .\hello.txt

18. Remove a folder

Remove-Item-Path .\test_folder -Recurse

19. How to get PowerShell’s help

Get-HelpGet-HelpGet-HelpRemove-Item

20. To get the list of different commands executed in the shell

Get-History

21. Clear the shell’s command history

Clear-History
Shell/CMD (Powershell Alias)Powershell Equivalent
pwdGet-Location
cdSet-Location
%date%%time%Get-Date
ls / dirGet-ChildItem
echoWrite-Output
cls/clearClear-Host
cpCopy-Item
mvMove-Item
renRename-Item
del/rmRemove-Item
man/helpGet-Help
Get-History
Clear-History

5. Variables

Variables are the names you give to computer memory locations which are used to store values in a computer program.

PowerShell uses variables as temporary, named storage for objects. Variable name begins with $.

Variables are objects. It is the name of a memory location, where objects are store.

Creating Powershell Variables

$MyVariable = "Some string value"$My_Variable1# to view the variable$global:var_name = "This will be accessible throughout the script"Set-Variable-Name"myVar"-Value"value of the variable"New-Variable-Name"myVar"-Value"value of the variable"

To access any variable, type $ and then the variable name

To get the complete list of variables in PowerShell

Get-Variable

Other important variable related commands

Get-VariableRemove-Variable# Remove-Variable My_Variable1Clear-Variable# Ex.$My_Variable1 = "Some string value"Clear-Variable-Name My_Variable1
$My_Variable1$My_Variable1.GetType()

By default, variable name cannot contain spaces and special characters (except a few). However in PowerShell, if you really need you can give any name to the variable

${give any _name&&**++} = "Value of this variable"${give any _name&&**++}

Constant

constant is a value that never changes Ex. value of Pi, speed of light, radius of earth

Set-Variable test -Option Constant -Value100Set-Variable test -Option ReadOnly -Value100

6. Data Type & Typecasting

Data Type

Type of data we are storing inside a variable.

You don’t have to explicitly declare the data type of a variable. PowerShell automatically chooses the data type for you when you initialize the variable – that is, when you first assign a value.

Some common data types: String, Integer, Boolean, Double, etc.

$var = "10"$var.GetType()

Integers

$var1 = 100$var2 = 205# Sum$sum = $var1 + $var2$sum# Difference$diff = $var2 - $var1$diff# Multiply$multiply = $var2 * $var1$multiply# Difference$divide = $var1 / $var2$divide# Type of variable$sum.GetType()
$diff.GetType()
$multiply.GetType()
$divide.GetType()

# Modulus Operator$Remainder = $var2 % $var1$Remainder$Remainder = 10 % 6$Remainder

String

$var_name = "John"$var_year = "Year: 2022"$current_temp = "40"$another_string_var = "$var_name is make a course in $var_year and current temperature is $current_temp"$another_string_var

Another way of creating String, is to place value inside single quotes. $another_string_var = 'This is also a string

Difference is that single quotes DO NOT resolve the variables to it’s value. Whereas double quotes resolve the variable and you will find only the value to be printed.

# Single quotes won't print the variable$temperature = 42Write-Output"Temperature : $temperature"Write-Output'Temperature : $temperature'

How to avoid resolving of a variable inside double quotes?

Use escape character (grave-accent(`)) before every variable which you do not want to be resolved.

$another_string_var = "$var_name is make a course in `$var_year and current temperature is $current_temp"$another_string_var

String Properties and Methods

$string_var = "a Random String  "$string_var.Length
$string_var.Contains("Random")
$string_var.Contains("random")
$string_var.IndexOf("Random")
$string_var.Trim()
$string_var.ToUpper()
$string_var.ToLower()
$string_var.Replace("a", "b")
$string_var.Replace("Random", "Nice")

here-string How to handle quotes inside string?

We can use here-string

Just need to close our string value inside @" "@

$text = @"
Question: "Who are you?"
Answer: "I am so & so and came here for this purpose"
"@$text$normal = "
I am $age years old
How old are you?
"$normal# I don't want to expand the value of $age, I want to store it as it is$normal = @'
I am $age years old
How old are you?
'@Write-Output$normal

How to validate the data type?

Using -is operator

$a_var = 1000$a_var-is [Int]
$a_var-is [String]

To get the list of properties and methods of any data type

$str = "Hello"$str | Get-Member$num = 10$num | Get-Member

TypeCasting

PowerShell, automatically assigns a data type to a variable. However, if you explicitly want to define a data type, you are allowed to do so.

Method 1: Apply data type on variable

[int]$x = 100$x.GetType()
# Once, we declared variable as Integer type, we cannot change it's type again$x = "a string value"# will throw an error

Method 2: Apply data type on value

$y = [int]100# We can re-use the same variable again & again with different data types$y.GetType()
$y = "a string value"$y.GetType()

Predict the type of variable – [int]$Variable = "121"

Typecasting advantage

  1. We don’t have to waste time in writing logics, if we use types smartly
$date_string = "01/26/2022"
[DateTime]$date_string = $date_string$date_string$date_string.GetType()
  1. Typecasting can be used for validation of data

7. Read, validate and write

Read Input

The Read-Host cmdlet reads a line of input from the console. You can use it to prompt a user for input. Because you can save the input as a secure string, you can use this cmdlet to prompt users for secure data, such as passwords, as well as shared data

[string]$name = Read-Host"What is your name"
[int]$age = Read-Host"What is your age"
[DateTime]$date = Read-Host"What is the date on which you are taking this course? Date format (dd/mm/yyyy)"
[char]$response = Read-Host"The trainer is doing a good job? (y/n)"$password = Read-Host"Enter your password"-AsSecureStringWrite-Host"****** Data Received ******"Write-Host"You name is: $name"Write-Host"You age is: $age"Write-Host"Date: $date"Write-Host"Response: $response"Write-Host"Response: $password"

Restrict user from entering whatever it wants

[ValidateSet("y", "Y", "n", "N")]$response = Read-Host"The trainer is doing a good job? (y/n)"$response
[ValidateLength(5, 120)]$name = [string](Read-Host"What is your name")
$name
[ValidateRange(5, 100)]$age = [Int](Read-Host"What is your age")
$age

To get help on the validate related commands

Get-Help about_Functions_Advanced_Parameters -ShowWindow

Write-Host

The Write-Host cmdlet customizes the output. You can specify the color of the text by using the foreground color paramter, and you can specify the background color by using the background color parameter.

Write-Host"Hello World! This is some sample text"Write-Host"Colorful Text"-ForegroundColor Cyan
Write-Host"More Colors"-ForegroundColor Black -BackgroundColor Red

# Better commandsWrite-Output"This is an output message"Write-Error"This is an error message"Write-Warning"This is a warning message"# These below 2 will print message only if you want them to print# If you pass the -Debug and -Verbose parameter they will print# or else won't do anythingWrite-Debug"This is debug message"-DebugWrite-Verbose"This is verbose"-Verbose

Write-Host vs others It does not send output through the pipeline

Write-Output"Hello" | Get-MemberWrite-Host"Hello" | Get-Member# does not work# Both the below work though$var_host = Write-Host"Hello"$var_host$var_output = Write-Output"Hello"$var_output

8. Comparison Operators

Comparison operators let you specify conditions for comparing values and finding values that match specified patterns. To use a comparison operator, specify the values that you want to compare together with an operator that separates those values. Examples -eq and -neq -le and -gt -like and -notlike -contains and -notcontains -match and -notmatch

# Operator -eq2-eq2"abc"-eq"abcd""abc"-eq"ABC"# True"abc"-ceq"ABC"# False# Operator -ne"abc"-ne"hello"# Operator -lt and -gt5-lt107, 8, 9, 10-gt8# will return all the values from the left that pass this condition# -ge,-le => less than or equal and greater than or equal# Operator: -like <string[]> -like <wildcard-expression> (substring)# Check if "shell" is a substring of "PowerShell" "PowerShell"-like"*shell*""PowerShell"-clike"*shell*"# Operator: -notlike Does not match using the wildcard character (*)"PowerShell"-notlike"*Shell"

All the strings in the array that have “SSS” in them will be listed

@( # This is an array"This is a line about XYZ country",
  "This is a line about ABC country",
  "This is a line about SSS country",
  "This is another SSS country info"
) -like"*SSS*"

-match operator – This matches the string with a regular expression

It will find a match and then store the result in the $matches variable

"Sunday"-match"sun"$matches"Sunday", "Monday", "Tuesday"-match"sun"$matches

-contains and -notcontains

The containment operators (-contains and -notcontains) are similar to the equality operators. However, the containment operators always return Boolean value, even when the input is a collection

"Windows", "PowerShell"-contains"Shell""abc", "def"-contains"def""Windows", "PowerShell"-notcontains"Shell"

To get help on all the operators:

Get-Help about_Comparison_Operators -ShowWindow

9. If/Else Loop and Switch

Just explain comparisons in general and how we use comparison operators to make decisions.

Comparison operators

Comparison operators let you specify conditions for comparing values and finding values that match specified patterns. To use a comparison operator, specify the values that you want to compare together with an operator that separates these values.

Example

-eq and -neq
-le and -gt
-like and -notlike
-contains and -notcontains
-match and -notmatch

If/Else

Comparison operators return true or false depending upong the inputs. Depending upon this result, we can proceed to make a decision within the script at runtime and do a set of tasks, if we get true and another set of tasks, if the output is false.

PowerShell syntax

If (condition) {
  # Do stuff if condition is true
} else {
  # Do stuff if condition is false
}

Examples

# Example 1If (10-eq10) {
  "True"
}
else {
  "False"
}

# Example 2
[int]$num1 = Read-Host"Enter a numberl"
[int]$num2 = Read-Host"Enter another number"If ($num1-eq$num2) {
  "You entered $num1"
}
else {
  "You entered $num1 and $num2 and they are not equal"
}

# Example 3
[int]$num = Read-Host"Enter a number"# -not is used for reversal of If logicif (-not ($num-le100)) {
  Write-Output"Entered number $num is NOT less than 100"
}

# Example 4 - Nested If/Else loop
[int]$num = Read-Host"Enter a number"if ($num-gt100) {
  if ($num-lt1000) {
    Write-Output"Entered number $num is somewhere between 100 and 1000"
  }
}
else {
  Write-Output"Entered number is not in the valid range"
}

# Example 5 - If/Else with multiple conditional statements using -and
[int]$num = Read-Host"Enter a number"# If & only if both conditions and True, PowerShell will execute the blockif (($num-gt100) -and ($num-lt1000)) {
  Write-Output"Entered number $num is somewhere between 100 and 1000"
}
else {
  Write-Output"Entered number is not in the valid range"
}

# Example 6 - If/Else with multiple conditional statements using -or
[int]$num = Read-Host"Enter a number"# If one of the conditions is True, PowerShell will execute the blockif (($num-gt100) -or ($num-lt1000)) {
  Write-Output"Entered number $num is somewhere between 100 and 1000"
}
else {
  Write-Output"Entered number is not in the valid range"
}

# Example 7
[int]$num = Read-Host"Enter a number"If ($num-eq10) {
  "You entered $num and that's equal to 10"
}
elseif ($num-eq100) {
  "You entered $num and that's equal to 100"
}
elseif ($num-eq1000) {
  "You entered $num and that's equal to 1000"
}
else {
  "The number you entered is not 10, 100 or 1000."
}

Switch Statement

To check multiple conditions, use a Switch statement. The Switch statement is equivalent to a series of If statements, but it is simpler. The Switch statement lists each condition and an optional action. If a condition obtains, the action is performed.

PowerShell Sytax

Switch (<test-value>) {
  <condition> {<action>}
  <condition> {<action>}
}

Example

[int]$num = Read-Host"Enter a number"Switch ($num) {
  10 { "You entered $num is equal to 10" }
  100 { "You entered $num is equal to 100" }
  1000 { "You entered $num is equal to 1000" }
  Default { "You entered $num is not 10, 100, 1000" }
}

We can use If else output and store it inside a PowerShell variable

# Example 1$num = if ($true) { 1 } else { 2 }
$num# Example 2$day = "Sunday"$activity = if ($day-like"Sunday") { 'FUN' }  else { "Work" }
$activity

9. PowerShell Collections

A Collection is nothing but an object that groups multiple elements into a single unit.

Collections can be used to store, retrieve and manipulate data.

It is also called as a container.

Example: Array, ArrayList, HashTable etc.

Array

A data structure to store an ordered collection of items.

To get the size of an array: $arrayname.Length
To access an item from array by index: $arrayname[index_number]
To access a range of items: $arrayname[1..5]

Array Index 0 is the index of the first item in the array, 1 is the second and (size-1) is the index of the last element in the array.

$a = 10$a.GetType()

$a = 10, 23$a = @(10, 23) # another way of creating an Array$a.GetType()
$a.Length

$names = @("John", "Jack", "James", "Jill", "Joe")
$names | Get-Member# To access array elementsWrite-Output"To get the size of an array: "$names.Length
Write-Output"To access an item from array by index: "$names[2]
Write-Output"To access a range of items: "$names[1..3]

$names_list1 = @("John", "James", "Jack", "Joe")
$names_list2 = @("Jill", "Jessica", "Jolene", "Jamie")

$all_names = $names_list1 + $names_list2$all_names.Length

# More array operations$all_names.Contains("John")
$all_names.Contains("james")
$all_names.IndexOf("Jill")

$all_names.IsFixedSize
$all_names.Add("Richard Roe") # won't work because this array is of fixed size

Since arrays are of fixed size you can’t add new elements. To make a dynamic Array, use an Array List.

ArrayList

To frequently add elements to, remove elements from, search and modify an Array.

Class: System.Collections.ArrayList

Examples

$names = New-Object System.Collections.ArrayList
$names.Add("John")
$names.AddRange(("Jane", "Jill"))
$names$names | Get-Member

Advantages

  • Simple and easy to use
  • Can be used to store objects of multiple data types

Disadvantages

  • We must know in advance that how many elements are to be stored in array as it is of a fixed size
  • Searching and sorting is slow & inefficient

Array and ArrayList allows duplicates and is not a good choice to store unique values. Use a HashTable instead.

HashTable

A hash table, also known as a dictionary or associative array, is a compact data structure that stores one or more key/value pairs.

Example, a hash table might contain a series of IP addresses and computer names, where the IP addresses are the keys and the computer names are the values, or vice versa.

Syntax

$hash = @{}
$student_data = @{
  "name" = "John Doe";
  "course" = "Advanced PowerShell";
  "sex" = "Male";
}
$server_data = @{
  "127.0.0.1"     = "LocalHost";
  "192.168.1.231" = "Telnet Server";
  "192.168.1.230" = "TDP";
}
$server_data# Different methods & properties$server_data | Get-Member# To see the number of items$server_data.Count

# Get all keys$server_data.Keys

# Get all values$server_data.Values

# Get value by key$result = $server_data.'192.168.1.230'$result# OR$result = $server_data['127.0.0.1']
$result# To check if hash is fixed sized or not$server_data.IsFixedSize

# Add a new pair to the hashtable$server_data.Add("127.0.0.5", "React-Scripts server")
$server_data# Remove a key value pair$server_data.Remove("127.0.0.1")
$server_data# Sorting
[hashtable]$hash = @{
  Detail_100 = "Value";
  Detail_01  = "Square";
  Detail_06  = "Blue";
}
# $hash.GetType()$hash.GetEnumerator() | Sort-Object-Property Key

# You can store anything inside hashtables$student_data = @{
  Person1 = @{first_name = "John"; last_name = "Doe" };
  Person2 = @{first_name = "Jack"; last_name = "Smith" };
}
$student_data$student_data.Person2

# Get HelpGet-Help about_Hash_Tables -ShowWindow

10. Iteration

While Loop

The logic of this loop is to execute the while(the condition is true)(--DO--). Pay close attention to the style of brackets, the curly brackets or parenthesis are guiding you to write the correct code.

Syntax

$i = 1while ($i-le10) {
  Write-Output"value of variable i: $i";
  # $i++$i += 1# this is similar to $i = $i + 1# $i += 2
}

Write-Output"Out of the loop and proceeding with futher statements in script"

Do While Loop

$i = 10;
do {
  Write-Output"Count: $i"$i--
} while ($i-ge0)

For Loop

The loop to perform a task, for set number of iterations until the condition is true.

Syntax

for (init; condition; repeat) {
  # statement 1# statement 2# 
}

Example

# Example 1for ($i = 0; $i-le10; $i++) {
  "Counter: $i"
}

# Example 2$arr = @("John Doe", "Jane Doe", "Jack Smith")

for ($i = 0; $i-lt$arr.Length; $i++) { # can also use $arr.CountWrite-Output$arr[$i]
}

ForEach Loop

# Iterating over arrays$names = @("John Doe", "Jane Doe", "Jack Smith", "Jocelyn Daimon")

foreach ($namein$names) {
  Write-Output"Hello! My name is $name"
}

# Iterating over hashtables$user_profile = @{
  Name       = "John Doe";
  Age        = 30;
  Profession = "Programmer"
}
foreach ($keyin$user_profile.Keys) {
  $message = "$key = " + $user_profile[$key]
  Write-Output$message
}

11. PowerShell Functions/Methods

A function is a list of Windows PowerShell statements that has a name that you assign. When you run a function, you type the function name. The statements in the list run as if you had typed them at the command prompt.

Syntax

function Verb-Noun {
  # Statement 1# Statement 2
}

# To call the function
Verb-Noun

Examples

function Say-Hello {
  Write-Output"Hello World"Write-Output"How are you doing?"Write-Output"Do you have any questions?"
}

Say-Hello
Say-Hello# Function that accepts argumentsfunction Add-Numbers {
  $total = $args[0] + $args[1]
  Write-Output"Total: $total"
}

Add-Numbers105# Function that accepts an unlimited number of argumentsfunction Add-Numbers {
  $total = 0foreach ($numin$args) {
    $total += $num
  }
  Write-Output"Total: $total"
}

Add-Numbers105105# Function that accepts an array (always use a list of approved verbs)function Write-Hello($names) {
  foreach ($namein$names) {
    Write-Output"Hello! My name is $name"
  }
}

$names = @("John Doe", "Jack Doe", "James McLaren")
Write-Hello$names# Function that accepts an unlimited number of argumentsfunction Write-Hello($names, $some_message) {
  foreach ($namein$names) {
    Write-Output"Hello! My name is $name"
  }
  Write-Output$some_message
}

$names = @("John Doe", "Jack Doe", "James McLaren")
$message = "Goodbye World!"Write-Hello$names$message

If you have a lot of paramters to pass, then it becomes difficult to remember the argument position.

Functions with named parameters

function Write-Introduction {
  param (
    [string]$Name, 
    [string]$Profession = "Programmer",  # Default argument
    [string]$Age
  )

  Write-Output"Hello there! My name is $Name and I am $Age years old and I work as a $Profession"
}

Write-Introduction-Name"John Doe"-Profession"Developer"-Age20Write-Introduction-Name"Jane Smith"-Profession"Full-Stack Engineer"-Age22Write-Introduction-Name"Jack Roe"-Age25

Remember in powershell, functions are called methods. Both these terms are interchangable most of the times. So when I call it a method, I’m talking about a function.

12. Error Handling

Error handling is important when creating PowerShell scripts. A script that runs correctly once may not run correctly every time.

There is always some kind of problem that can crop up when you least expect it. This is why error handling should be implemented in every important piece of PowerShell code you create.

Why Error Handling?

TaskUncertainties (Possibilies of exceptions)
Reading/Writing File ContentFile got deleted, network dependency, file locked, insufficient access
Database OperationDatabase table not existing, DB maintenance in progress, Database down, network dependency, insufficient access, data duplication not allowed
Sending Daily ReportSMTP Server Issue, Report file locked
VersionScript was developed for a particular version which wasn’t available in actual production envrionemnt

Types of Errors

Terminating Error:

A terminating error is an error that will halt/stop the function or an operation

Examples: syntax error, out of memory exception

Non-Terminating Error:

A non-terminating error is an error that will allow PowerShell to continue with the execution of the script or next set of statements

Examples: Errors thrown by cmdlets

$Updated_Geniune_Employees_List = Get-Content"\\FileServer\HRDepartment\Employees.txt"# Some cmdlet which returns the list of current employees# $Current_Employees = Get-MyEmployees $Current_Employees = @("John", "Jane", "Jack", "James", "Jill")

foreach ($empin$Current_Employees) {
  If ($Updated_Geniune_Employees_List-notcontains$emp) {
    Write-Output"Entry Deleted"# Delete-Employee $emp # pass employee variable as arg # to delete from the database
  }
  else {
    Write-Output"Geniune Employee"# Update-Employee $emp "Geniune" # pass employee as arg # to update as `Geniune` in the database
  }
}

-ErrorAction Parameter (Alias: -EA)

It is a parameter to any cmdlet to define the action to be taken upon error.

All cmdlets accept this parameter.

Write-Error-Message"4"-ErrorAction <Continue|Ignore|Inquire|Silently Continue|Stop|Suspend>

Most cmdlets, terminate the current statement, but continue with other statements, If we want to stop the script execution right there, we can use -EA Stop parameter to stop the execution.

$Updated_Geniune_Employees_List = Get-Content \\FileServer\HRDepartment\Employees.txt -ErrorAction Stop

$ErrorActionPreference

This is a PowerShell variable which sets the preference for action to be taken on errors for all different cmdlets, instead of defining actions on individual cmdlets.

  • Continue: Display errors, But continue to next statement if possible
  • SilentlyContinue: Supress the errors
  • Stop: Display the error. Terminate script execution there.
  • Inquire: Ask the user

The variable can used for setting the preference for the whole script. However, if you want to set the preference for an individual command, you can do that using the ErrorAction parameter.

$ErrorActionPreference = "Stop"$Updated_Geniune_Employees_List = Get-Content \\FileServer\HRDepartment\Employees.txt -ErrorAction Stop
$Current_Employees = @("John", "Jane", "Jack", "James", "Jill")

# $ErrorActionPreference = "SilentlyContinue"foreach ($empin$Current_Employees) {
  If ($Updated_Geniune_Employees_List-notcontains$emp) {
    Write-Output"Entry Deleted"# Delete-Employee $emp
  }
  else {
    Write-Output"Geniune Employee"# Update-Employee $emp "Geniune"
  }
}

-ErrorVariable (Alias -EV) – to store the error in a variable

Error variable will store the error message and you can use it later for showing it to the user or logging it inside your logfile.

It will store the error message even if error preference is set to be silently continue.

PS C:\Users\rst\Documents\Training\PowerShell> Get-Content-path"Filename.txt"-ErrorAction SilentlyContinue -ErrorVariable"error_var"PS C:\Users\rst\Documents\Training\PowerShell> $error_varGet-Content: Cannot find path 'C:\Users\rst\Documents\Training\PowerShell\Filename.txt' because it does not exist.
PS C:\Users\rst\Documents\Training\PowerShell>

Try-Catch

When an exception is thrown anywhere inside of a try block, there’s a catch block that’s there to catch the thrown exception and do something with it.

In PowerShell, an exception is a terminating error.

A terminating error stops a statement from running.

Try statement contains a try block, zero or more Catch blocks, and zero or one Finally block. A Try statement must have atleast one Catch block or one Finally block.

Syntax

try {
  # statement_list# You PowerShell commands that do some things
} 
catch {
  # statement_list# This will execute only if an exception occured in the try block
} 
finally {
  # statement_list# Finally this will execute irrespective of exceptions occurs or not
}

Example

$file_name = "ImportantData.txt"try {
  if (Test-Path$file_name) {
    Write-Output"Reading File"Get-Content$file_name-ErrorAction Stop -ErrorVariable'err'
  }
  else {
    throw"File not found at the shared location"
  }
}
catch [System.IO.FileNotFoundException] {
  Write-Output"File not available at $path"
}
catch [System.IO.IOException] {
  Write-Output"Input/Output error with the file: $path"
}
catch {
  $ErrorMessage = $_.Exception.Message # $_ represents the current object, which is nothing but the exception thrown aboveWrite-Host"Sorry, the file ($file_name) you are looking for does not exist"-ForegroundColor Yellow
  Write-Host"Err: $ErrorMessage"-ForegroundColor Red
}
finally {
  Write-Host"An attempt to access the file ($file_name) was made at $(Get-Date)"-ForegroundColor Green
}

To get more details and help on try/catch

Get-Help about_Try_Catch_Finally -ShowWindow

13. What is a Windows Process?

What is a process?

  • A process is an instance of a particular executable
  • An application consists of one or more processes
  • Each process provides the resources needed to execute a program
  • Each process is started with a single thread, often called the primary thread, but can create additional threads from any of it’s threads.

14. What is a Windows Service?

In Windows NT operating systems, a Windows service is a computer program that operates in the background. It is similar in concept to a Unix daemon.

Services have no UI and mostly do not interact directly with users.

service is a process which runs in the background and does not interact with the desktop

15. Get-Process

The Get-Process cmdlet provides a quick and easy way to retrieve information about the processes running on your computer.

Without parameters, this cmdlet gets all of the processes on the local computer. You can also specify a particular process by process name or process ID (PID) or pass a process object through the pipeline to this cmdlet.

By default, this cmdlet returns a process object that has detailed information about the process and supports methods that let you start and stop the process. You can also use the parameters of the Get-Process cmdlet to get file version information for the program that runs in the process and get the modules that the process loaded.

Get-Process
(Get-Process).GetType()
Get-Process | Sort-Object CPU
Get-Process | Sort-Object CPU | Select-Object-First10Get-Process | Sort-Object CPU -Descending | Select-Object-First10Get-Process-Name"*pwsh*"Get-Process-Name"*pwsh*" | Select-Object *
Get-Process | Where-Object { $_.CPU -gt100 }
Get-Process-Name"Notepad" | Stop-Process-WhatIfGet-Process-Name"Notepad" | Stop-Process# To get helpGet-HelpGet-Process-ShowWindow

15. Get-Service

The Get-Service cmdlet gets objects that represent the services on a local computer or on a remote computer, including running and stopped services.

You can direct this cmdlet to get only particular services by specifying the service name or display name of the services.

By default Get-Services will return all the services on the computer.

Get-ServiceGet-Service | Sort-Object Status
Get-Service | Sort-Object Status -DescendingGet-Service-Name"*ser*"Get-Service-Name"*ser*" | Sort-Object Name
Get-Service-Name"a*" | Where-Object { $_.Status -eq"running" }
Get-Service-Name"a*" | Where-Object { $_.Status -eq"running" } | Select-Object-First3# Break up long linesGet-Service-Name"a*" 
| Where-Object { $_.Status -eq"running" } 
| Select-Object-First3Get-Service-Name"*MongoDB*" | Stop-Service-WhatIfGet-Service-Name MongoDB | Stop-Service# would have to `run as administrator`Get-Service-Name MongoDB | Start-Service# would have to `run as administrator`

15. What is Windows Task Scheduler

The Task Scheduler is a tool included with Windows that allows predefined actions to be automatically executed whenever a certain set of conditions are met

Purpose

The task scheduler enables you to automatically perform routine tasks on a chosen computer. The task schedule does this by monitoring whatever criteria you choose to initiate the tasks (referred to as triggers) and then executing the tasks when the criteria is met.

Example

  • Send you an email, when a certain even occured in a machine
  • Purge files older than 30 days weekly
  • Run a system health check daily at 10:30AM
  • Send a monthly email containing detailed report of server usage, RAM, CPU disk space, etc.

To open a system’s task scheduler, we can either search task schedule in windows or we can go to Run and type taskschd.msc and search.

16. Text File Handling

Reading a file

To read a text file into PowerShell, we can use Get-Content cmdlet. You can use options of this command to read the desired content.

Get-Content C:\SomeDirectory\A_Random_File.txt

Test-Path

To check the existence of a file

if (Test-Path C:\PowerShell\test_file\random_file.txt) {
  Write-Output"File exists"
}
else {
  Write-Output"File does not exist"
}

# Reading file after verifying it's existenceif (Test-Path .\script1.ps1) {
  Get-Content .\script1.ps1

  # Store the read value in a powershell variable$file_content = Get-Content .\script1.ps1
  $file_content# Only read the last three lines of a large fileGet-Content .\script1.ps1 -Tail3# Read a specific line by it's line number
  (Get-Content .\script1.ps1)[2]

  # To read a range of lines $file_content = (Get-Content .\script1.ps1)[2..8]
  $file_content# Searching in the file content$file_content = Get-Content .\script1.ps1
  $file_content | Where-Object { $_-like"*ERROR*" }
  $file_content | Where-Object { $_-like"*INFORMATION*" }

  # Select-String for searching through the file$file_content = Get-Content .\script1.ps1
  $file_content | Select-String-Pattern"Error""powershell", "PowerShell", "SHELL", "Shell" | Select-String-Pattern"Shell"-CaseSensitiveSelect-String-Path .\Notes.md -Pattern"Important"# This can be used for multiple file searches as well. You just need to # specify a wild card in the file name and it will search all the files# for that patternSelect-String-Path".\*.txt"-Pattern"Error"# Explore `Select-String` in more detail. Read the help docs.Get-HelpSelect-String-ShowWindow
}
else {
  Write-Output"File does not exist"
}

With Try/Catch

try {
  $file_name = ".\script1.ps1"if (Test-Path$file_name) {
    Write-Output"File exists"$last_4_lines = Get-Content-Path$file_name-Tail4-ErrorAction Stop -ErrorVariable'err'Write-Output"As per the requirement here are the last 4 lines of your file"Write-Output$last_4_lines
  }
  else {
    Write-Output"Hello User, the file you are trying to access does not exist. Please check and try again."
  }
}
catch {
  Write-Output"Something went wrong. Error: $err"
}

Writing a file

To write into a text file, we can use Out-File cmdlet. You can use options like -Append-Force-Encoding as per your requirement. This cmdlet will create the file if it doesn’t already exist.

Syntax

Write-Output"Some Text" | Out-File-FilePath output.txt

Examples

# Write some text into a file (This will create the file if it does not exist already)Write-Output"$(Get-Date): This is a text" | Out-File-FilePath output.txt

# To append text at the end of an existing file (creates a file only if it does not exist)Write-Output"$(Get-Date): This is a text" | Out-File-FilePath output.txt -Append$var = "Some random variable"Write-Output"$(Get-Date): var = $var" | Out-File-FilePath output.txt -Append

17. CSV File handling

CSV files are plain text files in which values tabular data is saved as comma separated values.

File extension: .csv

Comma Separated Values (CSV)

To import CSV file content into a PowerShell variable.

$csv_content = Import-Csv"File-Location"

The Import-CSV cmdlet creates table-like custom objects from the items in CSV files. Each column in the CSV file becomes a property of the custom object and the items in rows become the property values. Import-Csv works on any CSV file, including files that are generated by the Export-Csv cmdlet.

# Reading CSV Files$csv_content = Import-Csv .\test.csv
# $csv_content$csv_content | Get-Member$csv_content | Sort-Object Age
$csv_content | Select-Object-First2$csv_content | Where-Object { $_.Name -like"R*" }

$csv_content.GetType()

CSV File Writing

# Writing the headersAdd-Content-Path users.csv -Value'Name,Age,Profession'# Array$user_data = @(
  'Rick Grimes,43,Rust Programmer';
  'Rick Harrison,47,Delphi Developer';
  'Harvey Specter,38,Cobol Developer';
)

# Adding content to CSV$user_data | ForEach-Object { Add-Content-Path users.csv -Value$_ }

# --------# Convert unsorted data file into sorted data fileImport-Csv .\test.csv | Sort-Object Age | Export-Csv sorted_test.csv -NoTypeInformation# -NoTypeInformation parameter omits unwanted lines of output in the starting

18. XML File Handling

XML Files

Extensible Markup Language (XML) is used to describe data. The XML standard is a flexible way to create information formats and electronically share structured data via the public internet, as well as via corporate networks.

XML code, a formal recommendation from the W3C, is similar to HTML. Both XML and HTML contain markup symbold to describe page or file contents.

PowerShell offers a number of different ways to read XML documents, without having to write a lot of code.

# Reading XML File
[xml]$xml_content = Get-Content .\books.xml
$xml_content$xml_content.GetType()
$xml_content | Get-Member# Reading XML data$xml_content.catalog
$xml_content.catalog.book
$xml_content.catalog.FirstChild
$xml_content.GetElementsByTagName("title")

foreach ($entityin$xml_content.GetElementsByTagName("book")) {
  Write-Output$entity.author
  $price = $entity.price
  Write-Output$price
}

# Practical Usage# 1. Prepare application's configuration file and read it to feed values inside script# 2. Save the object state into a file. This file can be later loaded into PowerShell#    and again access the same object as if we are accessing a live process (in this case).#    This can be used for debugging a system problemGet-Process | Select-Object-First2 | Export-Clixml process_output.xml

19. Objects

Object based PowerShell

↓ What is an object?

Views & Pipelines

Working with objects

Formatting & Converting

Class & Method

Save & Recreate Objects

A programming object

Real-world objects share two characteristics:

  • They all have state and behavior
  • Dogs have state (name, color, breed, hungry) and behavior (barking, wagging tail).
  • Bicycles also have state (current gear, current speed) and behavior (changing gear, applying brakes)

Programming Objects are conceptually similar to real-world objects:

They too consist of state and related behavior. An object stores it’s state in properties/variables/fields and exposes it’s behavior through methods.

An object is simply the programmatic representation of anything.

It is a good practice to take a look at Get-Memeber cmdlet’s output to understand what exactly is a particular object and what it can do.

Whatever cmdlets we have seen so far which seems to be displaying plain text on the console. None of that was plain text but they were all programmable Objects.

Cmdlets

A cmdlet is a lightweight command that is used in the Windows PowerShell environment. The Windows PowerShell runtime invokes these cmdlets within the context of automation scripts that are provided at the command line. The Windows PowerShell runtime also invokes them programatically throught Windows PowerShell APIs.

Most cmdlets are based on .NET framework classes that derive from the Cmdlet base class.

Get-ChildItem-Path C:\Windows

Everything in PowerShell is an Object.

Proof: In front of any PowerShell entity, do a Get-Member and observe the output. We will see both it’s properties and methods.

We can see the type of object as well.

"Something" | Get-Member

Run as administrator

Get-ServiceGet-Service-Name MongoDB | Stop-Service
(Get-Service-Name MongoDB).Start()

20. Views & Pipelines

Pipeline

Piping works virtually everywhere in Windows PowerShell. Although you see text on the screen, Windows PowerShell does not pipe text between commands. Instead, it pipes objects.

A pipeline is a series of commands connected by pipeline operators (|) (ASCII 124). Each pipeline operator sends the results of the preceding command to the next command.

You can use pipelines to send the objects that are output of one command to be used as input to another command for processing. And you can send the output of that command to yet another command. The result is a very powerful command chain or pipeline that is comprised of a series of simple commands.

Command 1 | Command 2 | Command 3

Example

Get-ChildItem-Path C:\Windows\ | Out-File-FilePath"OutputFile.txt"
Get-ProcessGet-Process | Select * # gives you everything, but do you need all this data?

21. Working with objects

# 1. Select-ObjectGet-Service | Select-Object-First5Get-Process | Select-Object-Last5Get-Process | Select-Object-First10-Property Name, BasePriority, CPU
Get-Process | Get-Member# To see all the available propertiesGet-Process | Select-Object-Property * # Get all the properties for each of the servicesGet-Process-Name"*a*" | Select-Object-First2# Get first 2 processes that start with have a# 2. Where-ObjectGet-Process | Where-Object { $_.CPU -ge400 } # Get those processes who's CPU utilization is higher than 400# Can have multiple Where-Object pipesGet-Service | Where-Object { $_.Name -like"*a*" } 
| Where-Object { $_.Status -eq"Stopped" } 
| Select-Object-First3# 3. Group-ObjectGet-Service | Group-Object-Property ServiceType

# 4. Sort-ObjectGet-Process | Sort-Object BasePriority | Group-Object-Property BasePriority
Get-Process | Sort-Object BasePriority | Group-Object-Property BasePriority | Select-Object-Last1Get-Process | Sort-Object BasePriority | Group-Object-Property BasePriority | Select-Object-Last1-ExpandPropertygroup# Find top 10 processes which are consuming highest CPUGet-Process | Sort-Object CPU -Descending | Select-Object-First10# Find top 10 processes which are consuming highest Physical MemoryGet-Process | Sort-Object PM -Descending | Select-Object-First10# 5. ForEach-Object# Performs an operation against each item in a collection of input objectsGet-Process | ForEach-Object {
  Write-Host"Processing: Doing something on "$_.Name
}

1..10 | ForEach-Object {
  Write-Output"Hello World"
}

Get-ChildItem-Path C:\Users\rst\Documents\Training\PowerShell -Recurse | ForEach-Object {
  Write-Output$_.FullName
}

# Delete all files older than 10 seconds from a directory# Do not delete subfolders$purge_dir = "C:\Users\rst\Documents\Training\PowerShell\scripts\testdir\"$rentention_secs = 10Get-ChildItem-File-Recurse$purge_dir 
| Where-Object { $_.LastAccessTime -lt (Get-Date).AddSeconds(-$rentention_secs) } 
| ForEach-Object { $_.FullName | Remove-Item-Force-WhatIf }

21. Formatting & Converting Objects

# Get all the results in the form of list. Selecting all properties by wildcard(*)Get-Service-Name"a*" | Format-List-Property *

# Selecting few properties by nameGet-Service-Name"a*" | Format-List-Property Name, Status, DisplayName

# Formatting TableGet-Service-Name"a*" | Format-Table-Property Name, Status
Get-Process-Name"*a*" | Format-Table Name, ProductVersion, Description -Wrap# Top 10 processes which are taking highest CPUGet-Process | Sort-Object cpu -Descending | Select-Object-First10 | Format-Table ProcessName, Id, CPU, WS, PM

Convert Objects

  • ConvertTo-Html
  • ConvertTo-Csv
  • ConvertTo-Json
  • ConvertTo-Xml
# ConvertTo-HtmlGet-Process | Sort-Object CPU -Descending | Select-Object-First10 | ConvertTo-Html-Property Name, CPU
Get-Service | ConvertTo-Html-Property Name, DisplayName, Status

Get-ChildItem-Path"C:\Users\rst"-Recurse | ConvertTo-Html-Property Name, FullName | Out-File"files_report.html"# Add CSS to HTML$head = @"
<style>
body: {font-family: sans-serif}
table: {margin: auto; border: thin ridge grey;}
</style>
"@$body = "<h1>System's Process Information</h1><h5>Updated: on $(Get-Date)</h5>"Get-Process | Select-Object-last20 | ConvertTo-Html-Property ProcessName, `
  Handles, NPM, WS, CPU, ID, SI-Head$head-Title"Report Title"-Body$body | 
Out-File"Process_Report.html"# ConvertTo-CsvGet-Service-Name MongoDB | ConvertTo-Csv-Delimiter","# ConvertTo-JsonGet-Service-Name"*z*" | ConvertTo-Json# ConvertTo-XML# This cmdlet is similar to Export-Clixml except that Export-Clixml stores# the resulting XML in a file.# ConvertTo-XML returns the XML, so you can continue to process it in# Windows PowerShell.$aa = Get-Process WindowsTerminal | ConvertTo-Xml$aa.FirstChild

Get-HelpFormat-Table-ShowWindowGet-HelpConvertFrom-Json-ShowWindowGet-HelpConvertTo-Csv-ShowWindowGet-HelpConvertTo-XML-ShowWindow

22. Class & Method

PowerShell Class

A class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods).

So inside a class we create variables and methods and then we can create Objects for the classes and these objects can be used just like the way we were using cmdlets.

class User {
  [string]$name = "NA"
  [int32]$age = 0
}

$user_obj = New-Object User
$user_obj$user_obj.GetType()
$user_obj | Get-Member$user_obj.age
$user_obj.name

$user_obj.age = 100$user_obj.name = "Jack Ma"$user_obj

Example

# Example 2class MathClass {
  [int32]$number1 = 0
  [int32]$number2 = 0# Method 1[int32]Addition() {
    return$this.number1 + $this.number2;
  }

  # Method 2[int32]Subtraction() {
    return$this.number1 - $this.number2;
  }

  # Method 3[String]ToString() {
    return"This class can be used to perform math operations on " + $this.number1 + " and " + $this.number2;
  }
}

$mathclass_obj = New-Object MathClass
# $mathclass_obj# $mathclass_obj | Get-Member$mathclass_obj.number1 = 10$mathclass_obj.number2 = 34$mathclass_obj$mathclass_obj.Addition()
$mathclass_obj.Subtraction()
$mathclass_obj.ToString()

# To get helpGet-Help about_Classes -ShowWindow

23. Save & Recreate Objects for Offline Analysis

Purpose

  • Remote troubleshooting
  • Offline analysis
  • It is a similar concept to serialization
  • Can be very effective medium when we need to do remote troubleshooting in a customer’s machine without having access to their machine. We can ask the customer to execute a PowerShell statement (which saves the required process or any other object as output) and sends the output. This output can then be used for analysis of the Customer’s system without accessing the system directlty.
# Export object as XMLGet-Process | Select-Object-Last4 | Export-Clixml'process_object.xml'# Convert the XML back to an ObjectImport-Clixml .\process_object.xml | Select-Object-Last2# If you want you can store it into a powershell variable$saved_processes = Import-Clixml .\process_object.xml | Select-Object-Last2

24. Sending Email

$SMTPServer = "smtpout.secureserver.net"$SMTPPort = "587"$Username = "<EMAIL_ADDRESS>"$Password = "--------------------"$to = "<EMAIL_ADDRESS>"$cc = "[email protected]"$subject = "Email automation using PowerShell"$body = @"
Hello User,
This is a system generated email. We just want to inform you about this.
Please do not reply to this email.

Best Regards,
Team PowerShell
Microsoft
"@$message = New-Object System.Net.Mail.MailMessage
# $message | Get-Member$message.Subject = $subject$message.Body = $body$message.To.Add($to)
$message.CC.Add($cc)
$message.From = "<EMAIL_ADDRESS>"# $message# Create an smtp object$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSsl = $true;
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($message)

26. Automation of daily reports

Write a PowerShell script to

  • Extract system’s performance data and send it to the performance engg team on a daily basis
  • Report should be in a well-defined format and it should get triggered at a fixed time
  • No human intervention in sending or formatting this report
#===============================================================================# System Performance Report#===============================================================================$SMTPServer = "smtpout.secureserver.net"$SMTPPort = "587"$Username = "<EMAIL_ADDRESS>"$Password = "----------------------"$to = "<EMAIL_ADDRESS>"$subject = "System Performance Report $(Get-Date)";

$services_to_monitor = @("MongoDB", "VSS", "WinRM", "TrustedInstaller", "Spooler", "msiserver");

$head = @"
<style>
body {font-family: sans-serif;}
h1,h5,th {text-align: center;}
table {margin: auto; font-family: sans-serif; border: thin ridge gray;}
th,td {border: 1px solid gray; padding: 7px 10px}
</style>
"@#===============================================================================$report = "system_performance.html";

# High CPU$body = @"
<h3>Summary of processes taking high CPU</h3>
<p>Updated on: $(Get-Date)</p>
"@Get-Process | Sort-Object CPU -Descending | Select-Object-First10 | ConvertTo-Html-Property ProcessName, Id, CPU, WS, PM `
  -Head$head-Body$body | Out-File$reportWrite-Output"<br /><br /><br /><br />" | Out-File$report-Append# High Physical Memory$body = @"
<h3>Summary of processes taking high Physical Memory</h3>
<p>Updated on: $(Get-Date)</p>
"@Get-Process | Sort-Object PM -Descending | Select-Object-First10 | ConvertTo-Html-Property ProcessName, Id, CPU, WS, PM `
  -Body$body | Out-File$report-AppendWrite-Output"<br /><br /><br /><br />" | Out-File$report-Append# Service Monitoring$body = @"
<h3>Services Status</h3>
<p>Updated on: $(Get-Date)</p>
"@Get-Service-name$services_to_monitor | ConvertTo-Html-Property Name, DisplayName, Status, ServiceType, CanPauseAndContinue `
  -Body$body | Out-File$report-Append#===============================================================================$message = New-Object System.Net.Mail.MailMessage
$message.Subject = $subject$message.To.Add($to)
$message.Body = Get-Content$report$message.IsBodyHtml = $true$message.From = "<EMAIL_ADDRESS>"$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
$smtp.EnableSsl = $true;
$smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
$smtp.Send($message)

27. Script Signing

Purpose

Advice PowerShell to execute only those scripts which are digitally signed by a trusted certificate authority.

Types of certificates

  1. Certificate from certificate authorities (Example: Verisign)
  2. Self signed certificate
Get-Help about_Signing -ShowWindow

Create a codesigning certicate

$params = @{
  Subject           = 'CN=PowerShell Code Signing Cert'Type              = 'CodeSigningCert'
  CertStoreLocation = 'Cert:\CurrentUser\My'
}
$cert = New-SelfSignedCertificate @params

Move-Item-Path$cert.PSPath -Destination"Cert:\CurrentUser\Root"$CodeCert = Get-ChildItem-Path"Cert:\CurrentUser\Root"-CodeSigningCert# $cert | Get-Member# $cert | Select-Object *$res = Set-AuthenticodeSignature-FilePath .\signing\unsigned.ps1 -Certificate$CodeCert$res.StatusMessage

28. Web Scraping

Invoke-WebRequest

Gets content from a web page on the internet

Description

The Invoke-WebRequest cmdlet sends HTTP, HTTPS, FTP and FILE requests to a web page or web service. It parses the response and returns collectins of forms, links, images and other significant HTML elements. This cmdlet was introduced in Windows PowerShell 3.0.

Example

$webrequest = Invoke-WebRequest-Uri'http://books.toscrape.com/'# Getting the status code$status_code = $webrequest.StatusCode
$status_desc = $webrequest.StatusDescription

Write-Output"Status code returned: $status_code"Write-Output"Status description: $status_desc"# Getting all links embeded in a web page$webrequest.Links
$webrequest.Links | Select-Object'href'$webrequest.Links | Select-Object'href' | Out-File'urls_found.txt'# Reading the content$webrequest.RawContent

######################################################   RUN THIS IN POWERSHELL 5   ####################################################### Reading table content from a web page $webrequest = Invoke-WebRequest-Uri"http://books.toscrape.com/catalogue/sapiens-a-brief-history-of-humankind_996/index.html"$webrequest.ParsedHtml.GetElementsByTagName('table')[0].outerHTML

$webrequest = Invoke-WebRequest-Uri"https://www.w3schools.com/html/html_forms.asp"$webrequest.Forms.fields

# Get more info$webrequest | Get-Member *
$webrequest.ParsedHtml | Get-Member# Getting information from a web page$HTML = Invoke-WebRequest-Uri"https://en.wikipedia.org/wiki/PowerShell"$tables = @($HTML.ParsedHtml.GetElementsByTagName("table"))
$tables[0].rows[8].GetElementsByTagName('div')[0].innerText.split('/')[0]

############################################### Download a file$url = "http://books.toscrape.com/media/cache/ce/5f/ce5f052c65cc963cf4422be096e915c9.jpg"## Method 1$web_client = New-Object System.Net.WebClient
$web_client.DownloadFile($url, "C:\Users\rst\Documents\Training\PowerShell\scripts\testimage.jpg")
## Method 2Invoke-WebRequest$url-OutFile"C:\Users\rst\Documents\Training\PowerShell\scripts\testimage2.jpg"# Download all images from a website$BASE_NAME = "http://books.toscrape.com/"$my_website = Invoke-WebRequest-Uri"http://books.toscrape.com/index.html"-UseBasicParsing$my_website.Images.src.ForEach({
    $filename = $_ | Split-Path-LeafWrite-Host"Download image file $filename"Invoke-WebRequest-Uri"$BASE_NAME$_"-OutFile"C:\Users\rst\Documents\Training\PowerShell\scripts\images\$filename"Write-Host"Image download complete"
  })

29. Invoke-Expression and Executing Web content as script

The Invoke-Expression cmdlet evaluates or runs a specified string as a command and returns the results of the expression or command. Without it, a string submitted at the command line would be returned (echoes) unchanged.

$Command = 'Get-Process | Where-Object {$_.CPU -gt 500}'Invoke-Expression$Command# Example 2$script = @'
Write-Host "===================================================="
$Celsius = [double](Read-Host "Enter temperature in Fahrenheit")

# Caluclating temperature in $Kelvin
$Kelvin = $Celsius + 273.15
Write-Output "$Celsius degree Celsius is equivalent to $Kelvin K"

Write-Host "FYI: Top 5 processes using maximum PM of your computer:" -BackgroundColor DarkBlue -ForegroundColor red
Get-Process | Sort-Object PM -Descending | Select-Object -First 5
'@Invoke-Expression$script

30. Automation: Daily reports using PowerShell-Database Interation

# ==============================================================================# CREATE AND FILL THE DATABASE# ==============================================================================Import-Module PSSQLite -Verbose# Create a database$Database = "system_info.db"$Conn = New-SQLiteConnection-DataSource$DatabaseInvoke-SqliteQuery-Query"DROP TABLE IF EXISTS DOMAIN_SYSTEM_INFORMATION"-SQLiteConnection$Conn# Create a database and a table$Query = "CREATE TABLE DOMAIN_SYSTEM_INFORMATION (id NUMBER PRIMARY KEY, ip_address VARCHAR(20), server_name VARCHAR(100), os_version VARCHAR(100), status VARCHAR(100))"Invoke-SqliteQuery-Query$Query-SQLiteConnection$Conn# # View table info# Invoke-SqliteQuery -SQLiteConnection $Conn -Query "PRAGMA table_info(DOMAIN_SYSTEM_INFORMATION)" | Out-GridView# Insert dataInvoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (1,'123.123.123.123','SEVER_NAME1','Windows Server 2008','Good')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (2,'321.321.123.123','SEVER_NAME2','Windows Server 2008','Ping Failure-Unable to connect')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (3,'123.123.123.123','SEVER_NAME3','Windows Server 2012','CPU utiliztion is high')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (4,'123.123.123.100','SEVER_NAME4','Windows Server 2008','Physical Memory Utilization is high')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (5,'321.321.123.101','SEVER_NAME5','Windows Server 2008','Good')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (6,'123.123.123.102','SEVER_NAME6','Windows Server 2012','Shutdown')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (7,'123.123.123.103','SEVER7','Windows Server 2008','Accidently Rebooted')"Invoke-SqliteQuery-SQLiteConnection$Conn-Query"INSERT INTO DOMAIN_SYSTEM_INFORMATION (id, ip_address, server_name, os_version, status) VALUES (8,'321.321.123.110','SEVER_NAME8','Windows Server 2008','Netowrk Latency Observed')"# # View thje data# Invoke-SqliteQuery -SQLiteConnection $Conn -Query "SELECT * FROM DOMAIN_SYSTEM_INFORMATION LIMIT 10"# ==============================================================================# VARIABLE SETUP# ==============================================================================$BASE_DIR = (Resolve-Path .\).Path
$LOG_FILE = $BASE_DIR + "\daily_server_status_report.log"# CSS Stylings$Head = @"
<style>
h1, h5, th { text-align: center; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
"@$Report = $BASE_DIR + "\daily_status.html"Write-Output"$(Get-Date) :INFO Staring the script Execution " | Out-File$LOG_FILE-Append-Force# ==============================================================================# PREPARING REPORT# ==============================================================================try {
  $Query = "SELECT * FROM DOMAIN_SYSTEM_INFORMATION"$DataTable = Invoke-SqliteQuery-SQLiteConnection$Conn-Query$QueryWrite-Output"$(Get-Date) :Executing queryString: $Query" | Out-File$LOG_FILE-Append-Force$Body = "<h3>Daily Summary</h3>`n<h3>Updated: on $(Get-Date)</h3>"$DataTable | ConvertTo-Html-Property ip_address, server_name, os_version, status -Head$head-Body$Body | Out-File$report
}
catch {
  $ErrorMessage = $_.Exception.Message
  Write-Output"$(Get-Date) :ERROR Something went Wrong ErrorMessage :  $ErrorMessage " | Out-File$LOG_FILE-Append-Force
}

Write-Output"$(Get-Date) :INFO Report Preparation Execution Over" | Out-File$LOG_FILE-Append-Force# ==============================================================================# SENDING EMAIL# ==============================================================================try {
  $SMTPServer = "smtpout.secureserver.net"$SMTPPort = "587"$Username = "<EMAIL_ADDRESS>"$Password = "<PASSWORD>"$To = "<EMAIL_ADDRESS>"$Subject = "Daily Status Report: $(Get-Date)";

  $Message = New-Object System.Net.Mail.MailMessage
  $Message.Subject = $Subject$Message.To.Add($To)
  $Message.Body = Get-Content$Report$Message.IsBodyHtml = $true$Message.From = "<EMAIL_ADDRESS>"$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
  $smtp.EnableSsl = $true;
  $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
  $smtp.Send($Message)
  
  Write-Output"$(Get-Date) :INFO Email Sent" | Out-File$LOG_FILE-Append-Force; 
}
catch {
  $ErrorMessage = $_.Exception.Message
  Write-Output"$(Get-Date) :ERROR Something went Wrong.  ErrorMessage :  $ErrorMessage " | Out-File$LOG_FILE-Append-Force; 
}
finally {
  Write-Output"$(Get-Date) : Script Execution Completed " | Out-File$LOG_FILE-Append-Force; 
}

31. Introduction to Windows Management Instrumentation (WMI)

WMI (Windows Management Instrumentation) is Microsoft’s implementation of the Web-Based Enterprise Management (WBEM) and Common Information Model (CIM) standards from the Distributed Management Task Force (DMTF).

WMI allows scripting languages (such as VBScript or Windows PowerShell) to manage Microsoft Windows personal computers and servers, both locally and remotely. WMI comes preinstalled in Windows 2000 and in newer Microosft OSes.

CIM

Common Information Model (CIM), a computer industry standard for defining device and application characteristics so that system administrators and management programs can control devices and applications from multiple manufacturers or sources in the same way.

WMI

WMI provides users with information about the status of local or remote computer systems.

It also supports such actions as the configuration of security settingsm settings and changing system properties, settings and changing permissions for authorized users and user groups, assigning and changing drive labels, scheduling processes to run at specific times, backing up the object repository, and enabling or disabling error logging.

Using WMI CIM We Can:

  • Connect to a chosen system and browse the CIM repository in any namespace available
  • Search for classes by their name, by the their descriptions or by property names
  • Review the properties, methods and associateions related to a given class
  • See the instance available for a given class of the examined system
  • Perform queries in the WQL language

32. Working with WMI

Get-WmiObject

Namespaces

There are several WMI namespaces created which are aligned to each major product, and depending on the namespace, hundred of classes can be created under each namespace.

Example:
root\directory\Idap
root\Microsoft\ComputerManagement12\instance_name
root\CCM\Schedules
root\cimv2
root\CCM\Events\

# Get a list of all the namespacesGet-WmiObject-Namespace"root"-Class"__Namespace" | Select Name

# To get all the classes from a namespaceGet-WmiObject-Namespace'root\cimv2'-List

(Get-WmiObject-Namespace'root\cimv2'-List).count

Get-WmiObject-Namespace'root\cimv2'-List | Out-GridView# Fetching all properties by class nameGet-WmiObject-Class'Win32_Service'# WMI on remote machinesGet-WmiObject-Class'Win32_Service'-ComputerName'localhost'# Using WQL# Fetching results by a QueryGet-WmiObject-Query"select * from Win32_Service"# Fetching results by a Query (remote)Get-WmiObject-ComputerName'localhost'-Query"select * from Win32_Service"

WQL (WMI Query Language)

Windows Management Instrumentation Query Language (WQL) is Microsoft’s implementation of the CIM Query Language (CQL), a query language for the Common Information Model (CIM) standard from the Distributed Management Task Force (DMTF).

It is a subset of ANSI standard SQL with minor semantic changes


Examples

# Get disk drivesGet-WmiObject-Class'Win32_LogicalDisk'-ComputerName'localhost'# Get different drives using WMIGet-WmiObject-Class'Win32_LogicalDisk'-ComputerName'localhost'-Filter'DriveType=3'# Doing the same as above but using WQL QueryGet-WmiObject-Query"select * from Win32_LogicalDisk"-ComputerName'localhost'Get-WmiObject-Query"select * from Win32_LogicalDisk where DriveType=3"-ComputerName'localhost'

Few important WMI classes

  • Win32_OperatingSystem
  • Win32_LogicalDisk
  • Win32_Service
  • Win32_Process
  • Win32_PhysicalMemory
  • Win32_ComputerSystem
# To get the information about operating systemGet-WmiObject-Query"select * from Win32_OperatingSystem"-ComputerName'localhost' | Out-GridView# To get the information about different processes running on a remote machineGet-WmiObject-Query"select * from Win32_Process"-ComputerName'localhost' | Out-GridView# To get information about computer systemGet-WmiObject-Query"select * from Win32_ComputerSystem"-ComputerName'localhost' | Out-GridView# To get information about running servicesGet-WmiObject-Query"select * from Win32_Service where State = 'Running'"-ComputerName'localhost' | Out-GridView

33. WMI Automation script

Requirement
Write a script to gather the disk space of multiple disk drives of multiple servers. Output should be sent as an email and disk should be categorized as:

  • GOOD: If % free space is above 20%
  • WARNING: if % free space is less than 20% but more than 10%
  • ERROR: if % free space is less than 10%

Script can be scheduled to send output in multiple ways:

  • Daily email containing disk space status of all the servers
  • Every hour and send status of only the servers which are in an error state

We should use a configuration file for feeding our preferences to the script

XML Configuration file (configuration.xml)

<DAILY_REPORT><MONITOR_SERVERS><SERVER>localhost</SERVER><SERVER>localhost</SERVER><SERVER>localhost</SERVER><SERVER>localhost</SERVER><!-- Any Extra row if required--></MONITOR_SERVERS><MONITOR_THRESHOLD ><!-- Since idea is to create an alarm if disk space is higher than a threshold value, --><!-- Threshold should be around 80-90 ideally, however it should be fine tuned as per your need --><!-- You can enhance your script logic and set different threshold for different type of drives --><DISKSPACE_ERROR>90</DISKSPACE_ERROR><DISKSPACE_WARNING>80</DISKSPACE_WARNING>
		</MONITOR_THRESHOLD >
		
		<EMAIL><FROM><EMAIL_ADDRESS></FROM><TO><EMAIL_ADDRESS></TO><SUBJECT>Disk Space Alert</SUBJECT><SMTP>smtpout.secureserver.net</SMTP><SMTP_USERNAME><EMAIL_ADDRESS></SMTP_USERNAME><SMTP_PASSWORD><PASSWORD></SMTP_PASSWORD></EMAIL></DAILY_REPORT>
# ==============================================================================# SETTING UP VARIABLES# ==============================================================================$BASE_DIR = (Resolve-Path .\).Path
$LOG_FILE = $BASE_DIR + "\daily_diskspace_status.log"# CSS Stylings$Head = @"
<style>
h1, h5, th { text-align: center; }
table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
"@$report = $BASE_DIR + "\daily_diskSpace_status.html"$xml_config = $BASE_DIR + "\configuration.xml"
[xml]$xml_content = Get-Content$xml_configWrite-Output"$(Get-Date) :INFO Staring the script Execution" | Out-File$LOG_FILE-Append-Force;  

$diskspace_error_threshold = [int32] $xml_content.DAILY_REPORT.MONITOR_THRESHOLD.DISKSPACE_ERROR
$diskspace_warning_threshold = [int32] $xml_content.DAILY_REPORT.MONITOR_THRESHOLD.DISKSPACE_WARNING

# ==============================================================================# PREPARING REPORT# ==============================================================================try {
  foreach ($entityin$xml_content.DAILY_REPORT.MONITOR_SERVERS ) { 
    $server = $entity.SERVER

    $body = "<h3>Daily Summary</h3>`n<h3>Updated: on $(Get-Date)</h3>"Get-WmiObject-Query"select * from Win32_LogicalDisk where drivetype=3"-ComputerName$server  |
    Select-Object SystemName, DeviceId, @{Name = "Size"; Expression = { [math]::Round($_.Size / 1GB, 2) } }`
      , @{Name = "FreeSpace"; Expression = { [math]::Round($_.FreeSpace / 1GB, 2) } } `
      , @{Name = "Occupied"; Expression = { [math]::Round(100 - ( [double]$_.FreeSpace / [double]$_.Size ) * 100) } } |
    Export-Csv disk_space.csv -NoTypeInformation
  }
   
  $csv_content = Import-CSV'disk_space.csv'$body = "<h3>Servers in Error Status</h3>`n<h3>Updated: on $(Get-Date)</h3>"$csv_content | Where-Object { $_.Occupied -ge$diskspace_error_threshold }  | ConvertTo-Html-property SystemName, DeviceId, Size, FreeSpace, Occupied `
    -Head$head-Body$body | Out-File$report$body = "<h3>Servers in Warning Status</h3>`n<h3>Updated: on $(Get-Date)</h3>"$csv_content | Where-Object { $_.Occupied -lt$diskspace_error_threshold-and$_.Occupied -gt$diskspace_warning_threshold }  |
  ConvertTo-Html-property SystemName, DeviceId, Size, FreeSpace, Occupied `
    -Body$body | Out-File$report-Append$body = "<h3>Servers in Good Status</h3>`n<h3>Updated: on $(Get-Date)</h3>"$csv_content | Where-Object { $_.Occupied -lt$diskspace_warning_threshold }  | ConvertTo-Html-property SystemName, DeviceId, Size, FreeSpace, Occupied `
    -Body$body | Out-File$report-Append
}
catch {
  $ErrorMessage = $_.Exception.Message
  Write-Output"$(Get-Date) :ERROR Something went Wrong ErrorMessage :  $ErrorMessage " | Out-File$LOG_FILE-Append-Force; 
}

Write-Output"$(Get-Date) :INFO Report Preparation Execution Over" | Out-File$LOG_FILE-Append-Force;

# ==============================================================================# SENDING EMAIL# ==============================================================================try {
  $SMTPServer = "smtpout.secureserver.net"$SMTPPort = "587"$Username = "<EMAIL_ADDRESS>"$Password = "<PASSWORD>"$To = "<EMAIL_ADDRESS>"$Subject = "Daily Status Report: $(Get-Date)";

  $Message = New-Object System.Net.Mail.MailMessage
  $Message.Subject = $Subject$Message.To.Add($To)
  $Message.Body = Get-Content$report$Message.IsBodyHtml = $true$Message.From = "<EMAIL_ADDRESS>"$smtp = New-Object System.Net.Mail.SmtpClient($SMTPServer, $SMTPPort);
  $smtp.EnableSsl = $true;
  $smtp.Credentials = New-Object System.Net.NetworkCredential($Username, $Password);
  $smtp.Send($Message)
  
  Write-Output"$(Get-Date) :INFO Email Sent" | Out-File$LOG_FILE-Append-Force; 
}
catch {
  $ErrorMessage = $_.Exception.Message
  Write-Output"$(Get-Date) :ERROR Something went Wrong.  ErrorMessage :  $ErrorMessage " | Out-File$LOG_FILE-Append-Force; 
}
finally {
  Write-Output"$(Get-Date) : Script Execution Completed " | Out-File$LOG_FILE-Append-Force; 
}

34. What is event viewer and how to use it

Event Viewer

Event Viewer is a component of Microsoft’s Windows NT line of operating systems that lets administrators and users view the event logs on a local or remote machine.

Event Viewer allows you to monitor events in your system. It maintains logs about programs, security, and system events on your computer. You can use Event Viewer to view and manage the event logs, gather information about hardware and software problems, and monitor security events.

To access Device Manager, on the Start menu, click Programs, point to Administration Tools, and then click on Event Viewer.

Event Logs

We can use the Event Viewer to view and manage the system, application, and security event logs.

System Log

The System log records events logged by the Windows system components.

For example, the failure of a driver or other system components to load during startup is recorded in the System log.

Application Log

The Application log records events logged by programs.

For example, a database program might record a file error in the Application log. Program developers decide which events to monitor.

Security Log

THe Security log records security events, such as valid and invalid logon attempts, and events related to resource use, such as creating, opening, or deleting files or other objects. The Security log helps track changes to the security system and identify any possible breaches to security. For example, attempts to log on the system might be recorded in the Security log, if logon and logoff auditing are enabled.

You can view the Security log only if you are an administrator for a computer.

35. Access Event Viewer logs using PowerShell

Get-EventLogGet-HelpGet-EventLog-Online# List different types of logs availableGet-EventLog-List# Get event logsGet-EventLog-LogName"Application"Get-EventLog-Newest5-LogName"Application"# Store the event log in a PowerShell Array$Events = Get-EventLog-LogName Application -Newest10$Events# and then use the array as per your need$Events | Group-Object-Property source -NoElement | Sort-Object-Property Count -Descending# Get only the `error` type messages from a particular type of logGet-EventLog-LogName"System"-EntryType Error -Newest10# Read specific events by filteringGet-EventLog-LogName"Application"-Source"Desktop Window Manager" | Where-Object { $_.eventID -eq9027 }

# Get all messages between a selected time range$endTime = Get-Date# present time# $endTime$startTime = $endTime.AddHours(-1) # last 1 hour (AddDays, AddMinutes)# $endTime# Get-EventLog -LogName "Application" -EntryType Information -After $startTime -before $endTime# Save the output to HTML/CSS and send in emailGet-EventLog-Log"Application"-EntryType Information -After$startTime-before$endTime | ConvertTo-Html Index, Source, Time, Message | Out-File"Out.html"# Read event logs of remote computersGet-EventLog-Newest2-LogName"Windows PowerShell"-ComputerName"localhost", "remoteMachine1", "remoteMachine2"Get-EventLog-Newest2-LogName"Windows PowerShell"-ComputerName'localhost', 'localhost', 'localhost'# DOESN'T WORK# Reading the event logs using WMIGet-WmiObject-class Win32_NTLogEvent

$Log_message = Get-WmiObject-class Win32_NTLogEvent -Filter"(logfile='Application') AND (type='error')" | Select-Object-First4$Log_message | Format-Table EventCode, EventType, Message -AutoSize

36. Write into event viewer logs

# Register source to an existing event log and writeNew-EventLog-LogName'Application'-Source'My Script'# RUN THE BELOW IN POWERSHELL ADMIN MODENew-EventLog-LogName'Application'-Source'My Script'# execute this firstWrite-EventLog-LogName'Application'-Source'My Script'-EntryType Error -EventId1-Message'This is a test message'

37. Advanced functions introduction

Advanced functions

Advanced functions allow you to write functions that can perform operations that are similar to the operations you can perform with cmdlets.

Advanced functions are helpful when you want to quickly write a function without having to write a compiled cmdlet using a Microsoft .NET Framework language.

These functions are also helpful when you want to restrict the functionality of a compiled cmdlet or when you want to write a function that is similar to a compiled cmdlet.

# Add-To Functionfunction Add-Numbers {
  param($num1, $num2)
  Write-Host ($num1 + $num2)
}

## Calling the function with argumentsAdd-Numbers1219Add-Numbers"Hello""World"# Typecasting to avoid concatenation and bringing unpredictibility# Add-To Functionfunction Add-Numbers {
  param([int]$num1, [int]$num2)
  Write-Host ($num1 + $num2)
}

## Calling the function with argumentsAdd-Numbers1219Add-Numbers"Hello""World"

CmdletBinding()

The cmdletbinding() attribute is an attribute of functions that makes them operate like compiled cmdlets that are written in C#, and it provides access to features of cmdlets.

Windows PowerShell binds the parameters of functions that have the CmdletBinding attribute in the same way that it binds the parameters of compiled cmdlets.

function Add-Numbers {
  [cmdletbinding()] # converts this function to an advanced functionparam([int]$num1, [int]$num2)
  Write-Verbose"Performing some really complex calculation"Write-Host ($num1 + $num2)
  Write-Debug"Calculation completed"
}

## Calling the function with argumentsAdd-Numbers1219-VerboseAdd-Numbers1121-DebugAdd-Numbers562-23-ErrorAction SilentlyContinue

Advanced function features

  • The ability to add [Parameter()] decorators to parameters
  • The ability to use Write-Verbose and Write-Debug in your script or function and have their output controlled by -Verbose and -Debug parameters of that script or function
  • Your script or function picks up the other common paramters, too, like -EV and -EA
  • The ability to have -whatif and -confirm added to your script or function

SupportsShouldProcess

The SupportsShouldProcess argument adds Confirm and WhatIf parameters to the function.

The Confirm parameter prompts the user before it runs the command on each object in the pipeline. The WhatIf parameter lists the changes that the command would make, instead of running the command.

The SupportsShouldProcess tells the shell that your function supports both -Confirm and -WhatIf. The way you actually implement that support is to write conditional code around whatever dangerous stuff your cmdlet is planning to do.

ShouldContinue

This method is called to request a second confirmation message. It should be called when the ShouldProcess method returns $true.

function Add-Numbers {
  [cmdletbinding(SupportsShouldProcess = $true)]param([int]$num1, [int]$num2)
  Write-Verbose"Performing some really complex calculation"Write-Host ($num1 + $num2)
  Write-Debug"Calculation completed"
}

Mandatory Parameters

function Add-Numbers {
  [cmdletbinding()]param(
    [parameter(Mandatory = $true)]
    [int]$num1, 
    [parameter(Mandatory = $true)]
    [int]$num2
  )
  Write-Verbose"Performing some really complex calculation"Write-Host ($num1 + $num2)
  Write-Debug"Calculation completed"
}

Add-Numbers10

Help Message

function Add-Numbers {
  [cmdletbinding()]param(
    [parameter(Mandatory = $true, HelpMessage = "Please pass the first number to add")]
    [int]$num1, 
    [parameter(Mandatory = $true, HelpMessage = "Please pass the second number to add")]
    [int]$num2
  )
  Write-Verbose"Performing some really complex calculation"Write-Host ($num1 + $num2)
  Write-Debug"Calculation completed"
}

Add-Numbers

WhatIf and Continue

function Delete-Something {
  [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "high")]Param ($param1)

  PROCESS {
    if ($PSCmdlet.ShouldProcess($param1)) {
      Write-Verbose"deleting the file"Remove-Item testfile.txt
    }
  }
}

Delete-Something"AdOrgUnit"
Delete-Something"AdOrgUnit"-WhatIf

Advanced functions snippet and blocks

Begin

This block is used to provide optional one-time preprocessing for the function. The Windows PowerShell runtime uses the code in this block one time for each instance of the function in the pipeline.

Process

This block is used to provide record-by-record processing for the function. This block might be used any number of times, or not at all, depending on the input to the function.

For example, if the function is the first command in the pipeline, the Process block will be used one time. If the function is not the first command in the pipeline, the Process block is used one time for every input that the function receives from the pipeline. If there is no pipeline input, the Process block is not used.

End

This block is used to provide optional one-time post-processing for the function.

function Hello-World {
  [CmdletBinding()]param (
    [Parameter(Mandatory = $false, ValueFromPipeline = $true)] # this function can accept values from pipeline$Param1
  )
  
  begin {
    "This is the Begin block $Param1"
  }
  
  process {
    "This is the Process block $Param1"
  }
  
  end {
    "This is the End block $Param1"
  }
}

# Hello-World1..10 | Hello-World# ==============================================================================# Example: Write an advanced function for getting the sum of whatever numbers# are passed to it via a pipefunction Add-All {
  [CmdletBinding()]param (
    [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
    [int32]$number
  )
  
  begin {
    Write-Host"Received the input to add the numbers"$total = 0
  }
  
  process {
    Write-Host"Adding $number to $total"$total = $total + $number
  }
  
  end {
    Write-Host"Total is: $total"
  }
}

1..20 | Add-All@(10, 100, 200) | Add-All

Advanced function example

Write a reusable advanced function such that it can be used for getting the status of one or more URLs.

Function should be written in such a way that debugging remains easy and function could be used for monitoring of multiple websites.

function Get-URLStatus {
  [CmdletBinding()]param (
    # Parameter help description
    [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $true, Position = 0)]
    $URI,
    [switch] $errorlog, # if passed then it is true, or else it will be false
    [String] $logfile = "C:\Users\rst\Documents\Training\PowerShell\scripts\test.log"
  )
  
  begin {
    if ($errorlog) {
      Write-Output"Checking the URL status" | Out-File$logfile-Append
    }
  }

  process {
    try {
      $webrequest = Invoke-WebRequest-Uri$URI# Getting the status code# 200 means no HTTP request error$status_code = $webrequest.StatusCode

      if ( $status_code-eq200) {
        if ($errorlog) { Write-Output"$URI :: $status_code :: GOOD" | Out-File$logfile-Append-Force; }
        Write-Host"$URI :: $status_code :: GOOD"-BackgroundColor Green -ForegroundColor  DarkBlue
      }
      else {
        if ($errorlog) { Write-Output"$URI ::  $status_code   :: GOOD" | Out-File$logfile-Append-Force; }
        Write-Host"$URI :: $status_code :: BAD"-BackgroundColor red -ForegroundColor Yellow
      }
    }
    catch {
      $ErrorMessage = $_.Exception.Message
	
      if ($errorlog) { 
        Write-Output"Something went wrong while checking status of URL : $URI" | out-file$logfile-Append-Force;
        Write-Output"#ERROR1a# ErrorMessage : $ErrorMessage" | out-file$logfile-Append-Force;
      }
    }
  }

  end {
    if ($errorlog) { Write-Output"Done with invoking web request for all URLs" | Out-File$logfile-Append }
  }
}

Get-URLStatus-URI'https://rstforum.net'-errorlogGet-URLStatus-URI'https://rstforum.net'-errorlog-logfile"new_name.log"# passing variable via a pipeline$arr_URIs = @("https://www.facebook.com/", "https://www.google.co.in", "http://mail.google.com");
$arr_URIs | Get-URLStatus-Verbose-errorlog