Powershell - Connecting to SQL Server using Connect-AzAccount (With MFA) - azure

We have a SQL Server set up (created by me, with my account as the admin account), and using Management Studio, I can access the database just fine. I'm now trying to achieve the same thing through Powershell.I have created my own user using:
CREATE USER [<my ccount>] FROM EXTERNAL PROVIDER
I start by connecting using MFA (and signing in using the pop-up window/MFA):
Connect-AzAccount -SubscriptionId $subscriptionID
This works fine, and returns the expected values of the subscription specified, and my Azure AD Login as account. I can access my KeyVault and pull secrets from there.
I now would like to connect to my SQL Server using the credentials I'm already signed in with within the PowerShell session, and that's where I get stuck.
I tried a ConnectionString including Authentication=Active Directory Integrated, but that throws an error Exception calling "Open" with "0" argument(s): "One or more errors occurred.".
I then tried using a connection string like this: Server=tcp:<server>.database.windows.net,1433;Initial Catalog=<database>;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30, and adding an AccessToken using SqlConn.AccessToken = $(Get-AzAccessToken -ResourceUrl "Https://database.windows.net/").
However, when I then try to Open the connection, I get the error: "Login failed for user '<token-identified principal>'."
Googling led to several SO articles, but none of those use Connect-AzAccount, but the (what I believe to be outdated) Az Account (I.e. Connecting to SQL Server using Powershell with Azure AD MFA).
Other examples I found all add UID and Password, which should not be needed considering I authenticated already.
Is there a way to use the existing Connect-AzAccount session to authenticate against my Azure SQL Server, and if so, what format should the ConnectionString have? I have a feeling I'm very close to a solution, but just cannot seem to actually achieve the opening of the connection.
Thanks in advance.

