Cannot Delete All Azure Website Connection Strings - powershell

For a website my team are currently working on we're trying to write some PowerShell automation to temporarily override a connection string which is otherwise usually in Web.config, and then at a later point delete it.
However, using both the PowerShell cmdlets and the Azure REST APIs, we don't seem to be able to ever reset the connection strings set to be empty. Whenever we try to delete it either the command says it succeeds but doesn't delete it, or the requests is rejected as invalid.
It's possible to delete all the connection strings using the Management Portal, so what are we missing that means we can't do it programmatically?
Here's an example PowerShell command we're using that isn't doing what we want:
$website = Get-AzureWebsite $websiteName -slot $websiteSlot
$connectionStrings = $website.ConnectionStrings
$result = $connectionStrings.Remove(($connectionStrings | Where-Object {$_.Name -eq $connectionStringName}))
Set-AzureWebsite $websiteName -slot $websiteSlot -ConnectionStrings $connectionStrings

I know this is really late.
I have faced the same situation.We are able to update the App settings but not the connection strings.I found the solution.Since it doesn't have an answer, I thought it would be good to update it..
As per the git-hub source, the hash-table(nested) needs to be in the format of
#{ KeyName = #{ Type = "MySql"; Value = "MySql Connection string"}};
after which I ran the code to update the App, it worked. Below is the whole code
$list = #{ ConnectionString1 = #{ Type = "MySql"; Value = "MySql Connection string"}};
set-AzureRMWebAppSlot -ResourceGroupName "resourceGroup" -Name "devTest" -ConnectionStrings $list -slot "production"

Related

Unable to update Azure Cloud Service Extended Support via Azure Powershell Task

