Azure terraform Use existing managed identity to authenticate web app - azure-devops

I hope somebody can help me to solve this issue.
Using terraform I scripted some resource groups and webapps. Those web app have some configurations that need to access a key vault to retrieve some secrets.
But to do so, I need to activate the azure identity on the web app.
So far everything is working just fine without any problem. But as I am still learning how to use terraform with azure, I need to keep destroying and spinning up the webapps, which mean everytime I need to activate the identity and add the access policy in the key vault.
So what I did, is to create a azure managed identity resource in the same resource group where I have the key vault. Now I would like to use this managed identity to authenticate my web app everytime I spin up the web app.
My web app code looks like this:
resource "azurerm_app_service" "app-hri-stg-eur-configurations-api" {
name = "app-hri-${var.env}-${var.reg-name}-webapp-testing"
app_service_plan_id = azurerm_app_service_plan.ASP-hri-stg-eur-webapp.id
location = var.location
resource_group_name = azurerm_resource_group.rg-hri-stg-eur-webapp.name
app_settings = {
"secret" = "#Microsoft.KeyVault(SecretUri=https://mykeyvault.vault.azure.net/secrets/test)"
...... <My configuration>
}
identity {
type = "UserAssigned"
}
}
And here is where I am getting confused, how can I reference the azure managed identity that I have already created to grant access to my web app to read the secrets?
I hope I made my question clear enough, and please if not just ask for more info
Thank you so much for any help you can provide

A identity block supports the following:
type - (Required) Specifies the identity type of the App Service. Possible values are SystemAssigned (where Azure will generate a Service Principal for you), UserAssigned where you can specify the Service Principal IDs in the identity_ids field, and SystemAssigned, UserAssigned which assigns both a system managed identity as well as the specified user assigned identities.
identity_ids - (Optional) Specifies a list of user managed identity ids to be assigned. Required if type is UserAssigned.
so you should have sth like this:
data "azurerm_user_assigned_identity" "example" {
name = "name_of_user_assigned_identity"
resource_group_name = "name_of_resource_group"
}
If your identity is in another resource group. It allows you to reference to already created azure resource.
identity {
type = "UserAssigned",
identity_ids = [data.azurerm_user_assigned_identity.example.id]
}

Related

Azure function ServiceBusTrigger connection string value not in application settings

Before I deploy a function app and functions through PowerShell > ARM template I dynamically get a service bus connection string and update the value in the connection binding in the function.json file. Then I deploy the function app and function.
Deployment works, but the function app returns an error message:
Microsoft.Azure.WebJobs.ServiceBus: Microsoft Azure WebJobs SDK ServiceBus connection string 'Endpoint=sb://sbname.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;[Hidden Credential]' is missing or empty.
The question is: Do we really have to create an application setting on the function app with this connection string in it? Is there no other option than using the app setting?
It feels like storing the connection string in another location than on the service bus would just create another vulnerability.
I am aware that I could define "connection": "RootManageSharedAccessKey_SERVICEBUS" in the function.json file and then create an app setting with that same name, but that's not the way forward I want to go.
Please check if my findings help to:
Instead of storing the Service Bus Connection String directly, you can utilize the Azure Key Vault Service or Azure App Configuration Service.
You can store the confidential values like Connection Strings, Key-value pairs, Client Secrets, Certificate Passwords, etc. in Azure Key Vault.
Assume you have stored the ServiceBus connection string in the Key Vault. That you want to retrieve in the Function App through Application Settings but here in the app setting you can define as:
"connection": #Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/RootManageSharedAccessKey_SERVICEBUS/)
This KeyVault URI cannot be accessed by any other user until they are assigned with System or User Assigned Managed Identity Permission.
Azure App Configuration is a central place where you can manage application settings and secure their accesses in one place. Refer here for more information.

Azure Terraform Unable to Set secret in KeyVault during deployment

