GuidesUpdated July 3, 2026
Azure Service Principal Rotation
guideazurespnservice-principalsecuritysecret-rotationvaultterraformazure-cli
How to Rotate Azure Service Principal Names (SPNs)
Requirements
Known SPNs
- service-principals/ohemr-spn-citrix-test_west-001
- service-principals/ohemr-spn-citrix-npd_west-001
Secrets for these SPNs are set to expire on 05/26/202!!!!
Owners:
- Palacios, Manuel
- Hudak, Thomas
- O'Shea, Patrick
- Shahsavand, Amir
Rotate SPN Secrets - Process
1. #Identify the SPNs
ohemr-spn-citrix-npd_west-001
ohemr-spn-citrix-test_west-001
2. Log onto Hashicorp Vault with your primary MSID
vault login -method=ldap -address=https://vault.uhg.com username=mpalac17
3. Validate that you can read the SPNs from Hashicorp Vault
vault kv get -address=https://vault.uhg.com --namespace="aide-0085665" kv/service-principals/ohemr-spn-citrix-npd_west-001
vault kv get -address=https://vault.uhg.com --namespace="aide-0085665" kv/service-principals/ohemr-spn-citrix-test_west-001
4. Log onto Azure Portal. Make sure to select your OptumCloud ID when prompted
az login
- If you do not have
azinstalled, you can install it via brew:brew install azure-cli - If you receive any SSL Certificate Verify Failed messages after running the command, you can export REQUESTS_CA_BUNDLE and set it to a path to any standard Optum CA on your device
export REQUESTS_CA_BUNDLE=/my/path/to/optum/standard_trusts.pem - CA Bundle can be downloaded from artifactory: standard_trusts
- After logging in you may be prompted to choose a subscription. SPNs are subscription agnostic so you may choose any one available (e.g. OHEMR-SUB-EPIC-SHARED-001)
5. Validate that you can read the SPNs from Azure. Use the ‘client_id’ values from step 3
az ad app credential list --id de3cba1e-836f-4849-b012-e9310f911851
az ad app credential list --id eeef1eda-462f-4b29-adad-f4edc9819d11
6. Locate script rotate_spn_secret.sh, ensure that the SPNs and IDs at the top of the file match the ones you have identified
cat ~/rotate_spn_secret.sh | grep -E "^spn_(names|ids)="
7. Execute script rotate_spn_secrets.sh
~/rotate_spn_secret.sh
8. Validate that secrets have rotated
vault kv get --namespace="aide-0085665" kv/service-principals/ohemr-spn-citrix-npd_west-001
az ad app credential list --id de3cba1e-836f-4849-b012-e9310f911851
vault kv get --namespace="aide-0085665" kv/service-principals/ohemr-spn-citrix-test_west-001
az ad app credential list --id eeef1eda-462f-4b29-adad-f4edc9819d11
Rotate SPN Secrets - rotate_spn_secret.sh
#!/bin/bash
spn_names=("ohemr-spn-citrix-npd_west-001" "ohemr-spn-citrix-test_west-001")
spn_ids=("de3cba1e-836f-4849-b012-e9310f911851" "eeef1eda-462f-4b29-adad-f4edc9819d11")
export VAULT_NAMESPACE="aide-0085665"
# Function to get service principal details
get_spn_details() {
local spn_id=$1
az ad sp show --id "$spn_id" --query "{client_id: appId}" -o json
}
# Function to rotate service principal secret (password)
rotate_spn_secret() {
local spn_id=$1
local spn_name=$2
local secret_name=${spn_name/spn/secret}
local end_date=$(date -v+90d "+%Y-%m-%d")
az ad app credential reset --id "$spn_id" --display-name "$secret_name" --end-date "$end_date" --query "{client_secret: password}" -o json
}
# Loop through each service principal name
for i in "${!spn_names[@]}"; do
spn_name="${spn_names[$i]}"
spn_id_value="${spn_ids[$i]}"
# Get the service principal details
spn_details=$(get_spn_details "$spn_id_value")
client_id=$(echo $spn_details | jq -r '.client_id')
# Check if details were retrieved successfully
if [[ -z "$client_id" ]]; then
echo "Failed to retrieve details for $spn_name"
continue
fi
# Output the Vault command that will be run
echo "vault kv put -namespace=$VAULT_NAMESPACE kv/service-principals/$spn_name client_id=\"$client_id\" client_secret=\"<to be fetched>\""
# Rotate the service principal secret
spn_secret=$(rotate_spn_secret "$spn_id_value" "$spn_name")
if [[ $? -ne 0 ]]; then
echo "Failed to rotate secret for $spn_name. Ensure you have sufficient privileges."
continue
fi
client_secret=$(echo $spn_secret | jq -r '.client_secret')
# Check if secret was retrieved successfully
if [[ -z "$client_secret" ]]; then
echo "Failed to retrieve secret for $spn_name"
continue
fi
# Output the full Vault command with the actual secret
echo "vault kv put -namespace=$VAULT_NAMESPACE kv/service-principals/$spn_name client_id=\"$client_id\" client_secret=\"$client_secret\""
# Store the credentials in Vault
vault kv put -namespace=$VAULT_NAMESPACE kv/service-principals/$spn_name client_id="$client_id" client_secret="$client_secret"
echo "Stored credentials for $spn_name in Vault."
done