Try/Catch Add-DnsServerResourceRecordA in PowerShell - powershell

I want to catch the exception that occurs, when adding a DNS record with Add-DnsServerResourceRecordA in PowerShell with the switch -CreatePTR, but no reverse lookupzone exists.
But there is no error. If I provoke the error a simple menu pops up and informs me about the situation. But independent from the -ErrorAction switch the $error variable does not receive an error. What's my fault?
Thanks for your reply.
PS C:\Users\xyz>> Add-DnsServerResourceRecordA -Name "test-mwi4" -IPv4Address 1.1.1.1 -CreatePtr -ZoneName contoso.biz -ErrorAction Continue
Add-DnsServerResourceRecordA : Failed to create PTR record. Resource record test-mwi4 in zone mn-man.biz on server MNDEMUCDC010 is created successfully, but corresponding PTR record could not be created.
At line:1 char:1
+ Add-DnsServerResourceRecordA -Name "test-mwi4" -IPv4Address 1.1.1.1 -CreatePtr - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (test-mwi4:root/Microsoft/...ResourceRecordA) [Add-DnsServerResourceRecordA], CimException
+ FullyQualifiedErrorId : WIN32 9715,Add-DnsServerResourceRecordA
Thank you!

In order to use this command in try catch block use below code:
Try
{
Add-DnsServerResourceRecordA -Name "test-mwi4" -IPv4Address 1.1.1.1 -CreatePtr -ZoneName contoso.biz -ErrorAction Stop
}
Catch
{
Write-Host "Error while adding pointer record:`n$($Error[0].Exception.Message)"
}
$Error[0] returns:
$Error[0]
Add-DnsServerResourceRecordA : Failed to get the zone information for
contoso.biz on server HYDLPT487.
At line:3 char:5
+ Add-DnsServerResourceRecordA -Name "test-mwi4" -IPv4Address 1.1.1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (test-mwi4:root/Microsoft/...Resou
rceRecordA) [Add-DnsServerResourceRecordA], CimException
+ FullyQualifiedErrorId : WIN32 1722,Add-DnsServerResourceRecordA

Related

PS Try-catch in VS Code call to Get-Mailbox don't catch error

