-debug requesting confirmation - debugging

I have a simple script running in Powershell 3.0 :
# Testing write-debug
[CmdletBinding()]
param()
write-debug "Debug message"
Write-Output "General output"
When I run it without parameters, I get the desired output:
PS C:\scripts\Test> .\debugtest.ps1
General output
When I run it with the -debug parameter, Powershell asks me to confirm after printing the Debug message:
PS C:\scripts\Test> .\debugtest.ps1 -Debug
DEBUG: Debug message
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"):
General output
Why am I being asked to confirm? Shouldn't write-debug simply write the debug output and continue with the script?
Update: $DebugPreference is set to SilentlyContinue:
PS C:\scripts\Test> $DebugPreference
SilentlyContinue
PS C:\scripts\Test> .\debugtest.ps1 -Debug
DEBUG: Debug message
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"):
General output

It sounds like your $DebugPreference variable is set to 'Inquire'.
From Get-Help about_preference_variables:
$DebugPreference
------------------
Determines how Windows PowerShell responds to debugging messages
generated by a script, cmdlet or provider, or by a Write-Debug
command at the command line.
Some cmdlets display debugging messages, which are typically very
technical messages designed for programmers and technical support
professionals. By default, debugging messages are not displayed,
but you can display debugging messages by changing the value of
$DebugPreference.
You can also use the Debug common parameter of a cmdlet to display
or hide the debugging messages for a specific command. For more
information, type: "get-help about_commonparameters".
Valid values:
Stop: Displays the debug message and stops
executing. Writes an error to the console.
Inquire: Displays the debug message and asks you
whether you want to continue. Note that
adding the Debug common parameter to a
command--when the command is configured
to generate a debugging message--changes
the value of the $DebugPreference variable
to Inquire.
Continue: Displays the debug message and continues
with execution.
SilentlyContinue: No effect. The debug message is not
(Default) displayed and execution continues without
interruption.
Edit: -Debug is also a cmdlet Common Parameter, and by adding CmdletBinding(), it is also a Common Parameter of your script.
From Get-Help about_common_parameters:
COMMON PARAMETER DESCRIPTIONS
-Debug[:{$true | $false}]
Alias: db
Displays programmer-level detail about the operation performed by the
command. This parameter works only when the command generates
a debugging message. For example, this parameter works when a command
contains the Write-Debug cmdlet.
**The Debug parameter overrides the value of the $DebugPreference
variable for the current command, setting the value of $DebugPreference
to Inquire.** Because the default value of the $DebugPreference variable
is SilentlyContinue, debugging messages are not displayed by default.
Valid values:
$true (-Debug:$true). Has the same effect as -Debug.
$false (-Debug:$false). Suppresses the display of debugging
messages when the value of the $DebugPreference is not
SilentlyContinue (the default).

You can override $DebugPreference in the script:
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ($PSBoundParameters['Debug']) {
$DebugPreference = 'Continue'
}
Write-Output "General output"
Write-Debug "Debug message"

I think second parameter declarationof the following function can be added any function
function SetDebugPreference {
[CmdletBinding()]
Param(
[Parameter(
Mandatory=$true,
HelpMessage="Please enter for `$DebugPreference a value`n('SilentlyContinue','Continue' ,'Inquire' or 'Stop')",
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true,
Position=0
)]
[ValidateSet("SilentlyContinue","Continue" ,"Inquire","Stop")]
[Alias("dbp","dbPref")]
[string]
$dbPreference,
[Parameter(
Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=1
)]
[ValidateSet("SilentlyContinue","Continue" ,"Inquire","Stop")]
[Alias("ldbp","ldbPref")]
[string]
$LocalDebugPreference="Continue"
)
Begin {
Write-Verbose ("Local DebugPreference: " + $DebugPreference)
$DebugPreference=$LocalDebugPreference
Write-Verbose ("Local DebugPreference set: " + $LocalDebugPreference)
Write-Debug "Local debug test"
}
Process {
Write-Verbose ("Global DebugPreference: " + $Global:DebugPreference)
$Global:DebugPreference=$dbPreference
Write-Verbose ("Global DebugPreference set: " + $Global:DebugPreference)
}
}

