<< A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

–A–

Active Directory Users and Computers (ADUC) - see RSAT

assoc command – associate a file extension with a command– see also ftype command

the assoc command doesn't work directly from within PowerShell. Instead, prefix with cmd. So, for example with PHP:

cmd /c assoc .php=phpfile

followed with

cmd /c ftype phpfile="C:\php8_2\php.exe" -f "%1" -- %~2

you can also put in the following to your profile

function assoc {cmd /c assoc $args}
function ftype {cmd /c ftype $args}

audio devices

Install-Module -Name AudioDeviceCmdlets

show defaults

Get-AudioDevice -List | ? {$_.DefaultCommunication -eq $true} | select ID, Type, Name

usage

–B–

background color, get

(Get-Host ).UI.RawUI.BackgroundColor

background color, set

$a = (Get-Host).UI.RawUI

$a.BackgroundColor = "DarkGreen"

There aren’t too many choices

[Enum]::GetValues([ConsoleColor])

only lists 16 color choices

–C–

c drive on remote computer – see directory, on remote computer

certs, list

Get-ChildItem -Recurse Cert: | ft

command, run remotely - see remote command

compress a file - see zip a file

computer name see also environment variables

$env:computername

with fully qualified domain name

[System.Net.Dns]::GetHostByName($env:computerName)

remote computer directory – see directory, on remote computer

computer remote session, - see remote session, initiate

computer serial number - see PC, get serial number

copy files from one directory to another

$destination = "D:\xml\"
$location = "D:\Client Portal\"
function processCopy
{
  $files = Get-ChildItem -Path:$location
  foreach($file in $files)
   {
   $path = $file.FullName
  if($file.Name -ne "copy.ps1"){
   Copy-Item -Path:$path -Destination:$destination -Force -Confirm:$false
   Write-Host "Item" $file.Name " has been sucessfully copied to" $destination
   }
   if($file.Attributes -eq "Directory")
   {
    copyFile $file $destination
   }
  }
}

function copyFile($directory, $target)
{
  $target += $directory.Name
  $files = Get-ChildItem -Path:$directory.FullName
  foreach($file in $files)
  {
  $path = $file.FullName
  if($file.Name -ne "copy.ps1"){
  Copy-Item -Path:$path -Destination:$target -Force -Confirm:$false
  Write-Host "Item" $file.Name " has been sucessfully copied to" $target
  }

  if($file.Attributes -eq "Directory")

  copyFile $file $target
  }
  }

function Pause ($Message="Press any key to continue...")
{
  Write-Host -NoNewLine $Message
  $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  Write-Host ""
}
#main
processCopy
Pause

Count how many files in a directory, excluding "bin" and "obj" files & directories

(gci "D:\VisualStudio\GID_CRM" -exclude bin, obj -Recurse).Count

CPU Utilization

top 20 threads (filtering out totals) shows

gwmi Win32_PerfFormattedData_PerfProc_Thread |
    ?{$_.Name -notmatch '_Total'} |
    sort PercentProcessorTime -desc |
    select -first 20 |
    ft -auto Name,IDProcess,IDThread,PercentProcessorTime

–D–

date of files, determine

ls d:\filemaker -rec | foreach {$_.creationtime -lt "02/25/2013"}

date of most recent file in a directory

$latest = Get-ChildItem -Path $backedUpDirectoryPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$latest.name
$latest.LastWriteTime

directory, on remote computer

C drive on remote computer

dir \\remote-server\C$

directory, loop through

$sourceDirectory= "D:\somedirectory"
ls $sourceDirectory -rec | foreach {
  $filename = $_.Name
  $length = $_.length
  $creationtime = $_.creationtime
  $DirectoryName = $_.Directory.Name
  Write-Host $i $filename ", " $DirectoryName ", " $length ", " $creationtime
  $i++
}

delete file

Remove-Item $a[$i] -recurse -force

directory, delete certain subdirectories and everything underneath them

gci $workingDirectory -include bin, obj, Upload -Recurse -Force | Remove-Item -Recurse -Force

