Powershell - Searching value for 32 and 64 bits in registry - powershell

I currently working on powershell script who collect some datas into registry.
To perform these action, I use a similar code:
$Ifkeyexist = Test-Path 'HKLM:\SOFTWARE\WOW6432Node\XXXX\YYY\ZZZ\environment\'
if ($Ifkeyexist -eq "True")
{
$GetProductHotfix = Get-ItemPropertyValue 'HKLM:\SOFTWARE\WOW6432Node\XXXX\YYY\ZZZ\environment\' 'ProductHotfix'
Write-host "- Product Hotfix: $GetProductHotfix"
}
else {
write-host "- Unable to find product hotfix" -ForegroundColor red
}
}
Question:
in the example above, is it possible to find the value "ProductHotfix" in the 32 and 64 bit registry path?
Thanks by advance for your advices :)
Regards,
LEFBE

I would do something like below:
foreach ($path in 'HKLM:\SOFTWARE\XXXX\YYY\ZZZ\environment', 'HKLM:\SOFTWARE\WOW6432Node\XXXX\YYY\ZZZ\environment') {
$hotfix = Get-ItemPropertyValue -Path $path -Name 'ProductHotfix' -ErrorAction SilentlyContinue
# assuming you want to exit the loop at the first successfull 'hit'
if ($hotfix) { break }
}
if ($hotfix) {
Write-Host "- Product Hotfix: $hotfix"
}
else {
Write-Host "- Unable to find product hotfix" -ForegroundColor Red
}

Related

How to fix line breaks?

I'm kind of new to PowerShell and I need to fix a bank statement file. The problem is that this file has a CNAB 240 standard, however, it comes with a line break, so we have to fix it manually.
I have a script that makes this correction, however, I can't make it run alone because I need to use CMD to run PowerShell.
My idea is to use this script in the task manager to look for the .txt file and fix it, does anyone have any ideas?
Follow the script:
if ($args.count -eq 0)
{
Write-Error "Please enter a file to read"
return
}
$file = $args[0]
if(![System.IO.File]::Exists($file))
{
Write-Error "File not found: $file"
return
}
$expectedsize = 240
$lastSize = 0
foreach($line in [System.IO.File]::ReadLines($file))
{
if ($line.length -gt 0)
{
if (!($row.length -eq $expectedsize) -And (($row.length + $lastSize) -eq $expectedsize))
{
Write-Host -NoNewLine $linha
$lastSize += $line.length
}
else
{
if ($lastSize -gt 0)
{
Write-Host
}
Write-Host -NoNewLine $linha
$lastSize = $line.length
}
}
else
{
Write-Host
}
}
The script above makes the normal correction of the file, however, I am not able to make it follow the steps below:
Browse the txt file
Validate that it does not have the 240 positions in each line
And then make the correction

ArrayList not displaying when first referenced in function

