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

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