Related

Try/Catch not working with a terminating error; script hangs

I'm trying to gracefully exit the program if the username/password is incorrect or (ORA-01017) but the script hangs when getting an error. My proposed error is in the try section of the code then the entire script hangs. I thought the catch is suppose to throw the exception. Ideally I will create a for loop that will connect to each database and if there is an error just capture the error and go on to the next. But right now just testing the try/catching the error option. Also, I'm encrypting the password. Any ideas or suggestions
enter code here
function Start-Something
{
$User = Read-Host -Prompt 'Input the Oracle database user name'
$psswd = Read-Host -Prompt 'Input Oracle database Password:' -AsSecureString
$cryptpasswd = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($psswd))
Write-Host "###############################################"
Write-Host Connecting to Oracle Database: testdb -ForegroundColor Green
sqlplus -s $User/$cryptpasswd#$line '#C:\Users\my\Documents\Scripts\oracledbinfo.sql'
Write-Host "###############################################"
Write-Host " ###########################################"
}
try
{
Start-Something
}
catch
{
Write-Output "Something threw an exception"
Write-Output $_
}
try
{
Start-Something -ErrorAction Stop
}
catch
{
Write-Output "Something threw an exception or used Write-Error"
Write-Output $_
}
Try/Catch does not hang. Looks like this line of code is waiting for input:
sqlplus -s $User/$cryptpasswd#$line '#C:\Users\my\Documents\Scripts\oracledbinfo.sql'
To verify run the codeblock of the function directly in the shell.
Currently you ask for the password and convert the string entered to a secureString Read-Host -Prompt 'Input Oracle database Password:' -AsSecureString then you probably try to convert the value back to string, because sqlplus won't accept a secureString. But you missed a part =(System.Runtime.InteropServices.Marshal]::PtrToStringAuto())...
Anyways, here are some tips for you:
Take a look at the oracle .NET provider its the better choice on PowerShell.
If you want the user to input credentials use the get-credential cmdlet. By doing so you get a credential object back which you can use to authenticate or by calling the method getNetworkCredential() to get the password in cleartext.
Implement also a try/catch block within the function and return errors always with write-error and learn about output streams. try/Catch will only consider terminating errors, but sqlplus won't give you that -> oracle .NET provider.
Preface:
Toni's answer makes a good point that the apparent hang may be due to sqlplus waiting for an interactive response. If that is the case, that problem must be solved first.
A simpler way to convert a SecureString instance to plain text - which notably defeats the (limited) security benefits that SecureString provides (on Windows only) - is the following:
[pscredential]::new('unused', $psswd).GetNetworkCredential().Password
As an inconsequential aside, with respect to your approach: it should be [Runtime.InteropServices.Marshal]::PtrToStringBSTR(...), not [Runtime.InteropServices.Marshal]::PtrToStringAuto(...)
As of PowerShell 7.2.x, calls to external programs (such as sqlplus) never cause a (statement-)terminating error[1] that you can trap with try / catch
A potential future feature - which in preview versions of v7.3 is available as an experimental feature that may or may not become official - may allow better integration with PowerShell's error handling, by setting the $PSNativeCommandUseErrorActionPreference preference variable to $true.
Unfortunately, as of v7.3.0-preview.8, the error that is reported in response to a nonzero exit code (see below) is unexpectedly a non-terminating error, which means it can not be trapped with try / catch by the caller - see GitHub issue #18368.
To determine if a call to an external program call failed, examine its process exit code, as reflected in the automatic $LASTEXITCODE variable afterwards.
By convention, exit code 0 signals success, any nonzero value failure.
Therefore:
# Note: Write-Host calls omitted for brevity.
function Start-Something {
$User = Read-Host -Prompt 'Input the Oracle database user name'
$psswd = Read-Host -Prompt 'Input Oracle database Password:' -AsSecureString
$psswdClearText = [pscredential]::new('unused', $psswd).GetNetworkCredential().Password
# Invoke sqlplus.
# !! AS NOTED, YOU MAY HAVE TO MODIFY THIS COMMAND TO PREVENT AN INTERACTIVE PROMPT.
# Stderr output from this call, if any, goes directly to the display.
sqlplus -s $User/$psswdClearText#$line '#C:\Users\my\Documents\Scripts\oracledbinfo.sql'
# If sqlplus reported a nonzero exit code,
# throw a script-terminating error that the caller must handle with try / catch.
if ($LASTEXITCODE -ne 0) {
throw "sqlplus signaled failure: exit code is: $LASTEXITCODE"
}
}
try {
Start-Something
}
catch {
# Convert the script-terminating error into a non-terminating one.
$_ | Write-Error
}
[1] There are only two exceptions: (a) If the external program cannot even be invoked, e.g., because its name/path is misspelled or it isn't installed. Or (b), due to a bug, present up to PowerShell 7.1, where the combination of redirecting stderr output (2> or *>) with $ErrorActionPreference = 'Stop' unexpectedly caused a script-terminating error if there's actual stderr output - see this answer.

