빠른 시작: Azure Managed Application 정의 만들기 및 게시

이 빠른 시작에서는 Azure Managed Applications를 사용하는 방법을 소개합니다. 서비스 카탈로그에 저장되고 조직 멤버를 대상으로 하는 관리형 애플리케이션 정의를 만들고 게시합니다.

관리되는 애플리케이션을 서비스 카탈로그에 게시하려면 다음 작업을 수행합니다.

  • 관리형 애플리케이션을 통해 배포할 리소스를 정의하는 ARM 템플릿(Azure Resource Manager 템플릿)을 만듭니다.
  • 관리 되는 애플리케이션을 배포할 때 포털에 대한 사용자 인터페이스 요소를 정의합니다.
  • 필요한 JSON 파일이 포함된 .zip 패키지를 만듭니다. .zip 패키지 파일은 서비스 카탈로그의 관리되는 애플리케이션 정의에 대해 120MB로 제한됩니다.
  • 서비스 카탈로그에서 사용할 수 있도록 관리형 애플리케이션 정의를 게시합니다.

관리형 애플리케이션 정의가 120MB를 초과하거나 조직의 규정 준수를 위해 고유한 스토리지 계정을 사용하려는 경우 빠른 시작: 사용자 고유의 스토리지를 가져와서 Azure Managed Application 정의 생성 및 게시로 이동합니다.

Bicep을 사용하여 관리형 애플리케이션 정의를 개발할 수 있지만 Azure에 정의를 게시하려면 먼저 ARM 템플릿 JSON으로 변환해야 합니다. 자세한 내용은 빠른 시작: Bicep을 사용하여 Azure 관리형 애플리케이션 정의 만들기 및 게시로 이동합니다.

Bicep을 사용하여 서비스 카탈로그에서 관리형 애플리케이션 정의를 배포할 수도 있습니다. 자세한 내용은 빠른 시작: Bicep을 사용하여 Azure 관리형 애플리케이션 정의 배포로 이동합니다.

필수 조건

이 빠른 시작을 완료하려면 다음 항목이 필요합니다.

ARM 템플릿 만들기

모든 관리되는 애플리케이션 정의에는 mainTemplate.json이라는 파일이 포함됩니다. 템플릿은 배포할 Azure 리소스를 정의하며 일반 ARM 템플릿과 다르지 않습니다.

Visual Studio Code를 열고 이름이 mainTemplate.json(대/소문자 구분)인 파일을 만들고 저장합니다.

다음 JSON을 추가하고 파일을 저장합니다. 리소스를 정의하여 애플리케이션의 App Service, App Service 요금제 및 스토리지 계정을 배포합니다. 이 스토리지 계정은 관리형 애플리케이션 정의를 저장하는 데 사용되지 않습니다.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    },
    "appServicePlanName": {
      "type": "string",
      "maxLength": 40,
      "metadata": {
        "description": "App Service plan name."
      }
    },
    "appServiceNamePrefix": {
      "type": "string",
      "maxLength": 47,
      "metadata": {
        "description": "App Service name prefix."
      }
    },
    "storageAccountNamePrefix": {
      "type": "string",
      "maxLength": 11,
      "metadata": {
        "description": "Storage account name prefix."
      }
    },
    "storageAccountType": {
      "type": "string",
      "allowedValues": [
        "Premium_LRS",
        "Standard_LRS",
        "Standard_GRS"
      ],
      "metadata": {
        "description": "Storage account type allowed values"
      }
    }
  },
  "variables": {
    "appServicePlanSku": "F1",
    "appServicePlanCapacity": 1,
    "appServiceName": "[format('{0}{1}', parameters('appServiceNamePrefix'), uniqueString(resourceGroup().id))]",
    "storageAccountName": "[format('{0}{1}', parameters('storageAccountNamePrefix'), uniqueString(resourceGroup().id))]"
  },
  "resources": [
    {
      "type": "Microsoft.Web/serverfarms",
      "apiVersion": "2022-03-01",
      "name": "[parameters('appServicePlanName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[variables('appServicePlanSku')]",
        "capacity": "[variables('appServicePlanCapacity')]"
      }
    },
    {
      "type": "Microsoft.Web/sites",
      "apiVersion": "2022-03-01",
      "name": "[variables('appServiceName')]",
      "location": "[parameters('location')]",
      "properties": {
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
        "httpsOnly": true,
        "siteConfig": {
          "appSettings": [
            {
              "name": "AppServiceStorageConnectionString",
              "value": "[format('DefaultEndpointsProtocol=https;AccountName={0};EndpointSuffix={1};Key={2}', variables('storageAccountName'), environment().suffixes.storage, listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-09-01').keys[0].value)]"
            }
          ]
        }
      },
      "dependsOn": [
        "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]",
        "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
      ]
    },
    {
      "type": "Microsoft.Storage/storageAccounts",
      "apiVersion": "2022-09-01",
      "name": "[variables('storageAccountName')]",
      "location": "[parameters('location')]",
      "sku": {
        "name": "[parameters('storageAccountType')]"
      },
      "kind": "StorageV2",
      "properties": {
        "accessTier": "Hot"
      }
    }
  ],
  "outputs": {
    "appServicePlan": {
      "type": "string",
      "value": "[parameters('appServicePlanName')]"
    },
    "appServiceApp": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.Web/sites', variables('appServiceName')), '2022-03-01').defaultHostName]"
    },
    "storageAccount": {
      "type": "string",
      "value": "[reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2022-09-01').primaryEndpoints.blob]"
    }
  }
}

