Attach certificate thumbprint to app registration in Azure using Terraform - azure

I'm wondering if anyone knows of a way to attach a certificate thumbprint to an app registration using either the azuread or azurerm providers? Currently I can make a valid cert with thumbprint and drop it into my keyvault, as well as make an app registration in Azure AD via terraform, however for the purpose of our apps I would need my app registration to have access to and consume certificates in my keyvault. Any input would be greatly appreciated.

you can do it like this. You will need to have the cert correctly encoded.
resource "azuread_application" "application" {
display_name = "nameofapp"
}
resource "azuread_application_certificate" "application_certificate" {
application_object_id = azuread_application.application.id
type = "AsymmetricX509Cert"
value = file("cert-der-base64.cer")
end_date = "2022-02-24T00:00:00Z"
}

Related

Give App Service enough permissions to link SSL certificate from Key Vault

I have a Bicep template where I create an App Service in which I need to link a SSL certificate that exists in Key Vault (both in same resource group).
The Key Vault has Azure RBAC enabled.
I use the following to Bicep template to link the SSL certificate from Key Vault to the App Service:
resource certEncryption 'Microsoft.Web/certificates#2018-02-01' = {
name: '${resourcePrefix}-cert-encryption'
location: location
properties: {
keyVaultId: resourceId('myResourceGroup', 'Microsoft.KeyVault/vaults', keyVaultName)
keyVaultSecretName: '${resourcePrefix}-cert-encryption'
serverFarmId: hostingPlan.id
password: 'SecretPassword'
}
dependsOn: [
webApi
]
}
But it fails with the following message:
The service does not have access to '/subscriptions/3449f-xxxx/resourcegroups/rgabptrialt/providers/microsoft.keyvault/vaults/my-test-vault' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.
This isn't really telling a lot...
What permission do I need to grant exactly? And to what? And where and how do I even grant these permissions?
Do I have to create a Managed Identity and link that in my App Service? And what Permissions/Roles do I need exactly? Or do I need to do something else to make this work?
I couldn't really find any good info on how to do this.
Give App Service enough permissions to link SSL certificate from Key Vault:
I've imported a self-signed certificate (.pfx) in keyvault -> secrets to authenticate.
To resolve,
The service does not have access to '/subscriptions/3449f-xxxx/resourcegroups/rgabptrialt/providers/microsoft.keyvault/vaults/my-test-vault' Key Vault. Please make sure that you have granted necessary permissions to the service to perform the request operation.
I tried in my environment by referring few steps from this article detailed by #Anuraj and modified accordingly to achieve the expected results.
Step-1: Setting up a Key Vault access policy:
Create a key vault from the portal and Goto Access Policies.
Now creating an access policy to link the ssl certificate by configuring a template as per your requirement.
I've selected key,secret & certificate management to enable the permissions as shown:
Search for the "Name/objectID/AppID" for the respective service principal as keyvault has RBAC enabled.
Note: Register an app under AzureAD -> App registrations if needed.
Review all the permissions and create an access policy:
Step-2: Under App Service -> Web Application -> Certificates, I've added the keyvault certificate ( self-signed certificate (.pfx) in keyvault -> secrets).
1.
2.
3.
Bind the SSL certificate by adding TLS/SSL settings and importing the key vault certificate when it has been added to key vault (.pfx).
Note: Make sure the certificate is in .pfx format to avoid any conflicts.
And I ran below script and deployment got succeeded without any permission blockers.
resource certEncryption 'Microsoft.Web/certificates#2018-02-01' = {
name: 'xcc-cert-encryption'
location: 'EastUS'
properties: {
keyVaultId: '/subscriptions/<subscriptionID>/resourceGroups/xxxxRG/providers/Microsoft.KeyVault/vaults/xxxxxkeyvaults'
keyVaultSecretName: 'jahnss'
password: 'xxxxx' //Certificate protected password
extensionResourceId: '/subscriptions/<subscriptionID>/resourceGroups/xxxxRG/providers/Microsoft.Web/serverfarms/xxxxxappserviceplan'
}
}
Output:
I think yes first you need to give permission in key vault
Ensure that the service principal has all the permissions. The only thing that worked for me though is adding the service principal 24681998-555f-4570-a559-2fced2d7e841 which shows up as Microsoft.Azure.WebSites. You can add this through the portal, by adding an access policy for Microsoft.Azure.WebSites or through arm with the GUID.
I added the following principal to the Key Vault access policies: Microsoft Azure App Service (object id: your object id). Permission to get secrets is enough.
By performing similar the steps mentioned in the below answer
How to access key vault from azure app service
Please let me know if you have any doubts or question. Even if you are facing any issues.

