I am writing an automation script for Infrastructure creation in Azure which contains the following code:
Azure-LoginAndPickSubscription -azureSubscriptionId $CONFIG_AZURE["SUBSCRIPTIONID"] `
-azureUsername $CONFIG_AZURE["USERNAME"] `
-azureEncryptedPassword $CONFIG_AZURE["PASSWORD"] `
-passwordKeyFilePath "$SCRIPT_DIRECTORY\private.key"
$solutionRoot = Split-Path -Path $SCRIPT_DIRECTORY -Parent
$storContext = (New-AzureStorageContext -StorageAccountName mystorename -StorageAccountKey "mystoragekey")
Publish-AzureVMDscConfiguration "$SCRIPT_DIRECTORY\NewDSC\InitialConfig.ps1" -ContainerName "windows-powershell-dsc" -Force -StorageContext $storContext
The last line (Publish-AzureVMDscConfiguration) throws an error:
Publish-AzureVMDscConfiguration : Object reference not set to an
instance of an object. At
C:\temp\Base.Deploy\ARM.Create.ps1:38 char:5
+ Publish-AzureVMDscConfiguration "$SCRIPT_DIRECTORY\NewDSC\Initia ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Publish-AzureVMDscConfiguration], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,Microsoft.WindowsAzure.Commands.ServiceManagement.IaaS.Extensions.DSC.PublishAzureVMDscConfigurationCommand
I have checked the script (InitialConfig.ps1) exists, and the $storContext object is populated. Any ideas what may be causing this?
#Carl,
From your PowerShell code and error message, it seems that you used the ARM mode in your PowerShell. However, your code should use the ASM mode. I recommedn you can use Switch-AzureMode to switch your powershell mode and then run your code again. Please refer to this blog to check their difference (https://blogs.msdn.microsoft.com/powershell/2015/07/20/introducing-azure-resource-manager-cmdlets-for-azure-powershell-dsc-extension/ ). Like this
PS C:\> Switch-AzureMode -Name AzureResourceManager
PS C:\>Switch-AzureMode -Name AzureServiceManagement
Any results, please let me know.
Related
I am trying to run a powershell script inside a SQL Agent job. I keep getting the error: Import-Clixml : The system cannot find the file specified. At \Data\Powershell\Collibra\Dev\test.ps1:95 char:21 + ... redential = Import-Clixml -Path Filesystem::\Data\Powershell... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [Import-Clixml], Cryptographic Exception + FullyQualifiedErrorId : System.Security.Cryptography.CryptographicExcept ion,Microsoft.PowerShell.Commands.ImportClixmlCommand
$Credential = Import-Clixml -Path
Filesystem::\\energy\data\apps\BISharedServices\Powershell\Collibra\Dev\credentials.xml
$username = $Credential.GetNetworkCredential().UserName
$password = $Credential.GetNetworkCredential().Password
$credPair = "$($username):$($password)"
$encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($credPair))
$H = #{ Authorization = "Basic $encodedCredentials" }
I have tried mapping a new drive and doing a set-location. Same results. This is driving me nuts!
Thanks for the help.
In basic Powershell without SSIS at least, it's only one slash at the beginning:
Import-Clixml -Path FileSystem::\path\to\file.txt
this probably evil to do, but in order to setup a managed identity in our azure db we are using the null_resource like this:
# https://www.terraform.io/docs/providers/null/resource.html
# This technique was stolen from https://stackoverflow.com/a/54523391/442773
resource "null_resource" "create-sql-user" {
triggers = {
db = azurerm_sql_database.x.id
}
# https://www.terraform.io/docs/provisioners/local-exec.html
provisioner "local-exec" {
# https://learn.microsoft.com/en-us/powershell/module/sqlserver/Invoke-Sqlcmd?view=sqlserver-ps
# Adding the Managed Identity to the database as a user and assign it the roles of db_datareader and db_datawriter
# NOTE: This is using the executing users credentials to connect to the db, this may not work if this is executed from a service principal within a devops pipeline
# NOTE: this requires powershell to have the SqlServer module installed. We tried a bunch of things to make it so it'd auto install the module but couldn't get it to work
command = <<EOF
Invoke-Sqlcmd `
-Query "CREATE USER [${azurerm_app_service.x.name}] FROM EXTERNAL PROVIDER; ALTER ROLE db_datareader ADD MEMBER [${azurerm_app_service.x.name}]; ALTER ROLE db_datawriter ADD MEMBER [${azurerm_app_service.x.name}];" `
-ConnectionString "Server=tcp:${azurerm_sql_server.x.fully_qualified_domain_name},1433;Initial Catalog=${azurerm_sql_database.x.name};Persist Security Info=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Authentication=Active Directory Integrated;" `
EOF
interpreter = ["PowerShell", "-Command"]
}
}
the problem is the requirement to have Invoke-Sqlcmd available, but that's there only by Install-Module SqlServer out of band w/ terraform. I tried a few different things in the command to make this happen. like:
# https://www.terraform.io/docs/provisioners/local-exec.html
provisioner "local-exec" {
# https://learn.microsoft.com/en-us/powershell/module/sqlserver/Invoke-Sqlcmd?view=sqlserver-ps
# Adding the Managed Identity to the database as a user and assign it the roles of db_datareader and db_datawriter
command = "Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;"
interpreter = ["PowerShell", "-ExecutionPolicy", "Bypass", "-Command"]
}
Error: Error running command 'Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;': exit status 1. Output: Install-Module : The 'Install-Module' command was found in the module 'PowerShellGet', but the module could not be loaded. For more information, run 'Import-Module PowerShellGet'.
so switching the command to
command = "Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;"
but that lead to this output
Error: Error running command 'Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublisherCheck -Force -AllowClobber -Scope CurrentUser;': exit status 1. Output: Import-Module : The specified module 'C:\program
files\powershell\6\Modules\PackageManagement\fullclr\Microsoft.PackageManagement.dll' was not loaded because no valid
module file was found in any module directory.
At line:1 char:1
+ Import-Module PowerShellGet; Install-Module -Name SqlServer -AcceptLi ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceUnavailable: (C:\program file...eManagement.dll:String) [Import-Module], FileNot
FoundException
+ FullyQualifiedErrorId : Modules_ModuleNotFound,Microsoft.PowerShell.Commands.ImportModuleCommand
PackageManagement\Get-PackageProvider : The term 'PackageManagement\Get-PackageProvider' is not recognized as the name
of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At C:\program files\powershell\6\Modules\PowerShellGet\PSModule.psm1:2926 char:26
+ ... $nugetProvider = PackageManagement\Get-PackageProvider -ErrorA ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (PackageManagement\Get-PackageProvider:String) [], CommandNotFoundExcept
ion
+ FullyQualifiedErrorId : CommandNotFoundException
PackageManagement\Get-PackageProvider : The term 'PackageManagement\Get-PackageProvider' is not recognized as the name
of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included,
verify that the path is correct and try again.
At C:\program files\powershell\6\Modules\PowerShellGet\PSModule.psm1:2940 char:40
+ ... ailableNugetProviders = PackageManagement\Get-PackageProvider -Name $ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (PackageManagement\Get-PackageProvider:String) [], CommandNotFoundExcept
ion
+ FullyQualifiedErrorId : CommandNotFoundException
Exception calling "ShouldContinue" with "2" argument(s): "Object reference not set to an instance of an object."
At C:\program files\powershell\6\Modules\PowerShellGet\PSModule.psm1:3115 char:8
+ if($Force -or $psCmdlet.ShouldContinue($shouldContinueQueryMessag ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NullReferenceException
Install-Module : NuGet provider is required to interact with NuGet-based repositories. Please ensure that '2.8.5.201'
or newer version of NuGet provider is installed.
At line:1 char:30
+ ... erShellGet; Install-Module -Name SqlServer -AcceptLicense -SkipPublis ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Install-Module], InvalidOperationException
+ FullyQualifiedErrorId : CouldNotInstallNuGetProvider,Install-Module
one thing i'm wondering is if powershell versions 6 vs 5 is getting in the way here somehow...
EDIT:
I believe you're using the wrong intepreter, try switching Powershell to pwsh to use powershell 6 as an interpreter.
provisioner "local-exec" {
...
interpreter = ["pwsh", "-Command"]
...
}
I'm unsure about the underlying infrastructure where you require powershell to run. It seems like you're using powershell 6.
I also use a null-provider resource, calling into a script, passing in arguments and then creating a user. One advantage of that is that I know which powershell verson I am running (core) as the trigger command is pwsh.
I'll show you how I'm creating the null resource and a snippet of the script, in the hopes it might help.
Null resource for calling a script responsible for creating the user
resource "null_resource" "create_sql_user" {
provisioner "local-exec" {
command = ".'${path.module}\\scripts\\create-sql-user.ps1' -password \"${random_password.sql_password.result}\" -username \"${var.sql_username}\" -sqlSaConnectionString \"${var.sql_server_connectionstring}\" -databaseName \"${azurerm_sql_database.db.name}\" "
interpreter = ["pwsh", "-Command"]
}
depends_on = [azurerm_sql_database.db]
}
create-sql-user.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]
$password,
[Parameter(Mandatory = $true)]
[string]
$username,
[Parameter(Mandatory = $true)]
[string]
$sqlSaConnectionString
)
Install-Module -Name SqlServer -Force
$sqlCmd = "CREATE LOGIN $username WITH PASSWORD = '$password'; ALTER LOGIN $username enable"
Invoke-Sqlcmd -ConnectionString $sqlSaConnectionString -Query $sqlCmd
...
Extras:
In this case I'm generating the sql password using the random resource. One can use similar approach for username:
resource "random_password" "sql_password" {
length = 54
special = true
override_special = "$%#&*()"
}
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("********Please Select the SOURCE Directory********",0,"Directory Selecter 5000",0x1)
Function Get-Folder($initialDirectory)
{
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$a = Get-Folder
$wshellb = New-Object -ComObject Wscript.Shell
$wshellb.Popup("********Please Select the DESTINATION Directory********",0,"Directory Selecter 5000",0x1)
Function Get-Folder($initialDirectory)
{
$foldername = New-Object System.Windows.Forms.FolderBrowserDialog
$foldername.rootfolder = "MyComputer"
if($foldername.ShowDialog() -eq "OK")
{
$folder += $foldername.SelectedPath
}
return $folder
}
$b = Get-Folder
Set-Content -Path "C:\script\scripts\script data.txt" -Value "$a" -Force
Set-Content -Path "C:\script\scripts\script data2.txt" -Value "$b" -Force
So this script works in ISE and if I copy/paste it into a CLI and that's it. If I navigate to the folder in the powershell CLI and run it from there it gives me this error:
New-Object : Cannot find type
[System.Windows.Forms.FolderBrowserDialog]: verify that the assembly
containing this type is loaded. At
C:\script\scripts\pathingworking.ps1:8 char:19
+ $foldername = New-Object System.Windows.Forms.FolderBrowserDialog
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand
The property 'rootfolder' cannot be found on this object. Verify that
the property exists and can be set. At
C:\script\scripts\pathingworking.ps1:9 char:5
+ $foldername.rootfolder = "MyComputer"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
You cannot call a method on a null-valued expression. At
C:\script\scripts\pathingworking.ps1:11 char:8
+ if($foldername.ShowDialog() -eq "OK")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
This error comes back twice, once for each instance of the folder selection window that is supposed to pop up.
I have tried -STA from run dialogue, shortcuts, and even from the CLI and it always gives me that error. I have verified using [System.Threading.Thread]::CurrentThread.GetApartmentState() that the open instance of powershell is STA. I can open a powershell CLI and navigate to the folder where the script is, invoke the script, it will give me the error, then I can copy\paste it in the same exact CLI and it works without issue. I have also right-clicked the file in Explorer and selected "run with powershell" and it also gives me the error. I have elevated the CLI to see if that helped with no success.
I fear this is some small oversight and hopefully someone can give me a hand.
using v1.0
windows 10
Cannot find type [System.Windows.Forms.FolderBrowserDialog] is due to the assembly not being loaded.
load the assembly with
Add-Type -AssemblyName "System.Windows.Forms"
rerun's answer solves the errors, but doesn't do a whole lot of explaining. Designing a script in the ISE (which is what I do almost exclusively) is convenient because it will automatically load types and modules for you. If you plan on running the script from another CLI (Command Line Interpreter) you should make it a habit of explicitly loading needed modules and assemblies early on in the script, so that when those things are called upon later PowerShell will know what you're asking for.
Unfortunately there's no easy way to tell what you will need to do that for when it comes to what types are going to be inherently available, but a little trial and error usually points it out rather quickly, or if you look at your script and see that you are making a new object with System.Kitchen.Pasta, then you probably need to load the assembly for System.Kitchen first, so that it has the Pasta type available when you want it, so you could just include a call to load that assembly near the top of the script with no harm done.
In your specific case, as has already been pointed out, you will need to load the assembly that contains the FolderBrowserDialog box that you want to display. Adding the following as the first line of your script will resolve the errors that you are seeing:
Add-Type -AssemblyName "System.Windows.Forms"
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.
I am using Azure PowerShell to create and add entries to an Azure queue, following example here: MSDN: Using Azure PowerShell with Azure Storage - How to manage Azure queues .
Here is my PowerShell script:
$storeAuthContext = New-AzureStorageContext -StorageAccountName '[my storage account name]' -StorageAccountKey '[my storage account key'
$myQueue = New-AzureStorageQueue –Name 'myqueue' -Context $storeAuthContext
$queueMessage = New-Object -TypeName Microsoft.WindowsAzure.Storage.Queue.CloudQueueMessage -ArgumentList 'Hello'
$myQueue.CloudQueue.AddMessage($queueMessage)
This works fine the first time I run it.
Second time, I get this:
New-AzureStorageQueue : Queue 'myqueue' already exists. At line:1
char:12
+ $myQueue = New-AzureStorageQueue –Name 'myqueue' -Context $storeAuthContext
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ResourceExists: (:) [New-AzureStorageQueue], ResourceAlreadyExistException
+ FullyQualifiedErrorId : ResourceAlreadyExistException,Microsoft.WindowsAzure.Commands.Storage.Queue.NewAzureStorageQueueCommand
In .NET Azure Storage API there is cloudqueue.createifnotexists (MSDN), but I cannot find the equivalent in Azure PowerShell.
What is the best way in PowerShell to create Azure storage queue if it does not already exist, otherwise get reference to the existing queue?
Afaik there is no CreateIfNotExist flag through the PowerShell module.
You can easily do the following to achieve the same:
$queue = Get-AzureStorageQueue -name 'myName' -Context $storeAuthContext
if(-not $queue){
# your code to create the queue
}
If you want to surpress errors and always try to create it (regardless if it exists or not); you should be able to use the -ErrorAction SilentlyContinue when creating the queue.
I would recommend the first approach though as it's a better practice.
As of 2017.03.14, the accepted answer did not work in a Powershell Azure Function, the Get-AzureStorageQueue throws an exception if the specified queue does not exist.
Example:
Here's the code
$storageAccountName = $env:AzureStorageAccountName
Write-Output ("storageAccountName: {0}" -f $storageAccountName)
$storageAccountKey = $env:AzureStorageAccountKey
Write-Output ("storageAccountKey: {0}" -f $storageAccountKey)
$storageQueueName = $env:AzureStorageQueueName
Write-Output ("storageAccountKey: {0}" -f $storageAccountKey)
Write-Output "Creating storage context"
$azureStorageContext = New-AzureStorageContext $storageAccountName - StorageAccountKey $storageAccountKey
Write-Output "Retrieving queue"
$azureStorageQueue = Get-AzureStorageQueue -Name $storageQueueName –Context $azureStorageContext
Here's the log
2017-03-15T04:16:57.021 Get-AzureStorageQueue : Can not find queue 'my-queue-name'.
at run.ps1: line 21
+ Get-AzureStorageQueue
+ _____________________
+ CategoryInfo : ObjectNotFound: (:) [Get-AzureStorageQueue], ResourceNotFoundException
+ FullyQualifiedErrorId : ResourceNotFoundException,Microsoft.WindowsAzure.Commands.Storage.Queue.GetAzureStorageQueueCommand
2017-03-15T04:16:57.021 Function completed (Failure, Id=58f35998-ebe0-4820-ac88-7d6ca42833df)
Resolution
I had to filter the results and only create the queue if it didn't exist. here's how to solve it:
Write-Output "Retrieving queue"
# Get-AzureStorageQueue returns an exception if the queue does not exists when passing -Name, so instead
# we need to get them all, filter by Name, and if null, create it
$azureStorageQueue = Get-AzureStorageQueue –Context $azureStorageContext | Where-Object {$_.Name -eq $storageQueueName}
if ($azureStorageQueue -eq $null)
{
Write-Output "Queue does not exist, creating it"
$azureStorageQueue = New-AzureStorageQueue -Name $storageQueueName -Context $azureStorageContext
}