PowerShell Scripting Basics
Data Types
PS C:\Users\User> $myString.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Obj
Casting
PS C:\Users\User> $a = 123
PS C:\Users\User> $b = "456"
PS C:\Users\User> $b + $a
456123
PS C:\Users\User> [int]$b + $a
579
Special variables
$Error
contains an array of error objects.$Host
contains information about the current hosting application.$Profile
contains the path to the current user profile for PowerShell.$PID
contains the process ID of current PowerShell session.$PSUICulture
contains the UI culture or the regional language of the user interface.$NULL
contains the value of NULL.$False
contains the value of False.$True
contains the value of True.
Cmdlets
In the PowerShell interpreter, we can directly execute a cmdlet, which implements a particular operation set. They are named by a verb and a noun separated by a hyphen such as Get-Item and Get-ChildItem.
PS C:\Users\User> Get-Verb
Verb Group
---- -----
Add Common
Clear Common
Close Common
Copy Common
Enter Common
Exit Common
Find Common
Format Common
Get Common
Hide Common
...
Debug Diagnostic
Measure Diagnostic
Ping Diagnostic
Repair Diagnostic
Resolve Diagnostic
Test Diagnostic
Trace Diagnostic
Connect Communications
Disconnect Communications
Read Communications
Receive Communications
Send Communications
Write Communications
Block Security
Grant Security
Protect Security
Revoke Security
Unblock Security
Unprotect Security
Use Other
We can retreive a full list of commands currently available by running Get-Command
. This will return a long table of commands and may be overwhelming if we are trying to find a specific cmdlet.
...
Backup Data
Checkpoint Data
Compare Data
Compress Data
Convert Data
ConvertFrom Data
ConvertTo Data
Dismount Data
Edit Data
Expand Data
Export Data
Group Data
Import Data
Initialize Data
Limit Data
Merge Data
Mount Data
Out Data
Publish Data
Restore Data
Save Data
Sync Data
Unpublish Data
Update Data
...
We can filter the output of Get-Command by using the -Verb
flag and the name of a verb. For example, we might try Get-Command -Verb export
, Get-Command -Verb edit
, or Get-Command -Verb out
.
We can also use the -Noun
flag to filter the results of Get-Command to find what we are looking for. If we are looking to output to a file, our noun may be file. We might try the following.
PS C:\Users\User> Get-Command -noun file
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Out-File 3.1.0.0 Microsoft.PowerShell.Utility
Cmdlet Unblock-File 3.1.0.0 Microsoft.PowerShell.Utility
We can also combine the two:
PS C:\Users\User> Get-Command -Verb out,edit,export -Noun *file*
CommandType Name Version Source
----------- ---- ------- ------
Cmdlet Out-File 3.1.0.0 Microsoft.PowerShell.Utility
Use the Get-Help
cmdlet to determine what a cmdlet does and how to use it:
PS C:\Users\User> Get-Help Out-File
NAME
Out-File
SYNOPSIS
Sends output to a file.
SYNTAX
Out-File [-FilePath] <System.String> [[-Encoding] {ASCII | BigEndianUnicode | Default | OEM | String | Unicode | Unknown | UTF7 | UTF8 | UTF32}] [-Append] [-Force] [-InputObject <System.Management.Automation.PSObject>] [-NoClobber] [-NoNewline] [-Width
<System.Int32>] [-Confirm] [-WhatIf] [<CommonParameters>]
Out-File [[-Encoding] {ASCII | BigEndianUnicode | Default | OEM | String | Unicode | Unknown | UTF7 | UTF8 | UTF32}] [-Append] [-Force] [-InputObject <System.Management.Automation.PSObject>] -LiteralPath <System.String> [-NoClobber] [-NoNewline] [-Width
<System.Int32>] [-Confirm] [-WhatIf] [<CommonParameters>]
DESCRIPTION
The `Out-File` cmdlet sends output to a file. It implicitly uses PowerShell's formatting system to write to the file. The file receives the same display representation as the terminal. This means that the output may not be ideal for programmatic processing unless all
input objects are strings. When you need to specify parameters for the output, use `Out-File` rather than the redirection operator (`>`). For more information about redirection, see about_Redirection (../Microsoft.PowerShell.Core/About/about_Redirection.md).
...
Providers
A provider in PowerShell is a .NET program to access data stores in a standardized manner. Providers make the data easier to access as it's presented in a consistent way that is similar to a file system drive. Providers are typically included in modules and are accessible after the module has been loaded into the current session. We can view a list of current providers by running Get-PSProvider
.
PS C:\Users\User> Get-PSProvider
Name Capabilities Drives
---- ------------ ------
Registry ShouldProcess, Transactions {HKLM, HKCU}
Alias ShouldProcess {Alias}
Environment ShouldProcess {Env}
FileSystem Filter, ShouldProcess, Credentials {C, A, D}
Function ShouldProcess {Function}
Variable ShouldProcess {Variable}
The above output contains the provider name, capabilities, and drives as mentioned before. We check what type of data these drives contain by using the Get-Item cmdlet. Let's check out the alias drive by running Get-Item alias..
PS C:\Users\User> Get-Item alias:
CommandType Name Version Source
----------- ---- ------- ------
Alias foreach -> ForEach-Object
Alias % -> ForEach-Object
Alias where -> Where-Object
Alias ? -> Where-Object
Alias ac -> Add-Content
Alias clc -> Clear-Content
Alias cli -> Clear-Item
Alias clp -> Clear-ItemProperty
Alias clv -> Clear-Variable
Alias compare -> Compare-Object
Alias cpi -> Copy-Item
Alias cpp -> Copy-ItemProperty
Alias cvpa -> Convert-Path
Alias dbp -> Disable-PSBreakpoint
Alias diff -> Compare-Object
...
Let's try working with the registry provider. Before we start, we need to open a PowerShell window with Administrator privileges. Open the Windows start menu, type "powershell", right-click on the "Windows Powershell" entry, and click Run As Administrator. A new PowerShell window will open with Administrator privileges so we will able to work with the registry.
PS C:\Windows\system32> cd HKLM:
PS HKLM:\>
We can now run Get-ChildItem to list the contents of HKLM. We will see some errors, but this is expected.
PS HKLM:\> Get-ChildItem
Hive: HKEY_LOCAL_MACHINE
Name Property
---- --------
BCD00000000
COMPONENTS StoreFormatVersion : {48, 0, 46, 0...}
StoreArchitecture : {9, 0, 0, 0}
HARDWARE
SAM
Schema
Get-ChildItem : Requested registry access is not allowed.
At line:1 char:1
+ Get-ChildItem
+ ~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (HKEY_LOCAL_MACHINE\SECURITY:String) [Get-ChildItem], SecurityException
+ FullyQualifiedErrorId : System.Security.SecurityException,Microsoft.PowerShell.Commands.GetChildItemCommand
SOFTWARE
SYSTEM
The errors we received are due to the Security Account Manager (SAM) section of the registry only being accessible by a user with System access. Because of this, when we attempt to list the items under HKLM, we are unable to list information about the SAM section of the registry and receive "access is not allowed" errors even if we are running PowerShell as an Administrator. We can ignore this error as we don't need access to the SAM at this time. In addition to SAM, we find entries for Hardware, Software, and System.
Let's move into the software directory and run ls
to list items.
PS HKLM:\> cd software
PS HKLM:\software> ls
Hive: HKEY_LOCAL_MACHINE\software
Name Property
---- --------
Classes
Clients
CVSM CVSM : {APPID, 0x1, Enabled, 0x0...}
DefaultUserEnvironment Path : C:\Users\User\AppData\Local\Microsoft\WindowsApps;
TEMP : C:\Users\User\AppData\Local\Temp
TMP : C:\Users\User\AppData\Local\Temp
Google
Intel
Microsoft
ODBC
OEM
OpenSSH
Partner
Policies
RegisteredApplications File Explorer : SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Capabilities
Windows Address Book : Software\Clients\Contacts\Address Book\Capabilities
Windows Disc Image Burner : Software\Microsoft\IsoBurn\Capabilities
Windows Search : Software\Microsoft\Windows Search\Capabilities
Internet Explorer : SOFTWARE\Microsoft\Internet Explorer\Capabilities
Paint : SOFTWARE\Microsoft\Windows\CurrentVersion\Applets\Paint\Capabilities
Notepad : Software\Microsoft\Windows\Notepad\Capabilities
Wordpad : Software\Microsoft\Windows\CurrentVersion\Applets\Wordpad\Capabilities
Windows Media Player : Software\Clients\Media\Windows Media Player\Capabilities
Windows Photo Viewer : Software\Microsoft\Windows Photo Viewer\Capabilities
Microsoft Edge : Software\Clients\StartMenuInternet\Microsoft Edge\Capabilities
VMware Host Open : SOFTWARE\VMware, Inc.\VMwareHostOpen\Capabilities
VMware, Inc.
Windows
WOW6432Node
Depending on the provider, we may be able to view, add, modify, and remove items. These actions are achieved using a variety of cmdlets such as Get-Item, New-Item, Set-Item, and Remove-Item. Let's test this out by creating a new registry key of "myKey" with a property of "Test" set to a value of 1. First, we will need to add the key with the New-Item cmdlet followed by the key name "myKey".
PS HKLM:\SOFTWARE\> New-Item myKey
Hive: HKEY_LOCAL_MACHINE\SOFTWARE
Name Property
---- --------
myKey
Next, let's add a property using the New-ItemProperty cmdlet. We will need to specify the key we just made (myKey) after the -Path argument, then the name of our new property after the -Name argument. Let's set the type of the property to DWORD with the -Type argument, and finally a -Value argument set to 1.
PS HKLM:\SOFTWARE\> New-ItemProperty -Path .\myKey\ -Name Test -Type DWORD -Value 1
Test : 1
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\myKey\
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE
PSChildName : myKey
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
Now that we have a new registry key and property set, let's verify using Get-Item followed by our key name of "myKey". This will show us the key we set and the property we assigned it.
PS HKLM:\SOFTWARE\> Get-Item .\myKey\
Hive: HKEY_LOCAL_MACHINE\SOFTWARE
Name Property
---- --------
myKey Test : 1
Now that we have successfully set a new registry key and value, let's remove it. We can use the Remove-Item cmdlet and enter Remove-Item .\myKey\ on the command line. This cmdlet doesn't print any response to the screen if it's successful. We have now accessed a PowerShell provider, added a new item, set its property, and removed it.
Modules
A module in PowerShell is package that contain additional cmdlets, functions, providers, and more. It can be imported into the current PowerShell session giving access to new functionality. A list of currently loaded modules can found by running the Get-Module cmdlet.
PS C:\Users\User> Get-Module
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 3.1.0.0 Microsoft.PowerShell.Management {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Con...
Manifest 3.1.0.0 Microsoft.PowerShell.Utility {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Script 2.0.0 PSReadline {Get-PSReadLineKeyHandler, Get-PSReadLineOption, Remove-PS...
By default, we are presented with the module type, version, name, and exported commands.
Additionally, we can list all available modules installed on the system not loaded into our current session by adding the -ListAvailable flag to Get-Module. This will check the PsModulePath environment variable and list the entries separated by their install location on the local system.
PS C:\Users\User> Get-Module -ListAvailable
Directory: C:\Program Files\WindowsPowerShell\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 1.0.1 Microsoft.PowerShell.Operation.V... {Get-OperationValidation, Invoke-OperationValidation}
Binary 1.0.0.1 PackageManagement {Find-Package, Get-Package, Get-PackageProvider, Get-Packa...
Script 3.4.0 Pester {Describe, Context, It, Should...}
Script 1.0.0.1 PowerShellGet {Install-Module, Find-Module, Save-Module, Update-Module...}
Script 2.0.0 PSReadline {Get-PSReadLineKeyHandler, Set-PSReadLineKeyHandler, Remov...
Directory: C:\Windows\system32\WindowsPowerShell\v1.0\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Manifest 1.0.0.0 AppBackgroundTask {Disable-AppBackgroundTaskDiagnosticLog, Enable-AppBackgro...
Manifest 2.0.0.0 AppLocker {Get-AppLockerFileInformation, Get-AppLockerPolicy, New-Ap...
Manifest 1.0.0.0 AppvClient {Add-AppvClientConnectionGroup, Add-AppvClientPackage, Add...
Manifest 2.0.1.0 Appx {Add-AppxPackage, Get-AppxPackage, Get-AppxPackageManifest...
Script 1.0.0.0 AssignedAccess {Clear-AssignedAccess, Get-AssignedAccess, Set-AssignedAcc...
...
Now that we know how to find loaded and unloaded modules, we need to find out what a module can do. To find a list of commands available in a specific module, we will need to use the Get-Command cmdlet. Running this cmdlet alone will print out all commands available in the current session, which can be over 1500 entries including cmdlets, functions, and aliases.
PS C:\Users\User> Get-Command
CommandType Name Version Source
----------- ---- ------- ------
Alias Add-AppPackage 2.0.1.0 Appx
Alias Add-AppPackageVolume 2.0.1.0 Appx
Alias Add-AppProvisionedPackage 3.0 Dism
Alias Add-ProvisionedAppPackage 3.0 Dism
Alias Add-ProvisionedAppxPackage 3.0 Dism
...
Function A:
Function Add-BCDataCacheExtension 1.0.0.0 BranchCache
Function Add-BitLockerKeyProtector 1.0.0.0 BitLocker
Function Add-DnsClientNrptRule 1.0.0.0 DnsClient
Function Add-DtcClusterTMMapping 1.0.0.0 MsDtc
...
Cmdlet Add-AppvClientConnectionGroup 1.0.0.0 AppvClient
Cmdlet Add-AppvClientPackage 1.0.0.0 AppvClient
Cmdlet Add-AppvPublishingServer 1.0.0.0 AppvClient
Cmdlet Add-AppxPackage 2.0.1.0 Appx
Cmdlet Add-AppxProvisionedPackage 3.0 Dism
To list just the commands in a specific module, we will need to append the -Module flag followed by the module name we want to list the commands of. The module does not need to be loaded in order to list the commands it provides. Let's take a closer look at the Defender module.
PS C:\Users\User> Get-Command -Module Defender
CommandType Name Version Source
----------- ---- ------- ------
Function Add-MpPreference 1.0 defender
Function Get-MpComputerStatus 1.0 defender
Function Get-MpPreference 1.0 defender
Function Get-MpThreat 1.0 defender
Function Get-MpThreatCatalog 1.0 defender
Function Get-MpThreatDetection 1.0 defender
Function Remove-MpPreference 1.0 defender
Function Remove-MpThreat 1.0 defender
Function Set-MpPreference 1.0 defender
Function Start-MpScan 1.0 defender
Function Start-MpWDOScan 1.0 defender
Function Update-MpSignature 1.0 defender
Based off of the names, we can take a guess of what each one does. However, in order to properly use these commands we will need more information on their usage and required arguments. Each command should have a help file associated with it that we can read to get more information (similar to a Bash man page). To do this, we can use the Get-Help cmdlet followed by the command name. Let's inspect Start-MpScan by running Get-Help Start-MpScan. Doing this will automatically load the module into the current session.
PS C:\Users\User> Get-Help Start-MpScan
NAME
Start-MpScan
SYNOPSIS
Starts a scan on a computer.
SYNTAX
Start-MpScan [-CimSession <CimSession[]>] [-ScanPath <String>] [-ScanType {QuickScan | FullScan | CustomScan}] [-ThrottleLimit <Int32>] [<CommonParameters>]
DESCRIPTION
The Start-MpScan cmdlet starts a scan on a computer. The cmdlet performs scans for the path you specify.
RELATED LINKS
Online Version: http://go.microsoft.com/fwlink/?LinkId=317472
REMARKS
To see the examples, type: "get-help Start-MpScan -examples".
For more information, type: "get-help Start-MpScan -detailed".
For technical information, type: "get-help Start-MpScan -full".
For online help, type: "get-help Start-MpScan -online"
Note that the last line in this listing indicates that we can run the same command with the -Online flag. This will open the default browser to the relevant help page, which usually contains more information and details on how to use the command.
From the output of the Get-Help_command for Start-MpScan, we can find out what it does and how to run it. Let's test it out by running a quick scan on our user directory from PowerShell. We will need to specify the directory we want to scan by using the -ScanPath flag followed by the path C:\Users<username>. Let's also specify that we want a quick scan by using the -ScanType flag.
PS C:\Users\User> Start-MpScan -ScanPath 'C:\\Users\\User\\' -ScanType QuickScan
Running this scan can take a few minutes. If nothing malicious was found during the scan, there isn't any output.
Scripts
Scripting in PowerShell is usually done by crafting a .ps1 file. On a Windows system, double-click script execution is disabled by default to protect users and the system. PowerShell maintains an execution policy that determines which type of PowerShell scripts (if any) can be run on the system. The default policy is Restricted for Windows clients, while it is RemoteSigned for Windows servers.
Restricted policy means the system will neither load PowerShell configuration files nor run PowerShell scripts. RemoteSigned policy allows the execution of PowerShell scripts only if they meet some conditions like if the script was written on the local system and not downloaded from the Internet, or the script contains a digital signature from a trusted publisher.
We can use the Get-ExecutionPolicy cmdlet to check what policy currently applies to us.
PS C:\Users\User> Get-ExecutionPolicy
Restricted
To be able to execute our scripts, we will need to set the RemoteSigned execution policy, for example.
Execution policies for the local computer and current user are stored in the registry. In order to make a change in the registry, we need administrator privileges. To set the policy from a PowerShell command prompt, we click the Windows Start button, right-click the Windows PowerShell application, and select Run as Administrator. When presented with a User Account Control prompt, we select Yes, and enter Set-ExecutionPolicy RemoteSigned.
Note that the execution policy for a particular session is stored only in memory and is lost when the session is closed. To set the policy for only a session we can start the PowerShell command line with -ExecutionPolicy switch to pass the desired policy. It's common to set the policy to "bypass" temporarily using this method. Another way is to use the Set-ExecutionPolicy cmdlet and pass the -Scope Process argument along with the desired policy name.
Let's create a simple PowerShell script and learn how to execute it when the execution policy is set to restrictive. First, we will need to create a new file with our favorite text editor (Notepad is fine). We'll enter the PowerShell commands below into our file and save it as computerInfo.ps1.
Get-ComputerInfo | Out-File .\computerInfoOut.txt
Now that our test script is saved, let's try to execute it.
In our PowerShell console, we'll attempt to execute the script by calling the full path to the file. In this case, it's on the Desktop of the account "User", and the full path of the file is C:\Users\User\Desktop\computerInfo.ps1.
PS C:\Users\User> C:\Users\User\Desktop\computerInfo.ps1
C:\Users\User\Desktop\computerInfo.ps1 : File C:\Users\User\Desktop\computerInfo.ps1 cannot be loaded
because running scripts is disabled on this system. For more information, see about_Execution_Policies at
https:/go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ C:\Users\User\Desktop\computerInfo.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
The error message lets us know that the script did not run due to the current execution policy. We can change the execution policy for our current PowerShell session using one of the methods we just covered, but let's examine a faster method.
From PowerShell, we can spawn a new PowerShell session with the execution policy we want that just runs our script and exits back to our original session. To do this, we call powershell.exe followed by the -exec argument followed by the desired policy and then the path to our script. This is a temporary PowerShell session so let's use a policy of Bypass.
PS C:\Users\vandelay> powershell.exe -exec bypass C:\Users\vandelay\Desktop\computerInfo.ps1
Running this should succeed and will result in a file being created named computerInfoOut.txt in the current directory of your PowerShell session. We can verify this by running type computerInfoOut.txr, which will output the contents of the file to the console. With that done, we have successfully created and executed a PowerShell script.
Relevant Note(s): Windows Basics