Terraform Azure Service Principal Client Secret expiration

Is it really not possible to set the expiration date for a client secret?
I tried looking at the docs (see below), and I can't find anything other than the output of the expiration Terraform which is two years.
I can't even view the secret on Azure AD since it doesn't show up in the Client Secrets area (though this might be due to some sort of replication/eventual consistency lag).
https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal_password
Service Principal Password cannot be seen in the Portal, you can only find the value of it from the Terraform state. As Service Principal is referred to Enterprise Application , we can use the secret with the client id but it won't be visible in portal as Enterprise application doesn't have a certificates and secrets blade.
So, in order to make it visible in portal you have to use Application Password so that it can be seen in the certificates and secrets blade of the App Registration.
Also in the Application Password you can use relative end date in hours corresponding to the time you want the secret to expire.
You can use something like below:
provider "azuread" {}
data "azuread_application" "example" {
display_name = "aksspansuman"
}
resource "azuread_application_password" "example" {
display_name = "Test"
application_object_id = data.azuread_application.example.object_id
end_date_relative = "24h"
}
Output:
Update:
resource "azuread_service_principal_password" "example" {
service_principal_id = data.azuread_service_principal.example.object_id
end_date = "2021-11-18T01:02:03Z"
}
If We try using end_date in SP password it gives the below error.

Terraform - Trying to create resources in Azure with service principal and pulling that SPs key from keyvault

