Here’s a quick snippet to get the IP, MAC address and Vendor using PowerShell in Windows with the native arp -a command and curl. It is based on accessing my MAC lookup tool.
This PowerShell one-liner is a convenient way to monitor log files in real-time and quickly spot error messages. The script continuously reads the log and highlights lines that contain the word “error” with a red background, making them easy to identify. It’s a handy tool for troubleshooting or monitoring system activities, especially when dealing with logs generated by tools like DISM.
gc .\dism.log -wait |foreach-object { if ($_ -match "error"){write-host -foregroundcolor white -BackgroundColor red $_} else {write-host $_}}
Explanation:
gc .\dism.log -wait
gc is short for Get-Content, a PowerShell cmdlet that reads the content of a file.
.\dism.log specifies the file to read, which is dism.log. This log file is typically generated by the Deployment Imaging Service and Management Tool (DISM), often used for Windows image management.
The -wait parameter makes Get-Content continuously monitor the log file in real-time, displaying new content as it is written to the file. This is especially useful for live monitoring of logs.
| foreach-object
The | symbol (pipeline) sends the output of the Get-Content cmdlet to the next part of the command.
foreach-object is a loop that processes each line of the log file one by one as it is being read.
if ($_ -match "error")
$_ represents the current line of the log file being processed in the loop.
-match "error" checks if the current line contains the word “error” (case-insensitive by default in PowerShell). This is the key part that identifies lines with errors in the log.
write-host -foregroundcolor white -BackgroundColor red $_
If the line contains the word “error,” this part of the command prints it to the console with a white foreground (text color) and a red background for visibility, signaling an error.
$_ again represents the line being processed.
else { write-host $_ }
If the line doesn’t contain “error,” it simply prints the line normally, without any special formatting.
Example:
Suppose you are monitoring the dism.log file and a line like this is written to the log:
2024-10-11 14:55:12, Error DISM DISM.EXE: Failed to load the provider
In the console, this line will be printed in white text with a red background, making it easy to spot among other log entries.
Awhile back I used a piece of software (iMazing) to backup an old iPhone of mine. The raw backup when extracted is a bunch of files without extensions in many subdirs. I needed a way to extract actual images from the backup folder and copy them to an extracted folder giving them the correct file extension and ensuring I kept the date created / modified in tact.
Here’s a PowerShell script that will recursively go through a specified folder and copy any files that are images or videos to an extracted folder location.
I want to start off with acknowledging that I know, but don’t have the ability, to manage Google Chrome in an organizational setting through Google Cloud which produces nice reports of extensions installed — and this can even be done with M365 Defender. My dilemma is that for some networks I do not have the ability to do this, so I needed an alternative way. This is what I’m developing.
I found an article on Spiceworks that someone created a PowerShell script already while I was trying to find the original article I got the inspiration from. I learned a little about how Chrome extensions use manifest.json and that “name” key doesn’t always, as of Manifest V2, give the actual extension name.
Reading through Chrome.18n documentation on how the manifest is laid out and internationalization structure, I was able to figure out a way to programmatically look things up.
In manifest.json the following keys are required:
default_locale is required. It can be any value, really, but standards are en_US, en, ru, etc.
name is required. It can be the actual name of the extension, but in Manifest V3, it is the string to translate which is preceded by __MSG_ and ends with __ (two underscores). The value between could be anything, for example __MSG_name__ or __MSG_AppName__.
So my thought is to look up the name key, check for __MSG_ and also the default_locale if the name key matches. This then lets me find messages.json which I can use the __MSG_name__ to find the actual extension name.
Disclaimer: This is my script so far; my PowerShell sucks. But it works for me. I do know I need to handle and log errors (unavailable computers, etc.) and not rely on -ErrorAction SilentlyContinue.
October 2024: I just realized this does not capture Developer mode unpacked/packed extensions that may be installed.
# Ensure necessary modules are loaded
Import-Module ActiveDirectory
# Define an array to store the extensions
$extensions = @()
# Define a hash table of browsers/paths
$browsers = @{
"Edge" = "AppData\Local\Microsoft\Edge\User Data\Default\Extensions"
"Chrome" = "AppData\Local\Google\Chrome\User Data\Default\Extensions"
}
Clear-Host
# Get a list of AD computers (handling domain and local environments)
try {
$computers = Get-ADComputer -Filter * -Credential (Get-Credential)
} catch {
Write-Host "Could not retrieve AD computers. Running on local machine."
$computers = @([pscustomobject]@{ Name = $env:COMPUTERNAME })
}
foreach ($computer in $computers) {
$computerName = $computer.Name
$computerPath = "\\$computerName\c$\Users"
Write-Host "******************** $computerName **********************"
# Get a list of users excluding 'Public' and 'Default'
try {
$users = Get-ChildItem -Path $computerPath -ErrorAction SilentlyContinue |
Where-Object { $_.PSIsContainer -and $_.Name -notmatch '^(Public|Default|All Users|Default User|defaultuser0|WDAGUtilityAccount)$' }
} catch {
Write-Host "Failed to access users on $computerName. Skipping."
continue
}
foreach ($user in $users) {
$userName = $user.Name
Write-Host "Checking $userName : $computerName`n"
foreach ($browser in $browsers.Keys) {
$browserPath = $browsers[$browser]
$extensionPath = Join-Path -Path $computerPath -ChildPath "$userName\$browserPath"
# Check if the Extensions folder exists
if (Test-Path -Path $extensionPath -ErrorAction SilentlyContinue) {
$manifestFiles = Get-ChildItem -Path $extensionPath -Filter manifest.json -Recurse -ErrorAction SilentlyContinue
foreach ($manifestFile in $manifestFiles) {
$extDir = $manifestFile.DirectoryName
$extID = $manifestFile.Directory.Parent.Name
$manifestData = Get-Content -Path $manifestFile.FullName | ConvertFrom-Json
$extensionName = if ($manifestData.name -like "*__MSG_*") {
$msgJSON = Join-Path -Path $extDir -ChildPath "_locales\$($manifestData.default_locale)\messages.json"
$extNameKey = $manifestData.name -replace "__MSG_", "" -replace "__", ""
(Get-Content -Encoding UTF8 -Path $msgJSON | ConvertFrom-Json).$extNameKey.message
} else {
$manifestData.name
}
$extensions += [PSCustomObject]@{
ComputerName = $computerName
UserName = $userName
ExtensionName = $extensionName
ExtensionID = $extID
Browser = $browser
}
Write-Host "[$browser]: $extensionName ($extID)"
}
Write-Host "`n"
}
}
}
}
# Export the extensions to a CSV file
$extensions | Export-Csv -Path "browser_extension_report.csv" -Encoding UTF8 -NoTypeInformation
Once it is done, a report should be created as a CSV. Here’s an example. It lists each extension installed for every user per machine scanned.
Managing Extensions in Google Chrome
What I’m working on now is a way to uninstall these after I review installed extensions and determine which ones I want to remove and block from re-installation. This is still a work in progress, here are some of my notes.
The PowerShell script is designed to extract information about Remote Desktop Protocol (RDP) local session manager events from the Windows event logs on a RDS host and save it to a CSV file.
Script: Get events with EventID 40 from Microsoft-Windows-TerminalServices-LocalSessionManager/Operational Event Log
First, the script uses the Get-WinEvent cmdlet to retrieve events from the ‘Microsoft-Windows-TerminalServices-LocalSessionManager/Operational’ log. This log contains information about RDP disconnections. The events are stored in the $RDPAuths variable.
The $RDPAuths variable is then piped to the ForEach-Object cmdlet to convert each event to XML format using the ToXml() method. The resulting array of XML objects is stored in the $xml variable.
Next, the script uses the ForEach-Object cmdlet again to iterate over the $xml.Event array. For each event, a new custom object is created using the [PSCustomObject]@{} syntax. This custom object contains the following properties: TimeCreated, Session, Reason, EventID, User, SessionID, and Address. These properties are extracted from the event XML data using dot notation.
Finally, the custom objects are piped to the Export-Csv cmdlet, which saves the objects as a CSV file at the specified path (c:\rdlog-LSM-Operational.csv) with ASCII encoding.
This PowerShell script is useful for extracting and analyzing RDP local session manager events, such as monitoring for disconnect reasons. The resulting CSV file provides a simple, convenient way to view and analyze the RDP events.
RDS Session Host Server Disconnect Codes
RDS server client disconnect code
Disconnect reason
0x00000001
The disconnection was initiated by an administrative tool on the server in another session.
0x00000002
The disconnection was due to a forced logoff initiated by an administrative tool on the server in another session.
0x00000003
The idle session limit timer on the server has elapsed.
0x00000004
The active session limit timer on the server has elapsed.
0x00000005
Another user connected to the server, forcing the disconnection of the current connection.
0x00000006
The server ran out of available memory resources.
0x00000007
The server denied the connection.
0x00000009
The user cannot connect to the server due to insufficient access privileges.
0x0000000A (10)
The server does not accept saved user credentials and requires that the user enter their credentials for each connection.
0x0000000B (11)
The disconnection was initiated by the user disconnecting his or her session on the server or by an administrative tool on the server.
0x0000000C (12)
The disconnection was initiated by the user logging off his or her session on the server.