I have a funktion to list all email-addresses on a specified user in Office365 and I use this function to check if an email-address is available.
Why is the Try-Catch not working in VS Code but in PS ISE?
Function ListAllEmailAdresses
{
param ($ID)
Try
{
$Mailbox = Get-Mailbox -Identity $ID -ErrorAction Stop
$VarUser = Get-User -Identity $ID -ErrorAction Stop
Write-Output "Email is occupied by: $Mailbox, $($VarUser.Title)"
Write-Output $Mailbox.EmailAddresses
}
Catch
{
Write-Output "E-mail $ID is available"
}
}
when calling this function in VS Code the Try-Catch is not working.
PS C:\script> ListAllEmailAdresses -ID 'lidingo#elon.se'
The operation couldn't be performed because object 'lidingo#elon.se' couldn't be found on 'VI1PR03DC0025.eurprd03.prod.outlook.com'.
+ CategoryInfo : NotSpecified: (:) [Get-Mailbox], ManagementObjectNotFoundException
+ FullyQualifiedErrorId : [Server=VI1PR03MB3165,RequestId=62016320-c687-41ed-9949-4b0fe854d821,TimeStamp=2017-08-09 13:54:40] [FailureCategory=Cmdlet-ManagementObjectNotFoundException] 2CD51ECE,Microsoft.Exchange.Management.RecipientTasks.GetMailbox
+ PSComputerName : ps.outlook.com
The operation couldn't be performed because object 'lidingo#elon.se' couldn't be found on 'VI1PR03DC0025.eurprd03.prod.outlook.com'.
+ CategoryInfo : NotSpecified: (:) [Get-User], ManagementObjectNotFoundException
+ FullyQualifiedErrorId : [Server=VI1PR03MB3165,RequestId=db435869-8b64-4edf-bb90-a2eb836b07fa,TimeStamp=2017-08-09 13:54:41] [FailureCategory=Cmdlet-ManagementObjectNotFoundException] DAFB12B9,Microsoft.Exchange.Management.RecipientTasks.GetUser
+ PSComputerName : ps.outlook.com
Email is occupied by: ,
The PS Error say that the called E-mailadrdress could not be found and therefore it should got to the Catch-code. But it doesnt :(
If I call the same function in Powershell ISE I get this result:
PS C:\Script> ListAllEmailAdresses -ID 'lidingo#elon.se'
E-mail lidingo#elon.se is available
The PS Error is catched and I get the wanted output.
/Anders

PowerShell module not dot-sourcing / importing functions as expected

Update 1:
Originally, I posted this with the title: "Scripts ignoring error handling in PowerShell module" as that is the current issue, however it seems more of a module issue, so I have renamed the title.
Update 2:
After a comment that made me question Azure cmdlets, I've tested with the most basic of scripts (added to the module) and the findings are the same, in that the error is not passed to the calling script, however, adding -errorVariable to Get-Service does return something (other than WriteErrorException) that I could probably harness in the handling of the error:
function Test-MyError($Variable)
{
Try
{
Get-Service -Name $variable -ErrorAction Stop -ErrorVariable bar
#Get-AzureRmSubscription -SubscriptionName $variable -ErrorAction Stop
}
Catch
{
Write-Error $error[0]
$bar
}
}
returns:
Test-MyError "Foo"
Test-MyError : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'foo'.
However, if I run "Test-MyError" in ISE, then call the function, I get:
Test-MyError "Foo"
Test-MyError : Cannot find any service with service name 'Foo'.
At line:3 char:1
+ Test-MyError "Foo"
+ ~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Test-MyError
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop: Cannot find any service with service name 'Foo'.
So I am not sure what is happening when running "Test-MyError" in ISE and calling it, against it being dot-sourced in the PSM1 file and then calling it?
Do I now have to use -ErrorVariable and handle on that?
Original Question:
I have two functions in a module: Get-Subscription and Get-AllSubscriptions. Each function sits in its own PS1 file and the PSM1 file dot-sources them. The module seems fine as the module scripts are accessible using intelisense and the module loads without issue. I've used this structure in many modules and I haven't come across this problem before. (Although I wonder if MS have changed the way modules work in PS 5.1 as I have noticed using FunctionsToExport='x','y','Z' and Export-ModuleMember don't seem to behave the same way as they used to.)
Get-AllSubscriptions calls Get-Subscription.
If I am not logged into Azure, Get-Subscription should throw an error which is handled, prompting me to log in. This works as expected, if I call Get-Subscription from the Get-Subscription.ps1.
However, when I call Get-Subscription from the a new PS1 file, Get-AllSubscriptions or from the powershell console, it doesn't work. It iterates all the way through the do..until loop, without "handling" the errors as I would expect. On each iteration, it seems to throw a generic error:
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
However, I do see the last error, Get-Subscription : Unable to find requested subscription after 3 login attempts.
If I execute Get-Subscription in ISE, then call Get-Subscription in a new PS1 file or from Get-AllSubscriptions, it works as expected, however, once I re-import the module (Import-Module AzureVnetTools -Force -Verbose), it goes back to the incorrect behaviour.
If I dot-source Get-Subscription, inside the caller script, it works, but why? This is what should happen with the module's PSM1.
Can anyone help me work out what I am doing wrong here?
(PS 5.1, Windows 7)
Get-Subscription:
function Get-Subscription
{
[cmdletbinding()]
Param
(
[string]$SubscriptionName,
[string]$UserName,
[string]$code
)
$c=1
Write-Verbose "Checking access to '$SubscriptionName' with user '$UserName'..."
Do
{
Write-Verbose "Attempt $c"
Try
{
$oSubscription = Get-AzureRmSubscription -SubscriptionName $SubscriptionName -ErrorAction Stop -WarningAction SilentlyContinue
Write-Verbose "Subscription found: $($oSubscription.SubscriptionName)."
}
Catch
{
if($error[0].Exception.Message -like "*Please verify that the subscription exists in this tenant*")
{
Write-Verbose "Cannot find subscription '$SubscriptionName' with provided credentials."
$account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
}
elseif($error[0].Exception.Message -like "*Run Login-AzureRmAccount to login*")
{
Write-Verbose "No logged in session found. Please log in."
$account = Login-AzureRmAccount -Credential (Get-Credential -UserName $Username -Message "Subscription '$SubscriptionName' user' password:")
}
else
{
Write-Error $error[0]
}
}
$c++
}
until(($oSubscription) -or ($c -eq 4))
if($c -eq 4)
{
Write-Error "Unable to find requested subscription after $($c-1) login attempts."
break
}
$oSubscription | Add-Member -MemberType NoteProperty -Name Code -Value $code
$oSubscription
}
Get-AllSubscriptions:
function Get-AllSubscriptions
{
[cmdletbinding()]
param
(
[string]$MasterSubscription,
[string]$MasterSubscriptionCode,
[string]$MasterSubscriptionUsername,
[string]$ChildSubscription,
[string]$ChildSubscriptionCode,
[string]$ChildSubscriptionUsername
)
Write-Verbose "Getting all subscriptions..."
$oAllSubscriptions = #()
$oMasterSubscription = Get-Subscription -SubscriptionName $MasterSubscription -UserName $MasterSubscriptionUsername -code $MasterSubscriptionCode -Verbose
$oChildSubscription = Get-Subscription -SubscriptionName $ChildSubscription -UserName $ChildSubscriptionUsername -code $ChildSubscriptionCode -Verbose
$oAllSubscriptions = ($oMasterSubscription,$oChildSubscription)
$oAllSubscriptions
}
Test:
$splat2 = #{
SubscriptionName = "SomeSubscription"
Code = "S02"
Username = "some.user#somewhere.com"
}
#Write-Output "Dot-source:"
#. "D:\Temp\PS.Modules\AzureVnetTools\functions\public\Get-Subscription.ps1"
Get-Subscription #splat2 -verbose
Output:
Get-Subscription #splat2 -verbose
VERBOSE: Checking access to 'SomeSubscription' with user 'some.user#somewhere.com'...
VERBOSE: Attempt 1
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
VERBOSE: Attempt 2
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
VERBOSE: Attempt 3
Get-Subscription : Exception of type 'Microsoft.PowerShell.Commands.WriteErrorException' was thrown.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
Get-Subscription : Unable to find requested subscription after 3 login attempts.
At line:7 char:1
+ Get-Subscription #splat2 -verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Get-Subscription
AzureVnetTools.psm1
#Get public and private function definition files.
$Public = #( Get-ChildItem -Path $PSScriptRoot\Functions\Public\*.ps1 -ErrorAction SilentlyContinue )
$Private = #( Get-ChildItem -Path $PSScriptRoot\Functions\Private\*.ps1 -ErrorAction SilentlyContinue )
#Dot source the files
Foreach($import in #($Public + $Private))
{
#write-error $import.fullname
Try
{
#Write-Host "Dot-sourcing file: $($import.fullname)."
. $import.fullname
}
Catch
{
Write-Error -Message "Failed to import function $($import.fullname): $_"
}
}
Export-ModuleMember -Function $Public.Basename
AzureVnetTools.psd1 (Relevant section):
FunctionsToExport = '*'
-ErrorAction Stop -WarningAction SilentlyContinue
Is it a warning that's being thrown instead of an error?
So my specific problem was that I was relying on handling the error and doing something based on that. The problem was caused by the way PowerShell's Write-Error works (or not) as I learned from the reply here, given by #Alek.
It simply wasn't passing the actual error back to the calling script. As #Alex suggested, I replaced Write-Error with $PSCmdlet.WriteError(). Although this didn't totally work.
In the Catch{} block, I then changed $error[0] to $_ and the full error was returned to the calling script / function.
I went one further and wrote a reusable function, added to my module:
function Write-PsError
{
[cmdletbinding()]
Param
(
[Exception]$Message,
[Management.Automation.ErrorCategory]$ErrorCategory = "NotSpecified"
)
$arguments = #(
$Message
$null #errorid
[Management.Automation.ErrorCategory]::$ErrorCategory
$null
)
$ErrorRecord = New-Object -TypeName "Management.Automation.ErrorRecord" -ArgumentList $arguments
$PSCmdlet.WriteError($ErrorRecord)
}
Which seems to be working well at the moment. I especially like the way intellisense picks up all the ErrorCategories. Not sure what or how ISE (PS 5.1 / Win 7) does that. I thought I was going to have to add my own dynamic parameter.
HTH.

Invalid namespace in PowerShell working with FSRM

Trying PS command:
Set-FsrmFileGroup -Name "Anti-Ransomware File Groups" -IncludePattern #((Invoke-WebRequest -Uri "https://fsrm.experiant.ca/api/v1/combined" -UseBasicParsing).Content | ConvertFrom-Json | % {$_.filters})
Getting error:
New-FsrmFileGroup : Invalid namespace
At line:1 char:1
+ New-FsrmFileGroup -Name "Anti-Ransomware File Groups" -IncludePattern #((Invoke- ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (MSFT_FSRMFileGroup:Root/Microsoft/...T_FSRMFileGroup) [New-FsrmFileGroup], CimException
+ FullyQualifiedErrorId : HRESULT 0x8004100e,New-FsrmFileGroup
I also notice some errors in FSRM:
Firewall is off and I am domain admin running this as admin. SFC and a WMI repair came back as good. I am going based off a guide (https://fsrm.experiant.ca/). This has worked across a ton of other servers so I don't believe the commands to be improperly formatted.
Output of Get-WmiObject -Namespace 'Root/Microsoft/Windows/Fsrm' -List:
Get-WmiObject : Could not get objects from namespace Root/Microsoft/Windows/Fsrm.
Invalid namespace
At line:1 char:1
+ Get-WmiObject -Namespace 'Root/Microsoft/Windows/Fsrm' -List
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-WmiObject], ManagementException
+ FullyQualifiedErrorId : INVALID_NAMESPACE_IDENTIFIER,Microsoft.PowerShell.Commands.G‌​etWmiObjectCommand

How the PowerShell Cmdlets that have a ComputerName parameter authenticate to remote computers?

Some PowerShell Cmdlets have a ComputerName parameter and I can use them to get information from remote computers. Like Get-Process, Get-Service etc. However, they don't have a Credential parameter, which in turn makes the command fail in some circumstances. Like the following example.
PS C:\Users\x\AppData\Roaming> Get-Service *sql* -ComputerName mylab.testing.com
Get-Service : Cannot open Service Control Manager on computer 'mylab.testing.com'. This operation might require other privileges.
At line:1 char:1
+ Get-Service *sql* -ComputerName mylab.testing.com
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Get-Service], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.GetServiceCommand
PS C:\Users\x\AppData\Roaming> Get-Error
******************************
Errors: 104
******************************
System.ComponentModel.Win32Exception (0x80004005): Access is denied
----------------------------------------------
System.Management.Automation.RuntimeException: ScriptHalted
----------------------------------------------
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
at System.Management.Automation.Interpreter.FuncCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
----------------------------------------------
System.Management.Automation.RuntimeException: You cannot call a method on a null-valued expression.
at CallSite.Target(Closure , CallSite , Object )
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at System.Management.Automation.Interpreter.DynamicInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
----------------------------------------------
You cannot call a method on a null-valued expression.
At line:18 char:21
+ write-host $err.Exception.ToString()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
----------------------------------------------
Collection was modified; enumeration operation may not execute.
At line:9 char:17
+ foreach($err in $Error)
+ ~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException
ScriptHalted
At line:22 char:9
+ throw
+ ~~~~~
+ CategoryInfo : OperationStopped: (:) [], RuntimeException
+ FullyQualifiedErrorId : ScriptHalted
PS C:\Users\x\AppData\Roaming> help Get-Service -full
Note I used a custom function Get-Error and its code is listed below.
function Get-Error
{
$errorsReported = $False
if($Error.Count -ne 0)
{
write-host "******************************"
write-host "Errors:", $Error.Count
write-host "******************************"
foreach($err in $Error)
{
$errorsReported = $True
if( $err.Exception.InnerException -ne $null)
{
write-host $err.Exception.InnerException.ToString()
}
else
{
write-host $err.Exception.ToString()
}
write-host "----------------------------------------------"
}
throw
}
}
I want to know whether my understanding is correct? Does that simply means it's impossible to authenticate to remote servers when using those commands?
Thanks.
You have to be running your PowerShell session with an account that has admin rights on the remote computer as capsch says. If the remote computer has remoting enabled, you could run the Get-Service command using Invoke-Command and remoting, which does support alternate credentials. Another alternative is to use WMI and query services that way which also supports alternate credentials.
UAC aside you by default have to be an admin of member to administer remotely
Cannot open Service Control Manager on computer