I am attempting to update Azure Cloud Service Extended Support using Azure PowerShell task in Azure DevOps Release pipeline. I receive the follow error when using Update-AzCloudService:
Specified api-version 2021-03-01 does not meet the minimum required api-version 2022-04-04 to set this property SlotType
Here is the code block used in the PowerShell task:
...
$cloudServiceIdentity = #{
CloudServiceName = $cloudServiceName
Location = $location
ResourceGroupName = $resourceGroupName
SubscriptionId = $subscription.Subscription.Id
}
Write-Host "-------------------------------"
Write-Host = "cloudServiceIdentity.CloudServiceName= " $cloudServiceIdentity.CloudServiceName
Write-Host = "cloudServiceIdentity.Location = " $cloudServiceIdentity.Location
Write-Host = "cloudServiceIdentity.ResourceGroupName = " $cloudServiceIdentity.ResourceGroupName
Write-Host = "cloudServiceIdentity.SubscriptionId = " $cloudServiceIdentity.SubscriptionId
Write-Host "-------------------------------"
$cloudService = #{
Location = $location
configurationUrl = $configSas
PackageUrl = $packageSas
OSProfile = $osProfile
}
Write-Host "-------------------------------"
Write-Host = "cloudService.Location= " $location
Write-Host = "cloudService.configurationUrl = " $configSas
Write-Host = "cloudService.PackageUrl = " $packageSas
Write-Host = "cloudService.OSProfile = " $osProfile
Write-Host "-------------------------------"
Write-Host "Starting Azure CSES Update"
Update-AzCloudService -InputObject $cloudServiceIdentity -Parameter $cloudService
Write-Host "Completed Azure CSES Update"
...
I have confirmed that Az.CloudService is running version is 1.1.0 (latest version)
Environment info:
| Environment | Value |
|:----------- |:------|
|Azure DevOps Version|18.181.31626.1 (Azure DevOps Server 2020 Update 1.1)|
|Azure DevOps Azure PowerShell Task |Version 5|
|Azure PowerShell version options - Preferred Azure PowerShell Version| 3.1.0|
|Az.CloudService | 1.1.0|
I have been struggling with this issue for several days and cannot seem to find any information on fixing the problem.
Any insights are greatly appreciated!
Given the error message you shared, I suppose that you could check this comment about api version error.
And run the command below to check the latest released api version.
((get-azresourceprovider -providernamespace Microsoft.Compute).resourcetypes | ?{$_.ResourceTypeName -eq "virtualmachines"}).apiversions
And remodify your api-version parameter in your json or bicep template.
The issue does appear to be related to the AzCloudService Powershell Module or the CloudService Resource by itself. When creating the resource, Azure does seem to select the 2021-03-01 API version even if you have a newer version selected in your ARM Template.
It could be that this tight versioning requirements with the way Powershell makes its queries to the resource doesn't seem to work well with the resource.
In my case, I was using the New-AzCloudService cmdlet and always encountered the issue (note, to update the CloudService resource, you may still need to use the New-AzCloudService cmdlet. If the resource exists, an update operation will be performed. If the resource doesn't exist, a new resource will be created).
I noticed though that if using Powershell, all the parameters need to be specified if using the New-AzCloudService cmdlet.
As specified here in the example Step 13:
https://learn.microsoft.com/en-us/azure/cloud-services-extended-support/deploy-powershell#create-cloud-service-using-profile-objects--sas-uris
you must have to pass in the other parameters before your query will be accepted (at least this is what worked for me). -Tag was not required.
So initially I was passing in my query as follows:
$cloudService = New-AzCloudService `
-Name “ContosoCS” `
-ResourceGroupName “ContosOrg” `
-Location “East US” `
-PackageUrl $cspkgUrl `
-ConfigurationUrl $cscfgUrl
because I only wanted to perform a release update and I always got the error:
Specified api-version 2021-03-01 does not meet the minimum required api-version 2022-04-04 to set this property SlotType
When I passed in all other parameters (I referenced the existing cloud service's parameters since I'm performing an in-place update), it got accepted.
$cloudService = New-AzCloudService `
-Name “ContosoCS” `
-ResourceGroupName “ContosOrg” `
-Location “East US” `
-PackageUrl $cspkgUrl `
-ConfigurationUrl $cscfgUrl `
-UpgradeMode 'Auto' `
-RoleProfile $roleProfile `
-NetworkProfile $networkProfile `
-ExtensionProfile $extensionProfile `
-OSProfile $osProfile
Hope this helps.

Using Powershell to update On Premise PowerBI Datasource causes Connection Test to fail

I have a script which updates PowerBI data sources on an on-premise PowerBI report server (script below is abridged for brevity) the script updates the connection string in all SQL datasources from $OldServerName to $NewServerName
The script below filters to just one report for the sake of testing. The real script loops through all reports from the root folder.
param ([Parameter(Mandatory=$true)]$ReportServerURI,
[Parameter(Mandatory=$true)]$OldServerName,
[Parameter(Mandatory=$true)]$NewServerName
);
$session = New-RsRestSession -ReportPortalUri $ReportServerURI;
# get all PowerBI reports
$powerBIs = Get-RsFolderContent -RsFolder '/MyFolder1/MyFolder2' -ReportServerUri $ReportServerURI -Recurse | Where-Object -Property "TypeName" -EQ "PowerBIReport"; #the real script starts at the root folder. I just restrict here to target one report for testing
foreach ($pbi In $powerBIs | Where-Object {$_.Name -eq "MyReport"}) #again, this restriction to one report is just for testing
{
# get all the datasources in the report
$rds = Get-RsRestItemDataSource -WebSession $session -RsItem $pbi.Path;
# if data sources have been found
if ($rds -ne $null)
{
# loop through all the datasources
foreach ($d in $rds)
{
if ($d.ConnectionString.ToUpper().Contains($OldServerName.ToUpper()) -and $d.DataModelDataSource.Kind -eq "SQL")
{
$d.ConnectionString = $d.ConnectionString -replace $OldServerName, $NewServerName;
Write-Host ("$($d.id) updated") -ForegroundColor Green;
};
};
};
Set-RsRestItemDataSource -WebSession $session -RsItem $pbi.Path -DataSources $rds -RsItemType PowerBIReport;
};
The script works and when I browse to /MyFolder1/MyFolder2/ in the web report manager, click manage for MyReport and then go to the datasources tab, I can see that the datasources are there, the SQL data source connection strings have been updated as hoped and the credentials are as they were before the update. However, When I click "Test Connection" I get the error
Log on failed. Ensure the user name and password are correct.
I can confirm that the connection succeeds before the update (although this is against $oldServerName).
The credentials for the SQL data sources are for a windows user and that Windows Login exists on the SQL Server $NewServerName and is a user in the database that the data source points to.
There are also some Excel data sources for the PowerBI report, which use the same windows user's credentials which, whilst not updated by the script, display the same behaviour (Connection Test succeeds before the script update but fails after)
If I re-enter the credentials manaually the test then succeeds, however, when I refresh the shared schedule via Manage --> Scheduled Refresh --> refresh now. The refresh fails and I get the following error
SessionID: 45944afc-c53c-4cca-a571-673c45775eab [0] -1055784932:
Credentials are required to connect to the SQL source. (Source at
OldServerName;Database.). The exception was raised by the IDbCommand
interface.
[1] -1055129594: The current operation was cancelled
because another operation in the transaction failed.
[2] -1055784932:
The command has been canceled.. The exception was raised by the
IDbCommand interface.
What am I missing? Is there something else I need to do?
I am using PowerBI Report Server version October 2020
I think the moment you execute Set-RsRestItemDataSource to modify the SQL DataSource Connection strings the password becomes invalid. This seems normal to me, as you don't want someone to modify the connection string and use it with someone else's credentials. So in a way this looks like a security feature and is behaving as desinged.
A possible workaround you could try is to set the credentials again:
Create a credential object with New-RsRestCredentialsByUserObject
From the docs:
This script creates a new CredentialsByUser object which can be used when updating shared/embedded data sources.
Update the SQL DataSource connection with the new credentials object
Something like this in your case might work:
$newCredentialsParams = #{
Username = "domain\\username"
Password = "userPassword"
WindowsCredentials = $true
}
$rds = Get-RsRestItemDataSource -WebSession $session -RsItem $pbi.Path
$rds[0].CredentialRetrieval = 'Store'
$rds[0].CredentialsByUser = New-RsRestCredentialsByUserObject #newCredentialsParams
$setDataSourceParams = #{
WebSession = $session
RsItem = $pbi.Path
DataSources = $rds
RsItemType = PowerBIReport
}
Set-RsRestItemDataSource #setDataSourceParams

Using Powershell to find last step run from an SQL agent job

I have a powershell script that provides SQL Agent Job information. The script is meant to monitor the jobs and tell me if they failed the last time they ran. However i also want to know which was the last run step. and i can't seem to figure out how.
I am querying the Sql server mangement object, because it needs to be usable on multiple remote servers (remote connections) and i wish to avoid running SQL scripts.
Keep in mind i'm rather new to powershell.
This is the code i have so far: And i have loaded the SMO libraries, i just didn't show it the copied script.
## Get Jobstep Class SMO
Push-Location; Import-Module SQLPS -DisableNameChecking; Pop-Location;
$JobStep = New-Object microsoft.sqlserver.management.smo.agent.jobstep
## Run the select from the local SQL server management objects
$SQLSvr = "."
$MySQLObject = new-object Microsoft.SqlServer.Management.Smo.Server `
$SQLSvr;;
$Select = ($MySQLObject.JobServer.jobs) `
| Select Name, isEnabled, `
lastRunDate, lastRunOutCome, NextRunDate `
| Where-Object {$_.LastRunDate -ge ((Get-Date).adddays(-2)) } `
| ft -AutoSize;
$Select
The push-location section is meant to get the correct smo class for selecting the job step (unsure if it is right), however i haven't added anything from it.
Now the script does work, i get no errors and i get returned the overall information i want, but i cannot figure out how to add the jobstep - and i have consulted google. I'm well aware that i need to add more to the select, but what is the issue for me. So, how do i extract the last run job step from SQL Agent job, using the SMO, and add it to the above script?
You can use the SqlServer module from the PowerShell Gallery (Install-Module SqlServer), and then something like:
$h = Get-SqlAgentJobHistory -ServerInstance servername -JobName jobname
$h[0] will give you the last step ran.
This will give you the result in the format you wanted:
Get-SqlAgentJob -ServerInstance servername |
Where-Object {$_.LastRunDate -ge ((Get-Date).AddDays(-2))} | ForEach-Object {
$h = Get-SqlAgentJobHistory -ServerInstance servername -JobName $_.Name
[PSCustomObject]#{
Name = $_.Name
IsEnabled = $_.IsEnabled
LastRunDate = $_.LastRunDate
LastRunOutcome = $_.LastRunOutcome
NextRunDate = $_.NextRunDate
LastRunStep = $h[0].StepName
}
}