After reading more and more, I finally stumbled across this thread. #golfalot's answer was exactly what I needed.
So the proper code to initialize the connection is:
# Sign in to Azure
Connect-AzAccount -SubscriptionID $subscriptionID
$connectionString = "Server=<server>.database.windows.net;Initial Catalog=<database>;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30"
$accessToken = (Get-AzAccessToken -ResourceUrl https://database.windows.net).Token
$sqlConn = New-Object System.Data.SqlClient.SqlConnection
$sqlConn.ConnectionString = $connectionString
$sqlConn.AccessToken = $accessToken
$sqlConn.Open()

Related

Automate connecting to Azure for checking and creating resources without prompt

I am required to write a PowerShell script, which can connect to my company's Azure account, check and create Azure resources (eg. Service Bus namespace, Service Bus topic, and Service Bus subscriptions). Everything worked well until I tried to deploy my script as a step in my project's on-premise TeamCity. I keep getting this error message
Exception calling "ShouldContinue" with "2" argument(s): "Windows
PowerShell is in NonInteractive mode. Read and Prompt functionality is
not available."
I investigated and found out that the problem is in this line
Connect-AzAccount
If I run the script manually, it will pop up a prompt asking me to login to Azure. I believe that's what went wrong. Because my project's on-premise TeamCity does not seem have an option to open a prompt for PowerShell command. I have read some workarounds, even on this website, but none of them is applicable to my case. Even a solution like https://stackoverflow.com/a/61099568/8213536 gave me these errors
WARNING: Unable to acquire token for tenant 'organizations' with error
'UsernamePasswordCredential authentication failed: There was an error
parsing WS-Trust response from the endpoint. This may occur if there
is an issue with your ADFS configuration. See
https://aka.ms/msal-net-iwa-troubleshooting for more details. Error
Message: Object reference not set to an instance of an object. See the
troubleshooting guide for more information.
https://aka.ms/azsdk/net/identity/usernamepasswordcredential/troubleshoot'
Connect-AzAccount : UsernamePasswordCredential authentication failed:
There was an error parsing WS-Trust response from the endpoint. This
may occur if there is an issue with your ADFS configuration. See
https://aka.ms/msal-net-iwa-troubleshooting for more details. Error
Message: Object reference not set to an instance of an object. See the
troubleshooting guide for more information.
https://aka.ms/azsdk/net/identity/usernamepasswordcredential/troubleshoot
At line:1 char:1
Connect-AzAccount -Credential $creds
+ CategoryInfo : CloseError: (:) [Connect-AzAccount], AuthenticationFailedException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.ConnectAzureRmAccountCommand
One of the other solutions https://stackoverflow.com/a/52014189/8213536 requires an application's principal id, which is not applicable for my scenario either, as I am not creating a new application. I just need to be able to automatically connect to Azure (without prompt), check and create SB Namespace, SB Topic and SB Subscription.
Could someone please help me on this? Thanks.
As promised, I would like to post my solution. First I created a service principal with a client secret key. Then I asked my company's cloud engineer to assign it to the Azure subscription of my company and to the resource group that I intended to group all my necessary resources into. Finally in my code, I implemented something similar to https://stackoverflow.com/a/61099568/8213536
$applicationId = $azServicePrincipalId
Write-Host "Connecting to Azure using principal $applicationId"
$securePassword = $azServicePrincipalPw | ConvertTo-SecureString -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $applicationId, $securePassword
Connect-AzAccount -ServicePrincipal -TenantId $azureTenantId -Credential $credential
$azServicePrincipalId and $azServicePrincipalPw came from the Service Principal itself, while $azureTenantId came from my company's Azure subscription.
It is now working as expected.

ClientSecretCredential authentication failed: A configuration issue is preventing authentication - check the error message from the server for details

I am trying to get the access token of the service principal using the following code.
$authUrl = "https://login.windows.net/" + $tenantid + "/oauth2/token/"
$body = #{
grant_type = "client_credentials"
client_id = $serviceprincipalid
resource = "https://management.azure.com/"
client_secret = $serviceprincipalkey
};
$response = Invoke-RestMethod –Uri $authUrl –Method POST –Body $body
Write-Host $response
Write-Output $response.access_token
##vso[task.setvariable variable=myToken;]$response.access_token
The above code is working perfectly at my local machine's PowerShell but it is giving the following error when I am running the same code base in the Azure DevOps pipeline.
ClientSecretCredential authentication failed: A configuration issue is preventing
authentication - check the error message from the server for details. You can modify the
configuration in the application registration portal. See https://aka.ms/msal-net-invalid-
client for details. Original exception: AADSTS7000222: The provided client secret keys are
expired. Visit the Azure Portal to create new keys for your app, or consider using certificate
credentials for added security: https://learn.microsoft.com/azure/active-
directory/develop/active-directory-certificate-credentials
Trace ID: 98787ui7-e8ae-4712-b8b5-7678u8765rt5
Correlation ID: yhjnbv43-56sy-9ksy-b8b5-mj876yu78i90
Timestamp: 2021-03-16 12:32:28Z
There was an error with the service principal used for the deployment.`
I checked the secret keys, but the secret keys not expired, it's expiry date is already set for the year 2022. And if it would expire then the code should not have worked at my local machine's PowerShell.
Does anyone have any idea? please let me know to resolve this issue.
Well, actually the error was not caused by the script above, per my test, it works fine in devops.
If you use the Azure PowerShell task, it will let you configure a service connection to use, when you run the task, it will connect Azure powershell with the service principal configured in the service connection automatically. The error was caused by the expired secret of the service principal configured in the service connection, not the one in your script.
I can also reproduce your issue on my side.
To solve this issue, please follow the steps below.
1.Navigate to the Azure PowerShell task, check which service connection you used.
2.Navigate to the Project Settings in devops -> Service connections -> find the one you used -> click it -> Manage Service Principal.
Then it will open the related AD App page, just create a new secret and service connection, use it in the Azure Powershell task, follow the same steps I have mentioned here.
3.After configuration, test it again, it will work fine.

How to Connect-AzAccount in Powershell Core (without prompt)?

I did see this q/a: Connect-AzAccount without prompt
But when I tried the accepted answer, I get the following error:
[6/12/2020 12:36:20 AM] ERROR: Connect-AzAccount : Username + Password authentication is not supported in PowerShell Core. Please use device code authentication for interactive log in, or Service Principal authentication for script log in.
So I went to example 3 of the Connect-AzAccount documentation which specifies the "Service Principal" authentication method, so I mix the two because the suggested vanilla Get-Credential triggers another interactive session. So here's the script now:
$User = "myemail#gmail.com"
$PWord = ConvertTo-SecureString -String "**********" -AsPlainText -Force
$tenant = "f*********************************"
$Credential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList $User,$PWord
# $Credential = Get-Credential
Connect-AzAccount -Credential $Credential -Tenant $tenant -ServicePrincipal
which brings my next error: [6/12/2020 12:45:45 AM] ERROR: Connect-AzAccount : AADSTS700016: Application with identifier 'myemail' was not found in the directory 'f*********************************'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.
I'm really confused at this point because all I have done at this point in Azure is:
Create a new Azure account
Provision an Azure API Management instance through the UI (which btw, takes like 20 minutes)
Try the above code to connect to Azure inside of a Powershell Azure Function locally.
I think something is wrong with the information I've provided or how I've configured something.
$User is the email I signed up to Azure with.
$PWord is my Azure password
$tenant is the first thing I saw when I opened Azure AD:
What's wrong with how I'm trying to connect to Azure through Powershell Core?
Based on Example 3, it asks for entering your application ID for the username and service principal secret as the password.
So you need to create a service principal at first. And then use its application ID and client secret as the credential.
$User = "{application id}"
$PWord = ConvertTo-SecureString -String "{client secret}" -AsPlainText -Force
I don't like Azure documentation. It gives off a very different vibe from GCP and feels much less beginner friendly.
With that said, they did have some kind of write-up that addresses my issue of creating a service principal and using it to authenticate.
I actually ended up just finding a video (and I never do this) because I wanted to skip past all the technical jargon and just create the darn service principal.
It's not even intuitive - it's like Microsoft could have added a button in AZ AD or IAM that said "Create Service Principal" but no, you have to go to a bunch of other pages that say nothing about service principals. You'll see:
In Azure Portal, navigate to the App Registrations page in Azure Active Directory. What an "app registration" has to do with a service principal, I couldn't tell you. I also couldn't tell you what a service principal is, but I'd imagine it has something to do with service accounts.
Make a New Registration and give it some sort of name to describe what the scope of this service principal will entail. Like normal service account naming conventions. I don't think the account type matters but I chose Multitenant. Redirect URL has nothing to do with service principals, and honestly makes it all the more confusing. I would never associate service accounts with any kind of redirect url, but here we are.
You're going to arrive at a page with Display Name (the name of the service principal you gave it in step 2), Application (client) ID (this is actually your service account username, which is imo non-intuitive), and Object ID (I have no idea what this is but I never needed to use it.
Guess what, you have only created 1/3 of your service account. It doesn't even have a password yet. Within your created app registration, there's a Certificates & Secrets page. On that page, you want to add a new client secret. For my description I just put my service principal "display name". I don't think that was necessary because this client secret is within the scope of the app registration, so even if I named it "poop" I could reasonably assume what it was for. Azure will generate a nuanced client secret and display it, but not warn you that this is the only time you will be able to see the key. Copy it. This is, in normal people talk, your service principal password.
For the last step, you need to get out of dodge, I mean Azure AD. Navigate to your Subscriptions page and click on your active subcription. For some reason IAM is here, so click on that. At this point, your service principal has a username and password, but no actual permissions - you have to configure that manually too. Click Add -> Add Role Assignment. For role, you should do your research but if it's not serious Contributor is probably a safe bet. It has read/write but it doesn't supersede Owner. Make sure you're assigning access to a service principal, and search for its display name. Save.
With all of that done, Connect-AzAccount finally worked.

Azure Devops: value cannot be null, parameter token

I am running an Azure Powershell task in Azure DevOps.
Inside the script I use the following command
Get-AzureRmADUser -UserPrincipalName $adusernameNewPermission
But my Release pipeline fails with following error code
##[error]Value cannot be null.
Parameter name: token
First I thought that the command didn't have the right context or enough permission so I've added the defaultprofile.
Get-AzureRmADUser -DefaultProfile (Get-AzureRmContext) -UserPrincipalName $adusernameNewPermission
The GetAzureRmContext did go to the right context if I print the output of that command.
The command itself didn't had any problem when running locally (with my own user account) So the only reason I think its heading is that the service connection doesn't have the right to perform that action. But my user account has the least permissions on the tenant whilst the service connnection in azure devops has much more permissions.
It's driving me crazy where the problem lays with this one. Which token does it mean ? No reasonable error message :(
Does someone encounter the same problem or knows what I am missing ?
PS: the $adusernameNewPermission variable works like I said the exact same code runs perfectly on my local machine with the only difference being the user that is logged in.
Did you try using Get-azureaduser instead? This is a command that requires the function to be authenticated against Azure AD.
$azurePassword = ConvertTo-SecureString $env:client_secret -AsPlainText -Force
$psCred = New-Object System.Management.Automation.PSCredential($env:clientid , $azurePassword)
Connect-AzAccount -Credential $psCred -TenantId $env:tenantid -ServicePrincipal
If you're using the Script Type of "Script File Path," check that you're not trying to pass in any arguments (such as -token).
I'd try 2 things to get back to basics. This will let you know if it is actually the AzDO Service Principal or not and the type of object to use in the pipeline.
Test the functionality of the command in its simplest form and run it with the account as a string:
Get-AzureRmADUser -UserPrincipalName "achahbar#stankoverflow.com"
#OR
$UPN = "achahbar#stankoverflow.com"
Get-AzureRmADUser -UserPrincipalName $UPN
Assuming this works and depending on what your variable contains you simply need to pass the the UPN/Email object into the command:
Get-AzureRmADUser -UserPrincipalName $adusernameNewPermission.UPN
If this doesn't work pdate your task to Version 4 (Preview) and update your commands to Get-AzADUser and test step 1. again
I fixed this issue by changing the service connection in Azure DevOps that was created with a managed identity. I created a new service connection with a service principal and this doesn't gave me any errors about the value token. Hope any person who looks for this issue in the future got an answer by this.

Running New-AzureRmResourceGroupDeployment from within a Function App

I need to wire up a stateless worker ad-hoc to perform a long running job based off a user action that self destructs when its done. I am trying to run New-AzureRmResourceGroupDeployment from within a PoSh Function App and cannot figure out how to authenticate to Azure from within the PoSh script.
I tried this:
$accountName = "myID#mydomain.com"
$pwd = ConvertTo-SecureString "password" -AsPlainText -Force
$cred = new-object PSCredential($accountName, $pwd)
Add-AzureRmAccount -Credential $cred
New-AzureResourceGroupDeployment -ResourceGroupName yadda yadda
And I get an error message that I need to use an Organization ID (which I am, our Azure AD is federated and we use AD Sync (and SiteMinder w/o WS-* if that matters)):
Add-AzureRmAccount : -Credential parameter can only be used with Organization ID credentials. For more information, please refer to http://go.microsoft.com/fwlink/?linkid=331007&clcid=0x409 for more information about the difference between an organizational account and a Microsoft account.
I tried "Login-AzureRMAccount -Credential $cred" with similar results.
If I do the Add- or Login- cmdlets from a PoSh window on my local machine (which is member joined to AD) with the -Credential flag I get a similar error. If I run the cmdlets without the credential I am prompted for credentials through an interactive ID/PW window (I do not have to enter my password once I type in my ID).
Does anyone know how I can do the authentication? I would be okay with authenticating like above, some sort of pass through credential from our web layer, or even an Option C I don't know about.
You will need to use service principal for authentication. A sample with instructions can be found here.
Azure Function role like permissions to Stop Azure Virtual Machines
For that you would need to use Service Principal auth. I don't think there is any sense of copypasting Azure Doc's to this answer, just consult this document:
https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-group-authenticate-service-principal

Resources