De beheerde identiteit van een Service Fabric-toepassing gebruiken voor toegang tot Azure-services

Service Fabric-toepassingen kunnen gebruikmaken van beheerde identiteiten voor toegang tot andere Azure-resources die ondersteuning bieden voor verificatie op basis van Microsoft Entra ID. Een toepassing kan een toegangstoken verkrijgen dat de identiteit vertegenwoordigt, die mogelijk door het systeem is toegewezen of door de gebruiker is toegewezen, en het als een bearer-token gebruiken om zichzelf te verifiëren bij een andere service, ook wel een beveiligde resourceserver genoemd. Het token vertegenwoordigt de identiteit die is toegewezen aan de Service Fabric-toepassing en wordt alleen uitgegeven aan Azure-resources (inclusief SF-toepassingen) die die identiteit delen. Raadpleeg de overzichtsdocumentatie voor beheerde identiteiten voor een gedetailleerde beschrijving van beheerde identiteiten, evenals het onderscheid tussen door het systeem toegewezen en door de gebruiker toegewezen identiteiten. In dit artikel wordt verwezen naar een Service Fabric-toepassing met beheerde identiteit als de clienttoepassing .

Zie een aanvullende voorbeeldtoepassing die demonstreert met behulp van door het systeem toegewezen en door de gebruiker toegewezen beheerde identiteiten van de Service Fabric-toepassing met Reliable Services en containers.

Belangrijk

Een beheerde identiteit vertegenwoordigt de koppeling tussen een Azure-resource en een service-principal in de bijbehorende Microsoft Entra-tenant die is gekoppeld aan het abonnement dat de resource bevat. In de context van Service Fabric worden beheerde identiteiten alleen ondersteund voor toepassingen die zijn geïmplementeerd als Azure-resources.

Belangrijk

Voordat u de beheerde identiteit van een Service Fabric-toepassing gebruikt, moet de clienttoepassing toegang krijgen tot de beveiligde resource. Raadpleeg de lijst met Azure-services die Ondersteuning bieden voor Microsoft Entra-verificatie om te controleren op ondersteuning en vervolgens naar de documentatie van de betreffende service voor specifieke stappen voor het verlenen van identiteitstoegang tot resources die van belang zijn.

Een beheerde identiteit gebruiken met Behulp van Azure.Identity

De Azure Identity SDK biedt nu ondersteuning voor Service Fabric. Met Behulp van Azure.Identity kunt u eenvoudiger code schrijven om beheerde identiteiten van de Service Fabric-app te gebruiken, omdat hiermee tokens worden opgehaald, tokens in de cache worden opgeslagen en serververificatie. Tijdens het openen van de meeste Azure-resources is het concept van een token verborgen.

Service Fabric-ondersteuning is beschikbaar in de volgende versies voor deze talen:

C#-voorbeeld van het initialiseren van referenties en het gebruik van de referenties om een geheim op te halen uit Azure Key Vault:

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace MyMIService
{
    internal sealed class MyMIService : StatelessService
    {
        protected override async Task RunAsync(CancellationToken cancellationToken)
        {
            try
            {
                // Load the service fabric application managed identity assigned to the service
                ManagedIdentityCredential creds = new ManagedIdentityCredential();

                // Create a client to keyvault using that identity
                SecretClient client = new SecretClient(new Uri("https://mykv.vault.azure.net/"), creds);

                // Fetch a secret
                KeyVaultSecret secret = (await client.GetSecretAsync("mysecret", cancellationToken: cancellationToken)).Value;
            }
            catch (CredentialUnavailableException e)
            {
                // Handle errors with loading the Managed Identity
            }
            catch (RequestFailedException)
            {
                // Handle errors with fetching the secret
            }
            catch (Exception e)
            {
                // Handle generic errors
            }
        }
    }
}

Een toegangstoken verkrijgen met behulp van REST API

In clusters die zijn ingeschakeld voor beheerde identiteit, maakt de Service Fabric-runtime een localhost-eindpunt beschikbaar dat toepassingen kunnen gebruiken om toegangstokens te verkrijgen. Het eindpunt is beschikbaar op elk knooppunt van het cluster en is toegankelijk voor alle entiteiten op dat knooppunt. Geautoriseerde bellers kunnen toegangstokens verkrijgen door dit eindpunt aan te roepen en een verificatiecode te presenteren; de code wordt gegenereerd door de Service Fabric-runtime voor elke activering van elk afzonderlijk servicecodepakket en is gebonden aan de levensduur van het proces dat als host fungeert voor dat servicecodepakket.