In Powershell 3.0, it is simpler:

dir -Directory #List only directories
dir -File #List only files

Or to limit to just 2 directories deep:

dir -Directory .\*\*

In PowerShell 2, to just get directories (with grouping):

Get-ChildItem -Recurse | ?{ $_.PSIsContainer }

Or to limit to just 2 directories deep:

Get-ChildItem .\*\* | ?{ $_.PSIsContainer }

To get a raw list of directories, without the grouping:

Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName

directory size

(gci -Force "C:\Users\gollum\myPrecious\odes" -Recurse -ErrorAction SilentlyContinue | measure Length -s).sum

or in Gb

(gci -Force "C:\Users\gollum\myPrecious\odes" -Recurse -ErrorAction SilentlyContinue | measure Length -s).sum 1Gb

disk space, free

get-psdrive c | % {$_.free/($_.used + $_.free)} | % tostring p

disk space consumed by a bunch of similarly-named files, percentage of disk space

How much space are our SQL server error logs consuming on the F drive?

$errorFileLogLocation = "F:\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Log"
"{0:P}" -f ((gci $errorFileLogLocation Errorlog* | measure Length -s).sum / (Get-PSDrive F | % {($_.used + $_.free)}))

disk drives, status of all – including free disk space

$props = @(
    'DriveLetter'
    'FileSystemLabel'
    'FileSystem'
    'DriveType'
    'HealthStatus'
    'OperationalStatus'
    @{Name = 'SizeRemaining'
      Expression = {"{0:N3} Gb" -f ($_.SizeRemaining/ 1Gb)}}
    @{Name = 'Size'
      Expression = {"{0:N3} Gb" -f ($_.Size / 1Gb)}}
    @{Name = '% Free'
      Expression = {"{0:P}" -f ($_.SizeRemaining / $_.Size)}}
)
Get-Volume -DriveLetter C | Select-Object $props | ogv

–E–

email

$smtpFrom = "fromEmail@yourDomain"

$smtpTo ="toEmail@yourDomain"

$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpTo

$message.Subject = $messageSubject

$message.IsBodyHTML = $true

$message.Body = $body

$smtpServer = "yourTenant-com01ec.mail.protection.outlook.com"

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$ credentials =new-object system.net.networkcredential("whosSending@yourDomain","TopSecret")

$smtp.credentials=$credentials.getCredential($smtpServer,"25","basic")

$smtp.Send($message)

This does not work all the time

send-mailmessage -to "user@mydomain.com" `
  -from "user@mydomain.com" `
  -subject "sample subject line" `
  -smtpServer mymailserver.mydomain.com `
  -body 'test body'

not only does it not work, putting in a "try-catch" block doesn't give you an error; it errors out right away when you first try to run.

event log -

Get-WinEvent -FilterHashtable @{logname = "Application"; starttime = "11/14/23 07:40 am"; endtime = "11/14/23 07:55 am"} | ? {$_.LevelDisplayName -eq "Error" -or $_.LevelDisplayName -eq "Warning"}

or (deprecated, may fail because not recognized)

Get-EventLog -LogName "Application" | Select TimeWritten, Message, EventID | Where-Object -FilterScript {$_.TimeWritten -ge "11/14/23 07:40 am" -and $_.TimeWritten -le "11/14/23 07:55 am"}

event ID

this retrieves security event logs with event IDs 2886,2887,2888,2889,3039 and 3040 that occurred within the last day from a single server

Get-EventLog -LogName "Directory Service" 2886,2887,2888,2889,3039,3040 -After ((Get-Date).AddDays(-1))

If you get

Get-EventLog: The term 'Get-EventLog' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

This likely means that Get-Eventlog is deprecated. Try this instead (this gets recent schannel event IDs):

