Azure Key Vault ile .NET: Managed Identity, RBAC, Configuration Provider ve Production'da Doğru Credential
Bir .NET uygulaması Key Vault'a parola taşımadan, managed identity ve vault seviyesinde verilmiş Key Vault Secrets User rolüyle erişmeli; production'da DefaultAzureCredential yerine ManagedIdentityCredential kullanılmalı. AddAzureKeyVault'u prefix'li secret manager ve ReloadInterval ile kuruyor, yerel emulator'de reload'u, managed identity yokken üç dakikalık açılışı ve App Service'te çözülemeyen reference'ın değer olarak kalmasını gerçek çıktılarla görüyoruz.
Sürüm notu: Bu yazıdaki örnekler .NET 10 (SDK 10.0.402), Azure.Extensions.AspNetCore.Configuration.Secrets 1.5.2, Azure.Identity 1.21.0, xUnit 2.9 ve yerel Key Vault emulator'ü Lowkey Vault 7.4 ile Docker 29.2 üzerinde denenmiştir. Azure'da bir vault oluşturulmadı; az komutları Microsoft Learn'den alınmıştır ve çalıştırılmadı.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) API'si staging'de açılırken secret'larını Key Vault'tan okuyor. Bir gün uygulamanın managed identity'si yanlışlıkla kaldırılıyor. Uygulama ne yapar: secret'sız açılıp ilk istekte mi hata verir, hemen mi kapanır, yoksa bir süre bekler mi? Bekliyorsa ne kadar? İkinci soru: aynı uygulama App Service'te Key Vault reference ile çalışıyor ve reference çözülemiyor. Jwt:SigningKey'in değeri ne olur?
Key Vault Temelleri: Secret, Key, Certificate ve Erişim Modeli
Azure Key Vault üç tür object tutar. Secret, uygulamanın düz metin olarak okuduğu her değer: connection string, API key, webhook secret. Key, vault'tan hiç çıkmayan kriptografik anahtar; imzalama ve şifreleme vault içinde yapılır, uygulama anahtarın kendisini görmez. Certificate ise bir X.509 certificate'ın yaşam döngüsünü (yenileme, issuer entegrasyonu) yönetir. Bu yazıda secret'lara odaklanıyoruz; JWT (JSON Web Token; API'nin kullanıcıya verdiği imzalı token) signing key'i şimdilik bir secret olarak tutuluyor, yedinci yazıda rotation tarafına döneceğiz.
Erişim iki modelle verilebilir: eski vault access policy ve Azure RBAC (Role-Based Access Control; yetkinin rol atamalarıyla verilmesi). Microsoft, 2026-02-01 ve sonrası API sürümleriyle oluşturulan yeni vault'larda RBAC'ı varsayılan yaptı; access policy kullanmak isteyen bunu oluştururken açıkça seçmek zorunda. RBAC'ta uygulamaya verilecek rol "Key Vault Secrets User": secret değerini okuyabilir, yazamaz, silemez. Rolü vault'un kendisine ver, abonelik ya da resource group seviyesine değil; yoksa uygulama aynı kapsamdaki bütün vault'ları okur.
İki koruma özelliğini baştan aç. Soft delete silinen secret'ı bir süre (7 ile 90 gün arası, varsayılan 90) geri alınabilir tutar ve yeni vault'larda zaten açık. Purge protection ise bu süre dolmadan kalıcı silmeyi engeller; bir kez açılınca kapatılamaz. Bir saldırgan ya da hatalı bir script secret'ları silse bile geri getirebilirsin. Ağ tarafında production vault'unu public erişime kapatıp private endpoint ya da virtual network kuralıyla yalnızca uygulamanın ağından erişilebilir yapmak iyi bir varsayılan.
Son kural en çok ihlal edileni: her environment'ın kendi vault'u olmalı. Microsoft Learn'deki Key Vault configuration provider sayfası da farklı uygulamalar ve environment'lar için ayrı vault önerir. Bookstore'da bookstore-kv-dev, bookstore-kv-staging ve bookstore-kv-prod var. Developer'lar dev vault'unu okuyabilir; production vault'una yalnızca production uygulamasının identity'si erişir.
Managed Identity: Parolası Olmayan Kimlik
Key Vault'u kullanmanın klasik çelişkisi şu: vault'a erişmek için bir credential gerekiyor, o credential'ı nerede saklayacaksın? Managed identity bu döngüyü kırar. App Service, Container Apps, Functions ya da bir VM'e identity verdiğinde Azure, Microsoft Entra ID'de o kaynağa bağlı bir service principal oluşturur ve credential'ını kendisi yönetir. Uygulama token'ı platformun yerel endpoint'inden ister; parola, client secret ya da certificate hiç görmez.

