下記の構成でLogic Appsをデプロイする。
※下記のBicepでは、SubnetとNSGを余分に作成しています。用途に合わせて削ってください。
※StorageAccountは診断設定に用いられるものですが、今回は診断設定は除いてデプロイします。
↓ 本来の想定
Logic Appsについて

今回はStandardプランのWS1でデプロイします。PrivateEndpointを付けてデプロイするにはLogic AppsはStandardプラン(ワークフロー or App Service Environment)である必要があります。
また、Logic AppsとPrivateEndpointを組み合わせる場合、AppServicePlanのデプロイも必要です。従量課金プランではMicrosoftがホストするため、AppServicePlanを作成する必要はありません。Standardプランでは固定の支払いとなり、ユーザーがホストする必要があるため、AppServicePlanの構築が必要です。
ディレクトリ構成
bicep-project/
├── deployments/
│ ├── main.bicep
│ └── rg.bicep
└── modules/
├── resourceGroup.bicep
├── vnet.bicep
├── NSG.bicep
├── logicapps.bicep
├── appserviceplan.bicep
├── blobstorage.bicep
└── privateEndpoint.bicep
Bicep
rg.bicep/resourceGroup.bicep
rg.bicep
targetScope = 'subscription'
param rgName string = 'myProjectRG'
param location string = 'japanwest'
var tags = {
Environment: 'dev'
}
module rgModule '../modules/resourceGroup.bicep' = {
name: 'resourceGroup'
params: {
rgName: rgName
location: location
tags: tags
}
}
resourceGroup.bicep
targetScope = 'subscription'
param rgName string
param location string
param tags object
resource RG 'Microsoft.Resources/resourceGroups@2022-09-01' = {
name: rgName
location: location
tags: tags
}
output rgName string = RG.name
vnet.bicep
vnet.bicep
param vnetName string
param location string
param tags object
param vnetAddressPrefix string
param subnets array
resource vnet 'Microsoft.Network/virtualNetworks@2023-05-01' = {
name: vnetName
location: location
tags: tags
properties: {
addressSpace: {
addressPrefixes: [
vnetAddressPrefix
]
}
subnets: [
for (subnet, index) in subnets: {
name: subnet.name
properties: union(
{
addressPrefix: subnet.addressPrefix
privateEndpointNetworkPolicies: 'Enabled'
networkSecurityGroup: {
id: resourceId('Microsoft.Network/networkSecurityGroups', subnet.nsgName)
}
},
index == 2
? {
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
: {}
)
}
]
}
}
output vnetId string = vnet.id
output vnetName string = vnet.name
output agwsubnetId01 string = vnet.properties.subnets[0].id
output pesubnetId01 string = vnet.properties.subnets[1].id
output vnetIntegrationSubnetId string = vnet.properties.subnets[2].id
output pesubnetId02 string = vnet.properties.subnets[3].id
NSG.bicep
NSG.bicep
param location string = 'japanwest'
param tags object = {
environment: 'sample'
}
param subnets array = [
{
name: 'subnet-app'
nsgName: 'nsg-app'
}
{
name: 'subnet-pe'
nsgName: 'nsg-pe'
}
]
var nsgRules = {
'${subnets[0].nsgName}': [
{
name: 'AllowAppSubnetInbound'
priority: 100
direction: 'Inbound'
access: 'Allow'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '10.1.0.0/24'
destinationAddressPrefix: '*'
}
{
name: 'DenyAllOutbound'
priority: 4096
direction: 'Outbound'
access: 'Deny'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
}
]
'${subnets[1].nsgName}': [
{
name: 'AllowHttpsOutbound'
priority: 100
direction: 'Outbound'
access: 'Allow'
protocol: 'Tcp'
sourcePortRange: '*'
destinationPortRange: '443'
sourceAddressPrefix: '*'
destinationAddressPrefix: 'Internet'
}
{
name: 'DenyAllOutbound'
priority: 4096
direction: 'Outbound'
access: 'Deny'
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: '*'
}
]
}
resource nsgs 'Microsoft.Network/networkSecurityGroups@2023-05-01' = [
for subnet in subnets: {
name: subnet.nsgName
location: location
tags: tags
properties: {
securityRules: [
for rule in nsgRules[subnet.nsgName]: {
name: rule.name
properties: {
priority: rule.priority
direction: rule.direction
access: rule.access
protocol: rule.protocol
sourcePortRange: rule.sourcePortRange
destinationPortRange: rule.destinationPortRange
sourceAddressPrefix: rule.sourceAddressPrefix
destinationAddressPrefix: rule.destinationAddressPrefix
}
}
]
}
}
]
output appNsgId string = nsgs[0].id
output peNsgId string = nsgs[1].id
main.bicep
targetScope = 'subscription'
param rgName string = 'sampleRG'
param location string = 'japanwest'
param environment string = 'dev'
param projectCode string = 'sample'
param tags object = {
Environment: 'dev'
}
var subnets = [
{
name: 'sample-agw-subnet01'
addressPrefix: '10.1.0.0/24'
nsgName: 'sample-agw-nsg01'
}
{
name: 'sample-pe-subnet01'
addressPrefix: '10.1.1.0/24'
nsgName: 'sample-pe-nsg01'
}
{
name: 'sample-integration-subnet01'
addressPrefix: '10.1.2.0/24'
nsgName: 'sample-integration-nsg01'
}
{
name: 'sample-pe-subnet02'
addressPrefix: '10.1.3.0/24'
nsgName: 'sample-pe-nsg02'
}
]
module nsgModule '../modules/NSG.bicep' = {
name: 'NetworkSecurityGroup'
scope: resourceGroup(rgName)
params: {
location: location
tags: tags
subnets: subnets
}
}
module vnetModule '../modules/vnet.bicep' = {
name: 'VirtualNetwork'
scope: resourceGroup(rgName)
params: {
vnetName: 'sampleVnet01'
location: location
tags: tags
vnetAddressPrefix: '10.1.0.0/16'
subnets: subnets
}
dependsOn: [
nsgModule
]
}
param appPlanSku string = 'WS1'
param appPlanTier string = 'WorkflowStandard'
module appServicePlanModule '../modules/appserviceplan.bicep' = {
name: 'AppsServicePlan'
scope: resourceGroup(rgName)
params: {
appServicePlanName: '${development}-${case}appplan001'
location: location
tags: tags
appPlanSku: appPlanSku
appPlanTier: appPlanTier
}
dependsOn: [
]
}
param logicAppStorageName string = '${toLower(development)}${toLower(case)}logicst01'
module logicAppStorage '../modules/blobstorage.bicep' = {
name: 'logicAppStorage'
scope: resourceGroup(rgName)
params: {
storageAccountName: logicAppStorageName
location: location
tags: tags
sku: 'Standard_LRS'
kind: 'StorageV2'
accessTier: 'Hot'
publicNetworkAccess: 'Enabled'
supportsHttpsTrafficOnly: true
minimumTlsVersion: 'TLS1_2'
}
}
param logicAppName string = 'son-logicapp001'
module logicAppsModule '../modules/logicapps.bicep' = {
name: 'logicApps'
scope: resourceGroup(rgName)
params: {
location: location
tags: tags
logicAppName: logicAppName
appServicePlanId: appServicePlanModule.outputs.appServicePlanId
storageAccountName: logicAppStorage.outputs.storageAccountName
storageAccountId: logicAppStorage.outputs.storageAccountId
vnetIntegrationSubnetId: vnetModule.outputs.vnetIntegrationSubnetId
}
dependsOn: [
appServicePlanModule
vnetModule
]
}
module logicAppsPrivateEndpoint '../modules/privateEndpoint.bicep' = {
name: 'logicapps-pe'
scope: resourceGroup(rgName)
params: {
privateEndpointName: '${development}-${case}-logicapps-pe01'
location: location
tags: tags
subnetId: vnetModule.outputs.pesubnetId01
targetResourceId: logicAppsModule.outputs.logicAppsId
groupIds: ['sites']
privateDnsZoneNames: [
'privatelink.azurewebsites.net'
]
vnetId: vnetModule.outputs.vnetId
}
dependsOn: [
vnetModule
logicAppsModule
]
}
logicapps.bicep
param location string
param tags object
param logicAppName string
param appServicePlanId string
param storageAccountName string
param storageAccountId string
param vnetIntegrationSubnetId string
resource logicAppStandard 'Microsoft.Web/sites@2023-01-01' = {
name: logicAppName
location: location
tags: tags
kind: 'functionapp,workflowapp'
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlanId
httpsOnly: true
virtualNetworkSubnetId: vnetIntegrationSubnetId
siteConfig: {
netFrameworkVersion: 'v6.0'
use32BitWorkerProcess: false
alwaysOn: true
vnetRouteAllEnabled: true
appSettings: [
{
name: 'AzureWebJobsStorage'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(storageAccountId, '2023-01-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};AccountKey=${listKeys(storageAccountId, '2023-01-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}'
}
{
name: 'WEBSITE_CONTENTSHARE'
value: toLower(logicAppName)
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'node'
}
{
name: 'APP_KIND'
value: 'workflowApp'
}
]
}
}
}
output logicAppsId string = logicAppStandard.id
output logicAppsName string = logicAppStandard.name
output principalId string = logicAppStandard.identity.principalId
appserviceplan.bicep
param location string
param tags object
param appServicePlanName string
param appPlanSku string
param appPlanTier string
resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = {
name: appServicePlanName
location: location
tags: tags
sku: {
name: appPlanSku
tier: appPlanTier
}
kind: 'linux'
properties: {
reserved: true
perSiteScaling: false
maximumElasticWorkerCount: 1
}
}
output appServicePlanId string = appServicePlan.id
output appServicePlanName string = appServicePlan.name
blobstorage.bicep
param storageAccountName string
param location string
param tags object
param sku string
param kind string
param accessTier string
param publicNetworkAccess string
param supportsHttpsTrafficOnly bool
param minimumTlsVersion string
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: storageAccountName
location: location
tags: tags
sku: {
name: sku
}
kind: kind
identity: {
type: 'SystemAssigned'
}
properties: {
accessTier: accessTier
publicNetworkAccess: publicNetworkAccess
supportsHttpsTrafficOnly: supportsHttpsTrafficOnly
minimumTlsVersion: minimumTlsVersion
allowBlobPublicAccess: false
allowSharedKeyAccess: true
networkAcls: {
defaultAction: publicNetworkAccess == 'Enabled' ? 'Allow' : 'Deny'
bypass: 'AzureServices'
ipRules: []
virtualNetworkRules: []
}
encryption: {
services: {
blob: {
enabled: true
}
file: {
enabled: true
}
}
keySource: 'Microsoft.Storage'
}
}
}
resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-01-01' = {
parent: storageAccount
name: 'default'
properties: {
cors: {
corsRules: []
}
deleteRetentionPolicy: {
enabled: true
days: 7
}
containerDeleteRetentionPolicy: {
enabled: true
days: 7
}
changeFeed: {
enabled: false
}
restorePolicy: {
enabled: false
}
isVersioningEnabled: false
}
}
resource defaultContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = {
parent: blobService
name: 'default-container'
properties: {
publicAccess: 'None'
}
}
output storageAccountId string = storageAccount.id
output storageAccountName string = storageAccount.name
output blobEndpoint string = storageAccount.properties.primaryEndpoints.blob
output storageAccountPrincipalId string = storageAccount.identity.principalId
privateEndpoint.bicep
param privateEndpointName string
param location string
param tags object
param subnetId string
param targetResourceId string
param groupIds array
param privateDnsZoneNames array
param vnetId string
resource privateDnsZones 'Microsoft.Network/privateDnsZones@2020-06-01' = [
for zoneName in privateDnsZoneNames: {
name: zoneName
location: 'global'
tags: tags
}
]
resource privateDnsZoneLinks 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = [
for (zoneName, index) in privateDnsZoneNames: {
parent: privateDnsZones[index]
name: 'vnet-link-${index}'
location: 'global'
tags: tags
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnetId
}
}
}
]
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
name: privateEndpointName
location: location
tags: tags
properties: {
subnet: {
id: subnetId
}
privateLinkServiceConnections: [
{
name: '${privateEndpointName}-connection'
properties: {
privateLinkServiceId: targetResourceId
groupIds: groupIds
}
}
]
}
}
resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = {
parent: privateEndpoint
name: 'default'
properties: {
privateDnsZoneConfigs: [
for (zoneName, index) in privateDnsZoneNames: {
name: replace(zoneName, '.', '-')
properties: {
privateDnsZoneId: privateDnsZones[index].id
}
}
]
}
dependsOn: [
privateDnsZoneLinks
]
}
output privateEndpointId string = privateEndpoint.id
output privateEndpointName string = privateEndpoint.name
output privateDnsZoneIds array = [for (zoneName, index) in privateDnsZoneNames: privateDnsZones[index].id]
デプロイ手順
1. Azure CLIへのログイン
2. リソースグループのデプロイ
az deployment sub create `
--location japanwest `
--template-file .\deployments\rg.bicep
3. メインリソースのデプロイ
az deployment sub create `
--location japanwest `
--template-file .\deployments\main.bicep
デプロイ時に起きた課題と解決案
- AppServicePlanの価格(SKU)に関して
- P0V3でデプロイしようとしたところ失敗
- 正しくは、WS1, WS2, WS3のグレードで設定可能
main.bicep
param appPlanSku string = 'WS1'
param appPlanTier string = 'WorkflowStandard'
module appServicePlanModule '../modules/appserviceplan.bicep' = {
name: 'AppsServicePlan'
scope: resourceGroup(rgName)
params: {
appServicePlanName: '${development}-${case}appplan001'
location: location
tags: tags
appPlanSku: appPlanSku
appPlanTier: appPlanTier
}
dependsOn: [
]
}
Source link
コメント