Create Vm in WAP using PowerShell, error 400

I'm trying to create a VM using PowerShell in Windows Azure Pack.
I've downloaded the subscription, and Get-WAPackVM returns the VM's already created.
I've tried running these two scripts:
$OSDisk = Get-WAPackVMOSDisk -Name "W2012R2 Template_disk_1"
$SizeProfile = Get-WAPackVMSizeProfile -Name "Template"
New-WAPackVM -Name "ContosoV073" -OSDisk $OSDisk -VMSizeProfile $SizeProfile
and
$Credentials = Get-Credential
$Template = Get-WAPackVMTemplate -Name "Template 1"
New-WAPackVM -Name "VirShits7" -Template $Template -VMCredential $Credentials -Windows
Both returns the same error:
New-WAPackVM : The remote server returned an error: (400) Bad Request.
All the Get cmdlets return values, and seem to be correct.
Anyone know how I get this to work?
You may reference the page:
https://msdn.microsoft.com/en-us/library/jj643289.aspx
The page say:
The key properties that you must set on the virtual machine object that is used with the Service Provider Foundation service are as follows: CloudId, StampId, VMTemplateId ,Name.
You may need to assign CloudId and StampId.
I did it by RESTApi and it works.

OpenRemoteBaseKey() credentials

I'm attempting to use powershell to access a remote registry like so:
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $server)
$key = $reg.OpenSubkey($subkeyPath)
Depending on some factors that I'm not yet able to determine I either get
Exception calling "OpenSubKey" with "1" argument(s): "Requested registry access is not allowed."
Or
System.UnauthorizedAccessException: Attempted to perform an unauthorized operation.
at Microsoft.Win32.RegistryKey.Win32ErrorStatic(Int32 errorCode, String str)
at Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(RegistryHive hKey, String machineName)
It seems pretty clear that this is because the user I'm running the powershell script as doesn't have the appropriate credentials to access the remote registry. I'd like to be able to supply a set of credentials to use for the remote registry access, but I can find no documentation anywhere of a way to do this. I'm also not clear on exactly where to specify which users are allowed to access the registry remotely.
Just thought I'd add my answer to anyone with this problem as well. It seems there is no way to add Credentials using RemoteRegistry. You can however use WMI to query a remote registry using alternative credentials as follows:
$reg = Get-WmiObject -List -Namespace root\default -ComputerName RemotePC -Credential "Domain\User" | Where-Object {$_.Name -eq "StdRegProv"}
From here you can call standard Registry methods. The below example will return the operating system.
$HKLM = 2147483650
$reg.GetStringValue($HKLM,"SOFTWARE\Microsoft\Windows NT\CurrentVersion","ProductName").sValue
Hope this helps someone :)
Are you running remote registry service? It is disabled by default and that must be causing the issue. Check the status of this service on all remote machines you are trying to access.
I couldn't comment directly on bentaylr's entry above, but I've taken what he contributed and added PSCredentials creation (figured out from here) to allow you to hard code credentials into the script.
Peace of mind disclaimer: Be careful when using plaintext credentials in a script. In my case, I'm using generic credentials on machines I'm launching. Depending on your case, you might consider creating an encrypted credential file to store the password in (see link above).
The credentials you use would need to be able to access the registry if you were logged into that user on the machine you are targeting.
$user = "Domain\Username"
$pass = ConvertTo-SecureString "Password" -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user,$pass
$reg = Get-WmiObject -List -Namespace root\default -ComputerName $server -Credential $cred | Where-Object {$_.Name -eq "StdRegProv"}
$HKLM = 2147483650
$value = $reg.GetStringValue($HKLM,"Software\Microsoft\.NetFramework","InstallRoot").sValue
$key.OpenSubKey($subkeyName) opens the subkey in write protected mode,
$key.OpenSubKey($subkeyName,$true) opens it in writable mode
Therefore after $key.OpenSubKey($subkeyName,$true) you should be able to create a new subkey or value
If you try the same thing after $key.OpenSubKey($subkeyName) you will get "UnauthorizedAccessException"
PS C:\>$regKey.OpenSubKey
OverloadDefinitions
Microsoft.Win32.RegistryKey OpenSubKey(string name, **bool Writable**)
try
PS C:\>$key.OpenSubKey($subkeyName,**$true**)
http://msdn.microsoft.com/en-us/library/xthy8s8d%28v=vs.110%29.aspx
Came looking for the answer to your question, but in a little googling this morning I noticed that the first parameter is a type rather than a String... hope this helps:
$machine = "<Machine Name Goes Here>"
$type = [Microsoft.Win32.RegistryHive]::LocalMachine
$regkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($type,$machine)
$subkey = $regKey.OpenSubKey($key)
foreach ($sub in $regKey.GetSubKeyNames()){$sub}
I wanted to first thank all for answers above really helpful, wanted to add that you can use Get-Credential command to collect credentials without having to hard code it in your script. I have written using the above suggestions into my script the following code and query:
$userCredentials = Get-Credential -Credential <domain\username>
$objReg = Get-WmiObject -List -Namespace root\default -ComputerName $server -Credential $userCredentials | Where-Object{$_.Name -eq "StdRegProv"}
$subKeyNames = $objReg.EnumKey($HKLM,"SOFTWARE\Microsoft\Updates\Microsoft .Net Framework 4.5.1").sNames
The above code returns all sub key names in the specified key so that I can determine installed updates other than OS which have been applied to a server. If you want to determine all collection possibilities with the $objReg variable then run:
$objReg | Get-Member
You will see a list of all possible queries which can be performed against the registry. Hope this helps!