powershell invoke-command does not work if I use -computerName

I want to execute below code in the either local or remote machine whith current user.
$BackUpSqlAgentAndRemoveOldbackup = {
param([string]$AppServer,[string]$SqlInstance,[string]$BackupShare,[string]$alias)
[Environment]::UserName #I got same user name in all cases.
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.Smo') | Out-Null
$server = New-Object ('Microsoft.SqlServer.Management.Smo.Server') $SqlInstance
$backupName = 'SqlAgentJob_' + $SqlInstance + '_' + (Get-Date –format ‘yyyyMMdd_HHmm’) + '_' + $alias + '.sql'
$backupPath = join-path $BackupShare $backupName
$oldBackups = Get-ChildItem $backupShare | where { ( $_.name -like 'SqlAgentJob_*.sql' ) }
$server.JobServer.Jobs.Script() | Out-File -filepath $backupPath
foreach ( $item in $oldBackups ) { remove-item $item.fullName }
}
the #argList is
#('hafcapp-1', 'hafcsql-1', '\\Host5FileSrv\Backup\test','auto')
I notice that
this one, it works well (no -comupterName and -session)
Invoke-Command -ScriptBlock $BackUpSqlAgentAndRemoveOldbackup -argumentList $argList
this one, it throw execption (I also tried "-session", get same result)
Invoke-Command -computerName localhost -ScriptBlock $BackUpSqlAgentAndRemoveOldbackup -argumentList $argList
the exception is as below, it seems the it can not access the folder.
Cannot find path '\\Host5FileSrv\Backup\test' because it does not exist.
+ CategoryInfo : ObjectNotFound: (\\Host5FileSrv\Backup\test:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
You cannot call a method on a null-valued expression.
+ CategoryInfo : InvalidOperation: (Script:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Cannot bind argument to parameter 'Path' because it is null.
+ CategoryInfo : InvalidData: (:) [Remove-Item], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.RemoveItemCommand
does anyone know how can I do if I want to add computerName or session?
(notes:[Environment]::UserName return identical user)
You have run into the double hop problem. Your credentials can be transferred to the next machine (first hop), but no further (second hop). This means that you can't use the credentials of the machine where you are executing Invoke-Command on, the remote machine (localhost) to connect to a file share (\Host5FileSrv\Backup). Even if you use localhost as computername, it is still remoting. A solution could be CredSSP. See here and here for more information.
This looks like a "second hop" remoting problem, and you'll need to configure WinRM on the computers involved to use CredSSP
http://msdn.microsoft.com/en-us/library/windows/desktop/ee309365(v=vs.85).aspx