In het bijzonder wordt de omgeving van een Service Fabric-service met beheerde identiteiten geseed met de volgende variabelen:

  • 'IDENTITY_ENDPOINT': het localhost-eindpunt dat overeenkomt met de beheerde identiteit van de service
  • 'IDENTITY_HEADER': een unieke verificatiecode die de service op het huidige knooppunt vertegenwoordigt
  • 'IDENTITY_SERVER_THUMBPRINT': Vingerafdruk van de beheerde identiteitsserver van Service Fabric

Belangrijk

De toepassingscode moet rekening houden met de waarde van de omgevingsvariabele 'IDENTITY_HEADER' als gevoelige gegevens. Deze moeten niet worden geregistreerd of anderszins worden verspreid. De verificatiecode heeft geen waarde buiten het lokale knooppunt of nadat het proces dat als host fungeert voor de service, maar het vertegenwoordigt wel de identiteit van de Service Fabric-service, en moet dus worden behandeld met dezelfde voorzorgsmaatregelen als het toegangstoken zelf.

Voor het verkrijgen van een token voert de client de volgende stappen uit:

  • vormt een URI door het eindpunt van de beheerde identiteit (IDENTITY_ENDPOINT waarde) samen te stellen met de API-versie en de resource (doelgroep) die vereist is voor het token
  • maakt een GET HTTP-aanvraag voor de opgegeven URI
  • voegt de juiste validatielogica voor servercertificaten toe
  • voegt de verificatiecode (IDENTITY_HEADER waarde) toe als header aan de aanvraag
  • verzendt de aanvraag

Een geslaagd antwoord bevat een JSON-nettolading die het resulterende toegangstoken vertegenwoordigt, evenals metagegevens die het beschrijven. Een mislukt antwoord bevat ook een uitleg van de fout. Zie hieronder voor meer informatie over foutafhandeling.

Toegangstokens worden opgeslagen in de cache van Service Fabric op verschillende niveaus (knooppunt, cluster, resourceproviderservice). Een geslaagd antwoord impliceert dus niet per se dat het token rechtstreeks is uitgegeven als reactie op de aanvraag van de gebruikerstoepassing. Tokens worden gedurende minder dan hun levensduur in de cache opgeslagen en een toepassing ontvangt dus gegarandeerd een geldig token. Het wordt aanbevolen dat de toepassingscode zichzelf in de cache opneemt van alle toegangstokens die worden verkregen; de cachesleutel moet de doelgroep (een afleiding) bevatten.

Voorbeeldaanvraag:

GET 'https://localhost:2377/metadata/identity/oauth2/token?api-version=2019-07-01-preview&resource=https://vault.azure.net/' HTTP/1.1 Secret: 912e4af7-77ba-4fa5-a737-56c8e3ace132

waarbij geldt:

Element Omschrijving
GET Het HTTP-woord dat aangeeft dat u gegevens wilt ophalen van het eindpunt. In dit geval een OAuth-toegangstoken.
https://localhost:2377/metadata/identity/oauth2/token Het eindpunt van de beheerde identiteit voor Service Fabric-toepassingen, geleverd via de omgevingsvariabele IDENTITY_ENDPOINT.
api-version Een queryreeksparameter die de API-versie van de Managed Identity Token Service opgeeft; momenteel is de enige geaccepteerde waarde , 2019-07-01-previewen is onderhevig aan wijzigingen.
resource Een queryreeksparameter die de app-id-URI van de doelresource aangeeft. Dit wordt weergegeven als de aud claim (doelgroep) van het uitgegeven token. In dit voorbeeld wordt een token aangevraagd voor toegang tot Azure Key Vault, waarvan de URI van een app-id is https://vault.azure.net/.
Secret Een http-aanvraagheaderveld dat is vereist voor de Service Fabric Managed Identity Token-service voor Service Fabric-services om de aanroeper te verifiëren. Deze waarde wordt geleverd door de SF-runtime via IDENTITY_HEADER omgevingsvariabele.

