How to connect Azure Paas Database using Powershell with intergrated security - powershell

How can I connect to an Azure SQLDatabase (Paas Instance) using Powershell without a username & password but rather with Active Directory. I can connect with a username and password when I use invoke-sqlcmd as below
$Servername = "XXXXXX.database.windows.net"
$params = #{
Database = $databasename
ServerInstance = $ServerName
Username = $UserName
Password = $password
'OutputSqlErrors' = $true
Query = $QueryString
}
Invoke-Sqlcmd #params
I need the ability to do it with Active Directory. I have read a few articles on how to connect with active directory as long as you specify it on the connection string (Context of C#) Using ODBC connection or other data sources. Since Powershell is based on .Net; this should be do-able..
$ConnString = "DRIVER=ODBC Driver 13 for SQL Server;SERVER=xxxxx.database.windows.net;DATABASE=DBName;Trusted_Connection=Yes"
$Conn = new-object System.Data.Odbc.OdbcConnection($ConnString)
$conn.open()
$cmd = new-object System.Data.Odbc.OdbcCommand("select getdate() as X", $Conn)
$data = new-object System.Data.Odbc.OdbcDataAdapter($cmd)
$dt = new-object System.Data.DataColumn
$data.Fill($dt)
$Conn.Close()
#`server=Server;database=Database;Authentication=ActiveDirectoryIntegrated;`
The objective of trying to use Active Directory is that, within Paas, only AD Accounts can Add other AD accounts. I want to be able to add other accounts with method rather than logging in the Azure portal and adding an AD account manually.

You could use Azure AD account to login Azure SQL database(Paas) by using Azure Active Directory Authentication. More information please refer to this link.
Note: Local domian AD user does not support this.
You could use following script to login with Azure AD authentication.
#You admin Azure AD user name
$Username = "shuitest#*****.onmicrosoft.com"
$Password = "********"
$Database = "testg"
$Server = 'test.database.windows.net'
$Port = 1433
$cxnString = "Server=tcp:$Server,$Port;Database=$Database;Authentication=Active Directory Password;UID=$UserName;PWD=$Password;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;"
$query = "select count(*) from dbo.Authors"
$cxn = New-Object System.Data.SqlClient.SqlConnection($cxnString)
$cxn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand($query, $cxn)
$cmd.CommandTimeout = 120
$cmd.ExecuteNonQuery()
$cxn.Close()

The following sample shows how to invoke Add-AzureAccount without the popup dialog:
$username = "someorgid#orgaccount.com"
$password = "Pa$$w0rd" | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
Add-AzureAccount -Credential $credential

AAD does not / can not parse Kerbs tokens. In order to get integrated auth to work from your desktop, you need to have ADFS (or similar) in your environment. That way your desktop will authenticate against AAD, redirect to ADFS, and your kerbs token will be recognized.
See https://learn.microsoft.com/en-us/azure/sql-database/sql-database-aad-authentication for more information.

this now works for me in the connection string instead of user and pass
Authentication=Active Directory Integrated;Trusted_Connection=False;Encrypt=True;Connection Timeout=30;

Depending on your query you might have to use $cmd.ExecuteScalar() instead of $cmd.ExecuteNonQuery() for example
$query = 'select ##version'
$cxn = New-Object System.Data.SqlClient.SqlConnection($cxnString)
$cxn.Open()
$cmd = New-Object System.Data.SqlClient.SqlCommand($query, $cxn)
$cmd.CommandTimeout = 120
$cmd.ExecuteScalar()
$cxn.Close()

Related

PowerShell - How do I add a User to an AD User Group using ADSI and alternate credentials

I need to add a user to an AD Group using specific credentials and cannot figure this out.... Here's what I have so far which gives me an "unspecified error" retrieving member 'Add'. Must be done using ADSI as the AD module won't work in my scenario.
$CredsUserName = 'domain\user'
$CredsPassword = 'password'
$GroupPath = "LDAP://CN=<UserGroup>...."
$UserPath = "LDAP://CN=<UserDN>...."
$Group = [ADSI]$GroupPath
$User = [ADSI]$UserPath
$GroupArgs = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList $Group, "$CredsUserName", "$CredsPassword"
$GroupArgs.Add($User.adspath)
Try this:
$CredsUserName = 'domain\user'
$CredsPassword = 'password'
$Domain = "<FQDN of the AD domain>/"
$GroupPath = "CN=<UserGroup>...."
$UserPath = "CN=<UserDN>...."
$Group = [adsi]::new("LDAP://$($Domain)$($GroupPath)",$CredsUserName,$CredsPassword)
$Group.member.Add($UserPath)
$Group.CommitChanges()
You only need $Domain if the computer you are using isn't part of the AD domain that contains the group.
Regards,
Stuart.

Connect an Azure App Registration to Azure SQL Database via Powershell

Need a Powershell script to run on-prem that will login into an Azure SQL database as an Azure AD App Registration. I am able to acquire an access token for the App Registration, but I get an error when I attempt to login to the database with it:
Exception calling "Open" with "0" argument(s): "Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'."
I have created the user and applied grants inside the target database.
CREATE USER [App Reg Dave Test] FROM EXTERNAL PROVIDER;
ALTER ROLE [db_datareader] ADD MEMBER [App Reg Dave Test];
ALTER ROLE [db_datawriter] ADD MEMBER [App Reg Dave Test];
I'm not sure it's necessary, but I have also granted api permissions to the App registration for SQL Database User_Impersonation and App_Impersonation. Here is a sample of my PS code. It's failing on the $connection.open() command.
#-- Intall ADAL.ps
# Import-Module ADAL.PS
#-----------------------------------
#-- Get Token for App Registration
#-----------------------------------
$tenantId = "448b9f7b-9e69-xxxx-xxxx-xxxxxxxxxxxx"
$authority = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize"
$resourceUrl = "api://123a7216-7b20-yyyy-yyyy-yyyyyyyyyyyy"
$clientId = "123a7216-7b20-yyyy-yyyy-yyyyyyyyyyyy"
$secret = ConvertTo-SecureString -String "X87ZK#NPw=zzzzzzzzzzzzzzzzzzzx/-" -AsPlainText -Force
$response = Get-ADALToken -Authority $authority -Resource $resourceUrl -ClientId $clientId -ClientSecret $secret
$token = $response.AccessToken
#-----------------------------------
#-- Connect to DB
#-----------------------------------
$sqlServerUrl = "dave-sqls2.database.windows.net"
$database = "advworks"
$connectionString = "Server=tcp:$sqlServerUrl,1433;Initial Catalog=$database;Persist Security Info=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
# Add AAD generated token to SQL connection token
$connection.AccessToken = $response.AccessToken
$connection #-- Output connection object for debugging.
$query = "SELECT ##Servername, ##Version"
$command = New-Object -Type System.Data.SqlClient.SqlCommand($query, $connection)
$connection.Open()
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter $command
$dataset = New-Object System.Data.DataSet
$adapter.Fill($dataSet) | Out-Null
$connection.Close()
$dataSet.Tables
According to the PowerShell script you provide, you use the wrong resourceUrl. Please update the resource url as https://database.windows.net/. For more details, please refer to the blog.
The detailed steps are as below.
1. Create service principal
$SecureStringPassword = ConvertTo-SecureString -String "[Enter SPN Password]" -AsPlainText -Force
New-AzureRmADApplication -DisplayName "[Enter name for Application]" -HomePage "https://www.contoso.com/sqldb-spn" -IdentifierUris "https://www.contoso.com/sqldb-spn" -Password $SecureStringPassword -OutVariable app
New-AzureRmADServicePrincipal -ApplicationId $app.ApplicationId
Use Azure AD admin to login Azure SQL via SSMS. Then run the following command
CREATE USER [your app name] FROM EXTERNAL PROVIDER;
ALTER ROLE [db_datareader] ADD MEMBER [App Reg Dave Test];
ALTER ROLE [db_datawriter] ADD MEMBER [App Reg Dave Test];
Get Access token
$tenantId = "your tenant id"
$authority = "https://login.microsoftonline.com/$tenantId/oauth2/authorize"
$resourceUrl = "https://database.windows.net/"
$clientId = "your application id"
$secret=ConvertTo-SecureString "" -AsPlainText -Force
$response = Get-ADALToken -Authority $authority -Resource $resourceUrl -ClientId $clientId -ClientSecret $secret
$token = $response.AccessToken
4 Connect SQL
$sqlServerUrl = "<your server name>.database.windows.net"
$database = "master"
$connectionString = "Server=tcp:$sqlServerUrl,1433;Initial Catalog=$database;Connect Timeout=30"
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$connection.AccessToken = $response.AccessToken
$connection.Open()
$query="SELECT name, database_id, create_date FROM sys.databases ; "
$command = New-Object -TypeName System.Data.SqlClient.SqlCommand($query, $connection)
$Result = $command.ExecuteScalar()
$Result
$connection.Close()

Maintaining user environment variables in Powershell logon script with admin privileges

I accidentally deleted 180 users from my AD and they aren't recoverable. I have recreated the accounts in AD and what not. This creates a new profile on their laptops when they login because of the new SID. I'm trying to write a script that grants them access to their old profile folder and create a shortcut on their desktop that leads there.
I've got the script working fine with one problem. The environment variables that are used, end up referring back to the admin account that runs the script. The users themselves don't have permission to change security on their old folder. I need to try and have the environment variables refer to the user yet have the privilege of an admin account to rewrite the permissions.
Here is the script so far.. I'm deploying this with Task Scheduler at the moment, which is another can of worms in that I'm not entirely understanding of the credential side of things there. I mean ideally, the task would run as a domain admin, execute the script asap, and have the script resolve the environment variables to the logged on user.
$permission = ":(OI)(CI)M"
$sam = $env:USERNAME
$folderName = "C:\Users\$($sam)"
Invoke-Expression -Command ( 'ICACLS $folderName /grant:r $sam$($permission) /t' )
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\Profile Backup.lnk")
$Shortcut.TargetPath = $folderName
$Shortcut.Save()
Its the $env:USERNAME and $home variables that are giving me trouble..
Or is there another way I should be tackling this problem?
You could use query session command to get the login name of the current logged on user. Then create NTAccount object based on that to retrieve SID and win32_userprofile WMI object to find out the profile path. Like this:
$m = query session | Select-String -Pattern "\>console\s*(\S*)\s"
$sam = $m.Matches[0].Groups[1].value
$acc = New-Object System.Security.Principal.NTAccount($sam)
$sid = $acc.Translate([System.Security.Principal.SecurityIdentifier]).Value
$profile = Get-CimInstance -ClassName win32_userprofile -Filter "SID='$sid'"
$folderName = $profile.LocalPath
Edit I have given it second thought over-night so I'll update the answer. You will be required to have domain admin password encrypted and then users will run the script.
It always sucks when something like this happens. I don't have a possibility to try this out, but I think the following approach would be feasible. The script asks user for password encrypts it and run the command as the user.
First phase would be to have a domain admin to encrypt his password to a file:
This is to be prepared by Domain Admin (distributed with the PS script) - I recommend changing password after the recovery is complete:
1) Read-Host -AsSecureString | ConvertFrom-SecureString | Out-File 'C:\<script_path>\admin_passwd.txt'
2) This is to be executed by user (you have to fill in the admin user id and have the password file distributed with the script). The script path can be obtained by (Get-Location).Path. I'm not adding it into the source code so you can decide how to implement it:
$permission = ":(OI)(CI)M"
$admin= "<your_admin_userid>"
$sam = $env:USERNAME
$domain = $env:UserDomain
$folderName = "C:\Users\$($sam)"
# get domain admin password
$encrypted_passwd = get-content 'C:\<script_path>\admin_passwd.txt' | ConvertTo-securestring
# Setting process invocation parameters.
$process_start_info = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$process_start_info.CreateNoWindow = $true
$process_start_info.UseShellExecute = $false
$process_start_info.RedirectStandardOutput = $true
$process_start_info.RedirectStandardError = $true
$process_start_info.UserName = $admin
$process_start_info.Domain = $domain
$process_start_info.Password = $encrypted_passwd
$process_start_info.Verb = 'runas'
$process_start_info.FileName = 'ICACLS'
$process_start_info.Arguments = "$folderName /grant:r $sam$($permission) /t"
# Creating process object.
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo = $process_start_info
# Start the process
[Void]$process.Start()
$process.WaitForExit()
# synchronous output - captures everything
$output = $process.StandardOutput.ReadToEnd()
$output += $process.StandardError.ReadToEnd()
Write-Output $output
$WshShell = New-Object -comObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$Home\Desktop\Profile Backup.lnk")
$Shortcut.TargetPath = $folderName
$Shortcut.Save()

