Azure API Managementにバックエンドを接続する

⏱️約4分
シェア:

ARM Templateを使って、API ManagementにAPIを追加する方法を説明します。 APIを追加し、バックエンドと接続するために、operations, policy, backendなどのリソースを使用します。 バックエンドはAzure functionsで実装されている状況を想定します。

API の定義

まず、API Management に API の定義を設定していきます。 API Management 上では個々の API はoperationsというリソースで扱われます。

Operations の定義

operations は一つの API に対応したリソースで、 API のパスやパラメータなどを定義することができます。 operations の ARM Template を見ていきましょう。

json
1{
2 "name": "[concat(parameters('serviceName'), '/', parameters('apiName'), ';rev=', parameters('apiRevision'), '/', parameters('operationName'))]",
3 "type": "Microsoft.ApiManagement/service/apis/operations",
4 "apiVersion": "2018-01-01",
5 "dependsOn": [
6 "[resourceId('Microsoft.ApiManagement/service/apis', parameters('serviceName'), concat(parameters('apiName'), ';rev=', parameters('apiRevision')))]",
7 "[resourceId('Microsoft.ApiManagement/service/backends', parameters('serviceName'), parameters('backendName'))]"
8 ],
9 "properties": {
10 "description": "GET Resource",
11 "templateParameters": [
12 {
13 "name": "resourceId",
14 "type": "string",
15 "values": []
16 }
17 ],
18 "responses": [],
19 "policies": null,
20 "displayName": "GetResource",
21 "method": "GET",
22 "urlTemplate": "resources/{resourceId}"
23 },
24 "resources": []
25}

前回同様に、特定のリビジョンとしてデプロイするので、名前にrev;=<apiRevision>がついている点に注意してください。 urlTemplateには url を指定します。可変なパラメータは{resourceId}のような形で表現します。なお、ここで指定したパラメータの詳細はtemplateParametersに指定します。 上の例では、resourceIdの type が string であることを定義しています。 methodには HTTP Method を指定します。

上記の例から作成される API は、GET http(s)://<api managementのfqdn>/api/v1/resources/{resourceId}となります。今回定義したのは/resources/{resourceId}の部分で、/api/v1の部分は前回のapisapiVersionSetsで定義しました。 なお、OpenAPI.yaml を import して API を定義することもできます。その方法については、別の記事で記載します。

Backends の定義

次に backends を定義します。

json
1{
2 "name": "[concat(variables('serviceName'), '/', parameters('backendName'))]",
3 "type": "Microsoft.ApiManagement/service/backends",
4 "apiVersion": "2018-01-01",
5 "dependsOn": [
6 "[resourceId('Microsoft.ApiManagement/service/properties', parameters('serviceName'), parameters('propertyName'))]"
7 ],
8 "properties": {
9 "title": null,
10 "description": "Backend of API management",
11 "url": "[concat('https://', parameters('functionsAppName'),'.azurewebsites.net/api')]",
12 "protocol": "http",
13 "credentials": {
14 "query": {
15 "code": ["[concat('{{', parameters('propertyName'), '}}')]"]
16 }
17 },
18 "resourceId": "[concat('https://management.azure.com/subscriptions/', subscription().id, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/sites/', parameters('functionsAppName'))]"
19 }
20}

backends は文字通り、API の実体となるバックエンドのサービスに相当するリソースです。今回はバックエンドとして Azure functions の HTTPTrigger を想定します。 urlには azure functions の url が指定され、resourceIdで azure functions のリソースを指定しています。この場合の前提として azure functions の HTTPTrigger の route はresources/{resourceId}になっている必要があります。

credentialsには azure functions と接続するための API Key を指定します。上記の template の例は、?code=というクエリパラメータの形式でクレデンシャルが指定されることを意味し、 propertyNameで指定される文字列が設定されます。credentials の値は{{<credential>}}というように、{}を 2 重に囲って指定する形式となります。 API Key のようなクレデンシャルは、後述のpropertiesというリソースに保存します。ここで指定したpropertyNameは、properties に保存した クレデンシャルを示す名前となります。