Voorbeeldantwoord:

HTTP/1.1 200 OK
Content-Type: application/json
{
    "token_type":  "Bearer",
    "access_token":  "eyJ0eXAiO...",
    "expires_on":  1565244611,
    "resource":  "https://vault.azure.net/"
}

waarbij geldt:

Element Omschrijving
token_type Het type token; In dit geval is een Bearer-toegangstoken, wat betekent dat de presentator ('bearer') van dit token het beoogde onderwerp van het token is.
access_token Het aangevraagde toegangstoken. Wanneer u een beveiligde REST API aanroept, wordt het token ingesloten in het veld van de Authorization aanvraagheader als een bearer-token, zodat de API de aanroeper kan verifiëren.
expires_on De tijdstempel van de vervaldatum van het toegangstoken; weergegeven als het aantal seconden van 1970-01-01T0:0:0Z UTC en komt overeen met de claim van exp het token. In dit geval verloopt het token op 2019-08-08T06:10:11+00:00 (in RFC 3339)
resource De resource waarvoor het toegangstoken is uitgegeven, opgegeven via de resource queryreeksparameter van de aanvraag; komt overeen met de claim aud van het token.

Een toegangstoken verkrijgen met behulp van C#

Het bovenstaande wordt, in C#:

namespace Azure.ServiceFabric.ManagedIdentity.Samples
{
    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web;
    using Newtonsoft.Json;

    /// <summary>
    /// Type representing the response of the SF Managed Identity endpoint for token acquisition requests.
    /// </summary>
    [JsonObject]
    public sealed class ManagedIdentityTokenResponse
    {
        [JsonProperty(Required = Required.Always, PropertyName = "token_type")]
        public string TokenType { get; set; }

        [JsonProperty(Required = Required.Always, PropertyName = "access_token")]
        public string AccessToken { get; set; }

        [JsonProperty(PropertyName = "expires_on")]
        public string ExpiresOn { get; set; }

        [JsonProperty(PropertyName = "resource")]
        public string Resource { get; set; }
    }

    /// <summary>
    /// Sample class demonstrating access token acquisition using Managed Identity.
    /// </summary>
    public sealed class AccessTokenAcquirer
    {
        /// <summary>
        /// Acquire an access token.
        /// </summary>
        /// <returns>Access token</returns>
        public static async Task<string> AcquireAccessTokenAsync()
        {
            var managedIdentityEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
            var managedIdentityAuthenticationCode = Environment.GetEnvironmentVariable("IDENTITY_HEADER");
            var managedIdentityServerThumbprint = Environment.GetEnvironmentVariable("IDENTITY_SERVER_THUMBPRINT");
            // Latest api version, 2019-07-01-preview is still supported.
            var managedIdentityApiVersion = Environment.GetEnvironmentVariable("IDENTITY_API_VERSION");
            var managedIdentityAuthenticationHeader = "secret";
            var resource = "https://management.azure.com/";

            var requestUri = $"{managedIdentityEndpoint}?api-version={managedIdentityApiVersion}&resource={HttpUtility.UrlEncode(resource)}";

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
            requestMessage.Headers.Add(managedIdentityAuthenticationHeader, managedIdentityAuthenticationCode);
            
            var handler = new HttpClientHandler();
            handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, certChain, policyErrors) =>
            {
                // Do any additional validation here
                if (policyErrors == SslPolicyErrors.None)
                {
                    return true;
                }
                return 0 == string.Compare(cert.GetCertHashString(), managedIdentityServerThumbprint, StringComparison.OrdinalIgnoreCase);
            };

            try
            {
                var response = await new HttpClient(handler).SendAsync(requestMessage)
                    .ConfigureAwait(false);

                response.EnsureSuccessStatusCode();

                var tokenResponseString = await response.Content.ReadAsStringAsync()
                    .ConfigureAwait(false);

                var tokenResponseObject = JsonConvert.DeserializeObject<ManagedIdentityTokenResponse>(tokenResponseString);

                return tokenResponseObject.AccessToken;
            }
            catch (Exception ex)
            {
                string errorText = String.Format("{0} \n\n{1}", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "Acquire token failed");

                Console.WriteLine(errorText);
            }

