Azure API Managementを経由してAzure Functionsを呼び出す際、サブスクリプションの指定をしないと、Azure API Managementを経由したAPI呼び出しができなくなるような設定を行うことで、アクセス制限をかけることができる。
今回は、Azure API Managementを経由してAzure Functionsを呼び出す際のサブスクリプションを必須にしてみたので、その手順を共有する。
前提条件
下記記事の実装が完了していること。

また、以下の記事のPostmanをインストール済であること。

やってみたこと
API Managementのサブスクリプション必須化
API Managementのサブスクリプションを必須化する前は、以下のように、サブスクリプションの指定がなくても、Azure API Managementを経由したAPI呼び出しが行える。
1) Azure環境でのAPI Management経由でAzure FunctionsのAPIを呼び出すために、Postmanを起動後、以下のようにメソッド・URL・リクエストボディを設定し、「Send」ボタンを押下する。
 
2) 指定したAPIが呼び出され、以下のように、レスポンスが画面下に表示されることが確認できる。
 
 なお、このとき指定するAPIの「https://azureapipurinit.azure-api.net/azureFuncDemoApp」の部分は、Azure Portal上のAPI Managementの「BaseURL」から確認できる。
 
また、API Managementのサブスクリプションの必須化は、Azure Portal上で行える。その手順は、以下の通り。
3) Azure Portalにログインし、作成済のAPI Managementの「API」メニューから「azureFuncDemoApp」を選択後、「Settings」タブを押下する。ここで「Subscription required」のチェックを入れ、「Save」ボタンを押下する。
 
4) 保存が完了すると、以下のように、右上に完了メッセージが表示される。
 
5) 再度Azure環境でのAPI Management経由でAzure FunctionsのAPIを呼び出すために、以下のようにメソッド・URL・リクエストボディを設定し、「Send」ボタンを押下する。
 
6) サブスクリプションの指定が必須になるため、以下のように、HTTP 401エラーが発生することが確認できる。
 
 
 
サブスクリプションの作成
サブスクリプションの作成は、Azure Portal上で行える。その手順は、以下の通り。
1) Azure Portalにログインし、作成済のAPI Managementの「サブスクリプション」メニューを選択する。
 
3) 以下のように、右側にサブスクリプションの追加画面が表示されることが確認できる。
 
4) 「スコープ(範囲)」は、「すべてのAPI」「API」「製品」が選択できるが、ここでは「API」を指定する。
 
5) スコープを「API」とした場合は、対象のAPIを指定する必要があるため、デプロイ済のAzure Functionsを選択する。
 
6) その他、任意の名前(必須)と表示名を指定し、「保存」ボタンを押下する。
 
7) 以下のように、指定した表示名「apiSubscription」のサブスクリプションが作成されたことが確認できる。
 
8) サブスクリプションの「主キー」「2次キー」を表示するには、コンテキストメニューを表示し、「キーの表示/非表示」を選択する。
 
9) 以下のように、主キー・2次キーの値が表示されることが確認できる。なお、キー右のコピーマークを押下すると、キー値のコピーが行える。
 
10) Azure環境でのAPI Management経由でAzure FunctionsのAPIを呼び出すために、Postmanを起動後、以下のようにヘッダーに、キーに「Ocp-Apim-Subscription-Key」、値にサブスクリプションの主キーをもつ値を追加した上で、「Send」ボタンを押下する。
 
11) 指定したAPIが呼び出され、以下のように、レスポンスが画面下に表示されることが確認できる。
 
 なお、このときのリクエストボディの値は以下の通りで、特に変更していない。
 
 
 
作成したサンプルプログラム(App Service側)の内容
修正したサンプルプログラム(App Service側)の構成は以下の通り。
 
 なお、上記の赤枠は、前提条件のプログラムから変更したプログラムである。また、Azure Functions側のソースコードは修正していない。
コントローラクラスの内容は以下の通りで、callFunctionApi関数の呼び出し時に、ヘッダーに、キーに「Ocp-Apim-Subscription-Key」、値にサブスクリプションの主キーをもつ値を追加している。
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.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
@Controller
public class DemoController {
    /** RestTemplateオブジェクト */
    @Autowired
    private RestTemplate restTemplate;
    /** ObjectMapperオブジェクト */
    @Autowired
    private ObjectMapper objectMapper;
    /** application.propertiesからdemoAzureFunc.urlBaseの値を取得 */
    @Value("${demoAzureFunc.urlBase}")
    private String demoAzureFuncBase;
    /**
     * メイン画面を初期表示する.
     * @return メイン画面
     */
    @GetMapping("/")
    public String index() {
        return "main";
    }
    /**
     * Azure FunctionsのcallFunctionApi関数呼出処理.
     * @param model Modelオブジェクト
     * @return 次画面
     */
    @RequestMapping("/callFunction")
    public String callFunction(Model model) {
        // Azure FunctionsのcallFunctionApi関数を呼び出すためのヘッダー情報を設定する
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        
        // Azure API Managementのサブスクリプションをヘッダーに追加する
        headers.add("Ocp-Apim-Subscription-Key", "(サブスクリプションの主キーの値)");
        // Azure FunctionsのcallFunctionApi関数を呼び出すための引数を設定する
        MultiValueMap<String, Object> map 
            = new LinkedMultiValueMap<>();
        try {
            map.add("param", "DemoController callFunction calling.");
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        HttpEntity<MultiValueMap<String, Object>> entity 
            = new HttpEntity<>(map, headers);
        // Azure FunctionsのcallFunctionApi関数を呼び出す
        ResponseEntity<String> response = restTemplate.exchange(
            demoAzureFuncBase + "callFunctionApi", HttpMethod.POST,
            entity, String.class);
        // callFunctionApi関数の呼出結果を設定する
        SearchResult searchResult = null;
        try {
            searchResult = objectMapper.readValue(
                response.getBody(), SearchResult.class);
            if (searchResult != null) {
                model.addAttribute("result", searchResult.getResult());
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        return "next";
    }
    /**
     * メイン画面に戻る処理.
     * @return メイン画面
     */
    @PostMapping("/backToMain")
    public String backToMain() {
        return "main";
    }
    
    /**
     * エラー画面に遷移する処理.
     * @return エラー画面
     */
    @RequestMapping("/toError")
    public String toError() {
        return "error";
    }
}その他のソースコード内容は、以下のサイトを参照のこと。
 https://github.com/purin-it/azure/tree/master/api-management-need-subscription/demoAzureApp
 
 
サンプルプログラムの実行結果
修正したサンプルプログラムの実行結果は、以下の通り。
1) Azure App ServiceのURL「https://azureappdemoservice.azurewebsites.net/」とアクセスすると以下の画面が表示されるため、「ファンクション呼び出し」ボタンを押下する。
 
 なお、上記URLは、下記Azure App ServiceのURLから確認できる。
 
2) callFunctionApi関数が呼び出され、以下の画面に遷移する。その後「戻る」ボタンを押下する。
 
 なお、App Serviceのapplication.propertiesを以下の設定にした状態で、Azure上に、Azure App Service, Azure Functionsのプログラムをデプロイし、実行している。
 
要点まとめ
- Azure API Managementを経由してAzure Functionsを呼び出す際、サブスクリプションの指定をしないと、Azure API Managementを経由したAPI呼び出しができなくなるような設定を行うことで、アクセス制限をかけることができる。
- サブスクリプションの指定は、ヘッダーに、キーに「Ocp-Apim-Subscription-Key」、値にサブスクリプションの主キーをもつ値を追加することで行える。







