빠른 시작: Bicep을 사용하여 Azure 관리형 애플리케이션 정의 만들기 및 게시

이 빠른 시작에서는 Bicep을 사용하여 서비스 카탈로그에 Azure 관리형 애플리케이션 정의를 만들고 게시하는 방법을 설명합니다. 서비스 카탈로그의 정의는 조직의 구성원이 사용할 수 있습니다.

관리형 애플리케이션 정의를 만들고 서비스 카탈로그에 게시하려면 다음 작업을 수행합니다.

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

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

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

필수 조건

이 문서의 작업을 완료하려면 다음 항목이 필요합니다.

Bicep 파일 만들기

모든 관리되는 애플리케이션 정의에는 mainTemplate.json이라는 파일이 포함됩니다. 템플릿은 배포할 Azure 리소스를 정의하며 일반 ARM 템플릿과 다르지 않습니다. Bicep을 사용하여 템플릿을 개발한 다음, Bicep 파일을 JSON으로 변환할 수 있습니다.

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

다음 Bicep 코드를 추가하고 파일을 저장합니다. 관리형 애플리케이션의 리소스를 정의하여 App Service, App Service 요금제 및 스토리지 계정을 배포합니다.

param location string = resourceGroup().location

@description('App Service plan name.')
@maxLength(40)
param appServicePlanName string

@description('App Service name prefix.')
@maxLength(47)
param appServiceNamePrefix string

@description('Storage account name prefix.')
@maxLength(11)
param storageAccountNamePrefix string

@description('Storage account type allowed values')
@allowed([
  'Premium_LRS'
  'Standard_LRS'
  'Standard_GRS'
])
param storageAccountType string

var appServicePlanSku = 'F1'
var appServicePlanCapacity = 1
var appServiceName = '${appServiceNamePrefix}${uniqueString(resourceGroup().id)}'
var storageAccountName = '${storageAccountNamePrefix}${uniqueString(resourceGroup().id)}'
var appServiceStorageConnectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};EndpointSuffix=${environment().suffixes.storage};Key=${storageAccount.listKeys().keys[0].value}'

resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSku
    capacity: appServicePlanCapacity
  }
}

resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
  name: appServiceName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
    siteConfig: {
      appSettings: [
        {
          name: 'AppServiceStorageConnectionString'
          value: appServiceStorageConnectionString
        }
      ]
    }
  }
}

resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: storageAccountType
  }
  kind: 'StorageV2'
  properties: {
    accessTier: 'Hot'
  }
}

output appServicePlan string = appServicePlan.name
output appServiceApp string = appServiceApp.properties.defaultHostName
output storageAccount string = storageAccount.properties.primaryEndpoints.blob

Bicep을 JSON으로 변환

PowerShell 또는 Azure CLI를 사용하여 mainTemplate.json 파일을 빌드합니다. Bicep 파일을 저장한 디렉터리로 이동하여 build 명령을 실행합니다.

bicep build mainTemplate.bicep

자세히 알아보려면 Bicep 빌드로 이동하세요.

Bicep 파일이 JSON으로 변환되면 mainTemplate.json 파일이 다음 예와 일치해야 합니다. versiontemplateHash에 대한 metadata 속성에 다른 값이 있을 수 있습니다.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.17.1.54307",
      "templateHash": "1234567891234567890"
    }
  },
  "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 packageStorageRG -Location westus3

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

$ctx = $storageAccount.Context

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

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

다음 명령을 사용하여 패키지 파일의 URI를 packageuri라는 변수에 저장합니다. 관리형 애플리케이션 정의를 배포할 때 변수 값을 사용합니다.

$packageuri=(Get-AzStorageBlob -Container appcontainer -Blob app.zip -Context $ctx).ICloudBlob.StorageUri.PrimaryUri.AbsoluteUri

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

이 섹션에서는 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

정의 배포 템플릿 만들기

Bicep 파일을 사용하여 서비스 카탈로그에 관리형 애플리케이션 정의를 배포합니다.

Visual Studio Code를 열고, 이름이 deployDefinition.bicep인 파일을 만들고, 저장합니다.

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

param location string = resourceGroup().location

@description('Name of the managed application definition.')
param managedApplicationDefinitionName string

@description('The URI of the .zip package file.')
param packageFileUri string

@description('Publishers Principal ID that needs permissions to manage resources in the managed resource group.')
param principalId string

@description('Role ID for permissions to the managed resource group.')
param roleId string

var definitionLockLevel = 'ReadOnly'
var definitionDisplayName = 'Sample Bicep managed application'
var definitionDescription = 'Sample Bicep managed application that deploys web resources'

