Tuesday, May 31, 2022

Handling errors in Powershell (terminating and non-terminating) and capturing errors into a variable without a red letter message being output

 


If you've ever written a script for someone else and wanted to hide (but still capture) the normal red letter output that happens from time to time, you've probably experienced confusion with how Powershell handles terminating and non-terminating errors. The example code below is non-terminating error examples and it is difficult to hide the red letter output but still catch the error to do some error handling within the script. Using " -erroraction silentlycontinue -errorvariable myErr" does not work as would be expected. 

With terminating errors, a standard try catch will suffice and you probably wouldn't want to suppress a terminating error or continue running the script if one occurs. Non-terminating errors are not detrimental to a script running and can continue through if you choose to.

Why you might ask? Sometimes the red letter output will confuse the end user and a simple "Write-Host" will suffice telling them a file doesn't exist, a process doesn't exist, a user doesn't exist or a user doesn't have a photo. Capturing the output will allow the script writer to instead tell the user that "The file doesn't exist, check the name again" in a yellow informational warning output. This way you can also customize what is output to any logging informational file. The script also doesn't appear broken, however if you need to instruct the user on what to do, you can do that with your own output. Below are examples of a standard cmdlet, an AD cmdlet, an Exchange cmdlet and an o365 cmdlet (notice I add a prefix of "eo" for the o365 cmdlet). You can also dot source the variable with different properties this way.

You can also use " -erroraction silentlycontinue" to hide the red letter response, although most people that read this probably already know that. Using -erroraction and using " -errorvariable myErr" does not work, it suppresses the error output and does not store anything in the variable. Take note that the error variable, when defined in the command, does not have a dollar sign before the text.

Here is a description of terminating vs non-terminating errors for your reading.

https://www.tutorialspoint.com/what-is-terminating-and-non-terminating-errors-in-powershell


Individual command examples

 
$suppressedError = $null; try {Get-ChildItem c:\temp\blahblah.txt -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} Write-Host ""; Write-Host ""; Write-Host "Error: " $suppressedError;

$suppressedError = $null; try {Get-Process 123456789 -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError = $error[0];} Write-Host ""; Write-Host ""; Write-Host "Error: " $suppressedError;

$suppressedError = $null; try {Get-ADUser abcdefghijklm -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError = $error[0];} Write-Host ""; Write-Host ""; Write-Host "Error: " $suppressedError;

$suppressedError = $null; try {Get-UserPhoto -Identity "abcdefghijklm" -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError = $error[0];} Write-Host ""; Write-Host ""; Write-Host "Error: " $suppressedError;

$suppressedError = $null; try {Get-eoUserPhoto "abcdefghijklm" -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError = $error[0];} Write-Host ""; Write-Host ""; Write-Host "Error: " $suppressedError;




Here is an example with all commands error output added to an array. The on prem and o365 Exchange commands might behave strangely if you don't have the cmdlets loaded.

Errors stored in an array example
$suppressedError = $null; try {Get-ChildItem c:\temp\abcdefghijklm.txt -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} 
try {Get-Process 123456789 -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} 
try {Get-ADUser abcdefghijklm -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} 
try {Get-UserPhoto -Identity "abcdefghijklm" -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} 
try {Get-eoUserPhoto "abcdefghijklm" -ErrorAction Stop -ErrorVariable suppressedError; } catch [System.Exception] { $suppressedError += $error[0];} 
Write-Host ""; Write-Host ""; 
Write-Host "Error count:" $suppressedError.Count;
Write-Host "Error:" $suppressedError;
Write-Host "";
Write-Host "Error 1:" $suppressedError[0]; Write-Host "";
Write-Host "Error 2:" $suppressedError[1]; Write-Host "";
Write-Host "Error 3:" $suppressedError[2]; Write-Host "";
Write-Host "Error 4:" $suppressedError[3]; Write-Host "";
Write-Host "Error 5:" $suppressedError[4]; Write-Host "";










Friday, February 11, 2022

How to keep your server from locking the screen or turning off the display, evading a timeout period

Sometimes you don't have access to group policy or powercfg.exe and have a virtualized server that makes no sense to have screen lock on, when your desktop/laptop has its own screen locking provision and remoting in through your hardware is the only way the VM can be accessed. It's like locking your car doors when your car is inside of your locked garage.

These 2 registry files will cure that, so that you aren't constantly unlocking your servers, while going back and forth between them and Outlook and whatever else your daily duties involve.

If they change back, a PowerShell script may be required to start upon login to monitor the changes

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Policies\Microsoft\Windows\Control Panel\Desktop]

"ScreenSaveActive"="0"

"ScreenSaverIsSecure"="0"

"ScreenSaveTimeOut"="4294000000"

"SCRNSAVE.EXE"="%systemroot%\\System32\\zscrnsave.scr"

and 

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System]
"NoDispScrSavPage"=dword:00000000
"HideLogonScripts"=dword:00000000


These will keep you from proverbially (and pointlessly) locking your car inside of your locked garage 

Here is a powershell script that will monitor the keys should they revert back inadvertently .

