Azure Function and PowerShell: unable to set connection strings with Set-AzWebApp - entity-framework

For deployment purposes I created a PowerShell script to set App Settings. This works fine via
$currentAppSettings = $app.SiteConfig.AppSettings
$appSettings = #{}
# Add existing App Settings
ForEach ($currentAppSetting in $currentAppSettings) {
$appSettings[$currentAppSetting.Name] = $currentAppSetting.Value
}
# Add new App Settings
$appSettings["someKey"] = "someValue"
# Update App Settings
Set-AzWebApp -ResourceGroupName $resourceGroupName -Name $appName -AppSettings $appSettings
As I am using now Entity Framework I also need to set the Connection Strings. First I thought this should work like with App Settings and just adding the ConnectionStrings Argument:
$currentConnectionStrings = $app.SiteConfig.ConnectionStrings
$connectionStrings = #{}
ForEach ($currentConnectionString in $currentConnectionStrings) {
$connectionStrings[$currentConnectionString.Name] = $currentConnectionString.ConnectionString
}
$connectionStrings["someKey"] = "someValue"
Set-AzWebApp -ResourceGroupName $resourceGroupName -Name $appName -ConnectionStrings $connectionStrings
But this fails with the error
Set-AzWebApp : Cannot validate argument on parameter 'ConnectionStrings'. Connection string type value pair must be of type 'System.Collections.Hashtable'
despite the fact that $connectionStrings is of type System.Collections.Hashtable
Also trying to work with Custom Objects, arrays and so on failed.
How do I pass the connection strings correctly?
And how do I specify the Type (e.g. SQLAZURE)?
Thanks