            return String.Empty;
        }
    } // class AccessTokenAcquirer
} // namespace Azure.ServiceFabric.ManagedIdentity.Samples

Toegang tot Key Vault vanuit een Service Fabric-toepassing met beheerde identiteit

Dit voorbeeld bouwt voort op het bovenstaande om toegang te krijgen tot een geheim dat is opgeslagen in een Key Vault met behulp van een beheerde identiteit.

        /// <summary>
        /// Probe the specified secret, displaying metadata on success.  
        /// </summary>
        /// <param name="vault">vault name</param>
        /// <param name="secret">secret name</param>
        /// <param name="version">secret version id</param>
        /// <returns></returns>
        public async Task<string> ProbeSecretAsync(string vault, string secret, string version)
        {
            // initialize a KeyVault client with a managed identity-based authentication callback
            var kvClient = new Microsoft.Azure.KeyVault.KeyVaultClient(new Microsoft.Azure.KeyVault.KeyVaultClient.AuthenticationCallback((a, r, s) => { return AuthenticationCallbackAsync(a, r, s); }));

            Log(LogLevel.Info, $"\nRunning with configuration: \n\tobserved vault: {config.VaultName}\n\tobserved secret: {config.SecretName}\n\tMI endpoint: {config.ManagedIdentityEndpoint}\n\tMI auth code: {config.ManagedIdentityAuthenticationCode}\n\tMI auth header: {config.ManagedIdentityAuthenticationHeader}");
            string response = String.Empty;

            Log(LogLevel.Info, "\n== {DateTime.UtcNow.ToString()}: Probing secret...");
            try
            {
                var secretResponse = await kvClient.GetSecretWithHttpMessagesAsync(vault, secret, version)
                    .ConfigureAwait(false);

                if (secretResponse.Response.IsSuccessStatusCode)
                {
                    // use the secret: secretValue.Body.Value;
                    response = String.Format($"Successfully probed secret '{secret}' in vault '{vault}': {PrintSecretBundleMetadata(secretResponse.Body)}");
                }
                else
                {
                    response = String.Format($"Non-critical error encountered retrieving secret '{secret}' in vault '{vault}': {secretResponse.Response.ReasonPhrase} ({secretResponse.Response.StatusCode})");
                }
            }
            catch (Microsoft.Rest.ValidationException ve)
            {
                response = String.Format($"encountered REST validation exception 0x{ve.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}' from {ve.Source}: {ve.Message}");
            }
            catch (KeyVaultErrorException kvee)
            {
                response = String.Format($"encountered KeyVault exception 0x{kvee.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}': {kvee.Response.ReasonPhrase} ({kvee.Response.StatusCode})");
            }
            catch (Exception ex)
            {
                // handle generic errors here
                response = String.Format($"encountered exception 0x{ex.HResult.ToString("X")} trying to access '{secret}' in vault '{vault}': {ex.Message}");
            }

            Log(LogLevel.Info, response);

            return response;
        }

        /// <summary>
        /// KV authentication callback, using the application's managed identity.
        /// </summary>
        /// <param name="authority">The expected issuer of the access token, from the KV authorization challenge.</param>
        /// <param name="resource">The expected audience of the access token, from the KV authorization challenge.</param>
        /// <param name="scope">The expected scope of the access token; not currently used.</param>
        /// <returns>Access token</returns>
        public async Task<string> AuthenticationCallbackAsync(string authority, string resource, string scope)
        {
            Log(LogLevel.Verbose, $"authentication callback invoked with: auth: {authority}, resource: {resource}, scope: {scope}");
            var encodedResource = HttpUtility.UrlEncode(resource);

            // This sample does not illustrate the caching of the access token, which the user application is expected to do.
            // For a given service, the caching key should be the (encoded) resource uri. The token should be cached for a period
            // of time at most equal to its remaining validity. The 'expires_on' field of the token response object represents
            // the number of seconds from Unix time when the token will expire. You may cache the token if it will be valid for at
            // least another short interval (1-10s). If its expiration will occur shortly, don't cache but still return it to the 
            // caller. The MI endpoint will not return an expired token.
            // Sample caching code:
            //
            // ManagedIdentityTokenResponse tokenResponse;
            // if (responseCache.TryGetCachedItem(encodedResource, out tokenResponse))
            // {
            //     Log(LogLevel.Verbose, $"cache hit for key '{encodedResource}'");
            //
            //     return tokenResponse.AccessToken;
            // }
            //
            // Log(LogLevel.Verbose, $"cache miss for key '{encodedResource}'");
            //
            // where the response cache is left as an exercise for the reader. MemoryCache is a good option, albeit not yet available on .net core.

            var requestUri = $"{config.ManagedIdentityEndpoint}?api-version={config.ManagedIdentityApiVersion}&resource={encodedResource}";
            Log(LogLevel.Verbose, $"request uri: {requestUri}");

            var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);
            requestMessage.Headers.Add(config.ManagedIdentityAuthenticationHeader, config.ManagedIdentityAuthenticationCode);
            Log(LogLevel.Verbose, $"added header '{config.ManagedIdentityAuthenticationHeader}': '{config.ManagedIdentityAuthenticationCode}'");

            var response = await httpClient.SendAsync(requestMessage)
                .ConfigureAwait(false);
            Log(LogLevel.Verbose, $"response status: success: {response.IsSuccessStatusCode}, status: {response.StatusCode}");

            response.EnsureSuccessStatusCode();

            var tokenResponseString = await response.Content.ReadAsStringAsync()
                .ConfigureAwait(false);

            var tokenResponse = JsonConvert.DeserializeObject<ManagedIdentityTokenResponse>(tokenResponseString);
            Log(LogLevel.Verbose, "deserialized token response; returning access code..");

            // Sample caching code (continuation):
            // var expiration = DateTimeOffset.FromUnixTimeSeconds(Int32.Parse(tokenResponse.ExpiresOn));
            // if (expiration > DateTimeOffset.UtcNow.AddSeconds(5.0))
            //    responseCache.AddOrUpdate(encodedResource, tokenResponse, expiration);

            return tokenResponse.AccessToken;
        }

        private string PrintSecretBundleMetadata(SecretBundle bundle)
        {
            StringBuilder strBuilder = new StringBuilder();

            strBuilder.AppendFormat($"\n\tid: {bundle.Id}\n");
            strBuilder.AppendFormat($"\tcontent type: {bundle.ContentType}\n");
            strBuilder.AppendFormat($"\tmanaged: {bundle.Managed}\n");
            strBuilder.AppendFormat($"\tattributes:\n");
            strBuilder.AppendFormat($"\t\tenabled: {bundle.Attributes.Enabled}\n");
            strBuilder.AppendFormat($"\t\tnbf: {bundle.Attributes.NotBefore}\n");
            strBuilder.AppendFormat($"\t\texp: {bundle.Attributes.Expires}\n");
            strBuilder.AppendFormat($"\t\tcreated: {bundle.Attributes.Created}\n");
            strBuilder.AppendFormat($"\t\tupdated: {bundle.Attributes.Updated}\n");
            strBuilder.AppendFormat($"\t\trecoveryLevel: {bundle.Attributes.RecoveryLevel}\n");

            return strBuilder.ToString();
        }

        private enum LogLevel
        {
            Info,
            Verbose
        };

        private void Log(LogLevel level, string message)
        {
            if (level != LogLevel.Verbose
                || config.DoVerboseLogging)
            {
                Console.WriteLine(message);
            }
        }

