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

この記事は1年以上前に公開されたものです。情報が古くなっている可能性があります。
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 を見ていきましょう。
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の部分は前回のapisやapiVersionSetsで定義しました。
なお、OpenAPI.yaml を import して API を定義することもできます。その方法については、別の記事で記載します。
Backends の定義
次に backends を定義します。
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。
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-Json4$key = $ret.functionKeys.defaultazure 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 つを使ってバックエンドとの接続を行います。
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 で指定しています。backendPolicyはvariablesで定義していて、xml がべた書きされているのがわかると思います。
ポイントはbackend-idで backend リソースを指定していることです。
これにより、先ほど作成した backend のリソースと、バックエンドポリシーが紐づくことになります。
properties の定義
propertiesは API Management で利用する設定値を key-value 形式で保存するためのリソースです。ここでは、API Management が backend の azure functions に接続するための API Key を保持するのに利用します。
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": true11 }12}displayName に先ほど backends で指定したのと同じプロパティの名前を指定します。
value に値を指定します。この場合は、先ほど Azure CLI で取得した azure functions の API Key を指定します。
tagsは後でプロパティを検索するときなどに使用するタグです。任意の文字列を指定しておきましょう。
secretは true にしておくことで、プロパティが暗号化され安全に保持されます。
デプロイ
それでは、今まで説明したことを踏まえて、ARM Template をデプロイしていきます。
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": true121 }122 }123 ]124}前回までに作られた サービスインスタンス に対して、上記の template を使って API をデプロイすることができました。 これで API が動くようになりましたね。