Key Vault

Azure Key Vaultを作成しKey Vaultのシークレットを取得してみた(ソースコード編)

今回も引き続き、Key Vaultを作成しKey Vaultのシークレットを取得する処理の実装について述べる。ここでは、具体的なサンプルプログラムのソースコードを共有する。

前提条件

下記記事のKeyVaultの作成が完了していること。

Azure Key Vaultを作成しKey Vaultのシークレットを取得してみた(ソースコード以外編)Azure Key Vaultを利用すると、パスワード等の機密情報へのアクセスをAzure Portal上のみに制限することができる。今...

作成したサンプルプログラム(App Service側)の内容

作成したサンプルプログラム(App Service側)の構成は以下の通り。
サンプルプログラムの構成_AppService
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。

pom.xmlの内容は以下の通りで、Key Vaultのライブラリの設定を追加している。

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" 
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  
  <modelVersion>4.0.0</modelVersion>  
  <parent> 
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-parent</artifactId>  
    <version>2.3.4.RELEASE</version>  
    <relativePath/>  
    <!-- lookup parent from repository --> 
  </parent>  
  <groupId>com.example</groupId>  
  <artifactId>demoAzureApp</artifactId>  
  <version>0.0.1-SNAPSHOT</version>  
  <packaging>war</packaging>  
  <name>demoAzureApp</name>  
  <description>Demo project for Spring Boot</description>  
  <properties> 
    <java.version>1.8</java.version> 
  </properties>  
  <dependencies> 
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-thymeleaf</artifactId> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-web</artifactId> 
    </dependency>
    <!-- lombokの設定 -->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <scope>provided</scope>
    </dependency>
    <!-- Key Vaultの設定 -->
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-security-keyvault-secrets</artifactId>
        <version>4.2.3</version>
    </dependency>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity</artifactId>
        <version>1.2.0</version>
    </dependency>
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure</artifactId>
        <version>1.36.3</version>
    </dependency>
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-tomcat</artifactId>  
      <scope>provided</scope> 
    </dependency>  
    <dependency> 
      <groupId>org.springframework.boot</groupId>  
      <artifactId>spring-boot-starter-test</artifactId>  
      <scope>test</scope>
    </dependency>
  </dependencies>  
  <build> 
    <plugins> 
      <plugin> 
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-maven-plugin</artifactId> 
      </plugin>  
      <plugin>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-webapp-maven-plugin</artifactId>
        <version>1.12.0</version>
        <configuration>
          <schemaVersion>v2</schemaVersion>
          <subscriptionId>(ログインユーザーのサブスクリプションID)</subscriptionId>
          <resourceGroup>azureAppDemo</resourceGroup>
          <appName>azureAppDemoService</appName>
          <pricingTier>B1</pricingTier>
          <region>japaneast</region>
          <appServicePlanName>ASP-azureAppDemo-8679</appServicePlanName>
          <appServicePlanResourceGroup>azureAppDemo</appServicePlanResourceGroup>
          <runtime>
            <os>Linux</os>
            <javaVersion>Java 8</javaVersion>
            <webContainer>Tomcat 8.5</webContainer>
          </runtime>
          <deployment>
            <resources>
              <resource>
                <directory>${project.basedir}/target</directory>
                <includes>
                  <include>*.war</include>
                </includes>
              </resource>
            </resources>
          </deployment>
        </configuration>
      </plugin>
    </plugins> 
  </build> 
</project>

また、application.propertiesの内容は以下の通りで、サービスプリンシパルを利用してKey Vaultにアクセスするための設定を追加している。

server.port = 8084
demoAzureFunc.urlBase = http://localhost:7071/api/
#demoAzureFunc.urlBase = https://azurefuncdemoapp.azurewebsites.net/api/

# Key Vaultへの設定
keyVaultClientId=cdbb5434-e455-4885-b2e6-911f78c4264b
keyVaultTenantId=f6a12dea-7649-441f-9e63-bc9e254b2500
keyVaultClientKey=(サービスプリンシパルのパスワード)
keyVaultUri=https://keypurinit.vault.azure.net/
keyVaultSecretKey=keySecret