Create Azure AD B2C local account user with Powershell New-AzureADUser

I'm trying to create a local user in an Azure AD B2C directory which can be used for authentication immediately after creation.
Connect-AzureAD -TenantId $targetB2cTenant
$passwordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile
$passwordProfile.Password = "Test-User-Password-Here"
$userName = "TestUser#MyTestB2CTenant.onmicrosoft.com"
$signInNames = #(
(New-Object `
Microsoft.Open.AzureAD.Model.SignInName `
-Property #{Type = "userName"; Value = $userName})
)
$newUser = New-AzureADUser -AccountEnabled $True -DisplayName "testpowershellusercreation" -PasswordProfile $passwordProfile -SignInNames $signInNames -CreationType "LocalAccount"
Disconnect-AzureAD
From reading the documentation I need to specify the CreationType parameter as "LocalAccount":
https://learn.microsoft.com/en-us/powershell/module/azuread/new-azureaduser?view=azureadps-2.0
Creating a B2C user with MFA that can immediately login
However when I run the powershell code I receive the following error:
New-AzureADUser : Error occurred while executing NewUser
Code: Request_BadRequest
Message: One or more properties contains invalid values.
This error message is not present when I remove the -CreationType parameter.
What is the correct way to create a local account in a B2C directory using Powershell?
A sign-in name of type "userName" can't contain the '#' character in the value property.
i.e. You can't set it to an email address.
You might want to also set the following parameters for the new user:
$passwordProfile = New-Object -TypeName Microsoft.Open.AzureAD.Model.PasswordProfile
$passwordProfile.ForceChangePasswordNextLogin = $False
$passwordProfile.Password = "<Password>"
$newUser = New-AzureADUser ... -PasswordPolicies "DisablePasswordExpiration"
I think you could also change the type of sign-in name from "userName" to "email", to work around this issue and allow users to continue using their foreign domain email addresses as login, if required.
$signInNames = (
(New-Object `
Microsoft.Open.AzureAD.Model.SignInName `
-Property #{Type = "email"; Value = "pstesta#fsdfsd.com"})
)

Powershell Connect to VSO

I'm trying to use Powershell to connect to VSO. Here is my code:
$tfsServer = New-Object System.Uri("the server is here")
$creds = [System.Net.CredentialCache]::DefaultNetworkCredentials
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($tfsServer,$creds)
$tfsCollection.Authenticate()
When it reaches the Authenticate line, it pops up a box for me to enter my credentials. I need it to not pop up this box, as this script will be scheduled, and I can't keep entering the credentials. How can I pass the current user's credentials to the TFS object?
Try this:
First, run this command which will prompt you once for your password, and then save it in an encrypted format.
read-host -prompt Password -assecurestring | convertfrom-securestring | out-file .\ps-password.pwd -ErrorAction Stop
Change the $username variable
$Username = 'jdoe'
$Password = Get-Content ".\ps-password.pwd" | ConvertTo-SecureString
$creds = New-Object -typename System.Management.Automation.PSCredential -ArgumentList $Username,$Password
$tfsServer = New-Object System.Uri("the server is here")
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($tfsServer,$creds)
$tfsCollection.Authenticate()
Use the constructor that just takes a URI. It will default to using the credentials of the current user.
To connect to Visual Studio Online, you have to follow the instructions at Buck's post. Shortly:
enable alternate credentials on the VSO account
set alternate user and password
use code similar to the following
$tfsServer = New-Object System.Uri("the server is here")
$netCred = New-Object NetworkCredential("alternate_user","alternate_password")
$basicCred = New-Object Microsoft.TeamFoundation.Client.BasicAuthCredential($netCred)
$tfsCred = New-Object Microsoft.TeamFoundation.Client.TfsClientCredentials($basicCred)
$tfsCred.AllowInteractive = $false
$tfsCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($tfsServer,$tfsCred)
$tfsCollection.EnsureAuthenticated()
I know no way of using current process credentials with VSO, but you must explicitly pass them.
Use EnsureAuthenticated and do not specify credentials.
$tfsCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection("the server is here")
$tfsCollection.EnsureAuthenticated()
This way it will use the account running the process.