Installing Modules in PowerShell Core on Linux

When you’d like to install modules for all users from the PowerShellGet repository, you need Administrator privileges. On PowerShell Core on Linux, you can use the command “sudo” to enable Administrator privileges, and run PowerShell. Just make sure you specify the command in braces.

This would install the module AzureRM.NetCore from the PowerShell Gallery with administrator privileges for all users on PowerShell Core on Linux:

sudo powershell -Command {Install-Module -Name AzureRM.Netcore}

Twitter This Tip! ReTweet this Tip!

“Braces Secret” with PowerShell.exe

Here is a little secret that applies to powershell.exe when it is called from within PowerShell or PowerShell Core: when you run powershell.exe and submit commands via the parameter -Command, PowerShell runs the command and returns plain text:

$a = powershell -noprofile -Command Get-Service
$a[0].GetType().FullName 
System.String

When you place your code in braces, PowerShell will serialize the results and return rich objects:

$a = powershell -noprofile -Command { Get-Service }
$a[0].GetType().FullName 
System.Management.Automation.PSObject 

$a[0] | Select-Object -Property * 

Name                : AdobeARMservice
RequiredServices    : {}
CanPauseAndContinue : False
CanShutdown         : False
CanStop             : True
DisplayName         : Adobe Acrobat Update Service
DependentServices   : {}
MachineName         : .
ServiceName         : AdobeARMservice
ServicesDependedOn  : {}
Status              : Running
ServiceType         : Win32OwnProcess
StartType           : Automatic
Site                : 
Container           :

Twitter This Tip! ReTweet this Tip!

Parsing Distinguished Names

Distinguished names are strings, and strings contain powerful ways of parsing data. The most powerful yet simple approach is the Split() method.

Check out how easy it is to get back the name of the last element of a distinguished name using Split():

$dn = 'CN=pshero010,CN=Users,DC=powershell,DC=local'
$lastElement = $dn.Split(',')[0].Split('=')[-1]
$lastElement

Split() always produces a string array. With brackets, you can access individual array elements. So the code first splits by comma, then takes the first element which is “CN=pshero010”. Next, use the same technique again, and split by “=”. Here, we are interested in the last array element. PowerShell supports negative array indices which count from the array end, so index -1 retrieves the last array element. Mission accomplished.

Twitter This Tip! ReTweet this Tip!

Auditing Logons

Have you ever wondered whether someone has logged into your PC while you were away? In a previous tip we explained how you can examine the rich auditing information found in the Windows Security log, provided you have Administrator privileges.

To find out who logged into your PC, try the code below! The function Get-LogonInfo searches for security events with ID 4624. Security information is protected, so you need to be an Administrator to run this code. This is why the code uses a #requires statement that prevents non-Admins from running the code.

#requires -RunAsAdministrator

function Get-LogonInfo
{
  param
  (
    [Int]$Newest = [Int]::MaxValue,
    [DateTime]$Before,
    [DateTime]$After,
    [string[]]$ComputerName,
    $Authentication = '*',
    $User = '*',
    $Path = '*'
  )

  $null = $PSBoundParameters.Remove('Authentication')
  $null = $PSBoundParameters.Remove('User')
  $null = $PSBoundParameters.Remove('Path')
  $null = $PSBoundParameters.Remove('Newest')
    

  Get-EventLog -LogName Security -InstanceId 4624 @PSBoundParameters |
  ForEach-Object {
    [PSCustomObject]@{
      Time = $_.TimeGenerated
      User = $_.ReplacementStrings[5]
      Domain = $_.ReplacementStrings[6]
      Path = $_.ReplacementStrings[17]
      Authentication = $_.ReplacementStrings[10]

    }
  } |
  Where-Object Path -like $Path |
  Where-Object User -like $User |
  Where-Object Authentication -like $Authentication |
  Select-Object -First $Newest
}


$yesterday = (Get-Date).AddDays(-1)
Get-LogonInfo -After $yesterday |
Out-GridView

The function also utilizes the automatic $PSBoundParameters hash table which contains all parameters that a user submitted. Only a part of these should be forwarded to Get-EventLog, so all parameters that serve other purposes are removed from the hash table. This way, the user can simply forward the parameters Before, After, and ComputerName to Get-EventLog.

Next, the event information is examined. All relevant information can always be found in the property ReplacementStrings which is an array. As it turns out, for events with ID 4624, the sixth (index 5) element is the user name, the seventh (index 6) is the domain, and the 18th (index 17) lists the path to the executable that performed the login.

Physical logins are typically performed by lsass, the local security authority. So to see just logins from humans that logged right into your machine, try this:

$yesterday = (Get-Date).AddDays(-1)
Get-LogonInfo -After $yesterday -Path *lsass.exe |
Out-GridView

Twitter This Tip! ReTweet this Tip!

Finding UAC Elevations

The Windows “Security” log contains rich audit information. By default, it logs all requests for privilege elevation which occurs when you run a program with full Administrator privileges.

To get a list of elevations that occurred on your machine, try this:

#requires -RunAsAdministrator

function Get-ElevationInfo
{
  param
  (
    [DateTime]$Before,
    [DateTime]$After,
    [string[]]$ComputerName,
    $User = '*',
    $Privileges = '*',
    $Newest = [Int]::MaxValue
  )

  $null = $PSBoundParameters.Remove('Privileges')
  $null = $PSBoundParameters.Remove('User')
  $null = $PSBoundParameters.Remove('Newest')
  


  Get-EventLog -LogName Security -InstanceId 4672 @PSBoundParameters |
  ForEach-Object {
    [PSCustomObject]@{
      Time = $_.TimeGenerated
      User = $_.ReplacementStrings[1]
      Domain = $_.ReplacementStrings[2]
      Privileges = $_.ReplacementStrings[4]
    }
  } |
  Where-Object Path -like $Privileges |
  Where-Object User -like $User |
  Select-Object -First $Newest
}

Get-ElevationInfo -User pshero* -Newest 2 |
Out-GridView

Get-ElevationInfo queries the System event log for events with ID 4672. Security information is protected, so you need to be an Administrator to run this code. This is why the code uses a #requires statement that prevents non-Admins from running the code.

The function also utilizes the automatic $PSBoundParameters hash table which contains all parameters that a user submitted. Only a part of these should be forwarded to Get-EventLog, so all parameters that serve other purposes are removed from the hash table. This way, the user can simply forward the parameters Before, After, and ComputerName to Get-EventLog.

Next, the event information is examined. All relevant information can always be found in the property ReplacementStrings which is an array. As it turns out, for events with ID 4672, the second (index 1) element is the user name, the third (index 2) is the domain, and the fifth (index 4) lists the security privileges granted.

Twitter This Tip! ReTweet this Tip!

How to Correctly Wrap Multiple Results

Whenever a PowerShell function needs to return more than one kind of information, it is important to wrap them as objects. Only then will the caller be able to discover and uniquely access the information. Here is a quick sample.

This is a function that simply outputs 3 pieces of data. They are returned as an array of different objects:

function test
{
    33.9
    "Hallo"
    Get-Date
}

$result = test

$result.Count
$result

And here is a much better function that returns the same information, but wrapped into a structured object. This way, the user can easily discover the information submitted by the function:

function test
{
    [PSCustomObject]@{
        Number = 33.9
        Text = "Hallo"
        Date = Get-Date
    }

}


$result = test
$result.Count

$result

$result.Number

Twitter This Tip! ReTweet this Tip!