なお、上記設定のkeyVaultClientId、keyVaultTenantId、keyVaultClientKey、keyVaultUriは、以下のサービスプリンシパル作成時のコマンド実行時に出力されたappId、tenant、password、nameの値を指定している。
サービスプリンシパルの作成

また、keyVaultSecretKeyは、以下の画面のシークレットのキー値を指定している。
シークレットのキー値



コントローラクラスの内容は以下の通りで、後述するAzure Functions側のgetKeySecret関数を呼び出す処理と、KeyVaultからシークレットを取得する処理を追加している。

package com.example.demo;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.azure.AzureEnvironment;
import com.microsoft.azure.credentials.ApplicationTokenCredentials;
import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.models.SecretBundle;

@Controller
public class DemoController {

    /** RestTemplateオブジェクト */
    @Autowired
    private RestTemplate restTemplate;

    /** ObjectMapperオブジェクト */
    @Autowired
    private ObjectMapper objectMapper;

    /** application.propertiesからdemoAzureFunc.urlBaseの値を取得 */
    @Value("${demoAzureFunc.urlBase}")
    private String demoAzureFuncBase;

    /** keyVaultのクライアントID */
    @Value("${keyVaultClientId}")
    private String keyVaultClientId;

    /** keyVaultのテナントID */
    @Value("${keyVaultTenantId}")
    private String keyVaultTenantId;

    /** keyVaultのクライアントキー */
    @Value("${keyVaultClientKey}")
    private String keyVaultClientKey;

    /** keyVaultのURI */
    @Value("${keyVaultUri}")
    private String keyVaultUri;

    /** keyVaultのシークレットのキー */
    @Value("${keyVaultSecretKey}")
    private String keyVaultSecretKey;

    @GetMapping("/")
    public String index(Model model) {
        // Azure FunctionsのgetKeySecret関数を呼び出すためのヘッダー情報を設定する
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        // Azure FunctionsのgetKeySecret関数を呼び出すための引数を設定する
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        try {
            map.add("key", "keySecret");
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        HttpEntity<MultiValueMap<String, String>> entity 
            = new HttpEntity<>(map, headers);

        // Azure FunctionsのgetKeySecret関数を呼び出す
        ResponseEntity<String> response = restTemplate.exchange(demoAzureFuncBase 
            + "getKeySecret", HttpMethod.POST, entity, String.class);

        // Azure Functionsの呼出結果のシークレットを、Modelオブジェクトに設定する
        try {
            GetKeySecretResult getKeySecretResult = objectMapper.readValue(
                response.getBody(), GetKeySecretResult.class);
            model.addAttribute("keySecretFunctions", getKeySecretResult.getSecret());
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }

        // Azure App Serviceでのシークレットを、Modelオブジェクトに設定する
        model.addAttribute("keySecretAppService", getKeySecret());
        return "index";
    }

    /**
     * KeyVaultからシークレットを取得する
     * 
     * @return シークレット値
     */
    private String getKeySecret() {
        ApplicationTokenCredentials credentials 
            = new ApplicationTokenCredentials(keyVaultClientId
              , keyVaultTenantId, keyVaultClientKey, AzureEnvironment.AZURE);
        KeyVaultClient keyVaultClient = new KeyVaultClient(credentials);
        SecretBundle bundle 
            = keyVaultClient.getSecret(keyVaultUri, keyVaultSecretKey);
        return bundle.value();
    }

}

また、Azure Functions側のgetKeySecret関数を呼び出した結果を格納するクラスは、以下の通り。

package com.example.demo;

import lombok.Data;

@Data
public class GetKeySecretResult {

    /** シークレットの値 */
    private String secret;
	
}

さらに、コンフィグクラスの内容は以下の通りで、コントローラクラスで呼び出すBean定義を行っている。

package com.example.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
public class DemoConfigBean {