ちなみに、ここで azure functions の API Key を指定するためには、azure functions から Key を取得する必要があります。 ARM Template における Key の取得は Template 関数の listSecrets や listKeys を使うのが一般的ですが、azure functions の v2 に対して期待通りに動作しません1。Azure CLI 等を使って事前に取得しておくのがよいでしょう2

powershell
1$resourceId = "/subscriptions/$subscriptionid/resourceGroups/$resourceGroup/providers/Microsoft.Web/sites/$functionsAppName"
2$url = "$resourceId/host/default/listKeys?api-version=2016-03-01"
3$ret = az rest --method post --uri $url | ConvertFrom-Json
4$key = $ret.functionKeys.default

azure functions には host key と function key があり、この場合は host key を使っています。簡単に言うと、function key は関数毎の key で、host key は全ての関数に接続可能な key となります。 これで backends が定義されますが、API の backend として機能させるにはさらにこの backends を policy で指定してあげる必要があります。

policy の定義

backend と API Management を接続するために、最後に policy を設定します。 policy は API Management に様々な処理を追加することができる重要な機能です。 ここでは、その中の 1 つを使ってバックエンドとの接続を行います。

json
1{
2 "variables": {
3 "backendPolicy": "[concat('<policies>\r\n <inbound>\r\n <base />\r\n <set-backend-service id=\"apim-generated-', 'policy','\" backend-id=\"', parameters('backendName'), '\" />\r\n </inbound>\r\n <backend>\r\n <base />\r\n </backend>\r\n <outbound>\r\n <base />\r\n </outbound>\r\n <on-error>\r\n <base />\r\n </on-error>\r\n</policies>')]"
4 },
5 "resources": [
6 {
7 "name": "[concat(parameters('serviceName'), '/', parameters('apiName'), ';rev=', parameters('apiRevision'), '/', 'policy')]",
8 "type": "Microsoft.ApiManagement/service/apis/policies",
9 "apiVersion": "2018-01-01",
10 "properties": {
11 "policyContent": "[variables('backendPolicy')]",
12 "contentFormat": "xml"
13 }
14 }
15 ]
16}

policyContentに policy を xml で指定しています。backendPolicyvariablesで定義していて、xml がべた書きされているのがわかると思います。 ポイントはbackend-idで backend リソースを指定していることです。 これにより、先ほど作成した backend のリソースと、バックエンドポリシーが紐づくことになります。

properties の定義

propertiesは API Management で利用する設定値を key-value 形式で保存するためのリソースです。ここでは、API Management が backend の azure functions に接続するための API Key を保持するのに利用します。

json
1{
2 "type": "Microsoft.ApiManagement/service/properties",
3 "name": "[concat(parameters('serviceName'), '/', parameters('propertyName'))]",
4 "apiVersion": "2018-06-01-preview",
5 "scale": null,
6 "properties": {
7 "displayName": "[parameters('propertyName')]",
8 "value": "[parameters('functionHostKey')]",
9 "tags": ["key", "function"],
10 "secret": true
11 }
12}

displayName に先ほど backends で指定したのと同じプロパティの名前を指定します。 value に値を指定します。この場合は、先ほど Azure CLI で取得した azure functions の API Key を指定します。 tagsは後でプロパティを検索するときなどに使用するタグです。任意の文字列を指定しておきましょう。 secretは true にしておくことで、プロパティが暗号化され安全に保持されます。

デプロイ

それでは、今まで説明したことを踏まえて、ARM Template をデプロイしていきます。