resource managedApplicationDefinition 'Microsoft.Solutions/applicationDefinitions@2021-07-01' = {
  name: managedApplicationDefinitionName
  location: location
  properties: {
    lockLevel: definitionLockLevel
    description: definitionDescription
    displayName: definitionDisplayName
    packageFileUri: packageFileUri
    authorizations: [
      {
        principalId: principalId
        roleDefinitionId: roleId
      }
    ]
  }
}

템플릿의 속성에 대한 자세한 내용을 보려면 Microsoft.Solutions/applicationDefinitions로 이동합니다.

관리형 리소스 그룹의 lockLevel은 고객이 이 리소스 그룹에 대해 바람직하지 않은 작업을 수행하지 못하도록 합니다. 현재 ReadOnly만 지원되는 잠금 수준입니다. ReadOnly는 고객이 관리형 리소스 그룹에 있는 리소스만 읽을 수 있도록 지정합니다. 관리형 리소스 그룹에 대한 액세스 권한이 부여된 게시자 ID는 잠금 수준에서 제외됩니다.

매개 변수 파일 만들기

관리형 애플리케이션 정의의 배포 템플릿에는 여러 매개 변수에 대한 입력이 필요합니다. 배포 명령은 값을 묻는 메시지를 표시하거나 값에 대한 매개 변수 파일을 만들 수 있습니다. 이 예제에서는 매개 변수 파일을 사용하여 매개 변수 값을 배포 명령에 전달합니다.

Visual Studio Code에서 deployDefinition.parameters.json이라는 새 파일을 만들고 저장합니다.

매개 변수 파일에 다음을 추가하고 저장합니다. 그런 다음, 대괄호(<>)를 포함한 <placeholder values>를 해당 값으로 바꿉니다.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "managedApplicationDefinitionName": {
      "value": "sampleBicepManagedApplication"
    },
    "packageFileUri": {
      "value": "<placeholder for the packageFileUri>"
    },
    "principalId": {
      "value": "<placeholder for principalid value>"
    },
    "roleId": {
      "value": "<placeholder for roleid value>"
    }
  }
}

다음 표에서는 관리형 애플리케이션 정의에 대한 매개 변수 값을 설명합니다.

매개 변수
managedApplicationDefinitionName 관리형 애플리케이션 정의의 이름입니다. 이 예제에서는 sampleBicepManagedApplication을 사용합니다.
packageFileUri .zip 패키지 파일의 URI를 입력합니다. packageuri 변수의 값을 사용합니다. 형식은 https://yourStorageAccountName.blob.core.windows.net/appcontainer/app.zip입니다.
principalId 관리되는 리소스 그룹의 리소스를 관리할 수 있는 권한이 필요한 게시자 보안 주체 ID입니다. principalid 변수의 값을 사용합니다.
roleId 관리형 리소스 그룹에 대한 권한의 역할 ID입니다. 예를 들어 소유자, 기여자, 읽기 권한자입니다. roleid 변수의 값을 사용합니다.

변수 값을 가져오려면:

  • Azure PowerShell: PowerShell에서 $variableName을 입력하여 변수 값을 표시합니다.
  • Azure CLI: Bash에서 echo $variableName을 입력하여 변수 값을 표시합니다.

정의 배포

관리형 애플리케이션의 정의를 배포하면 서비스 카탈로그에서 사용할 수 있게 됩니다. 이 프로세스는 관리형 애플리케이션의 리소스를 배포하지 않습니다.

bicepDefinitionRG라는 리소스 그룹을 만들고 관리되는 애플리케이션 정의를 배포합니다.

New-AzResourceGroup -Name bicepDefinitionRG -Location westus3

New-AzResourceGroupDeployment `
  -ResourceGroupName bicepDefinitionRG `
  -TemplateFile deployDefinition.bicep `
  -TemplateParameterFile deployDefinition.parameters.json

결과 확인

다음 명령을 실행하여 정의가 서비스 카탈로그에 게시되었는지 확인합니다.

Get-AzManagedApplicationDefinition -ResourceGroupName bicepDefinitionRG

Get-AzManagedApplicationDefinition은 지정된 리소스 그룹에서 sampleBicepManagedApplication과 같은 사용 가능한 모든 정의를 나열합니다.

사용자가 정의에 액세스할 수 있는지 확인

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

리소스 정리

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

관리형 애플리케이션 정의가 완료되면 생성한 packageStorageRGbicepDefinitionRG라는 리소스 그룹을 삭제할 수 있습니다.

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

Remove-AzResourceGroup -Name packageStorageRG

Remove-AzResourceGroup -Name bicepDefinitionRG

다음 단계

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