다음을 통해 공유


빠른 시작: ARM 템플릿에서 Azure Functions 리소스 만들기 및 배포

이 문서에서는 ARM 템플릿(Azure Resource Manager 템플릿)을 사용하여 필요한 Azure Resource와 함께 Azure의 Flex 사용량 플랜에 따라 함수 앱을 만듭니다. 함수 앱은 함수 코드 실행을 위한 서버리스 실행 컨텍스트를 제공합니다. 이 앱은 관리 ID와 함께 Microsoft Entra ID를 사용하여 다른 Azure 리소스에 연결합니다.

이 빠른 시작을 완료하면 Azure 계정에서 USD 센트 이하의 작은 비용이 발생합니다.

Azure Resource Manager 템플릿은 프로젝트에 대한 인프라 및 구성을 정의하는 JSON(JavaScript Object Notation) 파일입니다. 이 템플릿은 선언적 구문을 사용합니다. 배포를 만들기 위한 프로그래밍 명령의 시퀀스를 작성하지 않고 의도하는 배포를 설명합니다.

환경이 필수 구성 요소를 충족하고 ARM 템플릿 사용에 익숙한 경우 Azure에 배포 단추를 선택합니다. 그러면 Azure Portal에서 템플릿이 열립니다.

Resource Manager 템플릿을 Azure에 배포하는 단추

함수 앱을 만든 후 해당 앱에 Azure Functions 프로젝트 코드를 배포할 수 있습니다. 최종 코드 배포 단계는 이 빠른 시작 문서의 범위를 벗어납니다.

필수 구성 요소

Azure 계정

시작하기 전에 활성 구독이 포함된 Azure 계정이 있어야 합니다. 체험 계정을 만듭니다.

템플릿 검토