Foutafhandeling

Het veld statuscode van de HTTP-antwoordheader geeft de successtatus van de aanvraag aan; de status '200 OK' geeft aan dat het is gelukt en het antwoord bevat het toegangstoken zoals hierboven beschreven. Hieronder volgt een korte opsomming van mogelijke foutreacties.

Statuscode Reden voor de fout Hoe kan ik het afhandelen
404 Niet gevonden. Onbekende verificatiecode of de toepassing heeft geen beheerde identiteit toegewezen. Corrik de installatie- of token-overnamecode van de toepassing.
429 Te veel aanvragen. Beperkingslimiet bereikt, opgelegd door Microsoft Entra ID of SF. Probeer het opnieuw met Exponentieel uitstel. Zie de richtlijnen hieronder.
4xx Fout in aanvraag. Een of meer van de aanvraagparameters zijn onjuist. Probeer het niet opnieuw. Bestudeer de foutdetails voor meer informatie. 4xx-fouten zijn ontwerptijdfouten.
5xx-fout van de service. Het subsysteem van de beheerde identiteit of de Microsoft Entra-id heeft een tijdelijke fout geretourneerd. Het is veilig om het na een korte tijd opnieuw te proberen. U kunt bij het opnieuw proberen een beperkingsvoorwaarde (429) raken.

