Поделиться через


Руководство разработчика Spring Boot Starter для разработчика Microsoft Entra

Эта статья относится к:✅ версии 4.19.0 ✅ версии 5.19.0

В этой статье описываются функции и основные сценарии начального средства Spring Boot для идентификатора Microsoft Entra. В статье также содержатся рекомендации по общим проблемам, обходным решениям и диагностическим шагам.

При создании веб-приложения управление удостоверениями и доступом являются основными элементами. Azure предлагает облачную службу удостоверений, которая имеет глубокую интеграцию с остальной частью экосистемы Azure.

Несмотря на то, что Spring Security упрощает защиту приложений Spring, она не адаптирована к конкретному поставщику удостоверений. Начальный код Spring Boot для Microsoft Entra ID позволяет подключить веб-приложение к клиенту Microsoft Entra и защитить сервер ресурсов с помощью идентификатора Microsoft Entra. Он использует протокол Oauth 2.0 для защиты веб-приложений и серверов ресурсов.

Следующие ссылки предоставляют доступ к начальнму пакету, документации и примерам:

Необходимые условия

Чтобы выполнить инструкции в этом руководстве, необходимо иметь следующие предварительные требования:

Важный

Для выполнения действий, описанных в этой статье, требуется spring Boot версии 2.5 или более поздней.

Основные сценарии

В этом руководстве описывается использование начального средства Microsoft Entra в следующих сценариях:

веб-приложение — это любой интернет-сервис, который позволяет пользователю входить в систему. Сервер ресурсов будет принимать или запрещать доступ, после проверки токена доступа.

Доступ к веб-приложению

В этом сценарии используется код авторизации OAuth 2.0 поток для входа пользователя с помощью учетной записи Майкрософт.

Чтобы использовать стартовый пакет Microsoft Entra в этом сценарии, выполните следующие действия.

Задайте URI перенаправления для <application-base-uri>/login/oauth2/code/. Например, http://localhost:8080/login/oauth2/code/. Не забудьте включить конечный символ /. Дополнительные сведения о URI перенаправления см. в разделе Добавление URI перенаправления в кратком руководстве по . Регистрация приложения с помощью платформы удостоверений Майкрософт.

снимок экрана портала Azure, на котором показана страница проверки подлинности веб-приложения с выделенным URI перенаправления.

Добавьте следующие зависимости в файл pom.xml.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Заметка

Дополнительные сведения об управлении версиями библиотеки Spring Cloud Azure с помощью списка материалов (BOM) см. в разделе "Приступая к работе" руководства разработчика Spring Cloud Azure.

Добавьте следующие свойства в файл application.yml. Значения этих свойств можно получить из регистрации приложения, созданной на портале Azure, как описано в предварительных требованиях.

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>

Заметка

Значения, допустимые для tenant-id: common, organizations, consumersили идентификатор клиента. Дополнительные сведения об этих значениях см. в разделе Используется неправильная конечная точка (личные учетные записи и учетные записи организации) раздела Ошибка AADSTS50020 - Учетная запись пользователя от поставщика удостоверений не существует в арендаторе. Сведения о преобразовании вашего одноарендного приложения см. в разделе Конвертация одноарендного приложения в многопользовательское в идентификаторе Microsoft Entra.

Используйте конфигурацию безопасности по умолчанию или укажите собственную конфигурацию.

Вариант 1. Используйте конфигурацию по умолчанию.

С помощью этого параметра вам не нужно ничего делать. Класс DefaultAadWebSecurityConfiguration настраивается автоматически.

Вариант 2. Укажите самоопределимую конфигурацию.

Чтобы предоставить конфигурацию, примените метод AadWebApplicationHttpSecurityConfigurer#aadWebApplication для HttpSecurity, как показано в следующем примере:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
           // Do some custom configuration.
       return http.build();
   }
}

Доступ к серверам ресурсов из веб-приложения

Чтобы использовать стартер Microsoft Entra в этом сценарии, выполните следующие действия.

Задайте URI перенаправления, как описано ранее.

Добавьте следующие зависимости в файл pom.xml.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Заметка

Дополнительные сведения об управлении версиями библиотеки Spring Cloud Azure с помощью спецификации (BOM) см. в разделе "Начало работы" руководства для разработчиков Spring Cloud Azure.

