1. はじめに
1-1 ご挨拶
初めまして、井村と申します。
案件にてデプロイ済みの複数のパブリックIPアドレスをAzure Firewallを割り当てる必要がありましたので事前に動作確認をしました。
2. 事前準備
2-1 リソースグループの作成
公式ドキュメントに沿って Azure CLI、Visual Studio Code と Bicep 拡張機能をインスールします。
上記終了後、Azure上にリソースを作成します。
1 . Azureへログイン
Azure上にリソースを作成するためログインします。
az login
現在ログインしているAzureサブスクリプションを確認するには、以下のAzure CLIコマンドを使用します
az account show
2 . リソースグループの作成
東日本リージョンで作成します。
az group create --name rg-demo-dev --location japaneast
Azure上にリソースグループが作成されました。
3. 構築
3-1 Bicepの準備
main.bicep
@description('作成するシステム名を指定します。')
param systemName string = 'demo'
@description('リソースグループのロケーション。今回は東日本リージョン (Japan East) を指定します。')
param location string = 'japaneast'
@description('環境を指定します。dev, stg, pro のいずれかを選択してください。')
@allowed([
'dev'
'stg'
'pro'
])
param env string = 'dev'
@description('仮想ネットワークのアドレスプレフィックス(例: 10.0.0.0/16)')
param addressPrefixes array = [
'10.0.0.0/16'
]
@description('可用性ゾーンを設定します。Zone numbers e.g. 1,2,3.')
param availabilityZones array = []
@description('仮想マシンのOSバージョンを指定します。')
@allowed([
'2022-datacenter'
'2022-datacenter-azure-edition'
'2022-datacenter-azure-edition-core'
'2022-datacenter-azure-edition-core-smalldisk'
'2022-datacenter-azure-edition-smalldisk'
'2022-datacenter-core'
'2022-datacenter-core-g2'
'2022-datacenter-core-smalldisk'
'2022-datacenter-core-smalldisk-g2'
'2022-datacenter-g2'
'2022-datacenter-smalldisk'
'2022-datacenter-smalldisk-g2'
])
param OSVersion string = '2022-datacenter-azure-edition-smalldisk'
@description('仮想マシンのサイズを指定します。')
@allowed([
'Standard_B1s'
'Standard_B1ms'
'Standard_B2s'
'Standard_F1'
'Standard_B2ms'
])
param vmSize string = 'Standard_B2ms'
@description('マネージドディスクの種類を指定します。')
@allowed([
'PremiumV2_LRS'
'Premium_LRS'
'Premium_ZRS'
'StandardSSD_LRS'
'StandardSSD_ZRS'
'Standard_LRS'
'UltraSSD_LRS'
])
param storageAccountType string = 'Standard_LRS'
@description('仮想マシンのログインユーザー名を指定します。' )
param adminUsername string = 'azureuser'
@description('仮想マシンのログインパスワードを指定します。' )
@minLength(12)
@secure()
param adminPassword string // P@ssw0rd1234!
@description('ネットワークセキュリティグループの名前。')
var nsgName = 'nsg-${systemName}-${env}'
@description('仮想ネットワークの名前。')
var vnetName = 'vnet-${systemName}-${env}'
@description('作成するサブネットの一覧。各サブネットに名前とアドレスプレフィックスを指定します。')
var subnets = [
{
name: 'snet-worker'
subnetPrefix: '10.0.1.0/24'
}
{
name: 'AzureFirewallSubnet' // Azure Firewall 用の予約サブネット名
subnetPrefix: '10.0.10.0/26'
}
{
name: 'AzureFirewallManagementSubnet' // Azure Firewall Management用の予約サブネット名
subnetPrefix: '10.0.11.0/26'
}
]
@description('Azure Firewall のプライベートIPアドレス。')
var nextHopIP = '10.0.10.4'
@description('仮想マシン のプライベートIPアドレス。')
var vmPriIP = '10.0.1.4'
@description('ファイアウォールのパブリックIPアドレスの名前。')
var pipFWName = 'pip-${systemName}-${env}-fw'
@description('ファイアウォール管理用のパブリックIPアドレスの名前。')
var pipFWMName = 'pip-${systemName}-${env}-fwm'
@description('ファイアウォールの名前。')
var afwName = 'afw-${systemName}-${env}'
@description('ファイアウォールポリシーの名前。')
var afwpName = 'afwp-${systemName}-${env}'
@description('ルートテーブルの名前。')
var rtName = 'rt-${systemName}-${env}'
@description('カスタムルート(UDR:ユーザ定義ルート)の名前。' )
var udrName = 'udr-${systemName}-${env}'
@description('ネットワークインターフェイスの名前。')
var nicName = 'nic-${systemName}-${env}'
@description('仮想マシンの名前。')
var vmName = 'vm-${systemName}-${env}'
@description('仮想マシンのマネージドランコマンドの名前。')
var rcName = 'rc-${systemName}-${env}'
@description('ファイアウォールのDNATルールコレクショングループの名前。')
var dnatCGName = 'DnatRuleCollectionGroup-${systemName}-${env}'
@description('ファイアウォールのアプリケーションルールコレクショングループの名前。' )
var appCGName = 'ApplicationRuleCollectionGroup-${systemName}-${env}'
// 追加パブリックIPアドレス
@description('追加ファイアウォールのパブリックIPアドレスの名前。')
var pipFWName01 = 'pip-${systemName}-${env}-fw-01'
@description('追加ファイアウォールのパブリックIPアドレスの名前。')
var pipFWName02 = 'pip-${systemName}-${env}-fw-02'
// リソース作成
// ネットワークセキュリティグループの作成
resource nsg 'Microsoft.Network/networkSecurityGroups@2024-07-01' = {
name: nsgName
location: location
properties: {
securityRules: [
{
name: 'AllowVnetInBound'
properties: {
priority: 200
protocol: 'Tcp'
access: 'Allow'
direction: 'Inbound'
sourceAddressPrefix: subnets[1].subnetPrefix
destinationAddressPrefix: subnets[0].subnetPrefix
sourcePortRange: '*'
destinationPortRange: '80'
}
}
]
}
}
// ファイアウォール用パブリックアドレスアドレスの作成
resource pipFW 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipFWName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
// Azure Firewall Management用のパブリックアドレスの作成
resource pipFWManagement 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipFWMName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
// ルートテーブルの作成
resource rt 'Microsoft.Network/routeTables@2023-09-01' = {
name: rtName
location: location
properties: {
disableBgpRoutePropagation: false
routes: [
{
name: udrName
properties: {
addressPrefix: '0.0.0.0/0'
nextHopType: 'VirtualAppliance'
nextHopIpAddress: nextHopIP
}
}
]
}
}
// 仮想ネットワークの作成
resource vnet 'Microsoft.Network/virtualNetworks@2023-11-01' = {
name: vnetName
location: location
properties: {
addressSpace: {
addressPrefixes: addressPrefixes
}
subnets: [
{
name: subnets[0].name
properties: {
addressPrefix: subnets[0].subnetPrefix
routeTable: {
id: rt.id
}
networkSecurityGroup: {
id: nsg.id
}
}
}
{
name: subnets[1].name
properties: {
addressPrefix: subnets[1].subnetPrefix
}
}
{
name: subnets[2].name
properties: {
addressPrefix: subnets[2].subnetPrefix
}
}
]
}
}
// ファイアウォールポリシーの作成
resource afwp 'Microsoft.Network/firewallPolicies@2022-01-01'= {
name: afwpName
location: location
properties: {
threatIntelMode: 'Off'
}
}
// DNATルールコレクショングループの作成
resource dnatRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
parent: afwp
name: dnatCGName
properties: {
priority: 100
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyNatRuleCollection'
action: {
type: 'Dnat'
}
name: 'dnat-rule-collection'
priority: 100
rules: [
{
ruleType: 'NatRule'
name: 'dnat-rule-01'
sourceAddresses: [
'*'
]
destinationAddresses: [
pipFW.properties.ipAddress
]
destinationPorts: [
'80'
]
translatedAddress: vmPriIP
translatedPort: '80'
ipProtocols: [
'TCP'
]
}
]
}
]
}
}
// アプリケーションルールコレクショングループの作成
resource applicationRuleCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-01-01' = {
parent: afwp
name: appCGName
properties: {
priority: 110
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'update-rule-win'
priority: 100
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'winupdate-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
{
protocolType: 'Http'
port: 80
}
]
fqdnTags: [
'WindowsUpdate'
]
terminateTLS: false
sourceAddresses: [
subnets[0].subnetPrefix
]
}
]
}
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
name: 'global-rule-url-microsoft'
priority: 200
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'microsoft-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
]
targetFqdns: [
'*.microsoft.com'
]
terminateTLS: false
sourceAddresses: [
subnets[0].subnetPrefix
]
}
]
}
]
}
dependsOn: [
dnatRuleCollectionGroup
]
}
// ファイアウォールの作成
resource afw 'Microsoft.Network/azureFirewalls@2023-11-01' = {
name: afwName
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
properties: {
sku: {
name: 'AZFW_VNet'
tier: 'Standard'
}
firewallPolicy: {
id: afwp.id
}
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
subnet: {
id: vnet.properties.subnets[1].id
}
publicIPAddress: {
id: pipFW.id
}
}
}
]
managementIpConfiguration: {
name: 'ipconfig2'
properties: {
subnet: {
id: vnet.properties.subnets[2].id
}
publicIPAddress: {
id: pipFWManagement.id
}
}
}
}
dependsOn: [
dnatRuleCollectionGroup
applicationRuleCollectionGroup
]
}
// ネットワークインターフェイスの作成
resource nic 'Microsoft.Network/networkInterfaces@2021-02-01' = {
name: nicName
location: location
properties: {
ipConfigurations: [
{
name: 'ipconfig1'
properties: {
privateIPAllocationMethod: 'Dynamic'
subnet: {
id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, subnets[0].name)
}
}
}
]
}
}
// 仮想マシンの作成
resource vm 'Microsoft.Compute/virtualMachines@2021-07-01' = {
name: vmName
location: location
properties: {
hardwareProfile: {
vmSize: vmSize
}
osProfile: {
computerName: vmName
adminUsername: adminUsername
adminPassword: adminPassword
}
storageProfile: {
imageReference: {
publisher: 'MicrosoftWindowsServer'
offer: 'WindowsServer'
sku: OSVersion
version: 'latest'
}
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: storageAccountType
}
}
}
networkProfile: {
networkInterfaces: [
{
id: nic.id
}
]
}
}
}
// Managed Run Command
resource runCommand 'Microsoft.Compute/virtualMachines/runCommands@2024-03-01' = {
name: rcName
location: location
parent: vm
properties: {
source: {
script: 'Install-WindowsFeature -Name Web-Server -IncludeManagementTools'
}
}
}
// 追加ファイアウォールのパブリックIPアドレスの作成
resource pipFW01 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipFWName01
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
// 追加ファイアウォールのパブリックIPアドレスの作成
resource pipFW02 'Microsoft.Network/publicIPAddresses@2023-11-01' = {
name: pipFWName02
location: location
zones: ((length(availabilityZones) == 0) ? null : availabilityZones)
sku: {
name: 'Standard'
}
properties: {
publicIPAllocationMethod: 'Static'
publicIPAddressVersion: 'IPv4'
}
}
3-2 リソースのデプロイ
初期デプロイを行います。
az deployment group create --template-file main.bicep --resource-group rg-demo-dev --parameters adminPassword='P@ssw0rd1234!'
# az deployment group create --template-file main.bicep --resource-group <your resource group name> --parameters adminPassword='<login password for the Azure VM>'
正常終了するとリソースが作成されます。
パブリックIP構成が1つ作成されます。
3-3 パブリックIP構成の追加
「テンプレートのエクスポート」から取得したBicepテンプレートを使って再デプロイを行います。リソースグループ、Azue Firewallからテンプレートを取得します。
例)
Azure Firewallの「テンプレートのエクスポート」
以下テンプレートにて複数のパブリックIP構成を追加します。
main.bicep
param azureFirewalls_afw_demo_dev_name string = 'afw-demo-dev'
param publicIPAddresses_pip_demo_dev_fwm_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/publicIPAddresses/pip-demo-dev-fwm'
param virtualNetworks_vnet_demo_dev_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/virtualNetworks/vnet-demo-dev'
param publicIPAddresses_pip_demo_dev_fw_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/publicIPAddresses/pip-demo-dev-fw'
param publicIPAddresses_pip_demo_dev_fw_01_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/publicIPAddresses/pip-demo-dev-fw-01'
param publicIPAddresses_pip_demo_dev_fw_02_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/publicIPAddresses/pip-demo-dev-fw-02'
param firewallPolicies_afwp_demo_dev_externalid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/firewallPolicies/afwp-demo-dev'
param firewallPolicies_afwp_demo_dev_name string = 'afwp-demo-dev'
// Azure FirewallのId
param azureFirewalls_afw_demo_dev_name_resourceid string = '/subscriptions/<your subscriptions ID>/resourceGroups/rg-demo-dev/providers/Microsoft.Network/zureFirewalls'
// Azure Firewallポリシー
resource firewallPolicies_afwp_demo_dev_name_resource 'Microsoft.Network/firewallPolicies@2024-07-01' existing = {
name: firewallPolicies_afwp_demo_dev_name
}
// Azure Firewall
resource azureFirewalls_afw_demo_dev_name_resource 'Microsoft.Network/azureFirewalls@2024-07-01' = {
name: azureFirewalls_afw_demo_dev_name
location: 'japaneast'
properties: {
sku: {
name: 'AZFW_VNet'
tier: 'Standard'
}
threatIntelMode: 'Alert'
additionalProperties: {}
managementIpConfiguration: {
name: 'ipconfig2'
id: '${azureFirewalls_afw_demo_dev_name_resourceid}/azureFirewallIpConfigurations/ipconfig2'
properties: {
publicIPAddress: {
id: publicIPAddresses_pip_demo_dev_fwm_externalid
}
subnet: {
id: '${virtualNetworks_vnet_demo_dev_externalid}/subnets/AzureFirewallManagementSubnet'
}
}
}
ipConfigurations: [
{
name: 'ipconfig1'
id: '${azureFirewalls_afw_demo_dev_name_resourceid}/azureFirewallIpConfigurations/ipconfig1'
properties: {
publicIPAddress: {
id: publicIPAddresses_pip_demo_dev_fw_externalid
}
subnet: {
id: '${virtualNetworks_vnet_demo_dev_externalid}/subnets/AzureFirewallSubnet'
}
}
}
{
name: 'IpConfiguration1'
properties: {
publicIPAddress: {
id: publicIPAddresses_pip_demo_dev_fw_01_externalid
}
}
}
{
name: 'IpConfiguration2'
properties: {
publicIPAddress: {
id: publicIPAddresses_pip_demo_dev_fw_02_externalid
}
}
}
]
networkRuleCollections: []
applicationRuleCollections: []
natRuleCollections: []
firewallPolicy: {
id: firewallPolicies_afwp_demo_dev_externalid
}
}
dependsOn: [
firewallPolicies_afwp_demo_dev_name_resource
]
}
// Azure Firewallポリシーのアプリケーションルールコレクショングループ
resource firewallPolicies_afwp_demo_dev_name_ApplicationRuleCollectionGroup_demo_dev 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2024-07-01' = {
parent: firewallPolicies_afwp_demo_dev_name_resource
name: 'ApplicationRuleCollectionGroup-demo-dev'
location: 'japaneast'
properties: {
priority: 110
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'winupdate-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
{
protocolType: 'Http'
port: 80
}
]
fqdnTags: [
'WindowsUpdate'
]
webCategories: []
targetFqdns: []
targetUrls: []
terminateTLS: false
sourceAddresses: [
'10.0.1.0/24'
]
destinationAddresses: []
sourceIpGroups: []
httpHeadersToInsert: []
}
]
name: 'update-rule-win'
priority: 100
}
{
ruleCollectionType: 'FirewallPolicyFilterRuleCollection'
action: {
type: 'Allow'
}
rules: [
{
ruleType: 'ApplicationRule'
name: 'microsoft-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
]
fqdnTags: []
webCategories: []
targetFqdns: [
'*.microsoft.com'
]
targetUrls: []
terminateTLS: false
sourceAddresses: [
'10.0.1.0/24'
]
destinationAddresses: []
sourceIpGroups: []
httpHeadersToInsert: []
}
{
ruleType: 'ApplicationRule'
name: 'google-rule-01'
protocols: [
{
protocolType: 'Https'
port: 443
}
]
fqdnTags: []
webCategories: []
targetFqdns: [
'*.google.com'
]
targetUrls: []
terminateTLS: false
sourceAddresses: [
'10.0.1.0/24'
]
destinationAddresses: []
sourceIpGroups: []
httpHeadersToInsert: []
}
]
name: 'global-rule-url-microsoft'
priority: 200
}
]
}
dependsOn: [
firewallPolicies_afwp_demo_dev_name_resource
azureFirewalls_afw_demo_dev_name_resource
]
}
// Azure FirewallポリシーのDNATルールコレクショングループ
resource firewallPolicies_afwp_demo_dev_name_DnatRuleCollectionGroup_demo_dev 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2024-07-01' = {
parent: firewallPolicies_afwp_demo_dev_name_resource
name: 'DnatRuleCollectionGroup-demo-dev'
location: 'japaneast'
properties: {
priority: 100
ruleCollections: [
{
ruleCollectionType: 'FirewallPolicyNatRuleCollection'
action: {
type: 'Dnat'
}
rules: [
{
ruleType: 'NatRule'
name: 'dnat-rule-01'
translatedAddress: '10.0.1.4'
translatedPort: '80'
ipProtocols: [
'TCP'
]
sourceAddresses: [
'*'
]
sourceIpGroups: []
destinationAddresses: [
'172.192.42.7'
]
destinationPorts: [
'80'
]
}
{
ruleType: 'NatRule'
name: 'dnat-rule-02'
translatedAddress: '10.0.1.5'
translatedPort: '80'
ipProtocols: [
'TCP'
]
sourceAddresses: [
'*'
]
sourceIpGroups: []
destinationAddresses: [
'172.192.41.223' // pip-demo-dev-fw-01
]
destinationPorts: [
'80'
]
}
{
ruleType: 'NatRule'
name: 'dnat-rule-03'
translatedAddress: '10.0.1.6'
translatedPort: '80'
ipProtocols: [
'TCP'
]
sourceAddresses: [
'*'
]
sourceIpGroups: []
destinationAddresses: [
'172.192.16.192' // pip-demo-dev-fw-02
]
destinationPorts: [
'80'
]
}
]
name: 'dnat-rule-collection'
priority: 100
}
]
}
dependsOn: [
firewallPolicies_afwp_demo_dev_name_resource
azureFirewalls_afw_demo_dev_name_resource
firewallPolicies_afwp_demo_dev_name_ApplicationRuleCollectionGroup_demo_dev
]
}
[気づいた点やメモ]
- 追加分のパブリックIP構成はサブスクリプションIdを必要としない
- "your subscriptions ID"は実際のサブスクリプションIDから置き換えています
- azureFirewalls_afw_demo_dev_name_resourceidは追加します
- azureFirewalls_afw_demo_dev_name_resourceidは元々、azureFirewalls_afw_demo_dev_name_resourcei.dとなっていましたが、修正が必要になります
デプロイを行います。
az deployment group create --template-file main.bicep --resource-group rg-demo-dev
# az deployment group create --template-file main.bicep --resource-group <your resource group name>
パブリックIP構成が2つ作成されました。
3-4 DNATの確認
上記テンプレート内でDNATも更新しているので確認します。
パブリックIP構成で追加したパブリックIPアドレスがDNATとして設定されました。
[気づいた点やメモ]
- リソースのデプロイ順を考慮する必要がある
- DNATはAzure Firwallに関連づけされているパブリックIPアドレスを参照する。そのため、dependsOn使い、Azure Firewallポリシー→Azure Firwall(パブリックIP構成)→ルールの順に設定する
- existingからプロパティを取得出来ない
- existing リソース参照では、properties.ipAddress をデプロイ時に評価されないため、テンプレート内で使うことができない
- existingを利用しない方法か今回のように固定値(destinationAddresses)を入れる必要がある
4. 終わりに
本記事を最後まで読んで頂きましてありがとうございます。
まずは既存のパブリックIPアドレスをパブリックIP構成へ追加する方法から調査し、何とかIaCで一括デプロイできるところまで確認できました。