Registry Powershell Output

I need your help
I need to print an logic condition in the specifc PowerShell output command.
Get-ItemProperty -Path 'HKLM:SOFTWARE\Policies\Microsoft\WindowsNT\TerminalServices' -Name fDisableCdm | Select fDisableCdm
I wrote a little script for test, but in my script I made a own conditional output. But I want print that what value have inside registry key and not print an own condition.
My script
if ($process=Get-ItemProperty -Path 'HKLM:SOFTWARE\Policies\Microsoft\WindowsNT\TerminalServices' -Name fDisableCdm -ErrorAction SilentlyContinue) {write-host "1"} else {write-host "0"}
You can help me ?
I waiting for help and tanks for all !
$regKey = "HKLM:SOFTWARE\Policies\Microsoft\WindowsNT\Terminal Services"
$regValue = 'fDisableCdm'
Try {
$process=Get-ItemProperty -Path $regKey -Name $regValue -ErrorAction Stop
Write-Output (-join($regValue,": ",$process.$regValue))
}
Catch {
Write-Output "There was an error: $($PSItem.ToString())"
}
You will need to check to see if your registry key for Terminal Services has a space or not. Mine has a space.
Otherwise, this script allows you to set the two variables at the beginning of the script, performs the requested lookup and, if no error occurs, outputs the requested information.
In the event of an error, the script outputs an appropriate error message.
Reference for meaningful error handling:
https://learn.microsoft.com/en-us/powershell/scripting/learn/deep-dives/everything-about-exceptions?view=powershell-7.1

Remote Exchange Powershell session with variables