포털 환경 정의

게시자는 포털 환경을 정의하여 관리형 애플리케이션을 만듭니다. createUiDefinition.json 파일은 포털의 사용자 인터페이스를 생성합니다. 사용자는 드롭다운, 텍스트 상자 같은 컨트롤 요소를 사용하여 각 매개 변수에 대한 입력을 제공하는 방법을 정의합니다.

이 예제에서는 App Service 이름 접두사, App Service 요금제 이름, 스토리지 계정 접두사 및 스토리지 계정 유형을 입력하라는 메시지가 사용자 인터페이스에 나타납니다. 배포하는 동안 mainTemplate.json의 변수는 uniqueString 함수를 사용하여 이름 접두사에 13자 문자열을 추가하므로 Azure에서 이름이 전역적으로 고유합니다.

Visual Studio Code를 열고 이름이 createUiDefinition.json(대/소문자 구분)인 파일을 만들고 저장합니다.

파일에 다음 JSON 코드를 추가하고 저장합니다.

{
  "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
  "handler": "Microsoft.Azure.CreateUIDef",
  "version": "0.1.2-preview",
  "parameters": {
    "basics": [
      {}
    ],
    "steps": [
      {
        "name": "webAppSettings",
        "label": "Web App settings",
        "subLabel": {
          "preValidation": "Configure the web app settings",
          "postValidation": "Completed"
        },
        "elements": [
          {
            "name": "appServicePlanName",
            "type": "Microsoft.Common.TextBox",
            "label": "App Service plan name",
            "placeholder": "App Service plan name",
            "defaultValue": "",
            "toolTip": "Use alphanumeric characters or hyphens with a maximum of 40 characters.",
            "constraints": {
              "required": true,
              "regex": "^[a-z0-9A-Z-]{1,40}$",
              "validationMessage": "Only alphanumeric characters or hyphens are allowed, with a maximum of 40 characters."
            },
            "visible": true
          },
          {
            "name": "appServiceName",
            "type": "Microsoft.Common.TextBox",
            "label": "App Service name prefix",
            "placeholder": "App Service name prefix",
            "defaultValue": "",
            "toolTip": "Use alphanumeric characters or hyphens with minimum of 2 characters and maximum of 47 characters.",
            "constraints": {
              "required": true,
              "regex": "^[a-z0-9A-Z-]{2,47}$",
              "validationMessage": "Only alphanumeric characters or hyphens are allowed, with a minimum of 2 characters and maximum of 47 characters."
            },
            "visible": true
          }
        ]
      },
      {
        "name": "storageConfig",
        "label": "Storage settings",
        "subLabel": {
          "preValidation": "Configure the storage settings",
          "postValidation": "Completed"
        },
        "elements": [
          {
            "name": "storageAccounts",
            "type": "Microsoft.Storage.MultiStorageAccountCombo",
            "label": {
              "prefix": "Storage account name prefix",
              "type": "Storage account type"
            },
            "toolTip": {
              "prefix": "Enter maximum of 11 lowercase letters or numbers.",
              "type": "Available choices are Standard_LRS, Standard_GRS, and Premium_LRS."
            },
            "defaultValue": {
              "type": "Standard_LRS"
            },
            "constraints": {
              "allowedTypes": [
                "Premium_LRS",
                "Standard_LRS",
                "Standard_GRS"
              ]
            },
            "visible": true
          }
        ]
      }
    ],
    "outputs": {
      "location": "[location()]",
      "appServicePlanName": "[steps('webAppSettings').appServicePlanName]",
      "appServiceNamePrefix": "[steps('webAppSettings').appServiceName]",
      "storageAccountNamePrefix": "[steps('storageConfig').storageAccounts.prefix]",
      "storageAccountType": "[steps('storageConfig').storageAccounts.type]"
    }
  }
}