    /**
     * RestTemplateオブジェクトを作成する
     * @return RestTemplateオブジェクト
     */
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
    
    /**
     * ObjectMapperオブジェクトを作成する
     * @return ObjectMapperオブジェクト
     */
    @Bean
    public ObjectMapper getObjectMapper() {
        return new ObjectMapper();
    }
}

また、HTMLファイルの内容は以下の通りで、取得したシークレットの値を表示するようにしている。

<!DOCTYPE html>
<html lang="ja" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>初期表示画面</title>
</head>
<body>
	Azure App Serviceで取得したシークレット:
         <span th:text="${keySecretAppService}">keySecretAppService</span><br/><br/>
	Azure Functionsで取得したシークレット:
         <span th:text="${keySecretFunctions}">keySecretFunctions</span>
</body>
</html>

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-key-vault-2/demoAzureApp



作成したサンプルプログラム(Azure Functions側)の内容

作成したサンプルプログラム(Azure Functions側)の構成は以下の通り。
サンプルプログラムの構成_Function
なお、上記の赤枠は、前提条件のプログラムから追加・変更したプログラムである。

<2021年4月13日 追記>
spring-cloud-function-dependenciesのバージョンは、2021年3月16日にリリースしたバージョン3.1.2を利用すると、1つのAzure Functions内に複数のファンクションを含む場合の不具合が解消できている。


その場合、Handlerクラスの継承するクラスを「AzureSpringBootRequestHandler」クラスから「FunctionInvoker」クラスに変更する。


spring-cloud-function-dependenciesの3.1.2を利用した実装サンプルは、以下の記事を参照のこと。

spring-cloud-function-dependenciesのバージョンを最新(3.1.2)にしてみたこれまでこのブログで取り上げてきたAzure Functionsのサンプルプログラムでは、spring-cloud-function-d...

pom.xmlの内容は以下の通りで、App Service側と同様に、Key Vaultのライブラリの設定を追加している。

<project xmlns="http://maven.apache.org/POM/4.0.0" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>demoAzureFunc</artifactId>
  <version>0.0.1-SNAPSHOT</version>

    <name>Hello Spring Function on Azure</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <azure.functions.maven.plugin.version>1.9.0</azure.functions.maven.plugin.version>

        <!-- customize those properties. The functionAppName should be unique across Azure -->
        <functionResourceGroup>azureAppDemo</functionResourceGroup>
        <functionAppName>azureFuncDemoApp</functionAppName>
        <functionAppServicePlan>ASP-azureAppDemo-8679</functionAppServicePlan>
        <functionPricingTier>B1</functionPricingTier>

        <functionAppRegion>japaneast</functionAppRegion>
        <stagingDirectory>${project.build.directory}/azure-functions/${functionAppName}</stagingDirectory>
        <start-class>com.example.DemoAzureFunction</start-class>
        <spring.boot.wrapper.version>1.0.25.RELEASE</spring.boot.wrapper.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-function-adapter-azure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-function-web</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Spring Boot Webの設定 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- lombokを利用するための設定 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- Key Vaultの設定 -->
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-security-keyvault-secrets</artifactId>
            <version>4.2.3</version>
        </dependency>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-identity</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.microsoft.azure</groupId>
            <artifactId>azure</artifactId>
            <version>1.36.3</version>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-function-dependencies</artifactId>
                <version>2.0.1.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>com.microsoft.azure</groupId>
                    <artifactId>azure-functions-maven-plugin</artifactId>
                    <version>${azure.functions.maven.plugin.version}</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <version>3.1.2</version>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
            </plugins>
        </pluginManagement>