$StartTime = (Get-Date).AddMinutes(-5)
$EndTime = Get-Date
$Filter = @{
    LogName = "Directory Service"
    ID = @(36870, 36871, 36872, 36874, 36888)
    StartTime = $StartTime
    EndTime = $EndTime
}
try {
    Get-WinEvent -FilterHashtable $Filter -ErrorAction Stop
} catch {
    Write-Host "Error: $_"

By IP:

Get-WinEvent -FilterHashtable @{LogName="Directory Service"; ProviderName="Microsoft-Windows-ActiveDirectory_DomainService"; Data="123.45.67.89"}

event ID from several servers

runs same query on many servers (domain controllers in this case) to return an object containing rows from many domain controllers with several fields

function Get2889OneDC ($startTime, $endTime) {
    $Filter = @{
        LogName = "Directory Service"
        ID = @(2889)
        StartTime = $startTime
        EndTime = $endTime
    }
    $XML2889 = Get-WinEvent -FilterHashtable $Filter -ErrorAction Stop | % {$_.ToXml()}
    $LDAP2889 = @()
    foreach ($xml in $XML2889) {
        $xml = [xml]$xml
        # user
        $SID = $xml.Event.System.Security.UserID
        $objSID = New-Object System.Security.Principal.SecurityIdentifier $SID
        $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
        $user = $objUser.Value
        # time
        $systemTime = $xml.Event.System.TimeCreated.SystemTime
        $time = (Get-Date $systemTime).ToString("yyyy-MM-dd h:mm:ss tt")
        $LDAP2889 += New-Object PSObject -property @{
            # dc = $xml.Event.System.Computer
            user = $user
            IP = $xml.Event.EventData.Data[0]
            computer = $xml.Event.EventData.Data[1]
            TimeCreated = $time}
    }
    $LDAP2889 = $LDAP2889 | select computer, IP, user, TimeCreated #, dc, EventID
    return $LDAP2889
}

$DCs = @("DC1", "DC2")
$now = Get-Date
$endTime = $now.AddMinutes(-($now.Minute % 15)).AddSeconds(-$now.Second)
$startTime = $endTime.AddMinutes(-15)
$s = New-PSSession -ComputerName $DCs -ErrorAction Stop
$LDAP2889 = Invoke-Command -session $s -ScriptBlock ${function:Get2889OneDC} -ArgumentList $startTime, $endTime -ErrorAction Stop
Get-PSSession | Remove-PSSession
if ($LDAP2889) { # When any DC has no records that match, PowerShell helpfully puts in 5 empty, worthless rows, which we exclude by test whether IP field.
    # It also adds on a couple useless fields: RunspaceId, PSShowComputerName and one useful field - PSComputerName.
    # keep PSComputerName but do not select the extra RunspaceId, PSShowComputerName fields
    $LDAP2889 | ? {$_.IP } | select computer,IP,user,TimeCreated,@{n="DC";e={$_.PSComputerName}} | Export-CSV -Path "C:\reports\Event2889.csv" -NoTypeInformation -Encoding UTF8 -Append
}

–F–

folder on remote computer – see directory, on remote computer

fonts, install

$path = "directoryContainingFontFiles"
$shell = New-Object -ComObject Shell.Application
$shell.Namespace($path).Items().InvokeVerbEx("Install")

Or, if you want to check to see if the target PC already has it:

$sourcPath = "\\genetic-id.com\share\Dept_Shares\IT\Museo Sans Rounded\OpenType CFF Std"
$destinationPath = "c:\windows\fonts\"
$fontFileSource = Get-ChildItem $sourcPath | Select Name
# Check for presence of each file.  If any ONE of the 6 files are missing, install ALL of them.
foreach ($file in $fontFileSource)
{
    If (Test-Path "$($destinationPath)$($File.name)")
    {
        #"font $($File.name) found, no action necessary"
    }
    else
    {
        #"font $($File.name) not found, need to add"
        $shell = New-Object -ComObject Shell.Application
        $shell.Namespace($sourcPath).Items().InvokeVerbEx("Install")
        break
    }
}

If you want to deploy these fonts across your organization, then invoke this from Group Policy.

ftype command – associate a file extension with a command – see also assoc command

the assoc command doesn't work directly from within PowerShell. Instead, prefix with cmd. So, for example with PHP:

cmd /c ftype phpfile="C:\php8_2\php.exe" -f "%1" -- %~2

–G–

–H–

–I–

installed programs

The only reason I include the PSPath - which is quite lengthy & often useless - is because for some entries just about all the other fields are empty. I think these are mainly OS update entries.

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName,InstallLocation,DisplayVersion,PSPath,Publisher,InstallDate | ogv

–J–

–K–

–L–

language

for the machine you're on

Get-WinSystemLocale

What languages are available on the machine?

Get-WinUserLanguageList

local users and groups

lusrmgr.msc

| ft

–M–

MAC addresses for a PC

getmac

My Documents location

$mydocs = [ environment]::getfolderpath("mydocuments")

change directory to my documents

$dir = [environment]::getfolderpath("mydocuments")

cd $dir

or if you don't want to mess with a variable and want to embed date/time in a file name

Get-AdUser | Export-Csv -Path "$([environment]::getfolderpath("mydocuments"))\fileBaseName$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv" -NoTypeInformation -Encoding UTF8

–N–

name of the PC/server you're on right now

$env:COMPUTERNAME

or

$Get-WmiObject Win32_Computersystem).name