As per the Documentation of Set-AzwebApp ConnectionStrings should be Hastable.
Set-AzWebApp
[[-AppServicePlan] <String>]
[[-DefaultDocuments] <String[]>]
[[-NetFrameworkVersion] <String>]
[[-PhpVersion] <String>]
[[-RequestTracingEnabled] <Boolean>]
[[-HttpLoggingEnabled] <Boolean>]
[[-DetailedErrorLoggingEnabled] <Boolean>]
[[-AppSettings] <Hashtable>]
[[-ConnectionStrings] <Hashtable>]
[[-HandlerMappings] <System.Collections.Generic.IList`1[Microsoft.Azure.Management.WebSites.Models.HandlerMapping]>]
[[-ManagedPipelineMode] <String>]
[[-WebSocketsEnabled] <Boolean>]
[[-Use32BitWorkerProcess] <Boolean>]
[[-AutoSwapSlotName] <String>]
[-ContainerImageName <String>]
[-ContainerRegistryUrl <String>]
[-ContainerRegistryUser <String>]
[-ContainerRegistryPassword <SecureString>]
[-EnableContainerContinuousDeployment <Boolean>]
[-HostNames <String[]>]
[-NumberOfWorkers <Int32>]
[-AsJob]
[-AssignIdentity <Boolean>]
[-HttpsOnly <Boolean>]
[-AzureStoragePath <WebAppAzureStoragePath[]>]
[-ResourceGroupName] <String>
[-Name] <String>
[-DefaultProfile <IAzureContextContainer>]
[<CommonParameters>]
You should set connectionstring using a Hashtable as shown below:
$connectionStrings = #{connectionStrings = #{Name="<ConnectionStringName>";Value="<ConnectionSyring>";type="<SQLAzure/SQLServer/Custom/MySql>"}}
Set-AzWebApp -ResourceGroupName $resourceGroupName -Name $appName -ConnectionStrings $connectionStrings

#KetanChawda-MSFT: Thanks for your advice, it helped me a lot to develop the solution (but did not work as such in my context as mentioned below).
Here is the way it works for me now:
$currentConnectionStrings = $app.SiteConfig.ConnectionStrings
$connectionStrings = #{}
# Add existing connection strings
ForEach ($currentConnectionString in $currentConnectionStrings) {
$connectionStrings[$currentConnectionString.Name] =
#{
Value=$currentConnectionString.ConnectionString;
Type=($currentConnectionString.Type | Out-String).ToUpper()
}
}
# Add database connection string
$connectionStrings["connName"] = #{
Value="connString";
Type="SQLAZURE"
}
# Update settings
Set-AzWebApp -ResourceGroupName $resourceGroupName -Name $appName -ConnectionStrings $connectionStrings
What is interesting is that in case of existing connection strings I have to format it as string and use toUpper, otherwise it does not work for me (Error: Set-AzWebApp : Cannot validate argument on parameter 'ConnectionStrings'. Connection string type must be specified.). When adding a new connection string I can use e.g. SQLAZURE as well as SQLAzure.
EDIT:
Here is an approach where you don't need the connection strings section at all when working with Entity Framework and Azure Functions: Missing ProviderName when debugging AzureFunction as well as deploying azure function

Related

How to handle error response from AZ REST

How can I catch and handle error responses from Azure CLI az rest command?
If I paste the following into my console (Powershell 7.1 running on Windows), I see some error text written to the console, but it doesn't actually throw which would enter the catch block, nor is anything written into $rv.
What am I missing here and how can I inspect the return value of az rest after an error?
try {
$rv = (az rest --method post --url "https://graph.microsoft.com/v1.0/servicePrincipals/$DELIBERATE_ERROR") #--body $body)
Write-Host "rv:"
$rv
}
catch {
Write-Host "error:"
$error
}
Note: I also saved the above in a file called Untitled-2.ps1 and ran it but you can just copy/paste it into console.
PS D:\code\Powershell> .\Untitled-2.ps1
ERROR: Bad Request({"error":{"code":"BadRequest","message":"Write requests (excluding DELETE) must contain the Content-Type header declaration.","innerError":{"date":"2021-09-13T06:02:39","request-id":"447b4cdc-cc43-4e38-a960-d389a3ea3a87","client-request-id":"447b4cdc-cc43-4e38-a960-d389a3ea3a87"}}})
rv:
PS D:\code\Powershell>
Thank you John Hanley. Posting your suggestions as answer to help other community members.
As you $rv is the result written to stdout and you want to capture stderr.
The data from stdout will be strings while stderr produces
System.Management.Automation.ErrorRecord objects.
```swift
$allOutput = & myprogram.exe 2>&1
$stderr = $allOutput | ?{ $_ -is [System.Management.Automation.ErrorRecord] }
$stdout = $allOutput | ?{ $_ -isnot [System.Management.Automation.ErrorRecord] }
```
and,
If you want to capture standard out and error with start process without sending to file.
Start-Process
[-FilePath] <String>
[[-ArgumentList] <String[]>]
[-Credential <PSCredential>]
[-WorkingDirectory <String>]
[-LoadUserProfile]
[-NoNewWindow]
[-PassThru]
[-RedirectStandardError <String>]
[-RedirectStandardInput <String>]
[-RedirectStandardOutput <String>]
[-WindowStyle <ProcessWindowStyle>]
[-Wait]
[-UseNewEnvironment]
[-WhatIf]
[-Confirm]
[<CommonParameters>]
Check Start Process document for better understanding.
Also check the SO1 and SO2 with related information.

Splat parameters to a cmdlet without unrolling array

I have this cmdlet:
$dnstype = ,#("Primary","Secondary","Tertiary")
$dnsserver = ,#("192.168.1.11","192.168.1.12","192.168.150.13")
Set-HPEiLOIPv4NetworkSetting -connection $connection -DNSServer $dnsserver -DNSServerType $dnstype
This works fine.
However, I need to splat the parameters, because I am adding parameters depending on the known state of the object (this is because some parameters result in a reset which I'm trying to avoid if possible).
When doing the following the DNS servers are not amended as they need to remain "unrolled" when sent to the cmdlet.
$dnstype = ,#("Primary","Secondary","Tertiary")
$dnsserver = ,#("192.168.1.11","192.168.1.12","192.168.150.13")
$NewDCHPv4Settings = #{
Connection = $connection
InterfaceType = "Dedicated"
OutVariable = "IPv4Set"
OutputType = "Object"
DNSName = $ShortName.ToLower()
ErrorAction = "Stop"
Verbose = $true
}
# Check DNS and amend if required
if ($IPv4Settings.DNSServer -ne $dnsserver) {
Write-Host "DNS server entries not correct, amending"
$NewDCHPv4Settings.Add("DNSServer", $dnsserver)
$NewDCHPv4Settings.Add("DNSServerType", $dnstype)
}
Set-HPEiLOIPv4NetworkSetting #NewDCHPv4Settings
So the question - how do I add the unary arrays to the splat array so that the integrity is maintained and they process correctly?
As requested - here is the defintion:
Name : Set-HPEiLOIPv4NetworkSetting
ModuleName : HPEiLOCmdlets
Module : #{Name=HPEiLOCmdlets}
CommandType : Cmdlet
Definition :
Set-HPEiLOIPv4NetworkSetting [-Connection] <Connection[]> -InterfaceType <string[]> [-SharedNetworkPortType <string[]>] [-SNPPort <int[]>]
[-VLANEnabled <string[]>] [-VLANID <int[]>] [-DNSName <string[]>] [-DomainName <string[]>] [-NICEnabled <string[]>] [-FullDuplex <string[]>]
[-LinkSpeedMbps <string[]>] [-DHCPEnabled <string[]>] [-DHCPv4Gateway <string[]>] [-DHCPv4DomainName <string[]>] [-DHCPv4DNSServer <string[]>]
[-DHCPv4WINSServer <string[]>] [-DHCPv4StaticRoute <string[]>] [-DHCPv4NTPServer <string[]>] [-IPv4Address <string[]>] [-IPv4SubnetMask <string[]>]
[-IPv4Gateway <string[]>] [-IPv4StaticRouteIndex <int[][]>] [-IPv4StaticRouteDestination <string[][]>] [-IPv4StaticRouteMask <string[][]>]
[-IPv4StaticRouteGateway <string[][]>] [-DNSServerType <string[][]>] [-DNSServer <string[][]>] [-RegisterDDNSServer <string[]>] [-PingGateway
<string[]>] [-RegisterWINSServer <string[]>] [-WINSServerType <string[][]>] [-WINSServer <string[][]>] [-iLONICAutoDelay <int[]>] [-iLONICFailOver
<string[]>] [-iLONICFailOverDelay <int[]>] [-iLONICAutoSelect <string[]>] [-iLONICAutoSNPScan <int[]>] [-OutputType <string>] [-Force]
[<CommonParameters>]
ParameterSets : {#{Name=__AllParameterSets; IsDefault=False; Parameters=System.Management.Automation.PSObject[]}}
As in comments, my issue was being cause by the iLO itself/logic rather than the splat being incorrect. My original code does indeed work as intended:
$dnstype = ,#("Primary","Secondary","Tertiary")
$dnsserver = ,#("192.168.1.11","192.168.1.12","192.168.150.13")
$NewDCHPv4Settings = #{
Connection = $connection
InterfaceType = "Dedicated"
OutVariable = "IPv4Set"
OutputType = "Object"
DNSName = $ShortName.ToLower()
ErrorAction = "Stop"
Verbose = $true
}
# Check DNS and amend if required
if ($IPv4Settings.DNSServer -ne $dnsserver) {
Write-Host "DNS server entries not correct, amending"
$NewDCHPv4Settings.Add("DNSServer", $dnsserver)
$NewDCHPv4Settings.Add("DNSServerType", $dnstype)
}
Set-HPEiLOIPv4NetworkSetting #NewDCHPv4Settings

Change WebApp AppSetting values when creating a new Slot

When creating a new Slot for an Azure WebApp, how can I successfully change one or more of the AppSettings?
The docs for New-AzureRmWebAppSlot suggest that there is a parameter called -AppSettingsOverrides, but that does not work.
It should be noted however that the linked docs seem to incorrectly reference the New-AzureRmWebApp Cmdlet, so I can't be sure if the parameter is actually valid (although it seems to be accepted without error).
Here is the code that I am running.
New-AzureRmWebAppSlot -ResourceGroupName $resourceGroupName -Name $webAppName -Slot $slotName -AppSettingsOverrides #{"FUNCTION_APP_EDIT_MODE" = "readwrite"} -ErrorAction Stop
Has anyone else experienced this seemlying incorrect behaviour, and if so, how did you fix it?
My Azure version is 3.5.0.
You could create Slot firstly, then use Set-AzureRmWebAppSlot to change AppSetting. Following script works for me.
$myResourceGroup = "shuiapp"
$mySite = "shuicli"
$slotName = "Test1"
$webApp = Get-AzureRMWebAppSlot -ResourceGroupName $myResourceGroup -Name $mySite -Slot $slotName
$appSettingList = $webApp.SiteConfig.AppSettings
$hash = #{}
ForEach ($kvp in $appSettingList) {
$hash[$kvp.Name] = $kvp.Value
}
$hash['ExistingKey2'] = "NewValue12"
Set-AzureRMWebAppSlot -ResourceGroupName $myResourceGroup -Name $mySite -AppSettings $hash -Slot $slotName
The question will be helpful.
Credit to this dingmeng-xue on Github for pointing this out but for the benefit of everyone reading Stackoverflow, it appears that the cmdlet doesn't a) check that -SourceWebApp and -AppSettingsOverrides are set together and b) only does a null check if $SourceWebApp is defined and silently does nothing even if $AppSettingsOverrides was defined. So you might set them both and test if that solves the issue.

Copy parameter sets from a CmdLet to new CmdLet

I want to create a replacement function for Write-Error. To do this, it must support the same parameter sets as the original CmdLet.
I can see what parameter sets Write-Error supports by using
Get-Help Write-Error
or online on technet
NAME
Write-Error
SYNOPSIS
Writes an object to the error stream.
SYNTAX
Write-Error [-Message] [-Category ] [-CategoryActivity ] [-CategoryReason ]
[-CategoryTargetName ] [-CategoryTargetType ] [-ErrorId ] [-RecommendedAction ] [-
TargetObject ] []
Write-Error [-Category <ErrorCategory>] [-CategoryActivity <String>] [-CategoryReason <String>] [-CategoryTargetNam
e <String>] [-CategoryTargetType <String>] [-ErrorId <String>] [-Message <String>] [-RecommendedAction <String>] [-
TargetObject <Object>] -Exception <Exception> [<CommonParameters>]
Write-Error [-CategoryActivity <String>] [-CategoryReason <String>] [-CategoryTargetName <String>] [-CategoryTarget
Type <String>] [-RecommendedAction <String>] -ErrorRecord <ErrorRecord> [<CommonParameters>]
But how can i easily create a new PowerShell script CmdLet that uses the exact same parameter sets (including required parameters and default values)?
This is a perfect use case for proxy function. You can create proxy function using command metadata:
$command = Get-Command Write-Error
$proxy = [System.Management.Automation.ProxyCommand]::Create($command)
Some links that should help explain it:
Scripting Guys blog (post by PS MVP Shay Levy)
PowerShell Team blog
There is also PowerShell module (pspx) that could get you started.

Update-TFSWorkspace does not work

Why does
Update-TfsWorkspace [[-Item] <QualifiedItemSpec[]>] [-All] [-Force] [-Overwrite] [-Recurse] [-Version <String>]
not work?
The equivalent tf.exe works just fine in the exact same folder:
tf get itemspec [/version:versionspec] [/all] [/overwrite] [/force] [/preview] [/recursive] [/remap] [/noprompt]
Using Win 8.
Here are the exact commands I'm using:
Update-TfsWorkspace -Item "$/myfilepaht" this does NOT WORK
tf get itemspec "$/myfilepaht" #this WORKS
I had the same problem (probably because I have multiple workspaces)
So I solved it with the following code:
$tfsCredential = Get-Credential;
$tfsServer = Get-TfsServer -Name "https://tfs.tools4ever.com:443/tfs/t4edevnet2010" -Credential $tfsCredential;;
$tfsws = Get-TfsWorkspace -Server $tfsServer -Computer $hostname -Owner $tfsCredential.UserName;
$tfsPath = $tfsws.GetServerItemForLocalItem($filename);
$prop = Get-TfsItemProperty -Item $tfsPath -Server $tfsServer -Workspace $script:tfsws;
$tfsws.Get(#($tfsPath), [Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Parse($prop.VersionLatest, $script:tfsws.OwnerName)[0], [Microsoft.TeamFoundation.VersionControl.Client.RecursionType]::Full, [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::None)