Als er een fout optreedt, bevat de bijbehorende HTTP-antwoordtekst een JSON-object met de foutdetails:

Element Omschrijving
code Foutcode.
correlationId Een correlatie-id die kan worden gebruikt voor foutopsporing.
bericht Uitgebreide beschrijving van fout. Foutbeschrijvingen kunnen op elk moment wijzigen. Niet afhankelijk van het foutbericht zelf.

Voorbeeldfout:

{"error":{"correlationId":"7f30f4d3-0f3a-41e0-a417-527f21b3848f","code":"SecretHeaderNotFound","message":"Secret is not found in the request headers."}}

Hieronder volgt een lijst met typische Service Fabric-fouten die specifiek zijn voor beheerde identiteiten:

Code Bericht Omschrijving
SecretHeaderNotFound Geheim is niet gevonden in de aanvraagheaders. De verificatiecode is niet bij de aanvraag geleverd.
ManagedIdentityNotFound Beheerde identiteit is niet gevonden voor de opgegeven toepassingshost. De toepassing heeft geen identiteit of de verificatiecode is onbekend.
ArgumentNullOrEmpty De parameter resource mag geen null- of lege tekenreeks zijn. De resource (doelgroep) is niet opgegeven in de aanvraag.
InvalidApiVersion De API-versie '' wordt niet ondersteund. Ondersteunde versie is '2019-07-01-preview'. Ontbrekende of niet-ondersteunde API-versie die is opgegeven in de aanvraag-URI.
InternalServerError Er is een fout opgetreden. Er is een fout opgetreden in het subsysteem voor beheerde identiteiten, mogelijk buiten de Service Fabric-stack. Meest waarschijnlijke oorzaak is een onjuiste waarde die is opgegeven voor de resource (controleer op volgteken '/'?)

Richtlijnen opnieuw proberen

Normaal gesproken is de enige foutcode die opnieuw kan worden geprobeerd 429 (te veel aanvragen); interne serverfouten/5xx-foutcodes kunnen opnieuw worden geprobeerd, maar de oorzaak kan permanent zijn.

Beperkingslimieten zijn van toepassing op het aantal aanroepen dat wordt gedaan naar het subsysteem voor beheerde identiteiten, met name de upstream-afhankelijkheden (de Managed Identity Azure-service of de beveiligde tokenservice). Service Fabric slaat tokens op verschillende niveaus in de pijplijn in de cache op, maar gezien de gedistribueerde aard van de betrokken onderdelen kan de aanroeper inconsistente beperkingsreacties ondervinden (bijvoorbeeld worden beperkt op één knooppunt/exemplaar van een toepassing, maar niet op een ander knooppunt tijdens het aanvragen van een token voor dezelfde identiteit.) Wanneer de beperkingsvoorwaarde is ingesteld, kunnen volgende aanvragen van dezelfde toepassing mislukken met de HTTP-statuscode 429 (Te veel aanvragen) totdat de voorwaarde is gewist.

Het wordt aanbevolen dat aanvragen zijn mislukt vanwege beperking, als volgt opnieuw worden geprobeerd met een exponentieel uitstel:

Index aanroepen Actie voor ontvangst van 429
1 Wacht 1 seconde en probeer het opnieuw
2 Wacht 2 seconden en probeer het opnieuw
3 Wacht 4 seconden en probeer het opnieuw
4 Wacht 8 seconden en probeer het opnieuw
4 Wacht 8 seconden en probeer het opnieuw
5 Wacht 16 seconden en probeer het opnieuw

Resource-id's voor Azure-services

Zie Azure-services die ondersteuning bieden voor Microsoft Entra-verificatie voor een lijst met resources die ondersteuning bieden voor Microsoft Entra-id's en hun respectieve resource-id's.

Volgende stappen