        <plugins>
            <plugin>
                <groupId>com.microsoft.azure</groupId>
                <artifactId>azure-functions-maven-plugin</artifactId>
                <configuration>
                    <resourceGroup>${functionResourceGroup}</resourceGroup>
                    <appName>${functionAppName}</appName>
                    <appServicePlanName>${functionAppServicePlan}</appServicePlanName>
                    <region>${functionAppRegion}</region>
                    <pricingTier>${functionPricingTier}</pricingTier>
                    <runtime>
                    	<os>Linux</os>
                    	<javaVersion>8</javaVersion>
                    </runtime>
                    <appSettings>
                        <!-- Run Azure Function from package file by default -->
                        <property>
                            <name>WEBSITE_RUN_FROM_PACKAGE</name>
                            <value>1</value>
                        </property>
                        <property>
                            <name>FUNCTIONS_EXTENSION_VERSION</name>
                            <value>~3</value>
                        </property>
                        <property>
                            <name>FUNCTIONS_WORKER_RUNTIME</name>
                            <value>java</value>
                        </property>
                    </appSettings>
                </configuration>
                <executions>
                    <execution>
                        <id>package-functions</id>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-resources</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <overwrite>true</overwrite>
                            <outputDirectory>
                                ${project.build.directory}/azure-functions/${functionAppName}
                            </outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${project.basedir}/src/main/azure
                                    </directory>
                                    <includes>
                                        <include>**</include>
                                    </includes>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${stagingDirectory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                            <includeScope>runtime</includeScope>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!--Remove obj folder generated by .NET SDK in maven clean-->
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <filesets>
                        <fileset>
                            <directory>obj</directory>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework.boot.experimental</groupId>
                        <artifactId>spring-boot-thin-layout</artifactId>
                        <version>${spring.boot.wrapper.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

application.propertiesの内容は以下の通りで、App Service側と同様に、サービスプリンシパルを利用してKey Vaultにアクセスするための設定を追加している。

# Key Vaultへの設定
keyVaultClientId=cdbb5434-e455-4885-b2e6-911f78c4264b
keyVaultTenantId=f6a12dea-7649-441f-9e63-bc9e254b2500
keyVaultClientKey=(サービスプリンシパルのパスワード)
keyVaultUri=https://keypurinit.vault.azure.net/
keyVaultSecretKey=keySecret



サービスクラスの内容は以下の通りで、App Service側と同様に、KeyVaultからシークレットを取得する処理を追加している。

package com.example.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.example.model.GetKeySecretForm;
import com.example.model.GetKeySecretResult;
import com.microsoft.azure.AzureEnvironment;
import com.microsoft.azure.credentials.ApplicationTokenCredentials;
import com.microsoft.azure.keyvault.KeyVaultClient;
import com.microsoft.azure.keyvault.models.SecretBundle;

@Service
public class GetKeySecretService {

    /** keyVaultのクライアントID */
    @Value("${keyVaultClientId}")
    private String keyVaultClientId;

    /** keyVaultのテナントID */
    @Value("${keyVaultTenantId}")
    private String keyVaultTenantId;

    /** keyVaultのクライアントキー */
    @Value("${keyVaultClientKey}")
    private String keyVaultClientKey;

    /** keyVaultのURI */
    @Value("${keyVaultUri}")
    private String keyVaultUri;

    /** keyVaultのシークレットのキー */
    @Value("${keyVaultSecretKey}")
    private String keyVaultSecretKey;

    /**
     * 検索用Formに設定したキーにあてはまるシークレットを取得する
     * 
     * @param getKeySecretForm 検索用Form
     * @return 結果情報オブジェクト
     */
    public GetKeySecretResult getKeySecret(GetKeySecretForm getKeySecretForm) {
        GetKeySecretResult getKeySecretResult = new GetKeySecretResult();
        if (keyVaultSecretKey.equals(getKeySecretForm.getKey())) {
            // KeyVaultからシークレットを取得し設定する
            getKeySecretResult.setSecret(getKeySecret());
        }
        return getKeySecretResult;
    }

    /**
     * KeyVaultからシークレットを取得する
     * 
     * @return シークレット値
     */
    private String getKeySecret() {
        ApplicationTokenCredentials credentials 
            = new ApplicationTokenCredentials(keyVaultClientId
               , keyVaultTenantId, keyVaultClientKey, AzureEnvironment.AZURE);
        KeyVaultClient keyVaultClient = new KeyVaultClient(credentials);
        SecretBundle bundle 
            = keyVaultClient.getSecret(keyVaultUri, keyVaultSecretKey);
        return bundle.value();
    }
}

ハンドラークラスの内容は以下の通りで、Azure App Serviceのコントローラクラスから呼ばれるgetKeySecret関数である。

package com.example;

import java.util.Optional;

import org.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandler;

import com.example.model.GetKeySecretForm;
import com.example.model.GetKeySecretResult;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

public class GetKeySecretHandler 
    extends AzureSpringBootRequestHandler<GetKeySecretForm, GetKeySecretResult> {

    /**
     * HTTP要求に応じて、DemoAzureFunctionクラスのgetKeySecretメソッドを呼び出し、
     * その戻り値をボディに設定したレスポンスを返す
     * @param request リクエストオブジェクト
     * @param context コンテキストオブジェクト
     * @return レスポンスオブジェクト
     */
    @FunctionName("getKeySecret")
    public HttpResponseMessage execute(
        @HttpTrigger(name = "request"
          , methods = HttpMethod.POST, authLevel = AuthorizationLevel.ANONYMOUS) 
               HttpRequestMessage<Optional<String>> request,
            ExecutionContext context) {

        // リクエストオブジェクトからパラメータ値を取得し、検索用Formに設定する
        ObjectMapper mapper = new ObjectMapper();
        String jsonParam = request.getBody().get();
        jsonParam = jsonParam.replaceAll("\\[", "").replaceAll("\\]", "");

        GetKeySecretForm getKeySecretForm = new GetKeySecretForm();
        try {
            getKeySecretForm = mapper.readValue(jsonParam
                , new TypeReference<GetKeySecretForm>() {
            });
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }

        // handleRequestメソッド内でDemoAzureFunctionクラスのgetKeySecretメソッド
        // を呼び出し、その戻り値をボディに設定したレスポンスを、JSON形式で返す
        return request.createResponseBuilder(HttpStatus.OK)
            .body(handleRequest(getKeySecretForm, context))
            .header("Content-Type", "text/json").build();
    }
}

また、Azure Functionsのメインクラスは以下の通りで、前述のサービスクラスを呼び出していて、先ほどのハンドラークラスのhandleRequestメソッドで呼び出される。

package com.example;

import java.util.function.Function;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import com.example.model.GetKeySecretForm;
import com.example.model.GetKeySecretResult;
import com.example.service.GetKeySecretService;

@SpringBootApplication
public class DemoAzureFunction {

    /** シークレット取得サービスクラスのオブジェクト */
    @Autowired
    private GetKeySecretService getKeySecretService;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(DemoAzureFunction.class, args);
    }

    /**
     * キーに対応するシークレットを取得し結果を返却する関数
     * 
     * @return シークレット取得サービスクラスの呼出結果
     */
    @Bean
    public Function<GetKeySecretForm, GetKeySecretResult> getKeySecret() {
        return getKeySecretForm 
            -> getKeySecretService.getKeySecret(getKeySecretForm);
    }
}

さらに、Azure FunctionsのメインクラスのgetKeySecretメソッドの引数・戻り値は以下のクラスで定義している。

package com.example.model;

import lombok.Data;

@Data
public class GetKeySecretForm {

    /** キーの値 */
    private String key;
	
}
package com.example.model;

import lombok.Data;

@Data
public class GetKeySecretResult {

    /** シークレットの値 */
    private String secret;
	
}

その他のソースコード内容は、以下のサイトを参照のこと。
https://github.com/purin-it/azure/tree/master/azure-key-vault-2/demoAzureFunc

要点まとめ

  • プログラム上でKeyVaultのシークレットを取得するには、pom.xmlにKey Vaultにアクセスするためのライブラリを追加し、application.propertiesにサービスプリンシパルの設定値を追加し、KeyVaultClientクラスを使ってシークレットの取得を行えばよい。