이 빠른 시작에서 사용되는 템플릿은 Azure 빠른 시작 템플릿에서 나온 것입니다.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.33.93.31351",
      "templateHash": "7223343042960867068"
    }
  },
  "parameters": {
    "___location": {
      "type": "string",
      "defaultValue": "[resourceGroup().___location]",
      "minLength": 1,
      "metadata": {
        "description": "Primary region for all Azure resources."
      }
    },
    "functionAppRuntime": {
      "type": "string",
      "defaultValue": "dotnet-isolated",
      "allowedValues": [
        "dotnet-isolated",
        "python",
        "java",
        "node",
        "powerShell"
      ],
      "metadata": {
        "description": "Language runtime used by the function app."
      }
    },
    "functionAppRuntimeVersion": {
      "type": "string",
      "defaultValue": "8.0",
      "allowedValues": [
        "3.10",
        "3.11",
        "7.4",
        "8.0",
        "9.0",
        "10",
        "11",
        "17",
        "20"
      ],
      "metadata": {
        "description": "Target language version used by the function app."
      }
    },
    "maximumInstanceCount": {
      "type": "int",
      "defaultValue": 100,
      "minValue": 40,
      "maxValue": 1000,
      "metadata": {
        "description": "The maximum scale-out instance count limit for the app."
      }
    },
    "instanceMemoryMB": {
      "type": "int",
      "defaultValue": 2048,
      "allowedValues": [
        2048,
        4096
      ],
      "metadata": {
        "description": "The memory size of instances used by the app."
      }
    },
    "resourceToken": {
      "type": "string",
      "defaultValue": "[toLower(uniqueString(subscription().id, parameters('___location')))]",
      "minLength": 3,
      "metadata": {
        "description": "A unique token used for resource name generation."
      }
    },
    "appName": {
      "type": "string",
      "defaultValue": "[format('func-{0}', parameters('resourceToken'))]",
      "metadata": {
        "description": "A globally unigue name for your deployed function app."
      }
    }
  },
  "variables": {
    "deploymentStorageContainerName": "[format('app-package-{0}-{1}', take(parameters('appName'), 32), take(parameters('resourceToken'), 7))]",
    "storageAccountAllowSharedKeyAccess": false,
    "storageBlobDataOwnerRoleId": "b7e6dc6d-f1e8-4753-8033-0f276bb0955b",
    "storageBlobDataContributorRoleId": "ba92f5b4-2d11-453d-a403-e96b0029c9fe",
    "storageQueueDataContributorId": "974c5e8b-45b9-4653-ba55-5f855dd0fb88",
    "storageTableDataContributorId": "0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3",
    "monitoringMetricsPublisherId": "3913510d-42f4-4e42-8a64-420c390055eb"
  },
  "resources": [
    {
      "type": "Microsoft.Storage/storageAccounts/blobServices/containers",
      "apiVersion": "2023-05-01",
      "name": "[format('{0}/{1}/{2}', format('st{0}', parameters('resourceToken')), 'default', variables('deploymentStorageContainerName'))]",
      "properties": {
        "publicAccess": "None"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts/blobServices', format('st{0}', parameters('resourceToken')), 'default')]"
      ]
    },
    {
      "type": "Microsoft.Storage/storageAccounts/blobServices",
      "apiVersion": "2023-05-01",
      "name": "[format('{0}/{1}', format('st{0}', parameters('resourceToken')), 'default')]",
      "properties": {
        "deleteRetentionPolicy": {}
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Web/sites/config",
      "apiVersion": "2024-04-01",
      "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]",
      "properties": {
        "AzureWebJobsStorage__accountName": "[format('st{0}', parameters('resourceToken'))]",
        "AzureWebJobsStorage__credential": "managedidentity",
        "AzureWebJobsStorage__clientId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').clientId]",
        "APPLICATIONINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.Insights/components', format('appi-{0}', parameters('resourceToken'))), '2020-02-02').InstrumentationKey]",
        "APPLICATIONINSIGHTS_AUTHENTICATION_STRING": "[format('ClientId={0};Authorization=AAD', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').clientId)]"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Insights/components', format('appi-{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.Web/sites', parameters('appName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.OperationalInsights/workspaces",
      "apiVersion": "2023-09-01",
      "name": "[format('log-{0}', parameters('resourceToken'))]",
      "___location": "[parameters('___location')]",
      "properties": {
        "retentionInDays": 30,
        "features": {
          "searchVersion": 1
        },
        "sku": {
          "name": "PerGB2018"
        }
      }
    },
    {
      "type": "Microsoft.Insights/components",
      "apiVersion": "2020-02-02",
      "name": "[format('appi-{0}', parameters('resourceToken'))]",
      "___location": "[parameters('___location')]",
      "kind": "web",
      "properties": {
        "Application_Type": "web",
        "WorkspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', format('log-{0}', parameters('resourceToken')))]",
        "DisableLocalAuth": true
      },
      "dependsOn": [
        "[resourceId('Microsoft.OperationalInsights/workspaces', format('log-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2023-05-01",
      "name": "[format('st{0}', parameters('resourceToken'))]",
      "___location": "[parameters('___location')]",
      "kind": "StorageV2",
      "sku": {
        "name": "Standard_LRS"
      },
      "properties": {
        "accessTier": "Hot",
        "allowBlobPublicAccess": false,
        "allowSharedKeyAccess": "[variables('storageAccountAllowSharedKeyAccess')]",
        "dnsEndpointType": "Standard",
        "minimumTlsVersion": "TLS1_2",
        "networkAcls": {
          "bypass": "AzureServices",
          "defaultAction": "Allow"
        },
        "publicNetworkAccess": "Enabled"
      }
    },
    {
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities",
      "apiVersion": "2023-01-31",
      "name": "[format('uai-data-owner-{0}', parameters('resourceToken'))]",
      "___location": "[parameters('___location')]"
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2022-04-01",
      "scope": "[format('Microsoft.Storage/storageAccounts/{0}', format('st{0}', parameters('resourceToken')))]",
      "name": "[guid(subscription().id, resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken'))), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), 'Storage Blob Data Owner')]",
      "properties": {
        "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('storageBlobDataOwnerRoleId'))]",
        "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').principalId]",
        "principalType": "ServicePrincipal"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2022-04-01",
      "scope": "[format('Microsoft.Storage/storageAccounts/{0}', format('st{0}', parameters('resourceToken')))]",
      "name": "[guid(subscription().id, resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken'))), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), 'Storage Blob Data Contributor')]",
      "properties": {
        "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('storageBlobDataContributorRoleId'))]",
        "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').principalId]",
        "principalType": "ServicePrincipal"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2022-04-01",
      "scope": "[format('Microsoft.Storage/storageAccounts/{0}', format('st{0}', parameters('resourceToken')))]",
      "name": "[guid(subscription().id, resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken'))), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), 'Storage Queue Data Contributor')]",
      "properties": {
        "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('storageQueueDataContributorId'))]",
        "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').principalId]",
        "principalType": "ServicePrincipal"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2022-04-01",
      "scope": "[format('Microsoft.Storage/storageAccounts/{0}', format('st{0}', parameters('resourceToken')))]",
      "name": "[guid(subscription().id, resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken'))), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), 'Storage Table Data Contributor')]",
      "properties": {
        "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('storageTableDataContributorId'))]",
        "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').principalId]",
        "principalType": "ServicePrincipal"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Authorization/roleAssignments",
      "apiVersion": "2022-04-01",
      "scope": "[format('Microsoft.Insights/components/{0}', format('appi-{0}', parameters('resourceToken')))]",
      "name": "[guid(subscription().id, resourceId('Microsoft.Insights/components', format('appi-{0}', parameters('resourceToken'))), resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), 'Monitoring Metrics Publisher')]",
      "properties": {
        "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', variables('monitoringMetricsPublisherId'))]",
        "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))), '2023-01-31').principalId]",
        "principalType": "ServicePrincipal"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Insights/components', format('appi-{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    },
    {
      "type": "Microsoft.Web/serverfarms",
      "apiVersion": "2024-04-01",
      "name": "[format('plan-{0}', parameters('resourceToken'))]",
      "___location": "[parameters('___location')]",
      "kind": "functionapp",
      "sku": {
        "tier": "FlexConsumption",
        "name": "FC1"
      },
      "properties": {
        "reserved": true
      }
    },
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2024-04-01",
      "name": "[parameters('appName')]",
      "___location": "[parameters('___location')]",
      "kind": "functionapp,linux",
      "identity": {
        "type": "UserAssigned",
        "userAssignedIdentities": {
          "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken'))))]": {}
        }
      },
      "properties": {
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', format('plan-{0}', parameters('resourceToken')))]",
        "httpsOnly": true,
        "siteConfig": {
          "minTlsVersion": "1.2"
        },
        "functionAppConfig": {
          "deployment": {
            "storage": {
              "type": "blobContainer",
              "value": "[format('{0}{1}', reference(resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken'))), '2023-05-01').primaryEndpoints.blob, variables('deploymentStorageContainerName'))]",
              "authentication": {
                "type": "UserAssignedIdentity",
                "userAssignedIdentityResourceId": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
              }
            }
          },
          "scaleAndConcurrency": {
            "maximumInstanceCount": "[parameters('maximumInstanceCount')]",
            "instanceMemoryMB": "[parameters('instanceMemoryMB')]"
          },
          "runtime": {
            "name": "[parameters('functionAppRuntime')]",
            "version": "[parameters('functionAppRuntimeVersion')]"
          }
        }
      },
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', format('plan-{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.Storage/storageAccounts', format('st{0}', parameters('resourceToken')))]",
        "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('uai-data-owner-{0}', parameters('resourceToken')))]"
      ]
    }
  ]
}

이 템플릿은 Azure 서비스에 안전하게 연결되는 함수 앱에 필요한 다음과 같은 Azure 리소스를 만듭니다.

배포 고려 사항:

  • 스토리지 계정은 애플리케이션 코드 배포 패키지를 포함한 중요한 앱 데이터를 저장하는 데 사용됩니다. 이 배포에서는 Microsoft Entra ID 인증 및 관리 ID를 사용하여 액세스하는 스토리지 계정이 만들어집니다. ID 액세스는 최소 권한 기준으로 부여됩니다.
  • Bicep 파일은 격리된 프로세스에서 .NET 8을 사용하는 C# 앱을 만드는 것이 기본 설정입니다. 다른 언어의 경우 functionAppRuntimefunctionAppRuntimeVersion 매개 변수를 사용하여 앱을 실행할 특정 언어와 버전을 지정합니다. 문서의 상단에서 프로그래밍 언어를 선택합니다.

템플릿 배포

이 스크립트는 Azure Cloud Shell에서 디자인 및 테스트되었습니다. 시도를 선택하여 브라우저에서 바로 Cloud Shell 인스턴스를 엽니다. 메시지가 표시되면 Flex 사용량 플랜을 지원하는 지역 이름(예: eastus 또는 northeurope)을 입력합니다.

read -p "Enter a supported Azure region: " ___location &&
resourceGroupName=exampleRG &&
templateUri="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-flex-managed-identities/azuredeploy.json" &&
az group create --name $resourceGroupName --___location "$___location" &&
az deployment group create --resource-group $resourceGroupName --template-uri  $templateUri --parameters functionAppRuntime=dotnet-isolated functionAppRuntimeVersion=8.0 &&
echo "Press [ENTER] to continue ..." &&
read
read -p "Enter a supported Azure region: " ___location &&
resourceGroupName=exampleRG &&
templateUri="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-flex-managed-identities/azuredeploy.json" &&
az group create --name $resourceGroupName --___location "$___location" &&
az deployment group create --resource-group $resourceGroupName --template-uri  $templateUri --parameters functionAppRuntime=java functionAppRuntimeVersion=17 &&
echo "Press [ENTER] to continue ..." &&
read
read -p "Enter a supported Azure region: " ___location &&
resourceGroupName=exampleRG &&
templateUri="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-flex-managed-identities/azuredeploy.json" &&
az group create --name $resourceGroupName --___location "$___location" &&
az deployment group create --resource-group $resourceGroupName --template-uri  $templateUri --parameters functionAppRuntime=node functionAppRuntimeVersion=20 &&
echo "Press [ENTER] to continue ..." &&
read
read -p "Enter a supported Azure region: " ___location &&
resourceGroupName=exampleRG &&
templateUri="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-flex-managed-identities/azuredeploy.json" &&
az group create --name $resourceGroupName --___location "$___location" &&
az deployment group create --resource-group $resourceGroupName --template-uri  $templateUri --parameters functionAppRuntime=python functionAppRuntimeVersion=3.11 &&
echo "Press [ENTER] to continue ..." &&
read
read -p "Enter a supported Azure region: " ___location &&
resourceGroupName=exampleRG &&
templateUri="https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/quickstarts/microsoft.web/function-app-flex-managed-identities/azuredeploy.json" &&
az group create --name $resourceGroupName --___location "$___location" &&
az deployment group create --resource-group $resourceGroupName --template-uri  $templateUri --parameters functionAppRuntime=powerShell functionAppRuntimeVersion=7.4 &&
echo "Press [ENTER] to continue ..." &&
read

배포가 완료되면 배포가 성공했음을 나타내는 메시지가 표시됩니다.

함수 앱 시작 페이지 방문

  1. 이전 유효성 검사 단계의 출력을 사용하여 함수 앱에 대해 만든 고유 이름을 검색합니다.

  2. 브라우저를 열고 다음 URL을 입력합니다. <https://<appName.azurewebsites.net>. <\appName>을 함수 앱에 대해 만든 고유한 이름으로 바꿔야 합니다.

    URL을 방문하면 다음과 같은 페이지가 표시됩니다.

    함수 앱 시작 페이지

리소스 정리

이제 Azure에 함수 앱과 관련 리소스를 배포했으므로 앱에 프로젝트 코드를 게시하는 다음 단계로 진행할 수 있습니다. 그렇지 않은 경우, 더 이상 필요하지 않은 리소스를 삭제하려면 이러한 명령을 사용합니다.

az group delete --name exampleRG

Azure Portal을 사용하여 리소스를 제거할 수도 있습니다.

다음 단계

이제 Azure에서 만든 함수 앱 리소스에 코드 프로젝트를 배포할 수 있습니다.

다음 로컬 환경에서 새 함수 앱에 코드 프로젝트를 만들고, 확인하고, 배포할 수 있습니다.