Azure Key Vault Deployment with Terraform (Workday API Secret Management)

This project demonstrates my ability to design, build, and automate secure cloud infrastructure in Azure using Terraform, following enterprise‑grade best practices.

Project Overview

The goal of this project was to create a secure, isolated Azure Key Vault for storing Workday API credentials used by multiple client applications. The solution needed to ensure:

  • No secrets stored in code

  • Each application only accesses its own secrets

  • Centralized lifecycle management

  • Least‑privilege access model

  • Fully automated deployment using Infrastructure‑as‑Code (Terraform)

This Key Vault is entirely IaC‑driven and designed for production environments.

Why a Separate Key Vault?

A different Key Vault already existed for SQL Transparent Data Encryption (TDE).
I designed a separate vault to:

  • Avoid mixing database encryption keys with application secrets

  • Reduce blast radius

  • Enforce strict access separation

  • Follow Azure best practices for isolation

This ensures Workday credentials are not exposed to infrastructure admins managing SQL encryption keys.

Architecture

Resource Group
rg-workday-appsecrets-prod

Key Vault
kv-workday-appsecrets

  • RBAC-enabled

  • Soft delete + purge protection

  • Public/off by default (private endpoints optional)

RBAC Design

  • Integration Engineer → Key Vault Secrets Officer (write + rotate secrets)

  • Application A → Key Vault Secrets User (read-only for its secrets)

  • Application B → Key Vault Secrets User

  • Terraform manages all access declaratively

Terraform Highlights

I built the following Terraform modules/files:

providers.tf

Defines AzureRM provider.

versions.tf

Locks Terraform + provider versions for stability.

variables.tf

Inputs for:

  • Resource group

  • Location

  • Integration Engineer Object ID

  • Application identity IDs

  • Workday secret values

main.tf

Creates:

  • Resource Group

  • Key Vault

  • RBAC authorization mode

  • Security hardening (soft‑delete + purge protection)

rbac.tf

Assigns:

  • Integrations Engineer → Key Vault Secrets Officer

  • Applications → Key Vault Secrets User

secrets.tf

Defines secrets for:

  • workday-appA-client-id

  • workday-appA-client-secret

  • workday-appA-endpoint
    …and the same pattern for App B.

Everything is driven by terraform.tfvars so no secrets are stored in code.

What Terraform Deploys

  • Creates Resource Group

  • Deploys secure Key Vault

  • Enables RBAC authorization

  • Applies least‑privilege RBAC

  • Prepares vault for future Workday secret ingestion

  • Outputs the vault URI for application teams

Sample Terraform output:

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

key_vault_uri = "https://kv-workday-appsecrets.vault.azure.net/"

Secret Management Flow

  1. Integration Engineer creates/rotates secrets in the vault.

  2. Applications authenticate using Managed Identities or App Registrations.

  3. Apps fetch secrets securely at runtime using:

    • Azure Key Vault REST API, or

    • SDKs (DefaultAzureCredential)

No secrets are stored in configuration files.

Validation

Used Azure CLI + Portal to verify:

  • Vault existence

  • RBAC propagation

  • DNS registration

  • Access rules

  • API endpoint availability

Testing verified that RBAC restrictions prevent unauthorized access:
Owners can manage the vault but cannot read secrets without data-plane roles.

Security Best Practices Implemented

  • Dedicated Key Vault for application secrets

  • RBAC-only access model

  • Secret-per-application isolation

  • No direct-sharing of credentials

  • No use of Access Policies (deprecated model)

  • No hardcoded secrets in code

  • Soft-delete + purge protection enabled

  • IaC-driven for reproducibility and auditability

  • Supports future private endpoints

  • Supports future secret rotation automation

 Skills Demonstrated

  • Azure Key Vault

  • Azure RBAC

  • Terraform IaC (AzureRM provider)

  • Secret and credential lifecycle management

  • Secure application integration patterns

  • Cloud architecture & separation of duties

  • CLI validation & troubleshooting

  • Professional documentation & communication

  • Governance alignment with enterprise controls

 What’s next for this project?

I will extend this project with:

  • Private endpoints for network isolation

  • Log Analytics diagnostic logging for auditing

  • Automated secret rotation

  • Terraform modules for multi‑app onboarding

  • Optional Logic App for credential rotation workflows

Phase 2 – Application Integration Using Azure AD App Registration