json
1{
2 "$schema": "https://schema.management.azure.com/schemas/2018-05-01/deploymentTemplate.json#",
3 "contentVersion": "1.0.0.0",
4 "parameters": {
5 "serviceName": {
6 "type": "string",
7 "metadata": {
8 "description": "Service Name"
9 }
10 },
11 "apiName": {
12 "type": "string",
13 "metadata": {
14 "description": "API Name"
15 }
16 },
17 "apiRevision": {
18 "type": "string",
19 "metadata": {
20 "description": "API Revision"
21 }
22 },
23 "operationName": {
24 "type": "string",
25 "metadata": {
26 "description": "Operation name"
27 }
28 },
29 "backendName": {
30 "type": "string",
31 "metadata": {
32 "description": "Backend Name"
33 }
34 },
35 "functionHostKey": {
36 "type": "securestring",
37 "metadata": {
38 "description": "Host key for the functions app"
39 }
40 },
41 "functionAppName": {
42 "type": "string",
43 "metadata": {
44 "description": "Functions App Name"
45 }
46 },
47 "propertyName": {
48 "type": "string",
49 "metadata": {
50 "description": "Property Name"
51 }
52 }
53 },
54 "variables": {
55 "backendPolicy": "[concat('<policies>\r\n <inbound>\r\n <base />\r\n <set-backend-service id=\"apim-generated-', 'policy','\" backend-id=\"', parameters('backendName'), '\" />\r\n </inbound>\r\n <backend>\r\n <base />\r\n </backend>\r\n <outbound>\r\n <base />\r\n </outbound>\r\n <on-error>\r\n <base />\r\n </on-error>\r\n</policies>')]"
56 },
57 "resources": [
58 {
59 "name": "[concat(parameters('serviceName'), '/', parameters('apiName'), ';rev=', parameters('apiRevision'), '/', parameters('operationName'))]",
60 "type": "Microsoft.ApiManagement/service/apis/operations",
61 "apiVersion": "2019-01-01",
62 "dependsOn": [
63 "[resourceId('Microsoft.ApiManagement/service/backends', parameters('serviceName'), parameters('backendName'))]"
64 ],
65 "properties": {
66 "description": "GET Resource",
67 "templateParameters": [
68 {
69 "name": "resourceId",
70 "type": "string",
71 "values": []
72 }
73 ],
74 "responses": [],
75 "policies": null,
76 "displayName": "GetResource",
77 "method": "GET",
78 "urlTemplate": "resources/{resourceId}"
79 },
80 "resources": []
81 },
82 {
83 "name": "[concat(parameters('serviceName'), '/', parameters('apiName'), ';rev=', parameters('apiRevision'), '/', 'policy')]",
84 "type": "Microsoft.ApiManagement/service/apis/policies",
85 "apiVersion": "2018-01-01",
86 "properties": {
87 "policyContent": "[variables('backendPolicy')]",
88 "contentFormat": "xml"
89 }
90 },
91 {
92 "name": "[concat(parameters('serviceName'), '/', parameters('backendName'))]",
93 "type": "Microsoft.ApiManagement/service/backends",
94 "apiVersion": "2018-01-01",
95 "dependsOn": [
96 "[resourceId('Microsoft.ApiManagement/service/properties', parameters('serviceName'), parameters('propertyName'))]"
97 ],
98 "properties": {
99 "title": null,
100 "description": "Backend of API management",
101 "url": "[concat('https://', parameters('functionAppName'),'.azurewebsites.net/api')]",
102 "protocol": "http",
103 "credentials": {
104 "query": {
105 "code": ["[concat('{{', parameters('propertyName'), '}}')]"]
106 }
107 },
108 "resourceId": "[concat('https://management.azure.com/subscriptions/', subscription().id, '/resourceGroups/', resourceGroup().name, '/providers/Microsoft.Web/sites/', parameters('functionAppName'))]"
109 }
110 },
111 {
112 "type": "Microsoft.ApiManagement/service/properties",
113 "name": "[concat(parameters('serviceName'), '/', parameters('propertyName'))]",
114 "apiVersion": "2018-06-01-preview",
115 "scale": null,
116 "properties": {
117 "displayName": "[parameters('propertyName')]",
118 "value": "[parameters('functionHostKey')]",
119 "tags": ["key", "function"],
120 "secret": true
121 }
122 }
123 ]
124}

前回までに作られた サービスインスタンス に対して、上記の template を使って API をデプロイすることができました。 これで API が動くようになりましたね。

Footnotes

  1. Changes to Key Management in Functions V2

  2. Managing Azure Functions Keys (using the new ARM APIs!)

シェア:

関連記事