web-dev-qa-db-ja.com

ARMさまざまな環境とスロットに多くのappSettingsを持つAzure Functions用のテンプレート

ステージングと本番のデプロイメントスロットを使用する2つのAzure Functionアプリがあります。これらの2つのAzure Functionアプリは、さまざまなAPIキー、アプリケーションの動作、接続文字列などを定義するために、アプリケーション設定に約50〜key:valueペアを持っています。

これら2つのAzure Functionアプリを5つの異なる環境(CI、DEV、QA、STG、PROD)にデプロイしたいと思います。 ARMテンプレートを使用してこれらのリソースをAzureにデプロイすることは、Azure CLIよりも優れた選択です。これを実現するために、Azure DevOpsリリースパイプラインでタスクを作成します。

ARMテンプレートを保守しやすいものに分解するために、各環境にARMテンプレートパラメータファイルを作成しました。定義するプロパティの1つであるAzure関数は、 siteConfigオブジェクト です。ここで、NameValuePairオブジェクトを使用してappSettingsオブジェクトを定義します。環境ごとに、ステージとプロダクションスロットには、異なるAPIキー、接続文字列、アプリケーションの動作。デプロイメントファイルでは、プロダクションスロットとステージスロットの両方でAzure Functionアプリを作成します。デプロイメントファイルでは、appSettings NameValuePairオブジェクトを2回指定する必要があります。次に、環境ごとに5つの異なるパラメーターファイルを作成する必要があります。スロットが2つあるので、これに2を掛けます。

また、パラメーターファイルで定義されているすべてのパラメーターを、パラメーターオブジェクトの展開テンプレートファイルで定義する必要があるのも事実ですか。

パラメータファイルからNameValuePairsを持つオブジェクトの配列を渡すだけでよいので、関数アプリの上部のsiteConfig.appSettingsの下にあるデプロイメントファイルで定義されたパラメータのリスト全体を定義する必要はありませんか?

ここのドキュメンテーション は、文字列の配列または多数のkey:valueを持つ単一のオブジェクトのみを提供できることを示しています。しかし、appSettingsはオブジェクトの配列であり、各オブジェクトには3つのキーと値のペアがあります。

これは、デプロイメントファイルでリソースがどのように見えるかです。パラメーターファイルからオブジェクトの配列全体を単に参照したいのですが、ドキュメントでは、デプロイファイルの先頭ですべての50〜パラメーターを定義し、Azure CLIまたはAzure DevOpsタスク。

        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2018-11-01",
            "name": "[parameters('function-app-name')]",
            "location": "[parameters('location')]",
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "kind": "functionapp",
            "properties": {
                "enabled": true,
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]",
                "siteConfig": {
                    "appSettings": [] # I need to provide an array of objects here
                 }
            }
       }

私の不満に加えて... 5つの環境すべてと2つのスロットを持つ2つのAzure関数に対して20のパラメーターファイルを作成する必要があるとは信じられません。 ARMテンプレートと独自のアプリケーション設定を含むパラメーターファイルを使用して、すべての環境とその展開スロットに展開するより良い方法はありますか?

UPDATE:

環境固有のARMテンプレートを作成するためのさまざまな方法を組み合わせることができ、次の結果が得られましたが、いくつかの不便な問題がありました。最初に、今どこにいるかを説明し、次に設計に関連する問題を解決します。

私の配置テンプレートでは、2つのパラメーターを定義しました。はい、どうぞ:

        "deploymentEnvironment": {
            "type": "string",
            "allowedValues": [
                "CI",
                "DEV",
                "QA",
                "TRN",
                "STG",
                "PROD"
            ],
            "metadata": {
                "description": "Type of environment being deployed to. AKA \"Stage\" in release definition."
            }
        },
        "applicationSettings": {
            "type": "object",
            "metadata": {
                "description": "Application settings from function.parameters.json"
            }
        }

私のfunction.parameters.jsonの構造は次のとおりです。

{
    "$schema": "https://schema.management.Azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "applicationSettings": {
            "value": {
                "CI": {
                    "appsetting1": "",
                    "appsetting2": ""
                },
                "DEV": {
                    "appsetting1": "",
                    "appsetting2": ""            },
                "QA": {
                    "appsetting1": "",
                    "appsetting2": ""
                }
            }
        }
    }
}

