How can i retrieve username and password when retrieving Azure Database details via Powershell? - azure

I can list Azure databases as follows:
$servers = Get-AzSqlServer
foreach($server in $servers)
{
$databases = Get-AzSqlDatabase -ServerName $server.ServerName -ResourceGroupName
$server.ResourceGroupName
foreach($db in $databases){
Write-Host "Server " $db.ServerName "Database " $db.DatabaseName
$server.SqlAdministratorLogin "SqlAdminPwd "
$server.SqlAdministratorPassword
Invoke-SqlCmd -ServerInstance $db.ServerName -Database $db.DatabaseName -
Username <azuredb_admin_username> -Password
<azuredb_admin_password> -Query
'select * from [dbo].[TableName]'
}
}
$server.SqlAdministratorPassword is empty which is understandable.
But in the next line I invoke a sqlcmd which requires the password.
How can i get and use the password ?

PowerShell query should require login credential implicitly or explicitly through the command line. There are couple of ways to pass credentials.
Store credentials in credentials manager and fetch the information from there
example:
$psCred = Get-StoredCredential -Target "ABCD" –AsCredentialObject
Here is the code snippet to get the password using PowerShell
$serverName = $db.ServerName
$DB = $db.DatabaseName
$credential = Get-Credential
$userName = $credential.UserName.Replace("\","")
$pass = $credential.GetNetworkCredential().password
invoke-sqlcmd -query "select ##Servername" -database $DB -serverinstance $servername -username $username -password $pass -Query 'select * from [dbo].[TableName]'
Hope this helps!

Related

Issue with decryption while calling credentialssaved in registry using powershell

I have created a registry file to save Azure Password.
$path = "HKCU:\SOFTWARE\PowerShellCred"
$sec = Read-Host "Enter Password for $name" -AsSecureString
$hash = $sec | ConvertFrom-SecureString
Set-ItemProperty -Path $path -Name $admin -Value $hash -Force
but on calling the password, i am having trouble in in decrypting the password using ConvertTo-SecureString. I am using the same user Account to create password and Access it(Admin)
$admin = "xxxxxx#xxxx.com"
$pass = (Get-ItemProperty -Path $path -Name $name -ErrorAction SilentlyContinue)."$name"
$secpw = ConvertTo-SecureString -String $pass -AsPlainText -Force
$c = New-Object System.Management.Automation.PSCredential ($admin, $secpw)
$Azurelogin = Connect-AzureRmAccount -Credential $c
What you have stored in the registry is an encrypted string, not real PlainText.
Something like 01000000d08c9ddf0115d1118c7a00c04fc297eb010000001a114d45b8dd3f4aa11ad7c0abdae9800000000002000000000003660000a8000000100000005df63cea84bfb7d70bd6842e7
efa79820000000004800000a000000010000000f10cd0f4a99a8d5814d94e0687d7430b100000008bf11f1960158405b2779613e9352c6d14000000e6b7bf46a9d485ff211b9b2a2df3bd
6eb67aae41
To convert that back into a SecureString, you do not use the switches -AsPlainText and -Force as you would when converting a 'normal' string like P#ssW#rd.
Try
$secpw = ConvertTo-SecureString -String $pass

Use Azure Powershell Runbook to add AzureAD user as db_owner to an Azure SQL database

I have a Powershell Runbook where I am trying to add AzureAD user as Database owner on an Azure SQL database.
## Connect
$servicePrincipalConnection = Get-AutomationConnection -Name "AzureRunAsConnection"
Connect-AzureAD `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
## Generate new access token
$cert = Get-AutomationCertificate -Name 'AzureRunAsCertificate'
# Set Resource URI to Azure Database
$resourceAppIdURI = 'https://database.windows.net/'
# Set Authority to Azure AD Tenant
$authority = 'https://login.windows.net/' + $servicePrincipalConnection.TenantId
$ClientCred = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate]::new($servicePrincipalConnection.ApplicationId, $cert)
$authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($authority)
$authResult = $authContext.AcquireTokenAsync($resourceAppIdURI, $ClientCred)
$AccessToken = $authResult.Result.AccessToken
## Execute sql
$AccessToken
$connectionString = "Data Source=MYCOOLSQLSERVER.database.windows.net;Initial Catalog=MYCOOLDATABASE;Connect Timeout=30"
$connection = New-Object -TypeName System.Data.SqlClient.SqlConnection($connectionString)
$query = "Create User [abc#xyz.com] From EXTERNAL PROVIDER;"
$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $connection)
$connection.AccessToken = $AccessToken
$connection.Open()
$command.ExecuteNonQuery()
$connection.Close()
I end up getting the error below where abc#xyz.com is an AzureAD user.
Principal 'abc#xyz.com' could not be resolved.
Is there something that I have missed out?
Once the user gets created, I intend to use Alter role to make him the db_owner.
References:
https://blogs.technet.microsoft.com/stefan_stranger/2018/06/06/connect-to-azure-sql-database-by-obtaining-a-token-from-azure-active-directory-aad/
https://techcommunity.microsoft.com/t5/Azure-Database-Support-Blog/Azure-SQL-Database-Token-based-authentication-with-PowerShell/ba-p/369078
Turns out that there is an undocumented way to do this. Discovered it with the help of Azure support team.
The SQL query to be used is actually:
CREATE USER [abc#xyz.com] WITH SID = $hexSid, Type E;
Here $hexSid can be obtained by the following SQL query
Get the ObjectId of the AzureAD User
$objectId = (Get-AzureADUser -ObjectId "abc#xyz.com").ObjectId
Use the SQL below to get the sid
DECLARE #sid = uniqueidentifier = cast('$objectId' as
uniqueidentifier) select cast(#sid as varbinary(max))
Now ideally, the effective query would have directly used the #sid but that is not possible since the With SID needs a hard coded value. So I had to process the output of the sid query separately like below:
$result = $command.ExecuteScalar() # result is byte array
$hexSid = ($result | ForEach-Object ToString X2) -Join '' # convert to hex format
$hexSid = "0x$hexSid"
With those changes the code in the question just works fine!
Effectively, we are only passing the AzureAD User's objectId in the Create User query.
Complete code:
$targetUser = "abc#xyz.com"
$sqlServerName = "someserver"
$dbName = "somedb"
$accessToken = "" # obtained from SPN connection
# Get SQL SID for the targeted user
Write-Output "Getting AzureADUser $targetUser "
$objectId = (Get-AzureADUser -ObjectId $targetUser).ObjectId
$sidQuery = "DECLARE #sid uniqueidentifier = cast('$objectId' as uniqueidentifier) select cast(#sid as varbinary(max))"
$sidOutput = New-Object -TypeName HashTable
Write-Output "Invoking SQL Query execution to get SID for objectId: $objectId ($targetUser)"
Invoke-OrExecuteSqlQuery -ServerName $sqlServerName -DbName $dbName -Query $sidQuery `
-AccessToken $accessToken -Output $sidOutput
$sidBytes = $sidOutput.Result
Write-Output "SID bytes: $sidBytes"
$hexSid = ($sidBytes | ForEach-Object ToString X2) -Join '' # convert to hex format
$hexSid = "0x$hexSid"
Write-Output "SQL SID is : $hexSid"
# Add AzureAD user to the database
Write-Output "Adding AzureAD user : $targetUser to the db/dw: $dbName"
$createDBUserQuery = "CREATE USER [$targetUser] WITH SID = $hexSid, Type = E;" # create db user
$createUserOutput = New-Object -TypeName HashTable
Invoke-OrExecuteSqlQuery -ServerName $sqlServerName -DbName $dbName -Query $createDBUserQuery `
-AccessToken $accessToken -Output $createUserOutput
Write-Output "Created DB User : $targetUser on the DB : $dbName"
# Set targeted user as the database owner
Write-Output "Setting database admin for db/dw: $dbName"
$makeDBOwnerQuery = "ALTER ROLE db_owner ADD MEMBER [$targetUser];"
$alterSQLDBRoleOutput = New-Object -TypeName HashTable
Invoke-OrExecuteSqlQuery -ServerName $sqlServerName -DbName $dbName -Query $makeDBOwnerQuery `
-AccessToken $accessToken -Output $alterSQLDBRoleOutput
Write-Output "Made $targetUser as the DB Owner"
function Invoke-OrExecuteSqlQuery {
param (
[string] $ServerName,
[string] $DbName,
[string] $Query,
[string] $AccessToken,
[hashtable] $Output
)
$server = "$ServerName.database.windows.net"
$database = $DbName
$query = $Query
Write-Output "Connecting to the SQL DB: $database on the SQL Server: $server"
$connectionString = "Data Source=$server;Initial Catalog=$database;Connect Timeout=30;"
$connection = New-Object -TypeName System.Data.SqlClient.SqlConnection($connectionString)
$connection.AccessToken = $AccessToken
Write-Output "Executing SQL query.."
$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $connection)
$connection.Open()
$result = $command.ExecuteScalar()
$connection.Close()
Write-Output "Execution of SQL query complete"
if ($result) {
$Output.Result = $result
}
}
for this scenario, we should use user password flow to make this command work. Pls try the PS below :
$tenant='<your tenant name/id>'
$username='<SQL admin account>'
$password='<SQL admin password>'
$appid='<app id>'
$appsec='<app secret>'
$SQLServerName = '<azure sql servername>'
$DatabaseName='<db name>'
$body=#{
    "grant_type"="password";
    "resource"="https://database.windows.net/";
    "client_id"=$appid;