I have a beginner's knowledge in scripting and programming and have a set of PowerShell commands that I am having issues with figuring out how to turn it into a script.
I can successfully run the following remote commands from my Windows 7 machine by running the Exchange 2007 PowerShell console as my domain administrator account and then running the following commands to pass the commands to the Exchange 2013 hybrid server for use with Office 365:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://hybridexch2013.contoso.com/PowerShell/ -Authentication Kerberos
Import-PSSession $Session
Enable-RemoteMailbox jame.doe#contoso.com -RemoteRoutingAddress jane.doe#contosoinc.mail.onmicrosoft.com
The more I look into this I don't know that I am making progress. See below for how my logic is working in my head on this. I understand this is incorrect and incomplete. I have commented out a function I had earlier but wasn't sure if I was doing it correct or if it was needed at all.
param($enableMailbox)
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://hybridexch2013.contoso.com/PowerShell/ -Authentication Kerberos
Import-PSSession $Session -AllowClobber
$fname = Read-Host "What is the user's first name?"
$lname = Read-Host "What is the user's last name?"
#function Func($enableMailbox)
#{
$enableMailbox = "Enable-RemoteMailbox $fname.$lname#contoso.com -RemoteRoutingAddress $fname.$lname#contosoinc.mail.onmicrosoft.com"
#}
#Func $enableMailbox
Write-Host $enableMailbox
I also find that if I manually run:
Enable-RemoteMailbox $fname.$lname#contoso.com -RemoteRoutingAddress $fname.$lname#contosoinc.mail.onmicrosoft.com
I get nothing. So I am not even understanding how you pass variables to the string to run the command correctly. Even if I run:
$fname = "Jane"
$lname = "Doe"
$enableMailbox = "Enable-RemoteMailbox $fname.$lname#contoso.com -RemoteRoutingAddress $fname.$lname#contosoinc.mail.onmicrosoft.com"
Write-Host $enableMailbox
I get no results.
I was trying to understand the param function using help from these pages: Powershell script with params *and* functions
Passing a variable to a powershell script via command line
https://devcentral.f5.com/blogs/us/powershell-abcs-p-is-for-parameters
http://www.experts-exchange.com/Programming/Languages/Scripting/Powershell/Q_27900846.html
But I am finding the param function difficult to understand and not sure I am even going in the right direction here. So far the only thing that seems to work is connecting to the PowerShell remotely.
Please help if I am am to be helped with this one and lack of my abilities here.
Param Function in short...
Function Write-Something
{
Param($InputText)
Write-Host $InputText
}
Write-Something Hello
Result is: Hello
Use Param Section to add Parameters to your Function like in the example above,
So for your Script:
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://hybridexch2013.contoso.com/PowerShell/ -Authentication Kerberos
Import-PSSession $Session -AllowClobber
Function Enable-MBX
{
Param($Fname,$Lname)
Enable-RemoteMailbox "$Fname $Lname" -RemoteRoutingAddress "$fname.$lname#contoso.mail.onmicrosoft.com"
}
Enable-MBX Jane Doe
I think you're just confused about Write-Host.
I'm not certain if you're trying to write the enable-remotemailbox to console or execute it. The code you have should work fine for writing to console (screen) but won't execute the command. To execute the command:
Enable-RemoteMailbox "$fname.$lname#contoso.com" -RemoteRoutingAddress "$fname.$lname#contosoinc.mail.onmicrosoft.com"
Anything inside of double-quotes will expand. Ex: "$fname" will expand to "Bob" if $fname is equal to "Bob". If you encapsulate the whole command in quotes in a variable though, the Write-Host will NOT execute the command. Write-Host is designed to write output to the screen so you'll just see the command in the console.
If you want to further expand, you can use the sub-string operator $(). Ex:
Write-Host "$($fname.length).$($lname.length)"
Comes back as "3.5" for "Bob" and "Smith".
To avoid expansion, use single quotes:
Write-Host '$($fname.length).$($lname.length)'
Writes "$($fname.length).$($lname.length)" to the console.
The following (similar to your code) without quotes: Write-Host $fname.$lname#contoso.com will try to pull the $lname#contoso.com Property of $fname which in this context doesn't make sense (doesn't exist). To elaborate, it would be equivalent to Write-Host $fname.($lname#contoso.com).

Setting counter on Windows Event log for Email notification

