Terraform - Azure Service Principal deployment - insufficient permissions - azure-active-directory

I am trying to create azure service principal. I am connection to azure from my laptop using service principal, I have added required permissions, service principal (which I am using to connect to azure) is a member of global administrators, sp is a member of an application developer and application administrator role in azure AD. In order to connect to Azure I am using following PowerShell commands.
$ApplicationId = "aaa"
$AppPassword = "bbb"
$TenantId = "ccc"
$SecuredPassword = ConvertTo-SecureString -String $AppPassword -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList
$ApplicationId, $SecuredPassword
Connect-AzAccount -ServicePrincipal -TenantId $TenantId -Credential $Credential
When I try run - terraform apply -var-file="variables.tfvars" I am getting an error:
with azuread_application.azuread_app,
│ on service-principal.tf line 3, in resource "azuread_application" "azuread_app":
│ 3: resource "azuread_application" "azuread_app" {
│
│ ApplicationsClient.BaseClient.Post(): unexpected status 403 with OData error: Authorization_RequestDenied: Insufficient privileges to complete the operation.
I was able to deploy other resources without any problems. I am using remote backend state file, located on Azure Container.
terraform code below:
data "azuread_client_config" "client_config" {}
resource "azuread_application" "azuread_app" {
display_name = "sp_name"
owners = [data.azuread_client_config.client_config.object_id]
}
resource "azuread_service_principal" "azuread_sp" {
application_id = azuread_application.azuread_app.application_id
app_role_assignment_required = false
owners = [data.azuread_client_config.client_config.object_id]
}
resource "azuread_service_principal_password" "azuread_sp_password" {
service_principal_id = azuread_service_principal.azuread_sp.object_id
}

I tested the same scenario in my environment using the below code and the Service Principal was successfully created from terraform.
terraform {
backend "azurerm" {
storage_account_name = "cloudshellansuman123" # replace with your storage account name
container_name = "test" #replace with your container name
key = "terraform.tfstate"
access_key = "ukyaH/Jxxxxxxxxxxxxxxxxx="#replace with your storage account access key
}
}
provider "azurerm" {
features{}
client_id="de398e56-xxxxxxxxxxxxxxxxxx-20d07416ecb0"#replace with your service principal client ID which you are using to connect with Azure
client_secret= "-IP7Q~uDLoxxxxxxxxxxxxRGtHMMXj7-.-lA"#replace with your service principal client Secret which you are using to connect with Azure
tenant_id = "ab07xxxxxxxxxxxxxxx--xxx-620b694ded30"#replace with your AzureAD tenant ID which the subscription is a part of
subscription_id="8xxxxxxxxxxx-xxxxxxx-xxxxxxxxx-xxae"#replace with your Subscription ID on Which the Service Principal has Owner/Contributor access
}
provider "azuread" {
client_id="de398e-xx-x-x-x-x-x-x-x416ecb0"#replace with your service principal client ID which you are using to connect with Azure AD
client_secret= "-IP7Q~uDLoxxxxxxxxxxxxxxGtHMMXj7-.-lA"#replace with your service principal client Secret which you are using to connect with Azure AD
tenant_id = "ab0xxxxxxxxxxxxxxx-xxxxxxxxx-xxxxxxxx30"#replace with your AzureAD tenant ID which the subscription is a part of
}
data "azuread_client_config" "current" {}
resource "azuread_application" "terraform" {
display_name = "Ansumantest"
owners = [data.azuread_client_config.current.object_id]
}
resource "azuread_application_password" "terraform" {
application_object_id = azuread_application.terraform.object_id
}
resource "azuread_service_principal" "terraform" {
application_id = azuread_application.terraform.application_id
owners = [data.azuread_client_config.current.object_id]
}
For Testing I created a Service Principal which I am using to connect with Azure and granted all the same permissions that you have granted and also added a Owner access on the subscription like below :
Output:
Note: Using the above code , you don't need again to connect to Azure using PowerShell. It will get authenticated directly using the .tf configuration. Also make sure to use Latest AzureRM and AzureAD provider Versions in terraform i.e. 2.95.0 & 2.17.0 respectively.

Related

Unable to find a management group - terraform

Any advice on additional troubleshooting steps for the below error?
Error: Management Group "00000000-0000-0000-0000-000000000000" was not found
with data.azurerm_management_group.current
on main.tf line 40, in data "azurerm_management_group" "current":
data "azurerm_management_group" "current" {
I am using a service principal with the Contributor role assigned to authenticate to azure.
I have ensured the management group UUID set in the terraform config points to the correct ID.
I have ensured that access management for Azure resources in the azure active directory is enabled so that my user account can manage access to all Azure subscriptions and management groups in this tenant.
I know that the GET Management REST API throws errors when calls are made to azure ad tenants with large resource hierarchies that would return a payload greater than 15 MB. However, I only have 1 root tenant management group in my tenant that I want to reference.
The data source is configured as:
data "azurerm_management_group" "current" {
# parent management group ID pulled in as a variable from terraform cloud using interpolation
name = var.parent_mg_id
}
The management group resource which will be created once the data source can be referenced is currently set as follows:
resource "azurerm_management_group" "az104-02-mg1" {
display_name = "az104-02-mg1"
# current subscription associated with existing tenant assigned to this management group
subscription_ids = [data.azurerm_subscription.current.subscription_id]
# existing root management group within AAD tenant set as parent mg of az-104 lab 2 management group
parent_management_group_id = data.azurerm_management_group.current.parent_management_group_id
}
Thanks in advance!
I tried to reproduce the same issue in my environment and got the below results
I have used the following script for the management groups it worked for me
vi vm.tf
provider "azurerm" {
features {}
}
data "azurerm_subscription" "current" {
}
resource "azurerm_management_group" "example_parent" {
display_name = "ParentGroup"
subscription_ids = [
data.azurerm_subscription.current.subscription_id,
]
}
resource "azurerm_management_group" "example_child" {
display_name = "ChildGroup"
parent_management_group_id = azurerm_management_group.example_parent.id
subscription_ids = [
data.azurerm_subscription.current.subscription_id,
]
}
Follow the below steps to execute the file
Terraform init
It will initialize the file
terraform plan
This will creates an execution plan and it will preview the changes that terraform plans to make the infrastructure
terraform apply
This will creates or updates the infrastructure depending on the configuration

Connecting to SQL Server using Powershell with Azure AD MFA

I am trying to connect to my Azure SQL instance using an Access Token from Azure AD. I was following this tutorial over here: https://medium.com/microsoftazure/deploying-a-dacpac-to-azure-with-azure-pipelines-and-managed-identity-89703d405e00
But something is not working right in the approach.
The first thing was to make sure my user was setup in the database via:
CREATE USER [myemail#email.com] FROM EXTERNAL PROVIDER WITH DEFAULT_SCHEMA=[dbo]
Which is the same process from this answer: https://stackoverflow.com/a/62161471/1963929
And then I tested both SQL Server Management Studio and Azure Data Studio, both worked perfectly.
But when I try exactly the same thing in Powershell it does not work, all I get is the dread Login failed for user '<token-identified principal>'.
Here’s what I tried
$conn = New-Object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = "Server=tcp:azure-sql.database.windows.net,1433;Initial Catalog=default;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30"
$conn.AccessToken = $(az account get-access-token --resource=https://database.windows.net/ --query accessToken)
$conn.Open()
The error that I receive when connecting to my db is the following
MethodInvocationException: Exception calling "Open" with "0" argument(s): "Login failed for user ''."
Then I thought “maybe I’m using the wrong settings” so I tried the using a client id that my app is using to connect to the same db. And this time I tested multiple scopes: none, .default, and user_impersonation.
$clientid = "azure-data-studio-client-id"
$request = Invoke-RestMethod -Method GET -Uri "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode" -Body #{client_id=$clientid; scope="https://database.windows.net/user_impersonation"} -ContentType "application/x-www-form-urlencoded"
$request.message
$tokens = Invoke-RestMethod -Method POST -Uri "https://login.microsoftonline.com/organizations/oauth2/v2.0/token" -Body #{client_id=$clientid; grant_type="urn:ietf:params:oauth:grant-type:device_code"; code = $request.device_code} -ContentType "application/x-www-form-urlencoded"
$accesstoken = $tokens.access_token
So I thought maybe Azure Data Studio has superpowers, and used another account in there without doing CREATE USER and I got the right error
And this error proves me that CREATE USER is necessary, but it does not explain why I can't do this via Powershell.
I also tried Node and Tedious like the following:
const dbConfig = {
authentication: {
type: "azure-active-directory-access-token",
options: {
token: token
}
},
server: getDatabasePerEnvironment(environment),
database: databaseName,
options: {
trustServerCertificate: false,
encrypt: true,
port: 1433
}
};
const connection = new tedious.Connection(dbConfig);
Same error:
"ConnectionError: Login failed for user ''.
Does anyone know what I'm doing wrong?
I found the issue, and my problem is more minuscule than I thought.
I found my answer in this answer: Azure SQL Grant Access for AD User using PowerShell and ServicePrincipal
What's happening is this line:
$conn.AccessToken = $(az account get-access-token --resource=https://database.windows.net/ --query accessToken)
returns an Access Token wrapped in Double Quotes
$conn.AccessToken = $(az account get-access-token --subscription $subscription --resource https://database.windows.net --query accessToken -o tsv)
That -o tsv at the end will trim the double quotes from the output.
On TediousJS the problem was that I was doing
const tokenPayload = JSON.parse(execSync("az account get-access-token").toString());
What I needed to be doing is:
const tokenPayload = execSync(
"az account get-access-token --subscription YOUR-SUBSCRIPTION --resource https://database.windows.net --query accessToken -o tsv"
).toString();
So the steps you need to do to use the Azure CLI token with SQL Server are the following:
Configure an Active Directory Admin on Azure SQL
Execute something like the following to add your user
CREATE USER [youremail#mail.com] FROM EXTERNAL PROVIDER WITH DEFAULT_SCHEMA=[dbo]
ALTER ROLE db_datareader ADD MEMBER [youremail#mail.com];
ALTER ROLE db_datawriter ADD MEMBER [youremail#mail.com];
ALTER ROLE db_ddladmin ADD MEMBER [youremail#mail.com];
Confirm you can connect with Azure AD with SSMS or Azure Data Studio
Try the following:
$conn = New-Object System.Data.SqlClient.SQLConnection
$conn.ConnectionString = "Server=yourserver.database.windows.net;Initial Catalog=Subledger;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30"
$conn.AccessToken = $(az account get-access-token --subscription YOUR-SUBSCRIPTION --resource https://database.windows.net --query accessToken -o tsv)
$conn.Open()

How to Connect to azuread module non-interactively when MFA is enabled?

I need to connect to AD in azure function app using powershell script. (as it is in function i need to do it without prompt) I am trying this:
Import-Module D:\home\site\wwwroot\HttpTrigger1\AzureAD\AzureAD.psd1 -UseWindowsPowershell
$creds = Connect-AzureAD -TenantId $tenantId -Credential $Credential
In my function app I have enabled Authentication through Log in with Azure Active Directory.
Is there a way to use that authentication in powershell script to connect to azuread module. I mean the user clicks on the function-app url, logs-in with their credentials and that authentication can be used in the script for connect-azuread. The current script is not working as MFA is enabled, which cannot be removed as per our use-case.
Use-case: I have an application in the form of an ARM template that would be deployed as a managed application.
The ARM template is supposed to deploy a set of resources on the tenant of the user, whoever purchases the app. But I need "client id"
and "client secret" of the application registration on user/customer's tenant with O365 mgt api permissions, as input to
my mainTemplate.json.
This App registration is a one-time thing and is not possible through ARM template, that is why I am trying to achieve the above via
powershell. I am creating a powershell function-app, enabled Authentication through Log in with Azure Active Directory.
Idea behind this approach is that at the time of purchasing the app, while filling-in other details(like Resource group name and region) at the UI(created by createUIDefinition.json), the user clicks on the function app link,
logs-in and the script runs in the background. The script should be able to create the app registration at the user's tenant and provide
back the client id and client secret of that app reg.
Unfortunately No !
If MFA is enabled, you will not be able to login non-interactively. This is kind of intentional considering to make it more secure. You cannot as well pass along the authentication.
The workaround for this you could possibly make use of the Azure Service Principal.Get the function authenticated and make the Azure Service Principal to do the job.
What Are Azure Service Principal ?
Service principals are non-interactive Azure accounts. Like other user accounts, their permissions are managed within Azure Active Directory.
Sharing some reference articles to get a deeper insight on the Azure Service Principal:
Creating the Service Principal (The creation is one time process)
https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#app-registration-app-objects-and-service-principals
Creating the Service Principal through Powershell
https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-authenticate-service-principal-powershell#create-service-principal-with-self-signed-certificate
Authenticating the Service Principal
https://learn.microsoft.com/en-us/powershell/azure/authenticate-azureps?view=azps-5.1.0
Coming back to your scenario, to execute Connect-AzureAD with out the interactive login using the service principal you could use the below snippet
# Login to Azure AD PowerShell With Admin Account
Connect-AzureAD
# Create the self signed cert
$currentDate = Get-Date
$endDate = $currentDate.AddYears(1)
$notAfter = $endDate.AddYears(1)
$pwd = ""
$thumb = (New-SelfSignedCertificate -CertStoreLocation cert:\localmachine\my -DnsName com.foo.bar -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $notAfter).Thumbprint
$pwd = ConvertTo-SecureString -String $pwd -Force -AsPlainText
Export-PfxCertificate -cert "cert:\localmachine\my\$thumb" -FilePath c:\temp\examplecert.pfx -Password $pwd
# Load the certificate
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate("C:\temp\examplecert.pfx", $pwd)
$keyValue = [System.Convert]::ToBase64String($cert.GetRawCertData())
# Create the Azure Active Directory Application
$application = New-AzureADApplication -DisplayName "test123" -IdentifierUris "https://test123"
New-AzureADApplicationKeyCredential -ObjectId $application.ObjectId -CustomKeyIdentifier "Test123" -StartDate $currentDate -EndDate $endDate -Type AsymmetricX509Cert -Usage Verify -Value $keyValue
# Create the Service Principal and connect it to the Application
$sp=New-AzureADServicePrincipal -AppId $application.AppId
# Give the Service Principal Reader access to the current tenant (Get-AzureADDirectoryRole)
Add-AzureADDirectoryRoleMember -ObjectId 5997d714-c3b5-4d5b-9973-ec2f38fd49d5 -RefObjectId $sp.ObjectId
# Get Tenant Detail
$tenant=Get-AzureADTenantDetail
# Now you can login to Azure PowerShell with your Service Principal and Certificate
Connect-AzureAD -TenantId $tenant.ObjectId -ApplicationId $sp.AppId -CertificateThumbprint $thumb
Short Explanation of the code :
The above script creates Service Principal, grants a Read Access at the tenant level and connects to the Azure AD at the end using the created Service Principal.

Principal 'xyz' could not be resolved: how can I add a managed identity to Azure SQL Server when running under a Service Principal?

I want to add a managed identity (coming from an App Service) to Azure SQL Server.
I created an AAD group where a group of my team and the Service Principal is part of.
AzureSqlAdminGroup = TeamGroup + Service Principal
This AAD group is added as an Azure SQL admin during the provisioning of the Azure SQL Server.
When I run CreateSqlUserFromManagedIdentity under my personal account everything works fine. Whereas when I run the code under a service principal, SQL Server tells me that it can not resolve the managed identity of my app service and that the service principal doesn't have the permissions to do so.
System.Data.SqlClient.SqlException (0x80131904): Principal 'xyz' could not be resolved. Error message: ''
2020-06-10T16:34:12.6605990Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
2020-06-10T16:34:12.6606728Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
2020-06-10T16:34:12.6607420Z Cannot add the principal 'xyz', because it does not exist or you do not have permission.
Code:
public async Task CreateSqlUserFromManagedIdentity(string managedIdentityName, params string[] roles)
{
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
ExcludeVisualStudioCredential = true,
ExcludeVisualStudioCodeCredential = true
});
var accessToken = await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://database.windows.net/.default" }));
var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(_connectionString);
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"IF DATABASE_PRINCIPAL_ID('{managedIdentityName}') IS NULL");
stringBuilder.AppendLine("BEGIN");
stringBuilder.AppendLine($"\tCREATE USER [{managedIdentityName}] FROM EXTERNAL PROVIDER;");
stringBuilder.AppendLine("END");
Console.WriteLine($"Adding Managed Identity '{managedIdentityName}' to '{sqlConnectionStringBuilder.DataSource}\\{sqlConnectionStringBuilder.InitialCatalog}' with roles ...");
foreach (var role in roles)
{
Console.WriteLine($"\t{role}");
stringBuilder.AppendLine($"ALTER ROLE {role} ADD MEMBER [{managedIdentityName}];");
}
await using var sqlConnection = new SqlConnection(_connectionString) { AccessToken = accessToken.Token };
await sqlConnection.OpenAsync();
var sqlCommand = sqlConnection.CreateCommand();
sqlCommand.CommandText = stringBuilder.ToString();
await sqlCommand.ExecuteNonQueryAsync();
ConsoleEx.WriteSuccessLine("successfully");
}
How can I add a Managed Identity to Azure SQL Server when running under a Service Principal?
Clarification:
xyz is the Managed Identity I want to add as a user in Azure SQL. I am running the code under a service principal (which fails).

Add azure SQL Server login using terraform

Is it possible to add an user as active directory admin for an azure sql server using terraform?
https://learn.microsoft.com/pt-br/azure/sql-database/sql-database-aad-authentication
I need this to be enable users to authenticate through their company logins to a sql server created using Terraform.
I've found this question:
Add azure SQL user with terraform
But it is not what I need, it creates a new user for a login. Terraform docs regarding azure do not document this action.
https://www.terraform.io/docs/providers/azurerm/r/sql_server.html
Please reference this link: Active Directory Admin for azurerm_sql_server:
Support for configuring Azure Active Directory Administrators for a SQL Server Database can be found in the azurerm_sql_active_directory_administrator resource.
azurerm_sql_active_directory_administrator:
Allows you to set a user or group as the AD administrator for an Azure SQL server.
Example useage:
data "azurerm_client_config" "current" {}
resource "azurerm_resource_group" "example" {
name = "acceptanceTestResourceGroup1"
location = "West US"
}
resource "azurerm_sql_server" "example" {
name = "mysqlserver"
resource_group_name = "${azurerm_resource_group.example.name}"
location = "${azurerm_resource_group.example.location}"
version = "12.0"
administrator_login = "4dm1n157r470r"
administrator_login_password = "4-v3ry-53cr37-p455w0rd"
}
resource "azurerm_sql_active_directory_administrator" "example" {
server_name = "${azurerm_sql_server.example.name}"
resource_group_name = "${azurerm_resource_group.example.name}"
login = "sqladmin"
tenant_id = "${data.azurerm_client_config.current.tenant_id}"
object_id = "${data.azurerm_client_config.current.object_id}"
}
Hope this helps.

Resources