I am using an Azure keyvault to manage some secrets I am using in a deployment pipeline. All of the secrets are working except for a personal access token from Azure DevOps. I am using the personal access token to install an ADO agent on the virtual machines
data "azurerm_key_vault" "keyvault" {
name = "keyvault"
resource_group_name = "keyault-RG"
}
data "azurerm_key_vault_secret" "pat" {
name = "ADOPAT"
key_vault_id = data.azurerm_key_vault.keyvault.id
}
For example, in my virtual machine module I have something like this:
resource "azurerm_virtual_machine_extension" "ado" {
count = length(var.VMs)
name = "${element(var.VMs, count.index)}-TeamServicesAgent"
virtual_machine_id = azurerm_virtual_machine.VMs[count.index].id
publisher = "Microsoft.VisualStudio.Services"
type = "TeamServicesAgent"
type_handler_version = "1.0"
auto_upgrade_minor_version = true
settings = <<SETTINGS
{
"PATToken": "${data.azurerm_key_vault_secret.pat.value}",
"VSTSAccountName": "orgname",
"TeamProject": "${var.ado_project}",
"DeploymentGroup": "${var.deployment_group}"
}
SETTINGS
tags = var.tags
}
When I define the PAT as a variable, everything works fine. It's only when I implement the key vault the extension is unable to provision. I don't want to define the PAT as a variable because then I would have the PAT as a plaintext in my code. I don't see many examples online of folks using the ado agent extension, but does anyone have any ideas of what may be causing the issue?
I've tried setting the data.azurerm_key_vault_secret.pat.value to a locals variable but same issue. The issue does not happen when I define the PAT as a variable.
If the extension is not getting installed,
Add depends_on=[ azurerm_virtual_machine.example ]
parameter in azurerm_virtual_machine_extension block , as it depends on the VM creation.
Note: Make sure the keyvault is given proper permissions in the keyvault block
I tried below code:
resource "azurerm_key_vault" "example" {
name = "kasaraexmplkeyvault"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
enabled_for_disk_encryption = true
tenant_id = data.azurerm_client_config.current.tenant_id
soft_delete_retention_days = 7
purge_protection_enabled = false
sku_name = "standard"
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
certificate_permissions = [
"Create",
"Delete",
"DeleteIssuers",
"Get",
"GetIssuers",
"Import",
"List",
"ListIssuers",
"ManageContacts",
"ManageIssuers",
"Purge",
"SetIssuers",
"Update",
]
key_permissions = [
"Backup",
"Create",
"Decrypt",
"Delete",
"Encrypt",
"Get",
"Import",
"List",
"Purge",
"Recover",
"Restore",
"Sign",
"UnwrapKey",
"Update",
"Verify",
"WrapKey",
]
secret_permissions = [
"Backup",
"Delete",
"Get",
"List",
"Purge",
"Recover",
"Restore",
"Set",
]
storage_permissions = [
"Get","Set"
]
}
}
resource "azurerm_key_vault_secret" "pat" {
name = "patvalue"
value = "gsdnjgsgjh3.3jgjshgfdj"
key_vault_id = azurerm_key_vault.example.id
}
resource "azurerm_virtual_network" "example" {
name = "acctvnkav"
address_space = ["10.0.0.0/16"]
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
}
resource "azurerm_subnet" "example" {
name = "acctsubkav"
resource_group_name = data.azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
}
resource "azurerm_network_interface" "example" {
name = "acctnickav"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
ip_configuration {
name = "testconfiguration1"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
}
}
resource "azurerm_storage_account" "examplen" {
name = "kasaraaccsa"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "staging"
}
}
resource "azurerm_storage_container" "example" {
name = "vhdskav"
storage_account_name = azurerm_storage_account.examplen.name
container_access_type = "private"
}
resource "azurerm_virtual_machine" "example" {
name = "acctvmkav"
resource_group_name = data.azurerm_resource_group.example.name
location = data.azurerm_resource_group.example.location
network_interface_ids = [azurerm_network_interface.example.id]
vm_size = "Standard_F2"
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "16.04-LTS"
version = "latest"
}
storage_os_disk {
name = "myosdisk1"
//vhd_uri = "${azurerm_storage_account.examplen.primary_blob_endpoint}${azurerm_storage_containern.example.name}/myosdisk1.vhd"
caching = "ReadWrite"
create_option = "FromImage"
}
os_profile {
computer_name = "hostname"
admin_username = "testadmin"
admin_password = "Password1234!"
}
os_profile_linux_config {
disable_password_authentication = false
}
tags = {
environment = "staging"
}
}
resource "azurerm_virtual_machine_extension" "example" {
name = "hostname"
virtual_machine_id = azurerm_virtual_machine.example.id
publisher = "Microsoft.VisualStudio.Services"
type = "TeamServicesAgent"
type_handler_version = "1.0"
auto_upgrade_minor_version = true
# publisher = "Microsoft.Azure.Extensions"
# type = "CustomScript"
# type_handler_version = "2.0"
settings = <<SETTINGS
{
"PATToken": "${azurerm_key_vault_secret.pat.value}",
"VSTSAccountName": "orgname",
"TeamProject": "someproject",
"DeploymentGroup": "somegroup"
}
SETTINGS
tags = {
environment = "Production"
}
depends_on=[ azurerm_virtual_machine.example ]
}
authenticating_using_the_personal_access_token | terraform registry
PAT may require additional steps to be able to access it from the key vault. PAT-Azure Devops | Microsoft Learn
Try to Create an new Azure AD application in your Azure Active Directory.
Assign the "Key Vault Secrets User" role to that application in the Access policies of your Azure Key Vault.
Generate a new client secret for the Azure AD application.
Grant this application the necessary permissions to read secrets from the Key Vault.
Use the Azure CLI to set environment variables for the PAT:
AZURE_TENANT_ID: "" ,AZURE_CLIENT_ID:"", AZURE_CLIENT_SECRET: "",
KEYVAULT_NAME: "",SECRET_NAME: "".
In your Terraform code, use the "azurerm_key_vault_secret" data source to retrieve the PAT from the key vault:
data "azurerm_key_vault_secret" "ado_pat" {
name = var.secret_name
key_vault_id = var.key_vault_id
}
resource "azurerm_virtual_machine_extension" "ado_agent" {
name = "ado_agent_installation"
virtual_machine_id = azurerm_virtual_machine.vm.id
publisher = "Microsoft.Compute"
type = "CustomScriptExtension"
type_handler_version = "1.9"
settings = <<SETTINGS
{
"commandToExecute": "wget https://vs......tar.gz -P /tmp/ && cd /tmp && tar zxvf ........tar.gz && ./config.sh --unattended --url https://dev.azure.com/your-organization --auth pat --token ${data.azurerm_key_vault_secret.ado_pat.value} --pool your-pool --agent your-agent-name .."
}
SETTINGS
}
Extensions:
Also check Automate Azure DevOps self-hosted agent installation using Terraform - DEV Community