I am facing a blocker that I don't seem to find a practical solution.
I am using azure terraform to create a storage account, and I would like, during the release pipeline, to be able to set the connection string of this storage account as a secret in an existing KeyVault.
So far I am able to retrieve secret from this KeyVault as I am using a managed identity which has the following permission upon the KeyVault:
key = get, list
secret = get, list and set
cert = get , list
the workflow process in my terraform is as follow:
Retrieve the KeyVault data:
data "azurerm_key_vault" "test" {
name = "test"
resource_group_name = "KeyVault-test"
}
Retrieve the user assigned identity data:
data "azurerm_user_assigned_identity" "example" {
name = "mng-identity-example"
resource_group_name = "managed-identity-example"
}
Once I have those 2 data, I tried to create the secret as follow:
resource "azurerm_key_vault_secret" "secretTest" {
key_vault_id = data.azurerm_key_vault.test.id
name = "secretTest"
value = azurerm_storage_account.storageaccount.primary_connection_string
}
Once I set the release pipeline to run this terraform, it does fail with the error Access Denied
Which is fully understandable as this terraform does not have permission to set or retrieve the secret.
And this is the part on which I am blocked.
If anyone can help understand how can I use my managed identity to set this secret?
I looked into terraform documentation but couldn't find any step or explanation.
Thank you so much for your help and time, and please if you need more info just ask me.
Please make sure that the service principal that you are using to login into Azure using Terraform has the same permission which you assigned to the managed identity .
provider "azurerm" {
features {}
subscription_id = "00000000-0000-0000-0000-000000000000"
client_id = "00000000-0000-0000-0000-000000000000" ## This Client ID needs to have the permissions in Keyvault access policy which you have provided to the managed identity.
client_secret = var.client_secret
tenant_id = "00000000-0000-0000-0000-000000000000"
}
OR
If You are using a Service Connection to connect the Devops Pipeline to Azure and use it in Terrafarom , then you need to provide that Devops service connection (service principal) the permissions in the access policy.

Encryption at Rest, MongoDB, Azure Key vault - unable to connect to your azure vault account

Firstly, I am trying to connect my Azure Key Vault to my Encryption at Rest using your Key Management. I followed the guide on MongoDB documentation linkhttps://docs.atlas.mongodb.com/security-azure-kms/
image from MongoDB Atlas Setup
What I've done so far which havn't worked.
I have set up the application and added the client secret, the application has the role "Azure key Vault Reader" assigned to it through the subscription.
I have setup the Key vault under the same subscription as above - with its own resource group to match it. And generated the key.
The key has all the operations.
So I have the Application with Vault Key Reader access and the Key Vault containing the key.
Client(Application) ID is filled with info from the application.
Tenant ID is filled with tenant ID from the application.
Secret is created and stored in the application - is added. (Not the ID)
Subscription ID copied from key vault is added.
Resource group name copied from key vault is added.
Key Vault Name copied from key vault is added.
lastly the Key Identifier is copied from the vault and added.
Still I get this error - is there something wrong with the way I went about it?
I feel I have tried everything combination of setup but it seems like the credentials are setup in a wrong way which I do not understand since it was all copied directly from Azure.
"We were unable to connect to your Azure Key Vault account. Please check your credentials and try again."
"We were unable to connect to your Azure Key Vault account. Please
check your credentials and try again."
As per #Matt Small suggestion in the comment section, if we enable Azure Key Vault logging, we can check if the issue is with wrong credentials or with access policy or network related issue.
If the issue is with access, we can provide the Key Vault Contributor role or add an access policy to get, list permissions for Keys and Secrets for the service principal (App Registration)
As per #Hurup comment, Azure Key Vault Reader role was not enough and the role should not be under Resource Group. Giving the Application higher vault role and setting it under the subscription can resolve the issue
I had the exact same issue. In the end I figured out that I did not have to create the role assignment 'Have an Active Directory Application with the role of Azure key Vault Reader assigned to it.' on the active directory app, but on the Key Vault.
I followed the manual from MongoDb and then in a final step did;
Go to Key Vault
Select key vault
Select Access Control (IAM)
Select Grant access to this resource
Select role Key Vault Reader
Assign access to :User, group, or service principal
+Select Members
Type Application name
Review and assign...
After this I could save the settings on MongoDB to use encryption at rest.

Creating Service Principle for a specific Azure user

In the quickstart it talks about creating a service principle for the current user. I want to have more of a hierarchical security structure. I would be the current user and I want to have "super-user" or admin rights to this key vault. However I have created another user that would have lower access rights. For both of these scenarios I gather I need to generate a unique service principle name. How do I generate a service principle name for an arbitrary Azure User?
You can just create another service principal name under your current account for this created user. And you just need to assign the lower access rights to the key vault for this service principal. Then the user can access to the key vault with this service principal which has lower access permission.
An Azure service principal is an identity created for use with applications, hosted services, and automated tools to access Azure resources. You can create as many as service principals as you want for different access permissions. If you want to generate a service principal under the created user account, you might have to login as this created user. Otherwise, i am afraid it cannot be done.
You can also set the access permission for this user to this key vault directory without using service principal. See here
az keyvault set-policy --name keyVaultName --object-id userObjectId --secret-permissions permissions --key-permissions permissions
You can get the user's Object id with below command: See here
az ad user show --id <email-address-of-user>