Facing a couple logistical issues in PowerShell - clearly I'm missing a basic concept:
Setup: Create the menu.ps1 file (shown below), launch PowerShell 7.2.2 and call the file locally.
Issues:
The first time you choose option 1 for the ArrayList ($psArrayList), it does not display (although we see from the initial screen load that the items are populated). If you return to the menu and choose option 1 again, it will display on the second pass. ($psArray does load fine on first try, so is this is a type issue.?)
When the script ends, $psArrayList and $psArray are still in the current session variables, as indicated by: Get-Variable psArray*. Even if I instantiate them with $script:psArrayList = [System.Collections.ArrayList]#() and $script:psArray = #() they seem to stay within the session scope. Is there a "right" way to clear them when the ps1 ends?
menu.ps1 contents:
$psArrayList = [System.Collections.ArrayList]#()
# example of populating later in function etc...
$psArrayList.Add([pscustomobject]#{name="bird";color="blue"})
$psArrayList.Add([pscustomobject]#{name="cat";color="orange"})
$psArrayList.Add([pscustomobject]#{name="bear";color="brown"})
$psArray = #()
# example of populating later in function etc...
$psArray += "dog"
$psArray += "fish"
$psArray += "squirrel"
function End-Script {
Remove-Variable psArray*
Exit
}
function Display-Menu {
[int]$choice=-1
Write-Host "This is a menu..." -ForegroundColor Green
Write-Host "Here are your options:"
Write-Host
Write-Host "`t1 - ArrayList"
Write-Host "`t2 - Array"
Write-Host "`t0 - quit (do nothing)"
Write-Host
while ($choice -lt 0) { $choice= Read-Host -Prompt "Choose 1-2 (or 0 to quit)" }
Process-Menu($choice)
}
function Process-Menu([int]$choice) {
switch($choice) {
1 { Write-Host "You chose ArrayList:"; Write-Output $psArrayList }
2 { Write-Host "You chose Array:"; Write-Output $psArray }
0 { Write-Host "You chose to quit. Exiting."; End-Script }
}
$yn=""
while ($yn -eq "") { $yn= Read-Host -Prompt "Return to main menu? (y/n)" }
if ($yn -eq "y") { Display-Menu } else { Write-Host "Ending..."; End-Script }
}
Display-Menu
Regarding the first issue, you would need to use Out-Host or Out-Default so that both outputs (Write-Host together with the arrays) are correctly displayed to the console. See these helpful answers for in depth details on this:
https://stackoverflow.com/a/50416448/15339544
https://stackoverflow.com/a/34858911/15339544
Regarding the second issue, your End-Script function would have a scope issue, Remove-Variable is trying to remove variables defined inside the function's scope (Local), if you want to target the variables defined outside it (Script), you would need to use the -Scope parameter, for example:
function End-Script {
Get-Variable psArray* | Remove-Variable -Scope Script
# `Remove-Variable psArray* -Scope Script` would be valid too
}
From the cmdlet's Parameters section we can read the following for the -Scope parameter:
A number relative to the current scope (0 through the number of scopes, where 0 is the current scope and 1 is its parent)
In that sense, -Scope 1 would also work.
Below you can see an example of your script with some improvements as well as input validation:
$psArrayList = [System.Collections.ArrayList]#()
$psArrayList.AddRange(#(
[pscustomobject]#{name="bird";color="blue"}
[pscustomobject]#{name="cat";color="orange"}
[pscustomobject]#{name="bear";color="brown"}
))
$psArray = "dog", "fish", "squirrel"
function End-Script {
Get-Variable psArray* | Remove-Variable -Scope Script
}
function Display-Menu {
Write-Host "This is a menu..." -ForegroundColor Green
Write-Host "Here are your options:"
Write-Host
Write-Host "`t1 - ArrayList"
Write-Host "`t2 - Array"
Write-Host "`t0 - quit (do nothing)"
Write-Host
# one of many methods for input validation is a Recursive Script Block:
$tryInput = {
try {
[ValidateSet(0, 1, 2)] $choice = Read-Host "Choose 1-2 (or 0 to quit)"
$choice
}
catch {
Write-Warning 'Invalid choice!'
& $tryInput
}
}
Process-Menu (& $tryInput)
}
function Process-Menu([int] $choice) {
switch($choice) {
1 {
Write-Host "You chose ArrayList:"
$psArrayList | Out-Host
}
2 {
Write-Host "You chose Array:"
$psArray | Out-Host
}
0 {
Write-Host "You chose to quit. Exiting."
End-Script
Return # => Exit this function
}
}
$tryInput = {
try {
[ValidateSet('y', 'n')] $choice = Read-Host "Return to main menu? (y/n)"
$choice
}
catch {
Write-Warning 'Invalid choice!'
& $tryInput
}
}
# No need to check for `N`
if((& $tryInput) -eq 'y') { Display-Menu }
}
Display-Menu

Powershell - Checking variable for value in foreach - If no value then log other output

I'm having issue with my foreach method. I am checking in the registry whether a good amount of programs are installed. How would I write it to say something is not installed one time versus it saying something's not installed for each key it checks? Now, If I place a ElseIf it executes "PowerBroker not installed." about 16 times. This is due to it checking every key and writing it out for each key it does not find a match to the displayname. How do I go about it checking the key and only writing it out one time if it's not installed?? Thanks!
$UninstallKeys = Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
foreach($Key in $UninstallKeys){
if($Key.GetValue("DisplayName") -Match "BeyondTrust"){
$PBW = $Key.GetValue("DisplayName")
$PBWV = $Key.GetValue("DisplayVersion")
if ($PBW) {
$PBW = $PBW, $PBWV
}
else {
$PBW = "PowerBroker not installed."
$installsmissing = "True"
}
}
Give this script a whirl. If I've understood the requirement correctly it should give you what you need.
$displayName = "BeyondTrust"
$uninstallKeys = Get-ChildItem -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
# Filter the keys down by their display name property
$specificUninstallKeys = $uninstallKeys |
Where-Object {
$_.GetValue("DisplayName") -eq $displayName
}
# Did we find any keys of that name?
if ($specificUninstallKeys) {
Write-Output "Keys found: $($specificUninstallKeys.Length)"
}
else {
Write-Output "Sorry pal, no keys by that name here!"
}
# There may be more than one; hence the loop-y requirement here.
foreach ($specificUninstallKey in $specificUninstallKey) {
Write-Output $displayName
Write-Output $specificUninstallKey.GetValue("DisplayVersion")
}

Loop Foreach only repeat offline computers

Not sure where I am going wrong I want to skip the computers which are coming back online but it keeps checking both online and online computers.
1st update is fine that its coming online and it should skip and only ping the offline one's a screenhot of output
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Testing to see if zero is coming online...
zero is Offline. Pausing for 2 seconds. Remaining attempts: 1
Its fine upto here but online computers again repeated...
g-10 Is coming online...Skipping to offline one's
192.168.0.1 Is coming online...Skipping to offline one's
Testing to see if Hero is coming online...
Hero is Offline.
here is my code
$Comps = GC c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$SleepSeconds = $SleepTimer * 2
[int]$Attempts = "2"
[int]$AttemptsCounter = 0
Do
{
$AttemptsCounter++
$RemainingAttempts = ([int]$Attempts - [int]$AttemptsCounter)
Foreach($comp in $comps){
$Online = Test-Connection -ComputerName $Comp -Quiet
IF($online -eq "True"){
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
}
elseIf ($Online -NE "True")
{
Write-Host "Testing to see if $Comp is coming online..."
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
If ($AttemptsCounter -eq $Attempts) {
Write-Host "."
}
Else {
Write-Host ". Pausing for $SleepSeconds seconds. Remaining attempts: $RemainingAttempts"
}
}
}
#Check the number of attempts, break out if reached.
If ($AttemptsCounter -eq $Attempts) {break}
#Delay
Start-Sleep -s ($SleepTimer * 60)
}
While ($Online -NE "True")
If ($Online -NE "True") {
Write-Host "Maximum number of attempts reached"
}
Else {
Write-Host
Write-Host "Computer $Comp is " -NoNewline
Write-Host "ONLINE" -BackgroundColor Green
}
try the following code.
It basically does the following for each line in c:\restarted.txt:
tests if the computer is online (Note: I added -Count 1)
if it is online: prints some green text and break
if it is offline: print red text, wait and decrements the $RemainingAttempts variable and redo until $RemainingAttempts is 0 or the computer is online
it the $RemainingAttempts variable is 0 print out some more text
$ComputerNameArray = Get-Content -Path c:\restarted.txt
[int]$SleepTimer = "1" #minutes to attempt after
[int]$Attempts = "2"
$DefaultBackgroundColor = (Get-Host).ui.rawui.BackgroundColor
foreach($ComputerName in $ComputerNameArray) {
$AttemptsCounter = 0
$RemainingAttempts = $Attempts - $AttemptsCounter
Write-Host "Testing to see if ""$ComputerName"" is coming online..." -BackgroundColor $DefaultBackgroundColor
while($RemainingAttempts -gt 0) {
if(Test-Connection -ComputerName $ComputerName -Quiet -Count 1) {
Write-Host """$ComputerName""" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
break
} else {
Write-Host """$ComputerName""" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black -NoNewline
Write-Host ". Pausing for $SleepTimer minutes. Remaining attempts: $($RemainingAttempts - 1)"
Start-Sleep -Seconds ($SleepTimer * 60)
$RemainingAttempts--
}
}
if($RemainingAttempts -eq 0) {
Write-Host "Maximum number of attempts reached" -BackgroundColor $DefaultBackgroundColor
}
}
hope this helps!
Let me know if you have any question concerning the code.
KR
Guenther
Test-Connection returns an array of objects or a $null depending if the ip or hostname is live.
so its enough if you do this:
IF($online){
// if its alive
}
else
{
// if its not responding
}
another thing to point out is you dont have to
if(condition) {
}
elseif(condition) {
}
if you are testing "bool question-answer" you don't need the elseif. In your case, if you are checking the state of computer then you can't have more answers than 2 (yes/no). So the elseif is in this case redundant.
if(condition) {
// if the condition is true
}
else {
// if the condition is not true. you can't have the third option
}
Also your if condition If ($Online -NE "True") is checking if the variable $Online is not equal the exact string "true" or "True" since -ne is case insensitive condition operator.
You should check if the variable holds anything or holds $Null
While I know Guenther Schmitz has already got the green check, I wanted to follow up with some additional details and pointers.
Test-Connection with the -Quiet switch returns a [Boolean] type. This is a simple true/false. I like to be extra careful with comparing string values to Boolean values because you can get some odd results depending on how you frame your tests. For example:
'false' -eq $true
$true -eq 'false'
Top one equals false, bottom one equals true because PowerShell is converting the second object into the same type as the first, and a non-empty string is true. So my strong recommendation is to stick to typed comparisons. So the first code change I'd start with is:
if ($Online -eq $true) {
As #pandemic already mentioned, you're doing a simple comparison, so no need for elseif, just else.
The second issue you have is that there is nothing removing computers from your testing list. The way your code is written, if the last computer in your testing list is "down" then it'd go through all the computers again. If the last computer in the list is "up" then it'd quit the do|while loop, irregardless of how many computers you are testing and how many are down. You can verify this with a simple test of putting 1 known hosts, and 1 fake host in the test file. If the known good host is first, it'll keep looping until $AttemptsCounter reaches the maximum allowed. If you flip the list around, it'll bail out immediately after testing the known good host.
Your initial code looped through all the computers once then went back and started again. In Guenther's example, they loop one computer at a time, and check until it comes up, or bail out after exceeding the test count. I think Guenther's example was probably what you were after, but if you wanted to make it cycle through all the computers, and then test only those that had failed, I'd write something like this:
$comps = Get-Content -path 'c:\restarted.txt'
[int]$SleepTimer = 1
[int]$Attempts = 2
[int]$AttemptsCounter = 0
do {
Write-Host "Testing Network, attempt $($AttemptsCount + 1)"
$newComps = #()
foreach($comp in $comps) {
$online = Test-Connection -Quiet -Count 1 -ComputerName $comp
if ($online) {
Write-Host "$comp" -BackgroundColor Green -NoNewline
Write-Host " Is coming online...Skipping to offline one's"
} else {
$newComps += $comp
Write-Host "$comp" -BackgroundColor Red -NoNewline
Write-Host " is Offline" -BackgroundColor Red -ForegroundColor Black
}
}
$comps = $newComps
$AttemptsCounter++
Start-Sleep -Seconds ($SleepTimer * 60)
}
while (($comps.count -gt 0) -and ($AttemptsCounter -lt $Attempts))
if ($comps.Count -gt 0) {
Write-Host "Exceeded Attempts Counter" -BackgroundColor Red
}
In this code, it is taking the list of computers, and when it fails on a computer, it stuffs it into a temporary list for reuse.

Why aren't error thrown when I run this function as domain administrator?

This script is intended to recurse through a series of directories and when an error of type DirUnauthorizedAccessError,Microsoft or PowerShell.Commands.GetChildItemCommand is thrown it's supposed to call another function Take-Ownership which takes ownership of the directory and adds full permissions for the localAdmin and domain admin to the folder. (It's really a script used for easing the deletion of old user profiles):
function Test-Folder($FolderToTest, $localAdminName) {
# Remeber the old error preference...
$old_ErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'SilentlyContinue'
$error.Clear()
# Go through the directories...and capture errors in $error
Get-ChildItem $FolderToTest -Recurse -ErrorAction SilentlyContinue -ErrorVariable errz | Select FullName
Write-Host $errz.count
if ($errz.Count -eq 0) {
Write-Host "blah no errors"
foreach ($err in $errz) {
Write-Host "Error: $err"
if ($err.FullyQualifiedErrorId -eq "DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand") {
Write-Host Unable to access $err.TargetObject -Fore Red
Write-Host Attempting to take ownership of $err.TargetObject -Fore Yellow
Take-Ownership -Folder $err.TargetObject, -LocalAdminName $localAdminName
Test-Folder -FolderToTest $err.TargetObject -localAdminName $localAdminName
}
}
}
$ErrorActionPreference = $old_ErrorActionPreference
}
Unfortunately, it doesn't throw any errors when I run it as domain administrator. I've found a list of ErrorActionPreferences here, but the errors just seem to get ignored, and it outputs blah no errors What can I do to make sure I receive errors and that my Take-Ownership function is actually called?
Your code only enters the if block if $errz.Count is 0. With a count of 0 there are no elements in $errz, so there's nothing to do for the foreach loop.
Add an else branch to the conditional, move the foreach loop there, and the code should do what you want.
if ($errz.Count -eq 0) {
Write-Host "blah no errors"
} else {
foreach ($err in $errz) {
Write-Host "Error: $err"
...
}
}