"client_secret"=$appsec;
    "username"=$username;
    "password" = $password;
}
 
$result=Invoke-RestMethod -Uri "https://login.windows.net/$tenant/oauth2/token" -Method POST -Body $body 
$conn = New-Object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = "Data Source=$SQLServerName.database.windows.net;Initial Catalog=$DatabaseName;Connect Timeout=30"
$conn.AccessToken = $result.access_token
Write-Verbose "Connect to database and execute SQL script"
$conn.Open()
$query = 'CREATE USER [Account] FROM EXTERNAL PROVIDER;'
$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $conn)
$Result = $command.ExecuteScalar()
$Result
$conn.Close()
Before you run is script, you should grant user_impersonation Azure SQL API access permission for your SPN in Azure AD here :
Grant permission after added :
Btw, pls makre sure that the SQL admin account and SPN are all members of Active Directory admin group :
It works on my side and a record has been added successfully :
If there is anything unclear , pls feel free to let me know : )

Azure Analytics Database with automate Backup

The powershell is used to automate the backup of AAS instance.
The instance have Multi-factor authentication and I think that is the problem.
Powershell:
$TenantId = "TenentID"
$Cred = Get-AutomationPSCredential -Name 'SSASModelBackup'
$Server = "ServerName"
$RolloutEnvironment = "location.asazure.windows.net"
$ResourceGroup = "ReourceGroupName"
#Create Credentials to convertToSecureString
$applicationId = "applicationId "
$securePassword = "securePassword " | ConvertTo-SecureString -AsPlainText -Force $Credential = New-Object
-TypeName System.Management.Automation.PSCredential -ArgumentList $applicationId, $securePassword
#Define the list of AAS databases
$asDBs = #('database1','database2')
Write-Output "Logging in to Azure..."
#Add-AzureAnalysisServicesAccount -Credential $Credential -ServicePrincipal -TenantId $TenantId -RolloutEnvironment $RolloutEnvironment
ForEach($db in $asDBs)
{
Write-Output "Starting Backup..."
Backup-ASDatabase `
–backupfile ($db +"." + (Get-Date).ToString("ddMMyyyy") + ".abf") `
–name $db `
-server $Server `
-Credential $Cred
Write-Output "Backup Completed!"
}
You are correct that the issue is with multi-factor authentication. Since the point of multi-factor is the require interaction with a secondary source like your phone there is no way to automate the process.
I would suggest that you look into using service principle authentication for the purpose of taking backups. By using a service principle to your server you can allow for automated tasks to run without 2-factor while minimizing the security risk.