環境ごとに、すべての接続文字列、APIキー、およびアプリケーション設定を配置しました。

関数アプリの本番スロットには、設定を適用する「リソース」プロパティを追加できます。関数アプリ全体のデプロイは次のとおりです。

        {
            "name": "[parameters('function-app-name')]",
            "type": "Microsoft.Web/sites",
            "apiVersion": "2018-11-01",
            "kind": "functionapp",
            "location": "[parameters('location')]",
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "properties": {
                "enabled": true,
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
            },
            "dependsOn": [
                "[resourceId('Microsoft.Insights/components/', variables('applicationInsightsName'))]",
                "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
            ],
            "resources": [
                {
                    "name": "appsettings",
                    "type": "config",
                    "apiVersion": "2018-11-01",
                    "properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
                    ]
                }
            ]
        }

次に、ステージスロットデプロイメントリソースを定義しました。ここにあります:

        {
            "type": "Microsoft.Web/sites/slots",
            "apiVersion": "2018-11-01",
            "name": "[concat(parameters('function-app-name'), '/stage')]",
            "location": "[parameters('location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
            ],
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "kind": "functionapp",
            "properties": {
                "enabled": true,
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]"
            },
            "resources": [
                {
                    "name": "appsettings",
                    "type": "config",
                    "apiVersion": "2018-11-01",
                    "properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]",
                        "[resourceId('Microsoft.Web/sites/slots/', parameters('function-app-name'), 'stage')]"
                    ]
                }
            ]
        }

このソリューションでは、環境ごとにたくさんのparameters.jsonファイルを用意する必要はありません。

問題...

Parameters.jsonですべてのアプリケーション設定を定義すると、テンプレート関数を使用して接続文字列やAzure Key Vaultの値を取得できなくなります。

これは、テンプレート機能を使用するために、アプリケーション設定の一部を配置テンプレートに移動し始めたときです。したがって、parameters.jsonファイルにAPPINSIGHTS_INSTRUMENTATIONKEYおよびその他のAzureWebJobs *アプリケーション設定を指定する代わりに、 Microsoft.Web/Sitesリソース の「プロパティ」オブジェクトに siteConfigオブジェクト を指定しましたおよび Microsoft.Web/Sites/Slotsリソース

これは本当の厄介です-デプロイメントが実行されたときに、関数appでsiteConfig.appsettings値を適用し、parameters.jsonファイルを適用したときにアプリケーション設定を削除して、設定のみを適用しましたそれらを一緒にマージする代わりにjsonから。それは大きな失望でした。 AzureCLIを使用した最初のテストでは、このコマンドを使用しましたaz functionapp config appsettings set --name $functionAppName --resource-group $resourceGroupName --settings $settingsFile --slot $slot jsonファイルに含まれていないアプリケーション設定で何が起こるかをテストし、アプリケーション設定が削除されないことに満足しています。 powershellコマンドは値を取得および設定し、適切にマージし、削除することはありません。しかし、ARM APIはこれらの名前と値のペアをすべて削除し、定義されたもののみを適用します。これは、動的なアプリケーション設定とjsonを作成するためにテンプレート関数を使用できないことを意味します静的アプリケーション設定を適用するファイル。

今のところ、私はまともなARMテンプレートのデプロイメントを行う唯一の方法は、アプリケーション設定を適用するためのsiteConfigオブジェクトまたはconfigリソースなしでリソースをデプロイし、その後Azureをフォローアップすることですアプリケーション設定をデプロイするためのCLI。AzureCLIまたはAzure DevOpsパイプラインタスクを使用してKey Vaultシークレットを取得する方法を習得できたと思いますが、すべてを1つのARM =テンプレート。

参考までに、動的に生成されたappSettingsと構成リソースを使用して、さらに多くのappsettingsを定義しようとしたときの私の配置テンプレート全体を次に示します。

