過去記事
- Azure Resource Manager - 基本+ Functions のデプロイ
- Azure Resource Manager - テンプレートの階層化(テンプレートのリンク)
- Azure API Management - エラーレスポンスのカスタマイズ
目的
-
Functions
とApi Management
のデプロイを行う - カスタマイズ性を考慮し、以下定義をテンプレートから切り出す
- Functions の環境変数定義(環境/ユーザで変更する可能性のある値のみ)
- Api Management の API 定義(Open API)
- Api Management のポリシー定義
- その他パラメータ(リソースプレフィックス、Api Management に設定するメールアドレス、組織名等)定義
ファイル構成
以下のファイル構成とする
├─ ApiManagement/
│ ├─ openapi.yml # Api Management の API 定義(Open API)
│ ├─ policies.xml # Api Management のポリシー定義
│ └─ template.json
├─ ApplicationInsights/
│ └─ template.json
├─ Functions/
│ ├─ environmentVariable.json # Functions の環境変数定義
│ └─ template.json
├─ Storage/
│ └─ template.json
│
├─ template.json # Parentテンプレート
├─ parameters.json # パラメータ定義
│
├─ package.json
├─ package-lock.json
└─ deploy.js # deploy用スクリプト
ソースコード
トップ階層
parameters.json
{
"deployVersion": "1.0",
"resourceGroup": "<resource group name>",
"prefix": "<prefix>",
"administratorEmail": "xxx@yyy.co.jp",
"organizationName": "zzz"
}
deploy.js
const fs = require('fs');
const { execSync } = require('child_process');
async function deploy() {
try {
const parameters = JSON.parse(fs.readFileSync('./parameters.json', 'utf8'));
const environmentVariable = JSON.parse(
fs.readFileSync('./Functions/environmentVariable.json', 'utf8')
);
const openApi = fs.readFileSync('./ApiManagement/openapi.yml', 'utf8');
const policies = fs.readFileSync('./ApiManagement/policies.xml', 'utf8');
const {
deployVersion,
resourceGroup,
prefix,
administratorEmail,
organizationName,
} = parameters;
const deployParameters = {
deployVersion: { value: deployVersion },
prefix: { value: prefix },
administratorEmail: { value: administratorEmail },
organizationName: { value: organizationName },
environmentVariable: { value: environmentVariable },
openApi: { value: openApi },
policies: { value: policies },
storageSpecName: { value: 'storageSpec' },
applicationInsightsSpecName: { value: 'applicationInsightsSpec' },
functionsSpecName: { value: 'functionsSpec' },
apiManagementSpecName: { value: 'apiManagementSpec' },
};
fs.writeFileSync(
'_parameters.json',
JSON.stringify(deployParameters, null, ' ')
);
// Storage テンプレートスペックのデプロイ
execSync(
`az ts create --name ${deployParameters.storageSpecName.value} --version ${deployVersion} --resource-group ${resourceGroup} --template-file "Storage/template.json" --yes`
);
// Application Insights テンプレートスペックのデプロイ
execSync(
`az ts create --name ${deployParameters.applicationInsightsSpecName.value} --version ${deployVersion} --resource-group ${resourceGroup} --template-file "ApplicationInsights/template.json" --yes`
);
// Functions テンプレートスペックのデプロイ
execSync(
`az ts create --name ${deployParameters.functionsSpecName.value} --version ${deployVersion} --resource-group ${resourceGroup} --template-file "Functions/template.json" --yes`
);
// Api Management テンプレートスペックのデプロイ
execSync(
`az ts create --name ${deployParameters.apiManagementSpecName.value} --version ${deployVersion} --resource-group ${resourceGroup} --template-file "ApiManagement/template.json" --yes`
);
// 親テンプレートのデプロイ
execSync(
`az deployment group create --name ${prefix}-deploy \
--resource-group ${resourceGroup} \
--template-file template.json \
--parameters _parameters.json`
);
} catch (err) {
console.log(err);
}
}
deploy();
template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"deployVersion": {
"type": "string",
"defaultValue": "1.0"
},
"prefix": {
"type": "string",
"defaultValue": "prefix"
},
"administratorEmail": {
"type": "string",
"defaultValue": "xxxxx@yyyyy.co.jp"
},
"organizationName": {
"type": "string",
"defaultValue": "zzzzz"
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]"
},
"environmentVariable": {
"type": "array",
"defaultValue": []
},
"openApi": {
"type": "string",
"defaultValue": ""
},
"policies": {
"type": "string",
"defaultValue": ""
},
"storageSpecName": {
"type": "string",
"defaultValue": "storageSpec"
},
"applicationInsightsSpecName": {
"type": "string",
"defaultValue": "applicationInsightsSpec"
},
"functionsSpecName": {
"type": "string",
"defaultValue": "functionsSpec"
},
"apiManagementSpecName": {
"type": "string",
"defaultValue": "apiManagementSpec"
}
},
"functions": [],
"variables": {},
"resources": [
{
"name": "StorageDeployment",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"id": "[resourceId('Microsoft.Resources/templateSpecs/versions', parameters('storageSpecName'), parameters('deployVersion'))]"
},
"parameters": {
"prefix": { "value": "[parameters('prefix')]" },
"location": { "value": "[parameters('location')]" }
}
}
},
{
"name": "ApplicationInsightsDeployment",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"properties": {
"mode": "Incremental",
"templateLink": {
"id": "[resourceId('Microsoft.Resources/templateSpecs/versions', parameters('applicationInsightsSpecName'), parameters('deployVersion'))]"
},
"parameters": {
"prefix": { "value": "[parameters('prefix')]" },
"location": { "value": "[parameters('location')]" }
}
}
},
{
"name": "FunctionsDeployment",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', 'StorageDeployment')]",
"[resourceId('Microsoft.Resources/deployments', 'ApplicationInsightsDeployment')]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"id": "[resourceId('Microsoft.Resources/templateSpecs/versions', parameters('functionsSpecName'), parameters('deployVersion'))]"
},
"parameters": {
"prefix": { "value": "[parameters('prefix')]" },
"location": { "value": "[parameters('location')]" },
"storageConnectionString": {
"value": "[reference('StorageDeployment').outputs.storageConnectionString.value]"
},
"applicationInsightsInstrumentationKey": {
"value": "[reference('ApplicationInsightsDeployment').outputs.applicationInsightsInstrumentationKey.value]"
},
"environmentVariable": {
"value": "[parameters('environmentVariable')]"
}
}
}
},
{
"name": "ApiManagementDeployment",
"type": "Microsoft.Resources/deployments",
"apiVersion": "2021-04-01",
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', 'StorageDeployment')]",
"[resourceId('Microsoft.Resources/deployments', 'ApplicationInsightsDeployment')]",
"[resourceId('Microsoft.Resources/deployments', 'FunctionsDeployment')]"
],
"properties": {
"mode": "Incremental",
"templateLink": {
"id": "[resourceId('Microsoft.Resources/templateSpecs/versions', parameters('apiManagementSpecName'), parameters('deployVersion'))]"
},
"parameters": {
"prefix": { "value": "[parameters('prefix')]" },
"administratorEmail": {
"value": "[parameters('administratorEmail')]"
},
"organizationName": { "value": "[parameters('organizationName')]" },
"location": { "value": "[parameters('location')]" },
"openApi": { "value": "[parameters('openApi')]" },
"policies": { "value": "[parameters('policies')]" },
"functionsName": {
"value": "[reference('FunctionsDeployment').outputs.functionsName.value]"
},
"functionsResourceId": {
"value": "[reference('FunctionsDeployment').outputs.functionsResourceId.value]"
},
"functionsKey": {
"value": "[reference('FunctionsDeployment').outputs.functionsKey.value]"
}
}
}
}
],
"outputs": {}
}
Storage
template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string"
},
"location": {
"type": "string"
},
"storageAccountType": {
"type": "string",
"defaultValue": "Standard_LRS"
}
},
"functions": [],
"variables": {
"storageAccountName": "[concat(parameters('prefix'), 'storage')]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2021-04-01",
"kind": "Storage",
"name": "[variables('storageAccountName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('storageAccountType')]"
}
}
],
"outputs": {
"storageAccountName": {
"type": "string",
"value": "[variables('storageAccountName')]"
},
"storageConnectionString": {
"type": "string",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value, ';EndpointSuffix=', environment().suffixes.storage)]"
}
}
}
ApplicationInsights
template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string"
},
"location": {
"type": "string"
}
},
"functions": [],
"variables": {
"applicationInsightsName": "[concat(parameters('prefix'), '-Application-insights')]"
},
"resources": [
{
"type": "microsoft.insights/components",
"apiVersion": "2020-02-02",
"name": "[variables('applicationInsightsName')]",
"location": "[parameters('location')]",
"kind": "web",
"tags": {
"[concat('hidden-link:', resourceId('Microsoft.Web/sites', variables('applicationInsightsName')))]": "Resource"
},
"properties": {
"Application_Type": "web",
"ApplicationId": "[variables('applicationInsightsName')]"
}
}
],
"outputs": {
"applicationInsightsName": {
"type": "string",
"value": "[variables('applicationInsightsName')]"
},
"applicationInsightsInstrumentationKey": {
"type": "string",
"value": "[reference(variables('applicationInsightsName')).InstrumentationKey]"
}
}
}
Functions
environmentVariable.json
[
{
"name": "value-name-1",
"value": "value-1"
},
{
"name": "value-name-2",
"value": "value-2"
}
]
template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string"
},
"location": {
"type": "string"
},
"storageConnectionString": {
"type": "string"
},
"applicationInsightsInstrumentationKey": {
"type": "string"
},
"environmentVariable": {
"type": "array"
},
"functionsName": {
"type": "string",
"defaultValue": "[concat(parameters('prefix'), '-functions-app')]"
},
"hostingPlanName": {
"type": "string",
"defaultValue": "[concat(parameters('functionsName'), '-hosting-plan')]"
}
},
"functions": [],
"variables": {
"appSettings": [
{
"name": "APPINSIGHTS_INSTRUMENTATIONKEY",
"value": "[parameters('applicationInsightsInstrumentationKey')]"
},
{
"name": "AzureWebJobsDashboard",
"value": "[parameters('storageConnectionString')]"
},
{
"name": "AzureWebJobsStorage",
"value": "[parameters('storageConnectionString')]"
},
{
"name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING",
"value": "[parameters('storageConnectionString')]"
},
{
"name": "FUNCTIONS_EXTENSION_VERSION",
"value": "~4"
},
{
"name": "FUNCTIONS_WORKER_RUNTIME",
"value": "node"
},
{
"name": "WEBSITE_CONTENTSHARE",
"value": "[toLower('functionName')]"
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "~14"
}
]
},
"resources": [
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2020-12-01",
"name": "[parameters('hostingPlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Y1",
"tier": "Dynamic"
},
"properties": {
"name": "[parameters('hostingPlanName')]"
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2020-12-01",
"kind": "functionapp",
"name": "[parameters('functionsName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
],
"properties": {
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]",
"siteConfig": {
"appSettings": "[concat(variables('appSettings'), parameters('environmentVariable'))]"
}
}
}
],
"outputs": {
"functionsName": {
"type": "string",
"value": "[parameters('functionsName')]"
},
"functionsResourceId": {
"type": "string",
"value": "[concat('https://management.azure.com', resourceId('Microsoft.Web/sites', parameters('functionsName')))]"
},
"functionsKey": {
"type": "string",
"value": "[listkeys(concat(resourceId('Microsoft.Web/sites', parameters('functionsName')), '/host/default/'),'2021-02-01').functionKeys.default]"
}
}
}
ApiManagement
openapi.yml
openapi: 3.0.1
info:
title: APIs
description: ''
version: '1.0'
paths:
/HttpTrigger1:
get:
summary: HttpTrigger1
operationId: HttpTrigger1
responses:
'200':
description: null
components:
securitySchemes:
apiKeyHeader:
type: apiKey
name: Ocp-Apim-Subscription-Key
in: header
apiKeyQuery:
type: apiKey
name: subscription-key
in: query
security:
- apiKeyHeader: []
- apiKeyQuery: []
policies.xml
<policies>
<inbound>
<base />
<rate-limit calls="30" renewal-period="1" remaining-calls-variable-name="remainingCallsPerSubscription"/>
<set-backend-service backend-id="backend" />
</inbound>
<backend>
<forward-request timeout="30" fail-on-error-status-code="true" />
</backend>
<outbound>
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<base />
</outbound>
<on-error>
<choose>
<when condition="@(context.LastError.Reason == "HeaderNotFound" && context.Response.StatusCode == 401)">
<return-response>
<set-status code="401" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/authorization-missing"),
new JProperty("title", "Authorization information is missing."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Source == "validate-jwt")">
<return-response>
<set-status code="403" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/authorization-failed"),
new JProperty("title", "Authorization information is provided but access is forbidden."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Reason == "OperationNotFound")">
<return-response>
<set-status code="404" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/uri-not-found"),
new JProperty("title", "Resource specified by URI is not found."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Reason == "HeaderNotFound" && context.Response.StatusCode == 400)">
<return-response>
<set-status code="400" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/bad-request"),
new JProperty("title", "Bad Request."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Reason == "HeaderValueNotAllowed")">
<return-response>
<set-status code="415" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/media-type-request-unsupported"),
new JProperty("title", "Media type in request is not supported."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Reason == "RateLimitExceeded")">
<return-response>
<set-status code="429" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/api-too-many-requests"),
new JProperty("title", "Request to API is too many."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
<when condition="@(context.LastError.Reason == "Timeout")">
<return-response>
<set-status code="504" />
<set-header name="Content-Type" exists-action="override">
<value>application/json; charset=utf-8</value>
</set-header>
<set-header name="Access-Control-Allow-Origin" exists-action="skip">
<value>@("*")</value>
</set-header>
<set-body>@{
return new JObject(
new JProperty("type", "/api/common/v1/errors/gateway-time-out"),
new JProperty("title", "Backend server didn't respond in time."),
new JProperty("detail", new JObject(
new JProperty("Source", context.LastError.Source.ToString()),
new JProperty("Reason", context.LastError.Reason.ToString()),
new JProperty("Message", context.LastError.Message.ToString())
).ToString()
)
).ToString();
}</set-body>
</return-response>
</when>
</choose>
<base />
</on-error>
</policies>
template.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"prefix": {
"type": "string"
},
"administratorEmail": {
"type": "string"
},
"organizationName": {
"type": "string"
},
"location": {
"type": "string"
},
"openApi": {
"type": "string"
},
"policies": {
"type": "string"
},
"functionsName": {
"type": "String"
},
"functionsResourceId": {
"type": "String"
},
"functionsKey": {
"type": "String"
},
"apiManagementName": {
"type": "String",
"defaultValue": "[concat(parameters('prefix'), '-api-managment')]"
},
"apiManagementSku": {
"type": "String",
"defaultValue": "Developer"
},
"apiName": {
"type": "String",
"defaultValue": "api-1"
},
"namedValuesName": {
"type": "String",
"defaultValue": "functions-key"
},
"backendName": {
"type": "String",
"defaultValue": "backend"
},
"productName": {
"type": "String",
"defaultValue": "starter"
}
},
"functions": [],
"variables": {
"functionsUrl": "[concat('https://', parameters('functionsName'), '.azurewebsites.net/api')]",
"apiName": "[concat(parameters('apiManagementName'), '/', parameters('apiName'))]",
"policyName": "[concat(variables('apiName'), '/', 'policy')]",
"namedValuesName": "[concat(parameters('apiManagementName'), '/', parameters('namedValuesName'))]",
"backendName": "[concat(parameters('apiManagementName'), '/', parameters('backendName'))]",
"productApiName": "[concat(parameters('apiManagementName'), '/', parameters('productName'), '/', parameters('apiName'))]"
},
"resources": [
{
"type": "Microsoft.ApiManagement/service",
"apiVersion": "2021-08-01",
"name": "[parameters('apiManagementName')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('apiManagementSku')]",
"capacity": 1
},
"properties": {
"publisherEmail": "[parameters('administratorEmail')]",
"publisherName": "[parameters('organizationName')]"
}
},
{
"type": "Microsoft.ApiManagement/service/namedValues",
"apiVersion": "2021-08-01",
"name": "[variables('namedValuesName')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service', parameters('apiManagementName'))]"
],
"properties": {
"displayName": "[parameters('namedValuesName')]",
"value": "[parameters('functionsKey')]",
"secret": true
}
},
{
"type": "Microsoft.ApiManagement/service/backends",
"apiVersion": "2021-08-01",
"name": "[variables('backendName')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service/namedValues', parameters('apiManagementName'), parameters('namedValuesName'))]"
],
"properties": {
"url": "[variables('functionsUrl')]",
"protocol": "http",
"resourceId": "[parameters('functionsResourceId')]",
"credentials": {
"query": {},
"header": {
"x-functions-key": [
"{{functions-key}}"
]
}
},
"tls": {
"validateCertificateChain": false,
"validateCertificateName": false
}
}
},
{
"type": "Microsoft.ApiManagement/service/apis",
"apiVersion": "2021-08-01",
"name": "[variables('apiName')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service', parameters('apiManagementName'))]"
],
"properties": {
"apiRevision": "1",
"subscriptionRequired": true,
"protocols": [
"https"
],
"isCurrent": true,
"path": "",
"format": "openapi",
"value": "[parameters('openApi')]"
}
},
{
"type": "Microsoft.ApiManagement/service/apis/policies",
"apiVersion": "2021-08-01",
"name": "[variables('policyName')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementName'), parameters('apiName'))]",
"[resourceId('Microsoft.ApiManagement/service/backends', parameters('apiManagementName'), parameters('backendName'))]"
],
"properties": {
"format": "rawxml",
"value": "[parameters('policies')]"
}
},
{
"type": "Microsoft.ApiManagement/service/products/apis",
"apiVersion": "2021-08-01",
"name": "[variables('productApiName')]",
"dependsOn": [
"[resourceId('Microsoft.ApiManagement/service/apis', parameters('apiManagementName'), parameters('apiName'))]"
]
}
],
"outputs": {
"apiManagementName": {
"type": "string",
"value": "[parameters('apiManagementName')]"
},
"namedValues": {
"type": "Object",
"value": "[reference(parameters('namedValuesName'))]"
}
}
}
Functions の環境変数構築
環境変数のうち、環境/ユーザで変更する可能性のある値のみをテンプレートから切り出すため、
テンプレート内とテンプレート外で定義した環境変数が存在する
テンプレート外で定義した環境変数(Functions/environmentVariable.json
)の取り込み
デプロイ用スクリプト(deploy.js
)で取り込み、Parent テンプレートにパラメータとして受け渡す
(ファイル指定のほうが楽なため、パラメータは一旦 _parameters.json
として出力している)
const environmentVariable = JSON.parse(
fs.readFileSync('./Functions/environmentVariable.json', 'utf8')
);
// ...省略
// 親テンプレートのデプロイ
execSync(
`az deployment group create --name ${prefix}-deploy \
--resource-group ${resourceGroup} \
--template-file template.json \
--parameters _parameters.json`
);
テンプレート内/外で定義した環境変数の結合
テンプレート内で定義した環境変数(variables('appSettings')
)、
テンプレート外で定義した環境変数(parameters('environmentVariable')
)
をconcat
で結合する
"siteConfig": {
"appSettings": "[concat(variables('appSettings'), parameters('environmentVariable'))]"
}
他リソース(Storage
、Application Insights
)プロパティの参照
-
concat
内で配列定義はできないため、変数variables('appSettings')
として定義しておく
"siteConfig": {
"appSettings": "[concat(variables('appSettings'), parameters('environmentVariable'))]"
}
-
variables
で同テンプレート内のresources
で定義したリソースを参照できないため、
環境変数の変数(variables('appSettings')
)に含めるプロパティを持つ他リソースのテンプレートを分割する -
resourceId
によるリソース参照をparameters
、variables
で行うとエラーになることが多いため、
環境変数の変数(variables('appSettings')
)に含める他リソースのプロパティは文字列として output しておく -
output したプロパティの文字列を使用するテンプレートで文字列のパラメータとして受け取る
< ストレージ接続文字列の例 >
Storage/template.json
からストレージ接続文字列(storageConnectionString
)を文字列として output し、
Functions/template.json
で文字列のパラメータとして受け取ることでリソース参照する必要がなくなる
"outputs": {
...
"storageConnectionString": {
"type": "string",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value, ';EndpointSuffix=', environment().suffixes.storage)]"
}
}
"parameters": {
...
"storageConnectionString": {
"type": "string"
},
...
環境変数のデプロイ結果
Api Management の API 構築
API 定義( ApiManagement/openapi.yml
)、ポリシー定義(ApiManagement/policies.xml
)の取り込み
デプロイ用スクリプト(deploy.js
)で取り込み、Parent テンプレートにパラメータとして受け渡す
(ファイル指定のほうが楽なため、パラメータは一旦 _parameters.json
として出力している)
const openApi = fs.readFileSync('./ApiManagement/openapi.yml', 'utf8');
const policies = fs.readFileSync('./ApiManagement/policies.xml', 'utf8');
// ...省略
// 親テンプレートのデプロイ
execSync(
`az deployment group create --name ${prefix}-deploy \
--resource-group ${resourceGroup} \
--template-file template.json \
--parameters _parameters.json`
);
他リソース(Functions
)プロパティの参照
Functions/template.json
から API キー(functionsKey
)を文字列として output し、
ApiManagement/template.json
で文字列のパラメータとして受け取る
"outputs": {
...
"functionsKey": {
"type": "string",
"value": "[listkeys(concat(resourceId('Microsoft.Web/sites', parameters('functionsName')), '/host/default/'),'2021-02-01').functionKeys.default]"
}
}
"parameters": {
...
"functionsKey": {
"type": "String"
},
リソースのプロパティ確認
reference 関数でリソースのプロパティを参照できる
"applicationInsightsInstrumentationKey": {
"type": "string",
"value": "[reference(variables('applicationInsightsName')).InstrumentationKey]"
}
reference 関数の戻り値を Object として出力することでリソースのプロパティを確認することができる
"outputs": {
...
"namedValues": {
"type": "Object",
"value": "[reference(parameters('namedValuesName'))]"
}
}