I am having below script:
$pattern = 'Unable to authenticate user!'
$events = Get-WinEvent -ea SilentlyContinue `
-ProviderName "Windows DB Controller - Task Manager Service"|
Where-Object { $_.TimeCreated -gt [datetime]::today -and $_.Message -match $pattern }
$events >> D:\Error.txt
if ($events) {
Send-MailMessage -SmtpServer smtp.domain.com -From No-reply#domain.com -To sunny#domain.com -Subject 'Error found in log' -Body $events
}
I had scheduled it to run on every 10 mins and purposely ,I wanted to achieve following point using above script:
Search the specified error message in the event viewer log only for current-date and as soon as the error message encountered send a email notification to me but didn't want to receive email notification for the error message which appreared today and for which I had already been notified (I mean , wanted to receive error-notification only once for a specific time of current day).
But problem I am facing here is: Getting multiple notifications for same error message for which already being notified.
I hope I am clear enough to put my exact problem.
Could you please help me out, how to resolve this problem ?
If you are running the script every 10 minutes, I would change the condition on the Where-Object so instead of getting all of the events that are "today"; I would change it to get only the events that happened in the last 10 minutes. i.e. the code becomes:
Where-Object { $_.TimeCreated -gt [datetime]::now.AddMinutes(-10) -and $_.Message -match $pattern }
Have a look at this thread:
Powershell - Tail Windows Event Log? Is it possible?
It's on tailing an event log, but the same method should work for what you're tyring to do. Just save the last index number to a file between runs.
How about the following approach:
Register-WmiEvent -Query "select * from __InstanceCreationEvent where TargetInstance ISA 'Win32_NTLogEvent' and TargetInstance.SourceName = 'Windows DB Controller - Task Manager Service' and TargetInstance.Message LIKE '%Unable to authenticate user!%'" -SourceIdentifier "MyEventListener" -Action {
#Your actions
write-host "$($eventargs.NewEvent.TargetInstance.RecordNumber) at $($eventargs.NewEvent.TargetInstance.TimeGenerated)"
}
It uses WMI to subscribe to the event that occurs when an eventlog entry is generated with your criterias. The action itself will only return the new object(so no more duplicates). I've included a sample action to help you understand how to access the object. This method will give you live monitoring.
Inside the action, $eventargs.NewEvent.TargetInstance will give you the object which is an instance of win32_ntlogevent. To see properties of this class, check out TechNet or run the following command:
([wmiclass]'win32_ntlogevent').Properties | ft Name
To make the script run forever, just call your script with powershell -file script.ps1 -noexit or include a while($true) loop at the end of your script. (I'm not sure how the while-loop will affect resource usage longterm, you'd have to test).

PowerShell difference between Write-Host and Write-Output?

What is the difference between Write-Host and Write-Output in PowerShell?
Like...
Write-Host "Hello World";
Write-Output "Hello World";
In a nutshell, Write-Host writes to the console itself. Think of it as a MsgBox in VBScript. Write-Output, on the other hand, writes to the pipeline, so the next command can accept it as its input. You are not required to use Write-Output in order to write objects, as Write-Output is implicitly called for you.
PS> Get-Service
would be the same as:
PS> Get-Service | Write-Output
Write-Output sends the output to the pipeline. From there it can be piped to another cmdlet or assigned to a variable.
Write-Host sends it directly to the console.
$a = 'Testing Write-OutPut' | Write-Output
$b = 'Testing Write-Host' | Write-Host
Get-Variable a,b
Outputs:
Testing Write-Host
Name Value
---- -----
a Testing Write-OutPut
b
If you don't tell Powershell what to do with the output to the pipeline by assigning it to a variable or piping it to anoher command, then it gets sent to out-default, which is normally the console so the end result appears the same.
Write-Output sends the data as an object through the pipeline. In the Questions example it will just pass a string.
Write-Host is host dependent. In the console Write-Host is essentially doing [console]::WriteLine.
See this for more info.
Another difference between Write-Host and Write-Output:
Write-Host displays the message on the screen, but it does not write it to the log
Write-Output writes a message to the log, but it does not display it on the screen.
And Write-Host is considered as harmful. You can see a detailed explanation in Write-Host Considered Harmful.
One more thing about Write-Host vs Write-Output: inline String concatenation may not work as expected.
$sampleText = "World"
Write-Host "Hello" $sampleText
returns
Hello World
but
$sampleText = "World"
Write-Output "Hello" $sampleText
returns
Hello
World
This would encourage Write-Output with a variable (and use of concatenation) holding the entire string at once.
$hw = "Hello " + $sampleText
Write-Output $hw
You can understand the difference between the two cmds with below example:
Write-host "msgtxt" | Get-Service
On running above, you will get output as "msgtxt"
Write-output "msgtxt" | Get-Service
On running above, you will receive an error since msgtxt is not the name of any service.( In ideal condition) (Since you are writing it to a pipeline and it is being passed as input to Get-Service)

Resources