LoginSignup
0
1

More than 1 year has passed since last update.

Azure Resource Manager - テンプレート間のパラメータ受け渡し

Posted at

過去記事

目的

  • FunctionsApi 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
parameters.json
{
  "deployVersion": "1.0",
  "resourceGroup": "<resource group name>",
  "prefix": "<prefix>",
  "administratorEmail": "xxx@yyy.co.jp",
  "organizationName": "zzz"
}
deploy.js
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
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
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
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
environmentVariable.json
[
  {
    "name": "value-name-1",
    "value": "value-1"
  },
  {
    "name": "value-name-2",
    "value": "value-2"
  }
]

template.json
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.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.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
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 として出力している)

deploy.js
  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で結合する

Functions/template.json
  "siteConfig": {
    "appSettings": "[concat(variables('appSettings'), parameters('environmentVariable'))]"
  }

他リソース(StorageApplication Insights)プロパティの参照

  • concat内で配列定義はできないため、変数variables('appSettings')として定義しておく
Functions/template.json
  "siteConfig": {
    "appSettings": "[concat(variables('appSettings'), parameters('environmentVariable'))]"
  }
  • variables で同テンプレート内の resources で定義したリソースを参照できないため、
    環境変数の変数(variables('appSettings'))に含めるプロパティを持つ他リソースのテンプレートを分割する

  • resourceId によるリソース参照を parametersvariables で行うとエラーになることが多いため、
    環境変数の変数(variables('appSettings'))に含める他リソースのプロパティは文字列として output しておく

  • output したプロパティの文字列を使用するテンプレートで文字列のパラメータとして受け取る

< ストレージ接続文字列の例 >

Storage/template.json からストレージ接続文字列(storageConnectionString)を文字列として output し、
Functions/template.json で文字列のパラメータとして受け取ることでリソース参照する必要がなくなる

Storage/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)]"
    }
  }
Functions/template.json
  "parameters": {
    ...
    "storageConnectionString": {
      "type": "string"
    },
    ...

環境変数のデプロイ結果

image.png

Api Management の API 構築

API 定義( ApiManagement/openapi.yml)、ポリシー定義(ApiManagement/policies.xml)の取り込み

デプロイ用スクリプト(deploy.js)で取り込み、Parent テンプレートにパラメータとして受け渡す
(ファイル指定のほうが楽なため、パラメータは一旦 _parameters.json として出力している)

deploy.js
  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 で文字列のパラメータとして受け取る

Functions/template.json
  "outputs": {
    ...
    "functionsKey": {
      "type": "string",
      "value": "[listkeys(concat(resourceId('Microsoft.Web/sites', parameters('functionsName')), '/host/default/'),'2021-02-01').functionKeys.default]"
    }
  }
ApiManagement/template.json
  "parameters": {
    ...
    "functionsKey": {
      "type": "String"
    },

リソースのプロパティ確認

reference 関数でリソースのプロパティを参照できる

ApplicationInsights/template.json
    "applicationInsightsInstrumentationKey": {
      "type": "string",
      "value": "[reference(variables('applicationInsightsName')).InstrumentationKey]"
    }

reference 関数の戻り値を Object として出力することでリソースのプロパティを確認することができる

ApiManagement/template.json
  "outputs": {
    ...
    "namedValues": {
      "type": "Object",
      "value": "[reference(parameters('namedValuesName'))]"
    }
  }

出力結果
2022-05-04-13-25-00.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1