{
    "$schema": "https://schema.management.Azure.com/schemas/2019-08-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "function-app-name": {
            "defaultValue": "functionappname",
            "type": "String",
            "metadata": {
                "description": "The name of the function app that you wish to create."
            }
        },
        "sku": {
            "type": "string",
            "allowedValues": [
                "S1",
                "S2",
                "S3"
            ],
            "defaultValue": "S3",
            "metadata": {
                "description": "The pricing tier for the hosting plan."
            }
        },
        "storageAccountType": {
            "type": "string",
            "defaultValue": "Standard_LRS",
            "metadata": {
                "description": "Storage Account type"
            }
        },
        "location": {
            "type": "string",
            "defaultValue": "southcentralus",
            "metadata": {
                "description": "Location for all resources."
            }
        },
        "deploymentEnvironment": {
            "type": "string",
            "allowedValues": [
                "CI",
                "DEV",
                "QA",
                "TRN",
                "STG",
                "PROD"
            ],
            "metadata": {
                "description": "Type of environment being deployed to."
            }
        },
        "applicationSettings": {
            "type": "object",
            "metadata": {
                "description": "Application settings from function.parameters.json"
            }
        }
    },
    "variables": {
        "storageAccountName": "[concat('store', uniquestring(resourceGroup().id))]",
        "appServicePlanName": "[concat('ASP-', uniquestring(resourceGroup().id))]",
        "applicationInsightsName": "[concat('appInsights-', uniquestring(resourceGroup().id))]",
        "projectName": "DV"
    },
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-04-01",
            "name": "[variables('storageAccountName')]",
            "kind": "Storage",
            "location": "[parameters('location')]",
            "sku": {
                "name": "[parameters('storageAccountType')]"
            },
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            }
        },
        {
            "name": "[variables('appServicePlanName')]",
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2019-08-01",
            "location": "[parameters('location')]",
            "properties": {
            },
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "sku": {
                "Name": "[parameters('sku')]",
                "capacity": 2
            },
            "dependsOn": [
                "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
            ]
        },
        {
            "name": "[variables('applicationInsightsName')]",
            "apiVersion": "2015-05-01",
            "type": "Microsoft.Insights/components",
            "kind": "web",
            "location": "[parameters('location')]",
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "properties": {
                "Application_Type": "web"
            }
        },
        {
            "name": "[parameters('function-app-name')]",
            "type": "Microsoft.Web/sites",
            "apiVersion": "2018-11-01",
            "kind": "functionapp",
            "location": "[parameters('location')]",
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "properties": {
                "enabled": true,
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
                "siteConfig": {
                    "appSettings": [
                        {
                            "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                            "value": "[reference(concat('Microsoft.insights/components/', variables('applicationInsightsName'))).InstrumentationKey]"
                        },
                        {
                            "name": "AzureWebJobsDashboard",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                        },
                        {
                            "name": "AzureWebJobsStorage",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                        },
                        {
                            "name": "FUNCTIONS_EXTENSION_VERSION",
                            "value": "~1"
                        }
                    ]
                }
            },
            "dependsOn": [
                "[resourceId('Microsoft.Insights/components/', variables('applicationInsightsName'))]",
                "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
                "[resourceId('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]"
            ],
            "resources": [
                {
                    "name": "appsettings",
                    "type": "config",
                    "apiVersion": "2018-11-01",
                    "properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
                    "dependsOn": [
                        "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
                    ]
                }
            ]
        },
        {
            "type": "Microsoft.Web/sites/slots",
            "apiVersion": "2018-11-01",
            "name": "[concat(parameters('function-app-name'), '/stage')]",
            "location": "[parameters('location')]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
            ],
            "tags": {
                "project": "[variables('projectName')]",
                "env": "[parameters('deploymentEnvironment')]"
            },
            "kind": "functionapp",
            "properties": {
                "enabled": true,
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
                "siteConfig": {
                    "appSettings": [
                        {
                            "name": "APPINSIGHTS_INSTRUMENTATIONKEY",
                            "value": "[reference(concat('Microsoft.insights/components/', variables('applicationInsightsName'))).InstrumentationKey]"
                        },
                        {
                            "name": "AzureWebJobsDashboard",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                        },
                        {
                            "name": "AzureWebJobsStorage",
                            "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value)]"
                        },
                        {
                            "name": "FUNCTIONS_EXTENSION_VERSION",
                            "value": "~1"
                        }
                    ]
                },
                "resources": [
                    {
                        "name": "appsettings",
                        "type": "config",
                        "apiVersion": "2018-11-01",
                        "properties": "[parameters('applicationSettings')[parameters('deploymentEnvironment')]]",
                        "dependsOn": [
                            "[resourceId('Microsoft.Web/sites/', parameters('function-app-name'))]"
                        ]
                    }
                ]
            }
        }
    ]
}