자세히 알아보려면 CreateUiDefinition 시작을 참조하세요.

파일을 패키지로 만들기

이름이 app.zip인 패키지 파일에 두 개의 파일을 추가합니다. 두 파일은 .zip 파일의 루트 수준에 있어야 합니다. 파일이 폴더에 있으면 관리형 애플리케이션 정의를 만들 때 필요한 파일이 없다는 오류가 표시됩니다.

관리형 애플리케이션의 정의를 배포할 때 사용할 수 있도록 Azure Storage 계정에 app.zip을 업로드합니다. 스토리지 계정 이름은 Azure에서 전역적으로 고유해야 하며 길이는 소문자와 숫자만 포함된 3-24자여야 합니다. 명령에서 대괄호(<>)를 포함한 자리 표시자 <demostorageaccount>를 고유한 스토리지 계정 이름으로 바꿉니다.

Visual Studio Code에서 새 PowerShell 터미널을 열고 Azure 구독에 로그인합니다.

Connect-AzAccount

이 명령은 기본 브라우저를 열고 Azure에 로그인하라는 메시지를 표시합니다. 자세한 내용은 Azure PowerShell로 로그인을 참조하세요.

New-AzResourceGroup -Name packageStorageGroup -Location westus3

$storageAccount = New-AzStorageAccount `
  -ResourceGroupName packageStorageGroup `
  -Name "<demostorageaccount>" `
  -Location westus3 `
  -SkuName Standard_LRS `
  -Kind StorageV2

$ctx = $storageAccount.Context

New-AzStorageContainer -Name appcontainer -Context $ctx -Permission blob

Set-AzStorageBlobContent `
  -File "app.zip" `
  -Container appcontainer `
  -Blob "app.zip" `
  -Context $ctx

관리형 애플리케이션 정의 만들기

이 섹션에서는 Microsoft Entra ID에서 ID 정보를 가져오고, 리소스 그룹을 만들며, 관리형 애플리케이션 정의를 배포합니다.

그룹 ID 및 역할 정의 ID 가져오기

다음 단계는 고객의 리소스를 관리하기 위한 사용자, 보안 그룹 또는 애플리케이션을 선택하는 것입니다. 이 ID에는 할당된 역할에 따라 관리형 리소스 그룹에 대한 권한이 있습니다. 역할은 소유자 또는 기여자 같은 Azure 기본 제공 역할입니다.

이 예제에서는 보안 그룹을 사용하며, Microsoft Entra 계정은 그룹의 멤버여야 합니다. 그룹의 개체 ID를 가져오려면 대괄호(<>)를 포함한 자리 표시자 <managedAppDemo>를 그룹 이름으로 바꿉니다. 관리형 애플리케이션 정의를 배포할 때 변수 값을 사용합니다.

새 Microsoft Entra 그룹을 만들려면 Microsoft Entra 그룹 및 그룹 멤버 자격 관리로 이동합니다.

$principalid=(Get-AzADGroup -DisplayName <managedAppDemo>).Id

다음으로, 사용자, 그룹 또는 애플리케이션에 대한 액세스 권한을 부여하려는 Azure 기본 제공 역할의 역할 정의 ID를 가져옵니다. 관리형 애플리케이션 정의를 배포할 때 변수 값을 사용합니다.

$roleid=(Get-AzRoleDefinition -Name Owner).Id

관리형 애플리케이션 정의 게시

관리형 애플리케이션 정의의 리소스 그룹을 만듭니다.

New-AzResourceGroup -Name appDefinitionGroup -Location westus3

blob 명령은 패키지 .zip 파일의 URL을 저장하는 변수를 만듭니다. 해당 변수는 관리되는 애플리케이션 정의를 만드는 명령에서 사용됩니다.

$blob = Get-AzStorageBlob -Container appcontainer -Blob app.zip -Context $ctx

New-AzManagedApplicationDefinition `
  -Name "sampleManagedApplication" `
  -Location "westus3" `
  -ResourceGroupName appDefinitionGroup `
  -LockLevel ReadOnly `
  -DisplayName "Sample managed application" `
  -Description "Sample managed application that deploys web resources" `
  -Authorization "${principalid}:$roleid" `
  -PackageFileUri $blob.ICloudBlob.StorageUri.PrimaryUri.AbsoluteUri

명령이 완료되면 리소스 그룹에 관리되는 애플리케이션 정의가 있습니다.

앞의 예제에서 사용된 몇 가지 매개 변수는 다음과 같습니다.

  • ResourceGroupName: 관리형 애플리케이션 정의를 만든 리소스 그룹의 이름입니다.
  • LockLevel: 관리형 리소스 그룹의 lockLevel은 고객이 이 리소스 그룹에 대해 바람직하지 않은 작업을 수행하지 못하도록 합니다. 현재 ReadOnly만 지원되는 잠금 수준입니다. ReadOnly는 고객이 관리형 리소스 그룹에 있는 리소스만 읽을 수 있도록 지정합니다. 관리형 리소스 그룹에 대한 액세스 권한이 부여된 게시자 ID는 잠금 수준에서 제외됩니다.
  • Authorization: 관리되는 리소스 그룹에 권한을 부여하는 데 사용되는 보안 주체 ID 및 역할 정의 ID를 설명합니다.
    • "${principalid}:$roleid" 또는 각 변수 "${principalid}:${roleid}"에 중괄호를 사용할 수 있습니다.
    • 쉼표를 사용하여 여러 값("${principalid1}:$roleid1", "${principalid2}:$roleid2")을 구분합니다.
  • PackageFileUri: 필요한 파일이 포함되어 있는 .zip 패키지 파일의 위치입니다.

사용자가 정의를 볼 수 있는지 확인합니다.

사용자가 관리되는 애플리케이션 정의에 액세스할 수 있지만 조직의 다른 사용자도 액세스할 수 있는 것이 좋습니다. 정의에서 최소한 읽기 역할을 부여합니다. 구독 또는 리소스 그룹에서 이 수준의 액세스를 상속받을 수 있습니다. 정의에 대한 액세스 권한이 있는 사용자를 확인하고 사용자 또는 그룹을 추가하려면 Azure Portal을 사용하여 Azure 역할 할당을 참조하세요.

리소스 정리

정의를 배포하려는 경우 문서를 연결하여 정의를 배포하는 다음 단계 섹션을 계속 진행합니다.

관리형 애플리케이션 정의를 완료한 경우 packageStorageGroupappDefinitionGroup이라는 리소스 그룹을 삭제할 수 있습니다.

이 명령은 리소스 그룹을 제거할 것인지 확인하라는 메시지를 표시합니다.

Remove-AzResourceGroup -Name packageStorageGroup

Remove-AzResourceGroup -Name appDefinitionGroup

다음 단계

관리되는 애플리케이션 정의를 게시했습니다. 다음 단계는 해당 정의의 인스턴스를 배포하는 방법을 알아보는 것입니다.