Добавьте следующие свойства в файл application.yml, как описано ранее:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <your-client-ID>
         client-secret: <your-client-secret>
       authorization-clients:
         graph:
           scopes: https://graph.microsoft.com/Analytics.Read, email

Заметка

Значения, допустимые для tenant-id: common, organizations, consumersили идентификатор клиента. Дополнительные сведения об этих значениях см. в разделе Используется неправильная конечная точка (личные учетные записи и учетные записи организации) в документе Ошибка AADSTS50020 — учетная запись пользователя от поставщика удостоверений не существует в арендатора. Сведения о преобразовании однотенантного приложения см. в статье Преобразование однотенантного приложения в мультитенантное приложение на идентификаторе Microsoft Entra.

Здесь graph — это имя OAuth2AuthorizedClient, а scopes — запрашиваемые права, необходимые для согласия при входе.

Добавьте код в приложение, аналогичное следующему примеру:

@GetMapping("/graph")
@ResponseBody
public String graph(
   @RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graphClient
) {
   // toJsonString() is just a demo.
   // oAuth2AuthorizedClient contains access_token. We can use this access_token to access the resource server.
   return toJsonString(graphClient);
}

Здесь graph — это идентификатор клиента, настроенный на предыдущем шаге. OAuth2AuthorizedClient содержит маркер доступа, который используется для доступа к серверу ресурсов.

Полный пример, демонстрирующий этот сценарий, см. в разделе spring-cloud-azure-starter-active-directory: aad-web-application.

Защита сервера ресурсов или API

Этот сценарий не поддерживает вход, но защищает сервер, проверяя маркер доступа. Если маркер доступа действителен, сервер обслуживает запрос.

Чтобы использовать начальный комплект Microsoft Entra в этом сценарии, выполните следующие действия.

Добавьте следующие зависимости в файл pom.xml.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

Заметка

Дополнительные сведения об управлении версиями библиотеки Spring Cloud Azure с помощью спецификации компонентов (BOM) см. в разделе "Начало работы" руководства разработчика Spring Cloud Azure.

Добавьте следующие свойства в файл application.yml, как описано ранее:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       credential:
         client-id: <your-client-ID>
       app-id-uri: <your-app-ID-URI>

Вы можете использовать значения <идентификатора вашего клиента> и значения <идентификатора вашего приложения и URI> для проверки токена доступа. Вы можете получить <значение> идентификатора приложения и URI на портале Azure, как показано на следующих изображениях:

снимок экрана портала Azure, на котором показана страница веб-приложения

Используйте конфигурацию безопасности по умолчанию или укажите собственную конфигурацию.

Вариант 1. Используйте конфигурацию по умолчанию.

С помощью этого параметра вам не нужно ничего делать. Класс DefaultAadResourceServerConfiguration настраивается автоматически.

Вариант 2. Укажите самоопределимую конфигурацию.

Чтобы предоставить конфигурацию, примените метод AadResourceServerHttpSecurityConfigurer#aadResourceServer для HttpSecurity, как показано в следующем примере:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2ResourceServerSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
       http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       return http.build();
   }
}

Полный пример, демонстрирующий этот сценарий, см. в примере spring-cloud-azure-starter-active-directory: aad-resource-server.

Доступ к другим серверам ресурсов с сервера ресурсов

Этот сценарий поддерживает сервер ресурсов, посещающий другие серверы ресурсов.

Чтобы использовать начальную конфигурацию Microsoft Entra в этом сценарии, выполните следующие действия.

Добавьте следующие зависимости в файл pom.xml.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Заметка

Дополнительные сведения об управлении версиями библиотек Spring Cloud Azure с помощью спецификации (BOM) см. в разделе "Начало работы" руководства разработчика Spring Cloud Azure.

Добавьте следующие свойства в файл application.yml:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <web-API-A-client-ID>
         client-secret: <web-API-A-client-secret>
       app-id-uri: <web-API-A-app-ID-URI>
       authorization-clients:
         graph:
           scopes:
              - https://graph.microsoft.com/User.Read

Заметка