更新2:

私はgithubの問題を提起して、ARMテンプレートが各デプロイメントのすべてのアプリケーション設定を置き換えるテンプレートで問題を修正しました。 FWIW- 一部のAzureにも投票しましたフィードバック投稿

3
Anthony Klotz

申し訳ありませんが、答える時間はあまりありません。主に「何に最適な方法」に関連する質問がたくさんあります。答えは常に「依存する」です。

管理が簡単だと思うのは、siteConfigを使用してすべてのアプリ設定を設定する代わりに、Microsoft.Web/sites/configタイプのトップレベルリソースを作成できることです(後で作成できるので便利です)サイトが作成されるので、まだセットアップされていない依存関係がある場合は、構成とサイトを分離すると便利です)。

"parameters": {
  "appSettings": {
    "type": "object",
    "defaultValue": {
      "property1": "value1",
      "property2": "value2"
    }
  }
}

"resources": [
  {
    "type": "Microsoft.Web/sites",
    "apiVersion": "2018-11-01",
    "name": "[parameters('function-app-name')]",
    "location": "[parameters('location')]",
    "kind": "functionapp",
    "properties": {
      "enabled": true,
      "serverFarmId": "..."
    }
  },
  {
    "type": "Microsoft.Web/sites/config",
    "name": "[concat(parameters('function-app-name'), '/appsettings')]",
    "apiVersion": "2018-11-01",
    "properties": "[parameters('appSettings')]"
    "dependsOn": [ "[resourceId('Microsoft.Web/sites/sites', parameters('function-app-name'))]",
  }
]

上記の欠点の1つは、paramsセクションで特定の関数を使用できないため、listKeys()を使用してリソースへのキーを取得できないため、場合によってのみ、またはこの例のように便利です。同じテンプレートで作成されるアプリインサイトへの参照を追加する場合、設定をパラメーターとして渡すと、これを行うことはできません。

  {
    "type": "Microsoft.Web/sites/config",
    "name": "[concat(parameters('function-app-name'), '/appsettings')]",
    "apiVersion": "2018-11-01",
    "properties": {
      "property1": "value1",
      "property2": "value2",
      "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]"
    }
    "dependsOn": [ 
      "[resourceId('Microsoft.Web/sites/sites', parameters('function-app-name'))]",
      "[resourceId('Microsoft.insights/components', variables('appInsightsName'))]"
  }

展開時にできる限りすべてを解決する必要があります。たとえば、ストレージアカウント(たとえば)の接続文字列をテンプレートに安全に追加し、展開時にのみ解決できます。

もう1つの便利なヒントは、Key Vaultを使用して、テンプレートでcannotを解決できる安全な資格情報、APIキー、接続文字列などを格納することです。あなたはそれらが必要であると述べていますが、テンプレートのソース管理にそれらをコミットしています...まあ、それらはあまり長く秘密にされません(別のヒント、すべてが文字列型ではなくsecurestringを使用していることを確認してください。そうでない場合、ポータルはそれらを公開しますリソースグループのデプロイメントログ)。次のようなアプリ設定からキーコンテナーにアクセスできます。

"secretConnectionString": "[concat('@Microsoft.KeyVault(SecretUri=https://', variables('vaultName'), '.vault.Azure.net/secrets/my-connection-string/)')]",

ただし、上記が機能するためには、アプリケーションにボールト「vaultName」への読み取りアクセス権を与える必要があります。これは、マネージドサービスIDを使用している場合は問題ありません。

2
Daniel Morritt

この作品に答えるには:

また、パラメーターファイルで定義されているすべてのパラメーターを、パラメーターオブジェクトの展開テンプレートファイルで定義する必要があるのも事実ですか。

はい、パラメータファイル内のすべてをデプロイメントファイルで定義する必要があります。反対は真実ではありません。デプロイメントファイルで定義されているすべてのものをパラメーターファイルで定義する必要はありません。デプロイメントファイルの定義には、デフォルト値を設定できます。

"location": {
  "type": "string",
  "defaultValue": "Central US",
  "metadata": {
    "description": "Specifies the Azure location where the key vault should be created."
  }
},

または、リリースタスクでオーバーライドパラメータとしてパラメータを渡すこともできます。

0
DreadedFrost