while($true)
{
$arr_regValueNamesAndValues = New-Object System.Collections.Generic.List[String];
$int_changesCount = 0;
$regHKCU = [Microsoft.Win32.Registry]::CurrentUser;
$regKeyRoot = $regHKCU.OpenSubKey("Software\Policies\Microsoft\Windows\Control Panel\Desktop", $true)
$valueNames = $regKeyRoot.GetValueNames();
foreach ($valueName in $valueNames)
{
$valueNameValue = $regKeyRoot.GetValue($valueName)
$arr_regValueNamesAndValues.Add([string]$valueName + "|" + [string]$valueNameValue);
}
foreach ($regValueNameAndValue in $arr_regValueNamesAndValues)
{
$regValueNameAndValueSplit = $regValueNameAndValue.Split("|");

if ($regValueNameAndValueSplit[0] -eq "ScreenSaveActive")
{
if ($regValueNameAndValueSplit[1] -ne "0")
{
$regKeyRoot.SetValue("ScreenSaveActive", "0", [Microsoft.Win32.RegistryValueKind]::String);
Write-Host " *** Set ScreenSaveActive to 0" ([DateTime]::Now.ToString());
$int_changesCount++;
}
}
elseif ($regValueNameAndValueSplit[0] -eq "ScreenSaverIsSecure")
{
if ($regValueNameAndValueSplit[1] -ne "0")
{
$regKeyRoot.SetValue("ScreenSaverIsSecure", "0", [Microsoft.Win32.RegistryValueKind]::String);
Write-Host " *** Set ScreenSaverIsSecure to 0" ([DateTime]::Now.ToString());
$int_changesCount++;
}
}
elseif ($regValueNameAndValueSplit[0] -eq "ScreenSaveTimeOut")
{
if ($regValueNameAndValueSplit[1] -ne "900")
{
$regKeyRoot.SetValue("ScreenSaveTimeOut", "900", [Microsoft.Win32.RegistryValueKind]::String);
Write-Host " *** Set ScreenSaveTimeOut to 900" ([DateTime]::Now.ToString());
$int_changesCount++;
}
}
else
{
#Write-Host "Unlisted regValueName" ([DateTime]::Now.ToString());
}
}

if ($int_changesCount -eq 0)
{
Write-Host "No changes needed" ([DateTime]::Now.ToString());
}

Sleep -Seconds 30;
}


keywords: disable screen timeout lock registry screen saver off display off powercfg.exe powercfg power options advanced settings power plan view change annoying

Thursday, January 6, 2022

So you're trying to use Powershell Out-File or Add-Content to create a csv with data/a variable that has (contains) commas inside of it?

Below is the code example of why this is such a PITA. It uses Exchange Powershell to grab distribution groups from an OU and then to read the members of each group recursively.

When formatting for an Excel csv that has commas in the data, you have to put quotes around ONLY the cell data that innately contains a comma, if you put commas around each cell's data, only the first cell with commas contained in the data will format correctly and the next cell in that same row that has a comma inside of its data, will not format correctly, which will drive you bonkers if you are like me.

How it looks when wrong (row 2) vs correct (row 4)


The code below adds the data for each row into an array and then loops through it and only prepends/appends quotes to the cell data when the cell data itself has a comma within it. If you want to roll your own, it should be pretty easy to add your data for each cell to an array and then use the loop that has foreach ($outputTemp in $outputArr), down to the code $output = "";

I couldn't find this via a web search anywhere else on the internet, so hopefully this helps someone. Comment with questions.


$outFile = "c:\temp\filename.csv";
"ADGroupDN, ADGroupSam, MemberDN, MemberSam" | Out-File $outFile -Encoding utf8;
$groupCount = 0;
$memberCount = 0;
$adGroups = Get-ADGroup -SearchBase "OU HERE" -Filter * -ResultSetSize 99999999; # **************
#Write-Host "Adgroup count" $adGroups.Count;
foreach ($adGroup in $adGroups) 
{
    $groupCount++;
    $members = Get-ADGroupMember $adGroup -Recursive;
    foreach ($member in $members)
    {
        $memberCount++;
        $outputArr = New-Object System.Collections.ArrayList;
        $outputArr.Add($adGroup.DistinguishedName) | Out-Null;
        $outputArr.Add($adGroup.SamAccountName) | Out-Null;
        $outputArr.Add($member.distinguishedName) | Out-Null;
        $outputArr.Add($member.SamAccountName) | Out-Null;
    
        $index = 0;
        foreach ($outputTemp in $outputArr)
        {
            if ($outputTemp.ToString().Contains(","))
		    {
			    $output += "`"$outputTemp`",";
		    }
		    else
		    {
			    $output += $outputTemp + ",";
		    }

            $index++;
		
		    if ($index -eq $outputArr.Count)
            {
                if ($output.EndsWith(","))
                {
                    $output = $output.Remove($output.Length - 1);
                }
            }
        }

        $output | Out-File $outFile -Append -Encoding utf8;
        $outputTemp = $output;
        $output = "";
    }
}

Write-Host $groupCount;
Write-Host $memberCount;