Start-AzureSqlDatabaseExport Object Reference not set to an instance of an object

I'm not sure how to debug this, assuming it's not a problem with the cmdlet. I'm trying to replace the automated SQL export with an automation workflow, but I can't seem to get Start-AzureSqlDatabaseExport to work -- it keeps getting the following warning and error messages.
d4fc0004-0c0b-443e-ad1b-310af7fd4e2a:[localhost]:Client Session Id: 'c12c92eb-acd5-424d-97dc-84c4e9c4f914-2017-01-04
19:00:23Z'
d4fc0004-0c0b-443e-ad1b-310af7fd4e2a:[localhost]:Client Request Id: 'd534f5fd-0fc0-4d68-8176-7508b35aa9d8-2017-01-04
19:00:33Z'
Start-AzureSqlDatabaseExport : Object reference not set to an instance of an object.
At DBBackup:11 char:11
+
+ CategoryInfo : NotSpecified: (:) [Start-AzureSqlDatabaseExport], NullReferenceException
+ FullyQualifiedErrorId : Microsoft.WindowsAzure.Commands.SqlDatabase.Database.Cmdlet.StartAzureSqlDatabaseExport
This seems similar to some other questions, but they seem to be unanswered or not applicable. I did have a similar procedure working in the Powershell environment. I replaced that procedure with the automated export from Azure, which seems like a poor choice now! I've tried a number of variations, using sqlcontext and databasename instead of database, for example.
Here's my code with sensitive parts replaced with ****:
workflow DBBackup {
param(
[parameter(Mandatory=$true)]
[string] $dbcode
)
$cred = Get-AutomationPSCredential -Name "admindbcredentials"
$VerbosePreference = "Continue"
inlineScript {
$dbcode = $using:dbcode
$cred = $using:cred
if ($dbcode -eq $null)
{
Write-Output "Database code must be specified"
}
Else
{
$dbcode = $dbcode.ToUpper()
$dbsize = 1
$dbrestorewait = 10
$dbserver = "kl8p7d444a"
$stacct = $dbcode.ToLower()
$stkey = "***storagekey***"
Write-Verbose "DB Server '$dbserver' DB Code '$dbcode'"
Write-Verbose "Storage Account '$stacct'"
$url = "https://$dbserver.database.windows.net"
$sqlctx = New-AzureSqlDatabaseServerContext -ManageUrl $url -Credential $cred
# $sqlctx = New-AzureSqlDatabaseServerContext -ManageUrl $url -Credential $cred
$stctx = New-AzureStorageContext -StorageAccountName $stacct -StorageAccountKey $stkey
$dbname = "FSUMS_" + $dbcode
$dt = Get-Date
$timestamp = $dt.ToString("yyyyMMdd") + "_" + $dt.ToString("HHmmss")
$bkupname = $dbname + "_" + $timestamp + ".bacpac"
$stcon = Get-AzureStorageContainer -Context $stctx -Name "backups"
$db = Get-AzureSqlDatabase -Context $sqlctx -DatabaseName $dbname
Write-Verbose "Backup $dbname to $bkupname in storage account $stacct"
Start-AzureSqlDatabaseExport $sqlctx -DatabaseName $dbname -StorageContainer $stcon -BlobName $bkupname
}
}
}
Try New-AzureRmSqlDatabaseExport instead. This command will return export status object. If you want a synchronous export you can check for "export status" in a loop.
Adding the following lines corrected the problem:
In the workflow before inlineScript:
$cred = Get-AutomationPSCredential -Name "admincredentials"
(where admincredentials was an asset with my admin login credentials)
and inside the inlineScript:
Add-AzureAccount $cred
Select-AzureSubscription "My subscription"
Some runbooks don't seem to need this, but probably best to always include it.

How to deny Windows Credential Password Prompt everytime when operate remote windows service in powershell?

Below is my code:
$s = Get-WmiObject -computer 10.10.zz.zz Win32_Service -Filter "Name='XXX'" -credential (Get-Credential XXXXXX\fanwx)
$s.stopservice()
copy-item D:\.....\aaa.exe -destination \\10.10.zz.zz\c$\vvv\
copy-item D:\.....\aaa.pdb -destination \\10.10.zz.zz\c$\vvv\
$s.startservice()
everytime executed, will be prompted enter the password of the remote server. Is there a way allowed me only enter once in powershell OR read the credential in Credential Manager?
Thanks.
Just start by
$cred = Get-Credential "XXXXXX\fanwx"
and after :
$s = Get-WmiObject -computer 10.10.zz.zz Win32_Service -Filter "Name='XXX'" -credential $cred
You can put the password on the disk :
PS > $cred.Password | ConvertFrom-SecureString | Set-Content c:\temp\password.txt
And retreive it with :
$password = Get-Content c:\temp\password.txt | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PsCredential "UserName",$password

Resources