–O–

open files

all the open files in a certain directory

Get-SmbOpenFile | Where-Object -Property Path -Like "*somePath*" | ft ClientUserName,Path

or

Get-SmbOpenFile | Where-Object -Property ShareRelativePath -Match "someString" | select ShareRelativePath | fl

supposedly, this will find open files on another server

Get-SmbOpenFile -ScopeName "someServer"

but this always fails for me

Get-SmbOpenFile : No MSFT_SMBOpenFile objects found with property 'ScopeName' equal to 'someServer'. Verify the value of the property and retry.

operating system – see Windows version

OS – see Windows version

–P–

PC serial number

gwmi win32_bios

remote

$sessionOption = New-CimSessionOption -Protocol Dcom
$cimSession = new-cimsession -sessionOption $sessionOption -computername somePCName
Get-CimInstance Win32_BIOS -CimSession $cimSession

pc name - see name of the PC/server you're on right now

processes, how many are going on

(Get-Process).Count

process, is one particular process active?

process, kill one particular process

profile location - $profile

To create a whole new profile (it checks first to make sure we don't already have one):

if (!(test-path $profile)) {new-item -type file -path $profile -force}

Prompt, change - helpful to include this in scripts that connect to other servers

function global:prompt{

    $promptString = "US $($executionContext.SessionState.Path.CurrentLocation) $([datetime]::Now.ToLongTimeString())>"
    write-host -NoNewLine -ForegroundColor Cyan $promptString
    return " "
}
#change prompt
prompt

That global: up top is pretty important

–Q–

–R–

registry, can we view remote

Get-Service RemoteRegistry

Enable:

Set-Service –Name RemoteRegistry –Computer someComputer -StartupType Automatic

Get-Service RemoteRegistry -ComputerName someComputer | Start-Service

registry values for a key, read and display

$Srv = "PC_Name"
$key = "SOFTWARE\Microsoft\Windows\CurrentVersion"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type , $Srv)
$regKey = $regKey.OpenSubKey($key)
Write-Host "Values"
Write-Host "------"
Foreach($val in $regKey.GetValueNames()){
   Write-Host $val.PadRight(30) -nonewline
   Write-Host $regKey.GetValue("$val")
}

Note that the way you specify which of the topmost hives (HKEY_LOCAL_MACHINE in this case) is by the $type = [Microsoft.Win32.RegistryHive]::LocalMachine

remote command

on several different computers at once:

$Cred = Get-Credential
Invoke-Command -ComputerName Mordor, TheShire {Get-Service -Name W32time} -Credential $Cred

runs on the remote computer and the results are returned to your local computer as deserialized objects. That doesn't mean you can't start or stop a service using a method with Invoke-Command though. It just means that the method has to be called in the remote session.