How to use Azure Managed Identity in Azure Function to access Service Bus with a trigger?

I have created a ServiceBus namespace in Azure, along with a topic and a subscription. I also have a simple Azure version 1 function that triggers on a received topic in the ServiceBus, like this:
[FunctionName("MyServiceBusTriggerFunction")]
public static void Run([ServiceBusTrigger("myTopic", "mySubscription", Connection = "MyConnection")]string mySbMsg, TraceWriter log)
{
log.Info($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}
The function triggers nicely for the topics in the ServiceBus when I define the connection string in functions Application Settings by using Shared Access Policy for topic, like this:
Endpoint=sb://MyNamespace.servicebus.windows.net/;SharedAccessKeyName=mypolicy;SharedAccessKey=UZ...E0=
Now, instead of Shared Access Keys, I would like to use Managed Service Identity (MSI) for accessing the ServiceBus. According to this (https://learn.microsoft.com/en-us/azure/active-directory/managed-service-identity/services-support-msi) it should be possible, unless I have misunderstood something. I haven't managed to get it working though.
What I tried, was to
set the Managed Service Identity "On" for my function in Azure portal
give Owner role for the function in ServiceBus Access Control section in Azure Portal
set the connection string for MyFunction like this: Endpoint=sb://MyNamespace.servicebus.windows.net/
The function is not triggering in this set-up, so what am I missing or what am I doing wrong?
I'd be grateful for any advice to help me get further. Thanks.
Update for Microsoft.Azure.WebJobs.Extensions.ServiceBus version 5.x
There is now an offical docs for the latest version of the package in here.
{
"Values": {
"<connection_name>__fullyQualifiedNamespace": "<service_bus_namespace>.servicebus.windows.net"
}
}
Previous answer:
This actually seems to be possible now, at least worked just fine for me. You need to use this connection string:
Endpoint=sb://service-bus-namespace-name.servicebus.windows.net/;Authentication=ManagedIdentity
I have not actually found any documentation about this on Microsoft site, but in a blog here.
Microsoft does have documentation though on roles that you can use and how to limit them to scope in here. Example:
az role assignment create \
--role $service_bus_role \
--assignee $assignee_id \
--scope /subscriptions/$subscription_id/resourceGroups/$resource_group/providers/Microsoft.ServiceBus/namespaces/$service_bus_namespace/topics/$service_bus_topic/subscriptions/$service_bus_subscription
what am I missing or what am I doing wrong?
You may mix up with MSI and Shared Access Policy.They are using different provider to access to Azure servicebus. You could just use connectionstring or just use MSI to authenticate.
When you use Managed Service Identity(MSI) to authenticate, you need to create a token provider for the managed service identity with the following code.
TokenProvider.CreateManagedServiceIdentityTokenProvider(ServiceAudience.ServiceBusAudience).
This TokenProvider's implementation uses the AzureServiceTokenProvider found in the Microsoft.Azure.Services.AppAuthentication library. AzureServiceTokenProvider will follow a set number of different methods, depending on the environment, to get an access token. And then initialize client to operate the servicebus.
For more details, you could refer to this article.
When you use servicebus connectionstring to access which using the Shared Access Token (SAS) token provider, so you can operate directly.
Agreed that from azure function we cannot access the resource like ASB directly. However, one still does not need to put in the password in this case "SharedAccessKeyName" in the connectionstring directly.
Azure function can work with Azure KeyVault. Thus one can store the connectionstring with sensitive information as a secret in the KeyVault and then grant System assigned identity from azure functions access over KeyVault and then specify the value for the settings in the portal as
#Microsoft.KeyVault(SecretUri={theSecretUri})
Details on how to achieve the above is mentioned in the following blog.
https://medium.com/statuscode/getting-key-vault-secrets-in-azure-functions-37620fd20a0b
This will still avoid specifying the connectionstring directly in Azure functions and provides with single point of access via Vault to be disabled in case of a security breach