Been brushing up using Terraform to manage resources in Azure the past week or so.
Great tool.
I've found there is a distinction between using an AZ user account vs service principal.
The goal is to create resources in Azure using a designated service principal and referencing it's secret that is stored within AZ key vault. Moving away from locally stored secret (file, env var, etc).
I can successfully create resources using an authenticated service principal as long as I have my azurerm provider containing the subid, clientid, clientsecret & tenantid, it works great.
Example of what works when I store service principal secret as a var sp_secret in variables.tf (or even works as env var):
provider "azurerm" {
version = "=2.48.0"
features { }
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "${var.sp_secret}"
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
I have been able to successfully pull the service principals secret from the keyvault and 'output' it, but what I want to do is to pull that secret from kv and use, say as a var inside the provider client_secret value. ex. 'client_secret = "${link to secret sitting in kv}"'
Here is what I am doing to retrieve the SP secret from keyvault and output it:
data "azurerm_client_config" "current" {}
variable "keyvault_name" {
default = "blah-kv"
}
variable "kvrg_name" {
default = "blah-kv-rg"
}
data "azurerm_key_vault" "keyvault" {
name = "${var.keyvault_name}"
resource_group_name = "${var.kvrg_name}"
}
data "azurerm_key_vault_secret" "kv-sp" {
name = "blah-tf-sp-secret"
key_vault_id = "${data.azurerm_key_vault.keyvault.id}"
}
output "secret_value" {
value = "${data.azurerm_key_vault_secret.kv-sp.value}"
}
As mentioned, the above snippet successfully retrieves and outputs the secret. I just want to, instead of output the secret, just set that secret as client_secret value in the azurerm provider reference.
I've tried many variations of client_secret = "${data.azurerm_key_vault_secret.kv-sp.value}", and I get the following error:
Error: Cycle: data.azurerm_key_vault.keyvault, provider["registry.terraform.io/hashicorp/azurerm"], data.azurerm_key_vault_secret.kv-sp
I interpret the above error to indicate a circular reference. I've tried a few things i've picked up in my searching for an answer, but no dice.
Any guidance is appreciated.
Thanks!
As I know. it's impossible to achieve what you expect. When you use the Terraform to manage Azure resources, then you need to have an Azure account or service principle with enough permissions. If you use the service principle, it means you need to configure the provider azurerm with client id and client secret before running the Terraform code. But when you store the secret in the Azure Key Vault, then you need to run the code, and then you can get the secret. It causes cycle dependency.

Retrieve Azure KeyVault secret using client secret

I'm experimenting with various Azure features and currently want to retrieve a secret from KeyVault.
Straight to the case:
I'm using this nuget package to interact with my azure resources.
I've developed a simple .NET Core console app and run it locally.
I have a KeyVault resource with one secret defined which is active and not expired.
I've registered an App in AAD so my locally shipped .NET Core console app has an identity within AAD.
Than I've created a "client secret" within this registered app in AAD to use it to authenticate myself as an app.
After that I've added access policy in my KeyVault resource to allow GET operation for secrets for this registered app:
Then I've developed a small piece of code which should retrieve the desired secret:
public class AzureAuthentication
{
public async Task<string> GetAdminPasswordFromKeyVault()
{
const string clientId = "--my-client-id--";
const string tenantId = "--my-tenant-id--";
const string clientSecret = "--my-client-secret--";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var client = new SecretClient(new Uri("https://mykeyvaultresource.vault.azure.net"), credentials);
var secret = await client.GetSecretAsync("admincreds");
return secret.Value.Value;
}
}
However when I'm trying to do this I'm getting an AccessDenied error:
Am I missing something painfully obvious here? Or there is some latency (>30 min for this moment) for which changes from Access policies screen in KeyVault resource are applied?
I test your code and Get permission, it works fine.
From your screenshot, it looks you didn't add the correct service principal related to the AD App to the Access policies.
If you add the service principal related to the AD App, it will appear as APPLICATION, not COMPOUND IDENTITY.
So when you add it, you could search for the client Id(i.e. application Id) or the name of your App Registration directly, make sure you add the correct one.
Make sure your AD App(service principal) has the correct permission in your keyvault -> Access policies

Hiding a secret in Terraform for Azure with a caveat

So I am trying to find someway to hide a secret in Terraform. The caveat is the secret is a Service Principal that is used to connect to our Key Vault. I can't store the secret in the Key Vault as it hasn't connected to the Key Vault yet at that point. This is part of my main tf file.
provider "azurerm" {
alias = "kv_prod"
version = "1.28"
tenant_id = "<tenant id>"
subscription_id = "<sub id>"
client_id = "<SP client id>"
client_secret = "<SP secret>"
}
This is used further down my module to store Storage Account keys and other secrets. It just happens to be in a Prod subscription that not everyone has access to.
Has anyone run into something like this? If so, how would you go about securing that secret?
#maltman There are several ways to hide a secret in terraform. Here is a blog that talks about them:
https://www.linode.com/docs/applications/configuration-management/secrets-management-with-terraform/
However if you are only concerned about encrypting the secrets file while checking in and checking out from git, you can use something like git-crypt
You would have to create a couple of files:
variables.tf -> Define your variables here
variable "client_secret" {
description = "Client Secret"
}
terraform.tfvars -> Give the value of the variable here
client_secret = 'your-secret-value'
Now use git-crypt to encrypt terraform.tfvars while checking into git
For your requirements, I think there are two secure ways for you in comparison.
One is that stored the credential as environment variables so that you do not expose the secret in the tf files. Here's the example.
The other one is that you can log in with the credential for Azure CLI, then just need to set the subscription without exposing the secret in the tf file. Here's the example.
The above two ways are that what I think is secure and possible for you. Hope it helps you.
Terraform doesn't have this feature but by using third party integration it can be achieved.
Storing Secret in Terraform:
Terraform has an external data resource that can be used to run an external program and use the return value further. I have used Ansible vault feature to encrypt and decrypt the secrets and store it encrypted in repository rather as plaintext.
data "external" "mysecret" {
program = ["bash", "-c", "${path.module}/get_ansible_secret.sh"]
query = {
var = "${var.secret_value}"
vault_password_file = "${path.module}/vault-password.sh"
# The file containing the secret we want to decrypt
file = "${var.encrypted_file}"
}
}
Refer the working example: github example
Going to create an ADO pipeline to handle this instead where the code just does not have to be available.

Resources