Invoke-Command -ComputerName Mordor, TheShire {(Get-Service -Name W32time).Stop()} - Credential $Cred

Sometimes a simple command…

Invoke-Command -ComputerName 12.25.10.13{$env:COMPUTERNAME} -Credential $Cred

…fails with:

[12.25.10.13] Connecting to remote server 12.25.10.13 failed with the following error message : The WinRM client cannot process the request. Default authentication may be used with an IP address under the following conditions: the transport is HTTPS or the destination is in the TrustedHosts list, and explicit credentials are provided. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. For more information on how to set TrustedHosts run the following command: winrm help config. For more information, see the about_Remote_Troubleshooting Help topic.
    + CategoryInfo          : OpenError: (129.255.108.136:String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : CannotUseIPAddress,PSSessionStateBroken

Trying to fix with…

Set-Item WSMan:\localhost\Client\TrustedHosts -Value 12.25.10.13 -Force

…fails with:

Set-Item : Access is denied. At line:1 char:1
+ Set-Item WSMan:\localhost\Client\TrustedHosts -Value 12.25.10.13 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Set-Item], InvalidOperation Exception
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.SetItemCommand

remote session, initiate

$Cred = Get-Credential
Enter-PSSession -ComputerName MagicKindom -Credential $Cred

to end:

Exit-PSSession

RSAT (e.g., ADUC)

What available modules are installed?

Get-WindowsCapability -online | ? Name -like Rsat* | ft

Install RSAT for Windows 10 1809 and 1903 automated

RSAT, components included

RSAT, install

reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Servicing" /v RepairContentServerSource /d 2 /t REG_DWORD /f

Add-WindowsCapability -online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0
Add-WindowsCapability -online -Name Rsat.Dns.Tools~~~~0.0.1.0
Add-WindowsCapability -Online -Name Rsat.FileServices.Tools~~~~0.0.1.0
Add-WindowsCapability -Online -Name Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0
Add-WindowsCapability -Online -Name Rsat.DHCP.Tools~~~~0.0.1.0
Add-WindowsCapability -Online -Name Rsat.FailoverCluster.Management.Tools~~~~0.0.1.0
Add-WindowsCapability -Online -Name Rsat.WSUS.Tools~~~~0.0.1.0

–S–

schannel event IDs, find