In Phase 2, I extended my Azure Key Vault project by integrating it with a real application scenario for our Workday API integration effort. The objective was to enable secure, non-interactive access to secrets using an Azure AD App Registration and service principal authentication. This allows the application to authenticate using environment variables instead of developer credentials, ensuring a production-ready, least-privilege security model.

Overview

The Workday API Client requires programmatic access to credentials stored in Azure Key Vault (e.g., usernames, passwords, and API keys). To support this securely, I created a dedicated Azure AD App Registration named Workday API Client, generated a client secret, assigned Key Vault RBAC permissions, and validated end-to-end authentication using Azure CLI and the Azure SDK.

Steps Performed (GUI Workflow)

1. Created App Registration: “Workday API Client”

I created a new single-tenant App Registration in Azure Active Directory. This provides a unique application identity and isolates permissions from any existing Workday system integrations.

2. Generated a Client Secret

Under Certificates & Secrets, I generated a secure client secret for the application. The secret value is used to authenticate the application server and is treated with the same sensitivity as a password.

3. Collected Application Credentials

The following values were provided to the development team for use as environment variables:

  • AZURE_TENANT_ID – Directory (tenant) ID
  • AZURE_CLIENT_ID – Application (client) ID
  • AZURE_CLIENT_SECRET – Client secret value

4. Assigned Key Vault RBAC Permissions

Using RBAC-only mode, I assigned the service principal the Key Vault Secrets User role. This grants read-only access to secrets while following least-privilege best practices.

5. Validated Authentication Using Azure CLI

I verified that the application could authenticate by logging in with:

az login --service-principal --username <client-id> --password <secret> --tenant <tenant-id>

Then I confirmed that the service principal could read secrets:

az keyvault secret list --vault-name <your-key-vault-name>

6. Delivered Variables to the Developer

I provided the required environment variables to the developer, who uses the Azure SDK’s ClientSecretCredential class to authenticate and retrieve secrets:

new ClientSecretCredential(tenantId, clientId, clientSecret);

Why This Matters

This phase demonstrates how Azure Key Vault integrates into real enterprise applications through secure identity, least-privilege RBAC, and service principal authentication. It shows the complete story—from vault deployment to application consumption—and highlights collaboration between infrastructure and development teams. This makes the Key Vault deployment truly production-ready.

Phase 2 – Terraform Code (App Registration + RBAC Integration)


############################################################
# Phase 2 – Application Integration (Workday API Client)
# Azure AD App Registration + Service Principal + RBAC
############################################################

# -------------------------------------------
# Data source: retrieve current tenant details
# -------------------------------------------
data "azurerm_client_config" "current" {}

# -------------------------------------------
# App Registration (Azure AD Application)
# -------------------------------------------
resource "azuread_application" "workday_api" {
  display_name = "Workday API Client"

  owners = [
    data.azurerm_client_config.current.object_id
  ]
}

# -------------------------------------------
# Service Principal for the Application
# -------------------------------------------
resource "azuread_service_principal" "workday_api" {
  client_id = azuread_application.workday_api.client_id
}

# -------------------------------------------
# Client Secret for Workday API Client
# -------------------------------------------
resource "azuread_application_password" "workday_api" {
  application_id     = azuread_application.workday_api.id
  display_name       = "workday-api-client-secret"
  end_date_relative  = "8760h"   # 1 year expiration
}

# -------------------------------------------
# Retrieve Key Vault resource (Phase 1 vault)
# -------------------------------------------
data "azurerm_key_vault" "workday_vault" {
  name                = "kv-workday-appsecrets"
  resource_group_name = "rg-workday"
}

# -------------------------------------------
# Assign RBAC: Key Vault Secrets User
# Allows read-only access to secrets
# -------------------------------------------
resource "azurerm_role_assignment" "kv_spn_secrets_user" {
  scope                = data.azurerm_key_vault.workday_vault.id
  role_definition_name = "Key Vault Secrets User"
  principal_id         = azuread_service_principal.workday_api.id
}

############################################################
# Outputs for application integration
############################################################

output "workday_api_client_id" {
  value = azuread_application.workday_api.client_id
}

output "workday_api_tenant_id" {
  value = data.azurerm_client_config.current.tenant_id
}

output "workday_api_client_secret" {
  value     = azuread_application_password.workday_api.value
  sensitive = true
}
Next
Next

Automated Azure Storage DR Backup Using Data Factory