Some little powershell helpers

March 11, 2020
No Comments

Nothing better than to sometimes share things, so this this time we are just going to have some small bits and pieces which hopefully will help you in your daily tasks.
It’s probably all been written before, but most of the things I ran into at customers for which they needed some help or guidelines.

First thing thats comes to mind in beeing a handy trick is search on content within logfiles for example.

First start with selecting the logfiles. Let’s say for the example we want to search through all logfiles underneath a specific folder and where de modification date of the file is a day ago.
We can do something like

Get-ChildItem -Path c:\myfolder\ -Filter *.log -Recurse -File | Where-Object LastWriteTime -gt (Get-Date).AddDays(-1)

Now we have a list of all logfiles but now we want to search for specific words in those files. For this we can use Select-String.
So if we want to search for Errors or Warnings in all logfiles we can do the following

Get-ChildItem -Path c:\myfolder\ -Filter *.log -Recurse -File | Where-Object LastWriteTime -gt (Get-Date).AddDays(-1) | Select-String -pattern "error" ,"warning" 

Example

installer.log:32945:MSI (s) (8C:2C) [19:55:02:580]: File: C:\Program Files\ScriptPortal\wwwroot\svgs\regular\wind-warning.svg;       To be installed;        Won't patch;    No existing fileinstaller.log:35154:MSI (s) (8C:2C) [19:55:03:454]: Executing op: FileCopy(SourceName=ti4wfcis.svg|engine-warning.svg,SourceCabKey=filCBCE2A5F8BF079ADF915789AF96BB790,DestName=engine-warning.svg,Attributes=512,FileSize=1091,PerTick=65536, ,VerifyMedia=1,,,,,CheckCRC=0,,,InstallMode=58982400,HashOptions=0,HashPart1=618944891,HashPart2=851183689,HashPart3=229724231,HashPart4=1117705277,,)
 installer.log:35155:MSI (s) (8C:2C) [19:55:03:454]: File: C:\Program Files\ScriptPortal\wwwroot\svgs\regular\engine-warning.svg;     To be installed;        Won't patch;    No existing fileinstaller.log:38540:MSI (s) (8C:2C) [19:55:04:698]: Executing op: SetTargetFolder(Folder=C:\Program Files\ScriptPortal\wwwroot\images\error)
 installer.log:38541:MSI (s) (8C:2C) [19:55:04:698]: Executing op: SetSourceFolder(Folder=1\PFiles\qwe221rq\wwwroot\images\error|PFiles\ScriptPortal\wwwroot\images\error)
 installer.log:38543:MSI (s) (8C:2C) [19:55:04:698]: File: C:\Program Files\ScriptPortal\wwwroot\images\error\500.png;   To be installed;        Won't patch;    No existing file
 installer.log:40791:MSI (s) (8C:2C) [19:55:08:010]: Executing op: CustomActionSchedule(Action=InvokeTestPS1,ActionType=3073,Source=BinaryData,Target=CAQuietExec,CustomActionData="C:\Windows\SysNative\WindowsPowerShell\v1.0\powershell.exe"  -Version 4.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& 'C:\Program Files\ScriptPortal\ConfigIIS.ps1' -websitename ScriptPortal ; exit $($Error.Count)")
 installer.log:40830:MSI (c) (6C:8C) [19:55:10:634]: Windows Installer installed the product. Product Name: ScriptPortal. Product Version: 1.0.0. Product Language: 1033. Manufacturer: Methos B.V.. Installation success or error  status: 0.

Doing it like this show the exact line on which the worrs are found in the logfile, but in this case you can miss some context because it’s not always directly clear what the error is.
For this Select-String has the Context parameter which shows the selected numbers of lines before and after the found line.


Get-ChildItem -Path c:\myfolder\ -Filter *.log -Recurse -File | Where-Object LastWriteTime -gt  (Get-Date).AddDays(-1) | Select-String -pattern "error" ,"warning" -Context 3

Will show us the line on which the error ocurred and the 3 lines above and beyond it.

Example:

  installer.log:38545:MSI (s) (8C:2C) [19:55:04:700]: Executing op: SetTargetFolder(Folder=C:\Program Files\ScriptPortal\wwwroot\svgs\solid\)
  installer.log:38546:MSI (s) (8C:2C) [19:55:04:700]: Executing op: SetSourceFolder(Folder=1\PFiles\qwe221rq\wwwroot\svgs\solid\|PFiles\ScriptPortal\wwwroot\svgs\solid\)
  installer.log:40788:MSI (s) (8C:2C) [19:55:08:009]: Set LastUsedType to: n.  installer.log:40789:MSI (s) (8C:2C) [19:55:08:009]: Set LastUsedIndex to: 1.
  installer.log:40790:MSI (s) (8C:2C) [19:55:08:009]: Executing op: ActionStart(Name=InvokeTestPS1,,)
  installer.log:40791:MSI (s) (8C:2C) [19:55:08:010]: Executing op: CustomActionSchedule(Action=InvokeTestPS1,ActionType=3073,Source=BinaryData,Target=CAQuietExec,CustomActionData="C:\Windows\SysNative\WindowsPowerShell\v1.0\powershell.exe" -Version 4.0 -NoProfile -NonInteractive -InputFormat None -ExecutionPolicy Bypass -Command "& 'C:\Program Files\ScriptPortal\ConfigIIS.ps1' -websitename ScriptPortal ; exit $($Error.Count)")
  installer.log:40792:MSI (s) (8C:64) [19:55:08:011]: Invoking remote custom action. DLL: C:\Windows\Installer\MSIAFF5.tmp, Entrypoint: CAQuietExec
  installer.log:40793:CAQuietExec:  nt authority\system
  installer.log:40794:MSI (s) (8C:2C) [19:55:09:137]: Executing op: End(Checksum=0,ProgressTotalHDWord=0,ProgressTotalLDWord=393760866)
  installer.log:40827:MSI (c) (6C:8C) [19:55:10:633]: Note: 1: 1707
  installer.log:40828:MSI (c) (6C:8C) [19:55:10:633]: Product: ScriptPortal -- Installation completed successfully.
  installer.log:40829:
  installer.log:40830:MSI (c) (6C:8C) [19:55:10:634]: Windows Installer installed the product. Product Name: ScriptPortal. Product Version: 1.0.0. Product Language: 1033. Manufacturer: Methos B.V.. Installation success or error status: 0.
  installer.log:40831:
  installer.log:40832:MSI (c) (6C:8C) [19:55:10:638]: Grabbed execution mutex.
  installer.log:40833:MSI (c) (6C:8C) [19:55:10:638]: Cleaning up uninstalled install packages, if any exist

Another thing you might want to do is group the number of find entries per file. For example if use

Get-ChildItem -Path c:\myfolder\ -Filter *.log -Recurse -File | Select-String -pattern "error" ,"warning" | Group-Object Path

You will see how many times Error or Warning is mentioned in the file

Count Name                      Group
----- ----                      -----
   55 C:\myfolder\installer.log {C:\myfolder\installer.log:327:MSI (c) (6C:8C) [19:54:41:619]: PROPERTY CHANGE: Adding dir2D851CFBD60CB903EA46B3BA10971B41 property. Its value is... 

Archiving logfiles

Another thing we ran into a lot is growth of logfiles. Programs and people tend to create logfiles to monitor if certain jobs ran ok, but these files never get cleaned and before you know it there are several thousand logifles in a folder which as a side effect slows all processing down.

There are several ways to clean these files. One way of doing this is use the script below. It uses a CSV file to decide which folders and files to process and what age they must be.

$WorkingFolder     = "D:\Scripts\ArchiveLogfiles\"
$LogfileDefinition = "Logfolders.txt"

Set-Location -Path $WorkingFolder

$CheckFolders = Get-Content -Path "$WorkingFolder$LogfileDefinition"
foreach($line in $CheckFolders) {
    $WorkItem = $line.split(";")
    $destinationPath = "$($WorkItem[2])\$(Get-Date -format "yyyyMMddHHmmss").zip"  
    Write-Output "Processing folder: $($WorkItem[2])"
    Write-Output "Destination path: $DestinationPath"
    $logFiles = Get-ChildItem $WorkItem[2] -Filter *.$($WorkItem[1]) | Where-Object LastWriteTime -lt  (Get-Date).AddDays(-1 * $WorkItem[0])  
    
    $logFilePaths = @()  
    foreach($logFile in $logFiles){  
        $logFilePaths  += $logFile.FullName  
    }  
    if ($logFilePaths.Count -gt 0) {
        Write-Output "Adding $($logFilePaths.Count) files"
        Compress-Archive -Path $logFilePaths -DestinationPath $destinationPath -CompressionLevel Optimal
        Write-Output "Removing $($logFilePaths.Count) files"
        Remove-Item -Path $logFilePaths
    } else {
        Write-Output "No files to process..."
    }
}

This script needs a CSV file int the following format:

x;extension;folder

Where X is the number of days of logfiles to leave in the folder. Extension is the extension of the logfiles to process, en folder is the folder to process

For example

7;log;c:\myfolder
5;myext;c:\anotherfolder\logs

When the script run it will first create a zip file of the logfiles older than the set date and then deletes the logfiles.

Active Directory

In a lot of company Microsoft Active Directory is used. This is a well known directory provider and also has great powershell support.

Some of the things I ran into for example, was a company which used to store function titles of their employees in the active directory. Although the AD can be used for this, they never kept it up to date, so that after a couple years most of the titles are not correct any more or for newer employees they are not even filled anymore.

To keep their active directory a bit tidy the choice was made to delete the title from every uses which still had one. Now you can do this by hand of course, but since we had to go through approx. 4000 employees we created the following:

import-module ActiveDirectory

$OUpath = 'OU=Gebruikers,DC=domain,DC=local'
Get-ADUser -Filter * -SearchBase $OUpath -Properties sAMAccountName,Title | Where-Object {$_.title -ne $null} | Set-ADUser -Title $null

This will quick clean the title for every users in the specified OU. By omitting the Searchbase parameter it will go over the entire AD.

Another thing we wrote a quick script is which user logged onto a a device last. There is a several script that runs and greps some data from SCCM, for exmaple the currently logged on user and stores that in the description field for a computer in the AD. So if we want to find who logged on last to a device we can simple do

function Get-LastLoggedOnOnsystem {
    param (
        $UserName
    )

    $OUPath = "OU=Werkstations,DC=doamin,DC=local"

    $List = Get-ADComputer -filter * -Properties Description -SearchBase $OUPath | Select-Object Name, Description | Where-Object { $_.Description -like "*$UserName*" }
    $List

}

Get-LastLoggedOnOnsystem -Username rex

Hope these tips help you on your way if needed.

Rex de Koning

Your Turn To Talk

Leave a reply:

Your email address will not be published.