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 |
---|---|
pwd | Get-Location |
cd | Set-Location |
%date%%time% | Get-Date |
ls / dir | Get-ChildItem |
echo | Write-Output |
cls/clear | Clear-Host |
cp | Copy-Item |
mv | Move-Item |
ren | Rename-Item |
del/rm | Remove-Item |
man/help | Get-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
A 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
- 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()
- 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?
Task | Uncertainties (Possibilies of exceptions) |
---|---|
Reading/Writing File Content | File got deleted, network dependency, file locked, insufficient access |
Database Operation | Database table not existing, DB maintenance in progress, Database down, network dependency, insufficient access, data duplication not allowed |
Sending Daily Report | SMTP Server Issue, Report file locked |
Version | Script 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.
A 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.
A
service
is aprocess
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
- Certificate from certificate authorities (Example: Verisign)
- 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
andWrite-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