I am trying to handle an ActiveDirectoryObjectNotFoundException exception in PowerShell when using the Forest.GetForest method.
https://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectory.forest.getforest(v=vs.110).aspx
# Clear screen
Clear
# Change below as per your requirements
$context='forest'
$name='My.Lab.Local'
$username="fake\Administrator"
$password="FakePassword"
Write-Host -Object "Connecting $context... -> $name " -BackgroundColor Yellow -ForegroundColor Blue
try
{
$DC = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext($context,$name,$username,$password)
Write-Host -Object "Successfully connected to $context using discovery account $username." -BackgroundColor Green -ForegroundColor Blue
Write-Host -Object "Retrieving details of the forest..." -BackgroundColor Yellow -ForegroundColor Blue
$Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($DC)
$Forest.Name
Write-Host -Object "Successfully retrived your forest..." -BackgroundColor Green -ForegroundColor Blue
}
catch [System.DirectoryServices.ActiveDirectory.ActiveDirectoryObjectNotFoundException]
{
Write-Host "ActiveDirectoryObjectNotFoundException exception"
}
catch [System.Security.Authentication.AuthenticationException]
{
Write-Host "AuthenticationException exception ( Catch Block )"
}
finally
{
Write-Host "cleaning up ...( Finally Block )"
}
The Output
Connecting forest... -> Web.Metacash.Com
Successfully connected to forest using discovery account fake\Administrator.
Retrieving details of the forest...
AuthenticationException exception ( Catch Block )
cleaning up ...( Finally Block )
How to do I get the original failing message instead of giving my own message, like using $_.message or something?
$_ should have an ErrorRecord in the catch block. The exception should be in there. For example, use $_.Exception.Message to get its message. Of course the error record has more info about the error. $_.InvocationInfo.ScriptLineNumber would have the line number where the error occurred.
Related
I made a very very simple code that just checks a few regkeys. However to make it more nice to the eyes I was hoping that whenever it's False i can make it red and whenever it's True i can make it green.
I googled a bunch about this but couldn't find a clear solution for what i'm trying to accomplish. Any tips are very much appreciated.
Write-Host "Update Pending" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
""
Write-Host "Reboot Pending:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
""
Write-Host "Reboot Required:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
""
Write-Host "Pending File Rename Operations:" -ForegroundColor Cyan
Test-Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations'
""
Write-Host "Beschikbare Updates:" -ForegroundColor Cyan
Test-Path 'HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile'
Save the output from Test-Path to a variable, the use the value to specify one color or the other to pass as an argument to Write-Host:
Write-Host "Update Pending" -ForegroundColor Cyan
$testResult = Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
Write-Host $testResult -ForegroundColor #('Red', 'Green')[$testResult]
Write-Host ''
PowerShell will implicitly convert $False to 0 and $True to 1 when used in an array indexer, so $False results in 'Red' being picked.
Since you're basically repeating the same test operation every time, you can simplify your code by organizing the labels and registry keys into a hashtable and then only write the code to test and output once:
$testCases = [ordered]#{
"Update Pending" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Services\Pending'
"Reboot Pending" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
"Reboot Required" = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired'
"Pending File Rename Operations" = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations'
"Beschikbare Updates" = 'HKLM:\SOFTWARE\Microsoft\Updates\UpdateExeVolatile'
}
foreach($label in $testCases.psbase.Keys){
Write-Host "${label}:" -ForegroundColor Cyan
$testResult = Test-Path $testCases[$label]
Write-Host $testResult -ForegroundColor #('Red','Green')[$testResult]
Write-Host ''
}
I am trying to come up with a simple function to verify credentials before running other commands in ExchangeOnline.
Here is my problem(s):
IF I run the function with correct credentials the first time, it proceeds as expected. But if the credentials are wrong the first time, it get stuck in the "Get-credential" loop even when I provide the correct credentials. can't seem to find what the issue is.
try
{
$Global:ErrorActionPreference ='Stop'
$Usercreds= Get-Credential -Message "Please enter your Elevated account credentials"
Connect-MsolService -Credential $Usercreds
Write-Host "`nSuccesfully authenticated" -ForegroundColor Green
Get-MsolDomain
}
catch
{
#$Global:ErrorMessage = $_.Exception.Message
$Global:ErrMsg= $Error[0]
Write-Host `n$Global:ErrMsg -ForegroundColor Red
}
}
DO{
verCreds
}WHILE($Global:ErrMsg -like "Authentication Error: *")
This is because the ErrMsg is a global variable and it will always have the value of Authentication Error: even you entered the credential right.
try to clear its value in the last step of the try
try
{
$Global:ErrorActionPreference ='Stop'
$Usercreds= Get-Credential -Message "Please enter your Elevated account credentials"
Connect-MsolService -Credential $Usercreds
Write-Host "`nSuccesfully authenticated" -ForegroundColor Green
Get-MsolDomain
$Global:ErrMsg = $null
}
catch
{
#$Global:ErrorMessage = $_.Exception.Message
$Global:ErrMsg= $Error[0]
Write-Host `n$Global:ErrMsg -ForegroundColor Red
}
}
DO{
verCreds
}WHILE($Global:ErrMsg -like "Authentication Error: *")
I'm working on a script that gets executed only if X account is found, but is not working as intended the if/else statements get bypassed and the code gets executed anyways. What am i doing wrong?
$Account = "XXXX"
Get-LocalUser -name $Account
if (($Account) -eq $true) {
} else {
Write-host -foreground cyan "I found it"
}
exit
If i ran the script as is it will output the text on the console even tho "XXX" account is not present, could something like that can be done?
This should do it:
$Account = "XXXX"
$AccountObject=Get-LocalUser -name $Account -ErrorAction SilentlyContinue
if (($AccountObject)) {
Write-host -foreground cyan "I found it"
} else {
Write-host -foreground cyan "No luck"
}
The issue with the sniplet provided - the return of Get-LocalUser was not used. Instead you were using a string value which is always set therefore true - as you set it to 'XXXX' in your first line.
As Bill_Stewart explains, the reason that the else block is reached is because ($Account) -eq $true evaluates to $false unless the account name is "true".
In order to test whether Get-LocalUser succeeded or failed to retrieve the user account, you can instead inspect the $? automatic variable - it will have a value of $false only if the previous command threw an error:
$AccountName = "nonExistingUser"
# Try to fetch existing user account, don't show any errors to the user
$UserAccount = Get-LocalUser -Name $AccountName -ErrorAction SilentlyContinue
# Test if the call was successful
if($?) {
Write-Host "Found account named '$AccountName'!" -ForegroundColor Cyan
$UserAccount
} else {
Write-Host "No account named '$AccountName' was found ..." -ForegroundColor Magenta
}
Today I have dealt with logging in PowerShell as well as with the different streams and the pipeline. Unfortunately, none of these solutions really met my needs.
My requirements are:
I need to output information from PowerShell to a file log.
The file log has a predefined structure. So it's not enough to just redirect all streams to the file.
I want to use mainly standard PowerShell functions such as Write-Error, Write-Warning, Write-Verbose.
The overhead in the code through logging should be minimal.
I have now developed the following idea:
When calling a function from my script, all streams are piped to a logging function.
This function separates the debug, verbose, warning and error objects from the resulting object.
The resulting object is released back into the pipeline.
Here is my solution:
function Split-Streams {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
$InputStream
)
process{
switch($InputStream.GetType())
{
'System.Management.Automation.DebugRecord' {
# Do whatever you want, like formatting an writing to a file.
Write-Host $InputStream -ForegroundColor Gray
}
'System.Management.Automation.ErrorRecord' {
Write-Host $InputStream -ForegroundColor Red
Write-Host ('Error function: {0}' -f $InputStream[0].InvocationInfo.MyCommand.Name) -ForegroundColor Red
}
'System.Management.Automation.VerboseRecord' { Write-Host $InputStream -ForegroundColor Cyan }
'System.Management.Automation.WarningRecord' { Write-Host $InputStream -ForegroundColor Yellow }
default { return $InputStream }
}
}
}
function Write-Messages
{
[CmdletBinding()]
param()
Write-Debug "Debug message"
Write-Output "Output message"
Write-Verbose "Verbose message"
Write-Warning "Warning message"
Write-Error "Error message"
}
$Test2 = Write-Messages -Verbose -Debug *>&1 | Split-Streams
Write-Host $Test2 -ForegroundColor White
So now my question:
Is there something wrong with my solution? Have I missed any problems?
Translated with www.DeepL.com/Translator
I'm a Powershell newb, but I am trying to write a script to check the SSL certificate expiration dates for multiple remote websites.
I found this script (http://www.zerrouki.com/checkssl/) that does what I want, but only for a single site.
I am trying to modify it to allow for multiple sites/checks, but am getting an error when I do so. I've removed all of the email functionality from the script as I'll be using another to tool to alert on expiring certs. And I've hardcoded the URLs to check.
<#
Modified from Fabrice ZERROUKI - fabricezerrouki#hotmail.com Check-SSL.ps1
#>
$WebsiteURLs= #("URL1.com","URL2.com","URL3.com")
$WebsitePort=443
$CommonName=$WebsiteURL
$Threshold=120
foreach ($WebsiteURL in $WebsiteURLs){
Try{
$Conn = New-Object System.Net.Sockets.TcpClient($WebsiteURL,$WebsitePort)
Try {
$Stream = New-Object System.Net.Security.SslStream($Conn.GetStream())
$Stream.AuthenticateAsClient($CommonName)
$Cert = $Stream.Get_RemoteCertificate()
$ValidTo = [datetime]::Parse($Cert.GetExpirationDatestring())
Write-Host "`nConnection Successfull" -ForegroundColor DarkGreen
Write-Host "Website: $WebsiteURL"
$ValidDays = $($ValidTo - [datetime]::Now).Days
if ($ValidDays -lt $Threshold)
{
Write-Host "`nStatus: Warning (Expires in $ValidDays days)" -ForegroundColor Yellow
Write-Host "CertExpiration: $ValidTo`n" -ForegroundColor Yellow
}
else
{
Write-Host "`nStatus: OK" -ForegroundColor DarkGreen
Write-Host "CertExpiration: $ValidTo`n" -ForegroundColor DarkGreen
}
}
Catch { Throw $_ }
Finally { $Conn.close() }
}
Catch {
Write-Host "`nError occurred connecting to $($WebsiteURL)" -ForegroundColor Yellow
Write-Host "Website: $WebsiteURL"
Write-Host "Status:" $_.exception.innerexception.message -ForegroundColor Yellow
Write-Host ""
}
}
When I run this (with valid sites in the $WebsiteURLs variable) every site returns: Status: Authentication failed because the remote party has closed the transport stream.
If I only put one site in the $WebsiteURLs variable and remove the foreach function it runs ok.
Any idea what I can do to make this loop through each site in the variable?
Problem lies here:
$WebsiteURLs= #("URL1.com","URL2.com","URL3.com")
$WebsitePort=443
$CommonName=$WebsiteURL
When you call $Stream.AuthenticateAsClient($CommonName) it doesn't work, because $CommonName=$WebsiteURL is setting $commonName to null. When you remove the loop I assume you did as I did and changed $WebsiteURLs to $WebsiteURL so then you had a value to assign $CommonName.
If you move the declaration of $CommonName to within your loop it works.
$WebsiteURLs= #("URL1.com","URL2.com","URL3.com")
$WebsitePort=443
$Threshold=120
foreach ($WebsiteURL in $WebsiteURLs){
$CommonName=$WebsiteURL
Try{
$Conn = New-Object System.Net.Sockets.TcpClient($WebsiteURL,$WebsitePort)
Try {
$Stream = New-Object System.Net.Security.SslStream($Conn.GetStream())
$Stream.AuthenticateAsClient($CommonName)
$Cert = $Stream.Get_RemoteCertificate()
$ValidTo = [datetime]::Parse($Cert.GetExpirationDatestring())
Write-Host "`nConnection Successfull" -ForegroundColor DarkGreen
Write-Host "Website: $WebsiteURL"
$ValidDays = $($ValidTo - [datetime]::Now).Days
if ($ValidDays -lt $Threshold)
{
Write-Host "`nStatus: Warning (Expires in $ValidDays days)" -ForegroundColor Yellow
Write-Host "CertExpiration: $ValidTo`n" -ForegroundColor Yellow
}
else
{
Write-Host "`nStatus: OK" -ForegroundColor DarkGreen
Write-Host "CertExpiration: $ValidTo`n" -ForegroundColor DarkGreen
}
}
Catch { Throw $_ }
Finally { $Conn.close() }
}
Catch {
Write-Host "`nError occurred connecting to $($WebsiteURL)" -ForegroundColor Yellow
Write-Host "Website: $WebsiteURL"
Write-Host "Status:" $_.exception.innerexception.message -ForegroundColor Yellow
Write-Host ""
}
}