$StartTime = (Get-Date).AddMinutes(-5)
$EndTime = Get-Date
$Filter = @{
    LogName = Security
    ID = @(36870, 36871, 36872, 36874, 36888)
    StartTime = $StartTime
    EndTime = $EndTime
}
try {
    Get-WinEvent -FilterHashtable $Filter -ErrorAction Stop
} catch {
    Write-Host "Error: $_"

Schedule PowerShell Scripts (in Server 2008)

In the "Action" tab, for the "Start a program", need to have "powershell" for the "Program/script" and the file location for the "Add arguments (optional)". If you're attempting to run some commands - like New-Object IO.FileSystemWatcher then you must precede the file name with "-NoExit . " (including the dot).

scheduled tasks, list – see also task scheduler, run a PowerShell script in

Finds tasks in the root directory and omits tasks whose names start with either "Optimize Start Menu Cache Files" or "MicrosoftEdgeUpdateTask"

$taskDetails = @()
$tasks = Get-ScheduledTask | ? {$_.TaskPath.Trim("\\") -eq "" -and $_.TaskName -notlike "Optimize Start Menu Cache Files*" -and $_.TaskName -notlike "MicrosoftEdgeUpdateTask*"}
foreach ($task in $tasks) {
    if ($task.Triggers.Count -eq 1) {
        foreach ($trigger in $task.Triggers) {
            switch ($trigger) {
                "MSFT_TaskDailyTrigger" {$triggerType = "daily"}
                "MSFT_TaskWeeklyTrigger" {$triggerType = "weekly"}
                "MSFT_TaskTrigger" {$triggerType = "monthly"}
                default {$triggerType = "unknown"}
            }
            $startTime = $trigger.StartBoundary
            $inputDateTime= [DateTime]::Parse($startTime)
            $startTime = {0:h:mm:ss tt}-f$inputDateTime
        }
    }
    else {$triggerType = "multiple"}
    $taskDetails += New-Object -TypeName PSObject -Property @{
        TaskName = $task.TaskName
        type = $triggerType
        StartTime = $StartTime
        state = $task.state
        Execute = $task.Actions.Execute
        Arguments = $task.Actions.Arguments
    }
}
$taskDetails | select TaskName, type, StartTime, Execute, state, Arguments | ogv

serial number for a PC - see PC, get serial number

server shares – see shares for a server, list

Services

Find if you don't know the exact name but have some idea of the name. For example: you know the service has something to do with the word, profile:

get-service | Where-Object {$_.DisplayName -like "*profile*"} | ft

session, kill existing

Get-PSSession | Remove-PSSession

session, remote initiate - see remote session, initiate

shares for a server, list – aka: shared directories for a server, list

get-WmiObject -class Win32_Share -computer someServer

or

Get-SmbShare

–T–

task scheduler, list tasks in – see scheduled tasks, list

task scheduler, run a PowerShell script in

in the "Actions" tab, in the "Start a program" area, put in "powershell"

In the "Add arguments" area, put in "-ExecutionPolicy Bypass D:\path\YourPowerShellScript.ps1"

If you see, "This task requires that the user account specified has Log on as batch job rights" the user must be able to do the following

  1. Start -> Run
  2. Type in secpol.msc /s
  3. Select "Local Policies" in MSC snap in
  4. Select "User Rights Assignment"
  5. Right click on "Log on as batch job" and select Properties
  6. Click "Add User or Group", and include the relevant user.

tenant, which one am I on?

Get-MsolAccountSku

The command above returns a list of SKUs, each of which includes an AccountSkuID field. The first part of this each of the AccountSkuID field on every row in this list of SKUs has the tenant name. If you just want the tenant name all by itself, use this instead:

(Get-MsolAccountSku)[0].AccountSkuId.split(":")[0]

which looks at the AccountSkuID field of the first SKU in the array and then uses the portion before the colon.

–U–

UNC directory, change to

Set-Location \\yourDomain.com\share

updates, windows - see windows updates

user profile

$PROFILE

user profiles on remote PCs

Get-WmiObject -Class Win32_UserProfile -ComputerName 'SomePC'| ft LocalPath

–V–

–W–

Windows Services - see Services

Windows updates

Here's the shortest answer:

Get-Hotfix

But that seems to leave out a lot of updates. This shows more:

$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object {$_}

which I usually follow with an | Out-GridView so I don't just get a big screen dump.

Although this shows more records tha what Get-Hotfix does above, I can't tell for sure if there's any overlap because Get-Hotfix shows KB numbers whereas the 4 commands immediately above don't return any KB numbers.

or if you're not using PS 7 where none of these commands below work anymore, try

gwmi -cl win32_reliabilityRecords | select ProductName, SourceName, TimeGenerated, EventIdentifier, RecordNumber, __CLASS | ft

or

gwmi win32_quickfixengineering |sort installedon -desc | ft

Windows version - the single-line commands below will return various aspects

newer works with PS 7

[environment]::OSVersion.Version # same info as above but in table form
Get-CimInstance Win32_OperatingSystem # table with more entries including serial number and system directory
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName someServer | select Caption # plain English version e.g., Microsoft Windows 2022

older commands do not work with PS 7

(Get-WmiObject Win32_OperatingSystem).Version # short number e.g., 10.0.18363
Get-WindowsEdition -Online # edition e.g., Core
gwmi win32_operatingsystem | % caption # plain English version e.g., Microsoft Windows 10 Home
(gwmi win32_operatingsystem).caption # plain English version e.g., Microsoft Windows 10 Home

–X–

–Y–

–Z–

zip a file

[Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem")
$src_folder = "D:\project"
$destfile = "D:\project.zip"
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includeBaseDir = $false
[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder, $destfile, $compressionLevel, $includeBaseDir)