Значения, допустимые для tenant-id: common, organizations, consumersили идентификатор клиента. Дополнительные сведения об этих значениях см. в разделе Использована неправильная конечная точка (учетные записи для личного и организационного использования) в разделе ошибка AADSTS50020 - Учетная запись пользователя от поставщика удостоверений не существует у арендатора. Сведения о преобразовании одноарендного приложения см. в статье Преобразование одноарендного приложения в многоарендное приложение в документации по идентификатору Microsoft Entra.

Используйте атрибут @RegisteredOAuth2AuthorizedClient в коде для доступа к связанному серверу ресурсов, как показано в следующем примере:

@PreAuthorize("hasAuthority('SCOPE_Obo.Graph.Read')")
@GetMapping("call-graph")
public String callGraph(@RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient graph) {
   return callMicrosoftGraphMeEndpoint(graph);
}

Полный пример, демонстрирующий этот сценарий, см. в разделе spring-cloud-azure-starter-active-directory: aad-resource-server-obo.

Веб-приложение и сервер ресурсов в одном приложении

Этот сценарий поддерживает доступ к веб-приложению и защиту сервера ресурсов или API в одном приложении.

Чтобы использовать aad-starter в этом сценарии, выполните следующие действия.

Добавьте следующие зависимости в файл pom.xml.

<dependency>
   <groupId>com.azure.spring</groupId>
   <artifactId>spring-cloud-azure-starter-active-directory</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>

Заметка

Дополнительные сведения об управлении версиями библиотеки Spring Cloud Azure с помощью списка зависимостей (BOM) см. в разделе "Начало работы" руководства разработчика Spring Cloud Azure.

Обновите файл application.yml. Задайте для свойства spring.cloud.azure.active-directory.application-type значение web_application_and_resource_serverи укажите тип авторизации для каждого клиента авторизации, как показано в следующем примере.

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       profile:
         tenant-id: <tenant>
       credential:
         client-id: <Web-API-C-client-id>
         client-secret: <Web-API-C-client-secret>
       app-id-uri: <Web-API-C-app-id-url>
       application-type: web_application_and_resource_server  # This is required.
       authorization-clients:
         graph:
           authorizationGrantType: authorization_code  # This is required.
           scopes:
             - https://graph.microsoft.com/User.Read
             - https://graph.microsoft.com/Directory.Read.All

Заметка

Значения, допустимые для tenant-id: common, organizations, consumersили идентификатор клиента. Дополнительные сведения об этих значениях см. в разделе «Использована неправильная конечная точка (учетные записи личные и организации)» в разделе ошибки AADSTS50020 — учетная запись пользователя от поставщика удостоверений не существует варендатора. Для получения информации о преобразовании приложения с одним клиентом в мультитенантное приложение см. статью в идентификаторе Microsoft Entra.

Напишите код Java для настройки нескольких экземпляров HttpSecurity.

В следующем примере кода AadWebApplicationAndResourceServerConfig содержит два цепочки фильтров безопасности, один для сервера ресурсов и один для веб-приложения. apiFilterChain bean имеет высокий приоритет в настройке конфигуратора безопасности сервера ресурсов. htmlFilterChain bean имеет низкий приоритет для настройки построителя безопасности веб-приложений.

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadWebApplicationAndResourceServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        http.apply(AadResourceServerHttpSecurityConfigurer.aadResourceServer())
                .and()
            // All the paths that match `/api/**`(configurable) work as the resource server. Other paths work as the web application.
            .securityMatcher("/api/**")
            .authorizeHttpRequests()
                .anyRequest().authenticated();
        return http.build();
    }

    @Bean
    public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
        // @formatter:off
        http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
                .and()
            .authorizeHttpRequests()
                .requestMatchers("/login").permitAll()
                .anyRequest().authenticated();
        // @formatter:on
        return http.build();
    }
}

Тип приложения

Свойство spring.cloud.azure.active-directory.application-type является необязательным, так как его значение можно вывести по зависимостям. Необходимо вручную задать свойство только при использовании значения web_application_and_resource_server.

Имеет зависимость: spring-security-oauth2-client Имеет зависимость: spring-security-oauth2-resource-server Допустимые значения типа приложения Значение по умолчанию
Да Нет web_application web_application
Нет Да resource_server resource_server
Да Да web_application,resource_server,
resource_server_with_obo, web_application_and_resource_server
resource_server_with_obo

Настраиваемые свойства

Начальный код Spring Boot для Идентификатора Microsoft Entra предоставляет следующие свойства:

Свойства Описание
spring.cloud.azure.active-directory.app-id-uri Используется сервером ресурсов для валидации аудитории в токене доступа. Маркер доступа действителен только в том случае, если аудитория равна <> идентификатора клиента или <URI-идентификатора приложения> описанным выше значениям.
spring.cloud.azure.active-directory.authorization-clients Схема, которая настраивает API ресурсов, которые приложение будет использовать. Каждый элемент соответствует одному ресурсу API, который будет посещать приложение. В коде Spring каждый элемент соответствует одному объекту OAuth2AuthorizedClient.
spring.cloud.azure.active-directory.authorization-client.<имя клиента>.scopes Разрешения API сервера ресурсов, которые приложение собирается получить.
spring.cloud.azure.active-directory.authorization-clients.<имя вашего клиента>.тип-разрешения-на-использование Тип клиента авторизации. Поддерживаемые типы — это authorization_code (тип по умолчанию для веб-приложения), on_behalf_of (тип по умолчанию для resource-server), client_credentials.
spring.cloud.azure.active-directory.application-type См. тип приложения.
spring.cloud.azure.active-directory.profile.environment.active-directory-endpoint Базовый универсальный код ресурса (URI) для сервера авторизации. Значение по умолчанию — https://login.microsoftonline.com/.
spring.cloud.azure.active-directory.credential.client-id Зарегистрированный идентификатор приложения в Microsoft Entra ID.
spring.cloud.azure.active-directory.credential.client-secret Секретный ключ клиента зарегистрированного приложения.
spring.cloud.azure.active-directory.user-group.use-transitive-members Используйте v1.0/me/transitiveMemberOf, чтобы получить группы, если задано значение true. В противном случае используйте /v1.0/me/memberOf.
spring.cloud.azure.active-directory.post-logout-redirect-uri URI перенаправления для публикации выхода.
spring.cloud.azure.active-directory.profile.tenant-id Идентификатор клиента Azure. Значения, допустимые для tenant-id: common, organizations, consumersили идентификатор клиента.
spring.cloud.azure.active-directory.user-group.allowed-group-names Ожидаемые группы пользователей, которым будет предоставлен доступ, если он найден в ответе на вызов Graph API MemberOf.
spring.cloud.azure.active-directory.user-name-attribute Указывает, какое утверждение будет использоваться как имя принципала.

В следующих примерах показано, как использовать эти свойства:

Пример свойства 1: Чтобы использовать Azure China 21Vianet вместо Azure Global, выполните следующий шаг.

  • Добавьте следующие свойства в файл application.yml:

    spring:
       cloud:
         azure:
           active-directory:
             enabled: true
             profile:
               environment:
                 active-directory-endpoint: https://login.partner.microsoftonline.cn
    

С помощью этого метода можно использовать облака Azure для суверенных или национальных облаков вместо общедоступного облака Azure.

Пример свойства 2: Чтобы использовать имя группы для защиты некоторых методов в веб-приложении, следует выполнить следующие шаги.

Добавьте следующее свойство в файл application.yml:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       user-group:
         allowed-groups: group1, group2

Используйте конфигурацию безопасности по умолчанию или укажите собственную конфигурацию.

Вариант 1. Используйте конфигурацию по умолчанию. С помощью этого параметра вам не нужно ничего делать. Класс DefaultAadWebSecurityConfiguration настраивается автоматически.

Вариант 2. Укажите самоопределимую конфигурацию. Чтобы предоставить конфигурацию, примените метод AadWebApplicationHttpSecurityConfigurer#aadWebApplication для HttpSecurity, как показано в следующем примере:

@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@EnableMethodSecurity
public class AadOAuth2LoginSecurityConfig {

   /**
    * Add configuration logic as needed.
    */
   @Bean
   public SecurityFilterChain htmlFilterChain(HttpSecurity http) throws Exception {
       // @formatter:off
       http.apply(AadWebApplicationHttpSecurityConfigurer.aadWebApplication())
               .and()
           .authorizeHttpRequests()
               .anyRequest().authenticated();
       // @formatter:on
       // Do some custom configuration.
       return http.build();
   }
}

Используйте заметку @PreAuthorize для защиты метода, как показано в следующем примере:

@Controller
public class RoleController {
   @GetMapping("group1")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group1')")
   public String group1() {
       return "group1 message";
   }

   @GetMapping("group2")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_group2')")
   public String group2() {
       return "group2 message";
   }

   @GetMapping("group1Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group1-id>')")
   public String group1Id() {
       return "group1Id message";
   }

   @GetMapping("group2Id")
   @ResponseBody
   @PreAuthorize("hasRole('ROLE_<group2-id>')")
   public String group2Id() {
       return "group2Id message";
   }
}

пример свойства 3: Чтобы включить поток учетных данных клиента на сервере ресурсов, взаимодействующем с другими серверами ресурсов, выполните следующие действия.

Добавьте следующее свойство в файл application.yml:

spring:
 cloud:
   azure:
     active-directory:
       enabled: true
       authorization-clients:
         webapiC:   # When authorization-grant-type is null, on behalf of flow is used by default
           authorization-grant-type: client_credentials
           scopes:
             - <Web-API-C-app-id-url>/.default

Добавьте код в приложение, аналогичное следующему примеру:

@PreAuthorize("hasAuthority('SCOPE_Obo.WebApiA.ExampleScope')")
@GetMapping("webapiA/webapiC")
public String callClientCredential() {
   String body = webClient
       .get()
       .uri(CUSTOM_LOCAL_READ_ENDPOINT)
       .attributes(clientRegistrationId("webapiC"))
       .retrieve()
       .bodyToMono(String.class)
       .block();
   LOGGER.info("Response from Client Credential: {}", body);
   return "client Credential response " + (null != body ? "success." : "failed.");
}

Дополнительные функции

Обеспечение контроля доступа с использованием ID токена в веб-приложении

Стартер поддерживает создание GrantedAuthority из утверждения roles токена идентификатора для того, чтобы разрешить использование токена идентификатора для авторизации в веб-приложении. Для создания утверждения roles и реализации управления доступом можно использовать функцию appRoles идентификатора Microsoft Entra ID.

Заметка

Утверждение roles, созданное из appRoles, украшено префиксом APPROLE_.

При использовании appRoles в качестве утверждения roles не следует настраивать атрибут группы одновременно с roles. В противном случае атрибут группы переопределит утверждение, содержащее сведения о группе, а не appRoles. В манифесте следует избегать следующей конфигурации:

"optionalClaims": {
    "idtoken": [{
        "name": "groups",
        "additionalProperties": ["emit_as_roles"]
    }]
}

Чтобы поддерживать управление доступом по токену идентификатора в веб-приложении, выполните следующие действия.

Добавьте роли приложения в приложение и назначьте их пользователям или группам. Дополнительные сведения см. в разделе «Практическое руководство по добавлению ролей приложению и их получению в маркере».

Добавьте следующую конфигурацию appRoles в манифест приложения:

 "appRoles": [
   {
     "allowedMemberTypes": [
       "User"
     ],
     "displayName": "Admin",
     "id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
     "isEnabled": true,
     "description": "Full admin access",
     "value": "Admin"
    }
 ]

Добавьте код в приложение, аналогичное следующему примеру:

@GetMapping("Admin")
@ResponseBody
@PreAuthorize("hasAuthority('APPROLE_Admin')")
public String Admin() {
   return "Admin message";
}

Устранение неполадок

Включение ведения журнала клиентов

Пакеты SDK Azure для Java предлагают согласованную историю ведения журнала для устранения неполадок и устранения ошибок приложений. Созданные журналы будут фиксировать поток приложения до достижения терминала, помогая найти основную проблему. Просмотрите вики по ведению журнала, чтобы узнать, как его включить.

Включение ведения журнала Spring

Spring позволяет всем поддерживаемым системам логирования устанавливать уровни логирования в среде Spring (например, в application.properties) с использованием конструкции logging.level.<logger-name>=<level>, где уровнем может быть TRACE, DEBUG, INFO, WARN, ERROR, FATAL или OFF. Корневой логгер можно настроить с помощью logging.level.root.

В этом примере показаны возможные параметры ведения журнала в файле application.properties:

logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR

Для получения дополнительных сведений о настройке логирования в Spring см. раздел Логирование в документации Spring.

Дальнейшие действия

Чтобы узнать больше о Spring и Azure, перейдите в центр документации Spring в Azure.