İki çeşidi var. System-assigned identity kaynağın kendisine bağlıdır; App Service silinince identity de silinir, başka kaynakla paylaşılamaz. User-assigned identity ayrı bir Azure kaynağıdır; birden çok uygulamaya atanabilir ve uygulamadan önce oluşturulup yetkilendirilebilir. Bookstore'da API için system-assigned identity yeterli. User-assigned identity, uygulama oluşturulurken secret'a hemen erişmesi gereken durumlarda ve blue-green gibi birden çok kaynağın aynı yetkiyi paylaştığı senaryolarda işe yarar.
Azure tarafındaki kurulum üç adım. Bu komutlar Microsoft Learn'deki örneklere göre yazıldı, bir Azure aboneliğinde çalıştırılmadı:
# 1) App Service'e system-assigned identity ver; çıktıdaki principalId lazım
az webapp identity assign --resource-group bookstore-prod --name bookstore-api-prod
# 2) Identity'ye yalnız bu vault üzerinde "Key Vault Secrets User" rolü
az role assignment create \
--role "Key Vault Secrets User" \
--assignee-object-id <principalId> \
--assignee-principal-type ServicePrincipal \
--scope $(az keyvault show --name bookstore-kv-prod --query id -o tsv)
# 3) Uygulamaya yalnızca vault'un adresini ver
az webapp config appsettings set --resource-group bookstore-prod --name bookstore-api-prod \
--settings KeyVault__Uri=https://bookstore-kv-prod.vault.azure.net/
Uygulamaya giden tek değer vault'un adresi ve o da secret değil. Yerelde ise developer az login ile kendi hesabıyla girer; dev vault'unda kendi kullanıcısına aynı rol verilmiştir.
Production'da Doğru Credential
Azure Identity library'sinin en bilinen class'ı DefaultAzureCredential: environment variable, workload identity, managed identity, Visual Studio, Azure CLI gibi bir dizi yöntemi sırayla dener ve ilk çalışanı kullanır. Geliştirmede çok rahat, production'da riskli. Microsoft'un Azure Identity best practices sayfası bu riski bir senaryoyla anlatıyor: managed identity bir gün sessizce başarısız olursa zincir bir sonraki halkaya geçer; sunucuda biri az login yapmışsa uygulama o kişinin yetkileriyle çalışmaya başlar. Öneri, production'da tek ve belirli bir credential kullanmak. Bookstore'un credential seçimi şöyle:
// Production'da yalnız managed identity; geliştirmede geliştiricinin kendi Azure oturumu
public static TokenCredential CreateCredential(IHostEnvironment environment, IConfiguration configuration)
{
if (environment.IsDevelopment())
{
return new ChainedTokenCredential(new AzureCliCredential(), new VisualStudioCredential());
}
return configuration["KeyVault:ManagedIdentityClientId"] is { Length: > 0 } clientId
? new ManagedIdentityCredential(ManagedIdentityId.FromUserAssignedClientId(clientId))
: new ManagedIdentityCredential(ManagedIdentityId.SystemAssigned);
}
DefaultAzureCredential'dan vazgeçmek istemiyorsan AZURE_TOKEN_CREDENTIALS environment variable'ı zinciri daraltır: dev yalnız geliştirici araçlarını, prod yalnız deploy edilmiş servis credential'larını bırakır; Azure.Identity 1.15'ten beri tek bir credential adı da verilebilir (ManagedIdentityCredential gibi). Aynı sayfa bir davranış farkını da belirtiyor: DefaultAzureCredential managed identity'yi "fail fast" modunda dener, yani kısa bir zaman aşımından sonra retry yapmadan vazgeçer. ManagedIdentityCredential'ı doğrudan kullandığında ya da 1.16 ve sonrasında AZURE_TOKEN_CREDENTIALS=ManagedIdentityCredential verdiğinde ise "resilient" moda geçer: 0,8 saniyeden başlayan exponential backoff ile beş retry. Production'da istediğin bu; geçici bir IMDS (Instance Metadata Service; Azure VM'lerinin yerel metadata ve token endpoint'i) hatası uygulamayı düşürmemeli. Bunun bedelini aşağıda ölçeceğiz.
Configuration Provider: AddAzureKeyVault ve Prefix'li Secret Manager
Azure.Extensions.AspNetCore.Configuration.Secrets paketi Key Vault'u sıradan bir configuration provider olarak ekler. Key Vault secret adlarında : kullanılamadığı için provider --'yi :'a çevirir: Jwt--SigningKey, Jwt:SigningKey olur. Varsayılan davranışta vault'taki bütün secret'lar yüklenir. Aynı vault'u birden çok uygulama paylaşıyorsa (Bookstore'da API ve Notifications worker'ı), her uygulamanın yalnız kendi secret'larını görmesi için bir KeyVaultSecretManager yazıyoruz:
// "BookstoreApi-Jwt--SigningKey" → "Jwt:SigningKey"; başka uygulamanın secret'ları yüklenmez
public sealed class PrefixKeyVaultSecretManager(string appPrefix) : KeyVaultSecretManager
{
private readonly string _prefix = $"{appPrefix}-";
public override bool Load(SecretProperties secret) =>
secret.Name.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase)
&& (secret.ExpiresOn is null || secret.ExpiresOn > DateTimeOffset.UtcNow);
public override string GetKey(KeyVaultSecret secret) =>
secret.Name[_prefix.Length..].Replace("--", ConfigurationPath.KeyDelimiter);
}
Load'daki ikinci koşulun bir nedeni var: Microsoft Learn'e göre provider süresi dolmuş secret'ları varsayılan olarak yükler; yalnızca disable edilmiş secret'lar hiç okunmaz. Süresi geçmiş bir key'le çalışmaya devam etmek yerine onu yok saymak, eksik değeri ValidateOnStart'ın yakalamasını sağlar. Prefix, isolation için bir kolaylık, güvenlik sınırı değil: aynı vault'ta Secrets User rolü olan her identity bütün secret'ları okuyabilir. Gerçek ayrım istiyorsan rolü secret seviyesinde ver ya da uygulamaları ayrı vault'lara ayır. Davranışı xUnit testleriyle sabitledim:
$ dotnet test --logger "console;verbosity=normal"
Passed ...PrefixKeyVaultSecretManagerTests.Load_accepts_only_this_apps_secrets(name: "BookstoreApi-Jwt--SigningKey", expected: True)
Passed ...PrefixKeyVaultSecretManagerTests.Load_accepts_only_this_apps_secrets(name: "NotificationsWorker-Email--ApiKey", expected: False)
Passed ...PrefixKeyVaultSecretManagerTests.Load_skips_expired_secrets
Passed ...PrefixKeyVaultSecretManagerTests.GetKey_strips_prefix_and_maps_double_dash_to_colon
...
Passed! - Failed: 0, Passed: 11, Skipped: 0, Total: 11
Provider'ı eklemek Program.cs'te birkaç satır. Vault adresi environment'tan geliyor; adres yoksa (yerelde user-secrets ile çalışırken) Key Vault hiç eklenmiyor:
public static IConfigurationBuilder AddBookstoreKeyVault(
this IConfigurationBuilder builder,
Uri vaultUri,
TokenCredential credential,
TimeSpan reloadInterval,
SecretClientOptions? clientOptions = null)
{
var client = new SecretClient(vaultUri, credential, clientOptions ?? new SecretClientOptions());
return builder.AddAzureKeyVault(client, new AzureKeyVaultConfigurationOptions
{
Manager = new PrefixKeyVaultSecretManager(AppPrefix),
ReloadInterval = reloadInterval
});
}
// Vault adresi environment'tan gelir: her environment'ın kendi vault'u var
if (builder.Configuration["KeyVault:Uri"] is { Length: > 0 } vaultUri)
{
var credential = KeyVaultConfigurationExtensions.CreateCredential(builder.Environment, builder.Configuration);
var clientOptions = new SecretClientOptions
{
// Yalnız yerel emulator için; Production'da bu kontrol asla kapatılmaz
DisableChallengeResourceVerification = !builder.Environment.IsProduction()
&& builder.Configuration.GetValue<bool>("KeyVault:DisableChallengeResourceVerification")
};
var reloadInterval = builder.Configuration.GetValue("KeyVault:ReloadInterval", TimeSpan.FromMinutes(30));
builder.Configuration.AddBookstoreKeyVault(new Uri(vaultUri), credential, reloadInterval, clientOptions);
}
Key Vault provider'ı listenin sonuna eklendiği için environment variable'ları da ezer: bir secret vault'ta varsa, aynı adlı environment variable'la override edilemez. Secret'lar için istediğimiz de bu; tek doğru kaynak vault.
Azure Olmadan Denemek: Yerel Key Vault Emulator'ü
Microsoft'un resmi bir Key Vault emulator'ü yok. Bu yazıdaki koşular için açık kaynak bir topluluk projesi olan Lowkey Vault'u kullandım; Key Vault'un secret API'sini ve App Service'in managed identity token endpoint'ini taklit ediyor. Gerçek Azure'daki RBAC kontrolünü yapmıyor, yani yetki hatalarını burada göremezsin. Emulator'ü ve API'yi aynı Docker network'ünde çalıştırdım; API container'ı App Service'teki gibi IDENTITY_ENDPOINT ve IDENTITY_HEADER değişkenleriyle token istiyor:
$ docker run -d --name secretseries-kv --network secretseries-net \
--network-alias default.lowkey-vault.local -p 8443:8443 nagyesta/lowkey-vault:7.4.0
$ docker run -d --init --name secretseries-api --network secretseries-net -p 18083:8080 \
-v ./lowkey-ca.pem:/certs/lowkey-ca.pem:ro -e SSL_CERT_FILE=/certs/lowkey-ca.pem \
-e ASPNETCORE_ENVIRONMENT=Staging \
-e KeyVault__Uri=https://default.lowkey-vault.local:8443 \
-e KeyVault__DisableChallengeResourceVerification=true \
-e KeyVault__ReloadInterval=00:00:15 \
-e IDENTITY_ENDPOINT=http://default.lowkey-vault.local:8080/metadata/identity/oauth2/token \
-e IDENTITY_HEADER=local-test \
secretseries-api:03
$ curl -s localhost:18083/api/info
{"environment":"Staging","freeShippingThreshold":750,"jwtKeyFingerprint":"2fd269ee","secrets":{"ConnectionStrings:Bookstore":true,"Jwt:SigningKey":true,"Email:ApiKey":true,"Payments:Webhook:Secret":false}}
Vault'a dört secret koymuştum: üçü BookstoreApi- önekli, biri NotificationsWorker- önekli. Uygulama yalnızca kendi üç secret'ını aldı ve ValidateOnStart geçti; container'da hiçbir secret environment variable'ı yok. jwtKeyFingerprint, key'in SHA (Secure Hash Algorithm; yaygın kriptografik hash ailesi) ailesinden SHA-256 özetinin ilk sekiz karakteri; değeri göstermeden hangi sürümün yüklendiğini anlamak için ekledim.
Emulator için yapılan iki ayar production'a taşınmaz. SSL_CERT_FILE container'a emulator'ün self-signed certificate'ına güvenmesini söylüyor. DisableChallengeResourceVerification ise Key Vault client'ının güvenlik kontrollerinden birini kapatıyor: client, token göndermeden önce vault'un döndürdüğü challenge'daki domain'in istek yaptığı adresle uyuştuğunu kontrol eder ve token'ın sahte bir vault'a sızmasını böyle engeller. Emulator bu domain'i Azure'dan farklı döndürdüğü için kontrolü kapatmak gerekiyor; koddaki koşul Production environment'ında bu ayarı yok sayıyor.
ReloadInterval, Throttling ve Değişen Secret
Provider varsayılan olarak secret'ları bir kez okur ve uygulama ömrü boyunca cache'ler; vault'ta değişen ya da disable edilen bir secret uygulamaya yansımaz. ReloadInterval verirsen o aralıkta bütün listeyi yeniden okur. Yukarıdaki koşuda aralığı 15 saniyeye indirmiştim; uygulama çalışırken JWT key'inin yeni bir sürümünü yazdım:
09:09:34 {"environment":"Staging","freeShippingThreshold":750,"jwtKeyFingerprint":"2fd269ee",...}
# vault'a BookstoreApi-Jwt--SigningKey için yeni sürüm yazıldı (09:09:34)
09:09:39 "jwtKeyFingerprint":"2fd269ee"
09:09:44 "jwtKeyFingerprint":"b51b8da0"
09:09:49 "jwtKeyFingerprint":"b51b8da0"
Yeni sürüm en geç bir reload aralığı içinde geldi ve IOptionsMonitor ile okuyan kod onu gördü. Aralığı seçerken iki şeyi hesaba kat. Birincisi throttling: Microsoft Learn'deki Key Vault service limits sayfasına göre secret okumaları dahil "diğer" işlemler için sınır vault başına, bölge başına 10 saniyede 4.000 işlem. Her reload, yüklenen secret sayısı kadar GET demek; 50 instance, 20 secret ve 30 saniyelik aralık 10 saniyede yaklaşık 330 istek eder, yani sınıra uzak ama aynı vault'u kullanan diğer uygulamalarla toplanır. İkincisi rotation: reload aralığı, yeni bir secret'ın uygulamaya yansıması için gereken en uzun süre. Yedinci yazıda iki aktif secret ile çalışırken bu süreyi overlap penceresine göre seçeceğiz.
Alternatif: App Service Key Vault Reference'ları
Kodu hiç değiştirmeden de Key Vault kullanılabilir. App Service, Functions ve Logic Apps'te bir app setting'in değeri şu biçimde bir reference olabilir:
Jwt__SigningKey = @Microsoft.KeyVault(SecretUri=https://bookstore-kv-prod.vault.azure.net/secrets/BookstoreApi-Jwt--SigningKey)
Platform reference'ı uygulamanın managed identity'siyle çözer ve uygulamaya düz bir environment variable olarak verir. Sürüm belirtilmemişse en güncel sürüm kullanılır; App Service değerleri cache'ler ve Microsoft Learn'e göre yeni sürümü 24 saat içinde alır, herhangi bir config değişikliği ise restart ile anında yeniden okumaya yol açar. Azure Container Apps'in secret'ları da benzer şekilde Key Vault'taki bir secret'a referans verebilir.
Reference'ın tuzağı hata durumunda: çözülemeyen bir reference, değer olarak olduğu gibi kalır. Uygulama Jwt:SigningKey olarak @Microsoft.KeyVault(...) metnini alır. Bunu emulator'süz denedim; iki secret'ı reference metniyle verdim:
$ docker run -d --init --name secretseries-api -p 18083:8080 \
-e "Jwt__SigningKey=@Microsoft.KeyVault(SecretUri=https://bookstore-kv-prod.vault.azure.net/secrets/BookstoreApi-Jwt--SigningKey)" \
-e "Email__ApiKey=@Microsoft.KeyVault(SecretUri=https://bookstore-kv-prod.vault.azure.net/secrets/BookstoreApi-Email--ApiKey)" \
secretseries-api:03
$ curl -s localhost:18083/api/info
{"environment":"Production","freeShippingThreshold":500,"jwtKeyFingerprint":"2cc13d41","secrets":{"ConnectionStrings:Bookstore":false,"Jwt:SigningKey":true,"Email:ApiKey":true,"Payments:Webhook:Secret":false}}
Uygulama sorunsuz açıldı: metin 44 karakterden uzun olduğu için [MinLength(44)] doğrulamasını da geçti ve JWT'ler bu metinle imzalanacaktı. Açılışta bütün configuration'ı tarayan küçük bir kontrol ekledim:
// App Service çözemediği reference'ı değer olarak bırakır; bunu açılışta yakala
public static void ThrowIfAny(IConfiguration configuration)
{
var unresolved = configuration.AsEnumerable()
.Where(pair => pair.Value?.StartsWith(ReferencePrefix, StringComparison.OrdinalIgnoreCase) == true)
.Select(pair => pair.Key)
.Order()
.ToList();
if (unresolved.Count > 0)
{
throw new InvalidOperationException(
$"Unresolved Key Vault references: {string.Join(", ", unresolved)}");
}
}
$ docker run --init --name secretseries-api -e "Jwt__SigningKey=@Microsoft.KeyVault(...)" -e "Email__ApiKey=@Microsoft.KeyVault(...)" secretseries-api:03
Unhandled exception. System.InvalidOperationException: Unresolved Key Vault references: Email:ApiKey, Jwt:SigningKey
$ docker inspect secretseries-api -f '{{.State.ExitCode}}'
134
Hangisini seçmeli? Reference'lar kod değişikliği istemez ve uygulamayı Azure SDK'sından bağımsız tutar; az sayıda secret'ı olan, App Service'te yaşayan bir uygulama için iyi bir başlangıç. Provider ise reload aralığını, prefix ile filtrelemeyi ve hata davranışını senin kontrolüne verir; Kubernetes, Container Apps ve App Service arasında aynı kod çalışır. Bookstore'da provider'ı seçtik, çünkü aynı API altıncı yazıda Kubernetes'e de gidecek.
Üretimde Dikkat
Baştaki ilk sorunun cevabını ölçtüm. API'yi managed identity endpoint'i olmadan başlattım; ManagedIdentityCredential resilient modda IMDS'i retry'larla denedi ve uygulama yaklaşık üç dakika sonra hata verdi:
Unhandled exception. Azure.Identity.AuthenticationFailedException: ManagedIdentityCredential authentication failed: All Managed Identity sources are unavailable. The Azure Instance Metadata Service (IMDS) that runs on VMs was not detected: IMDSv2: IMDSv2 probe failed. ... (Connection refused (169.254.169.254:80)) ...
Vault adresi yanlış olduğunda ise (DNS çözülemiyor) SDK dört denemeden sonra yaklaşık 23 saniyede vazgeçti. İki durumda da uygulama secret'sız açılmadı; sessizce varsayılan değerlerle açılmaktan çok daha iyi. Ama süreleri health check ve deploy timeout'larına göre ayarla: üç dakikalık açılış, kısa bir startup probe'u olan bir pod'u gereksiz yere yeniden başlatır.
Bu denemede bir şey daha gördüm: --init olmadan çalışan container'da exception log'a yazıldı ama process kapanmadı ve container iki dakika sonra hâlâ running durumdaydı. Aynı denemeyi --init ile yaptığımda container 134 çıkış koduyla kapandı. Uygulamanın container'daki ilk process olarak, yani PID (process ID; işletim sisteminin process'e verdiği numara) değeri 1 iken çalışmasının bir yan etkisi; çözümü docker run --init ya da image'a küçük bir init process eklemek. Kubernetes'te de readiness probe'u olmayan bir pod'un bu durumda trafik almaya devam edebileceğini unutma.
Key Vault'un diagnostic log'larını bir Log Analytics workspace'ine gönder: hangi identity'nin hangi secret'ı ne zaman okuduğu sızıntı incelemesinin temel kaynağı. Bir secret'ı yalnızca bir uygulama okuyorsa, başka bir identity'den gelen SecretGet bir alarm sebebidir.
Sık Düşülen Tuzaklar
Tek vault'u bütün environment'lara paylaştırmak: dev'de çalışan herkes production secret'ına bir rol ataması uzaklıkta kalır. Rolü abonelik ya da resource group seviyesinde vermek. Production'da DefaultAzureCredential kullanıp hangi credential'ın seçildiğini bilmemek. Key Vault eklemeyi try/catch içine alıp hata olursa uygulamayı "secret'sız" açmak; ValidateOnStart yoksa bu, boş key'le çalışan bir API demektir. App Service reference'ının çözülemediğinde metnin kendisinin değer olarak kalmasını bilmemek. Prefix'i güvenlik sınırı sanmak. ReloadInterval'ı çok kısa tutup throttling'e girmek ya da hiç vermeyip rotation'ın yansımasını restart'a bağlamak. Son olarak, DisableChallengeResourceVerification gibi yerel test ayarlarını koşulsuz bırakmak.
Küçük Bir Deneme
★☆☆ Kendi API'nde secret'ları okuyan credential'ı bul. DefaultAzureCredential ise production için ManagedIdentityCredential'a çevir ve geliştirme için ayrı bir ChainedTokenCredential yaz.
★★☆ Lowkey Vault'u Docker'da çalıştır, uygulamanın önekiyle iki, başka bir önekle bir secret yaz. PrefixKeyVaultSecretManager ile uygulamanın yalnızca kendi secret'larını aldığını ve ReloadInterval'ı kısaltınca yeni sürümün ne kadar sürede geldiğini ölç.
★★★ API'yi önce managed identity endpoint'i olmadan, sonra yanlış vault adresiyle başlat ve açılışın kaç saniye sürdüğünü ölç. Bu süreleri deploy ettiğin platformun startup probe ya da health check ayarlarıyla karşılaştır ve gerekiyorsa ManagedIdentityCredentialOptions.Retry ayarlarını değiştir.
Secret'lar artık vault'ta ve uygulama onlara parolasız ulaşıyor. Gizli olmayan ama sık değişen ayarlar, örneğin kargo eşiği, hâlâ her değişiklikte deploy ya da restart istiyor. Bir sonraki yazıda Azure App Configuration ile bu ayarları merkezde topluyor, label'larla environment'lara ayırıyor ve sentinel key ile deploy'suz yeniliyoruz.