Terraform を利用して、Linux (Ubuntu) VM および同一サブネットに Blob Storage の Private Endpoint を作成してみました。
■providers.tf
terraform { required_version = ">= 1.4" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } random = { source = "hashicorp/random" version = "~> 3.0" } } } provider "azurerm" { features {} }
■variables.tf
variable "resource_group_location" { default = "japaneast" description = "Location of the resource group." } variable "resource_group_name_prefix" { default = "<resource_group_name-prefix>" description = "Prefix of the resource group name that's combined with a random ID so name is unique in your Azure subscription." }
■main.tf
resource "random_pet" "rg_name" { prefix = var.resource_group_name_prefix } resource "azurerm_resource_group" "rg" { location = var.resource_group_location name = random_pet.rg_name.id } # Create a virtual network resource "azurerm_virtual_network" "vnet" { name = "${random_pet.rg_name.id}-vnet" address_space = ["10.10.0.0/16"] location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name } # Create a subnet resource "azurerm_subnet" "subnet" { name = "${random_pet.rg_name.id}-subnet" resource_group_name = azurerm_resource_group.rg.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = ["10.10.10.0/24"] } # Create a network security group resource "azurerm_network_security_group" "nsg" { name = "${random_pet.rg_name.id}-nsg" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name } # Create a network security group rule resource "azurerm_network_security_rule" "nsg_rule" { name = "SSH" priority = 1001 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = "*" destination_address_prefix = "*" resource_group_name = azurerm_resource_group.rg.name network_security_group_name = azurerm_network_security_group.nsg.name } # Create a network interface resource "azurerm_network_interface" "nic" { name = "${random_pet.rg_name.id}-vm01-nic" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name ip_configuration { name = "${random_pet.rg_name.id}-vm01-ip1-config" subnet_id = azurerm_subnet.subnet.id private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.publicip.id } } # Create a public IP address resource "azurerm_public_ip" "publicip" { name = "${random_pet.rg_name.id}-vm01-publicip" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name allocation_method = "Dynamic" } # Create a Linux virtual machine resource "azurerm_linux_virtual_machine" "vm" { name = "${random_pet.rg_name.id}-vm01" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name size = "Standard_DS1_v2" admin_username = "admin" network_interface_ids = [ azurerm_network_interface.nic.id, ] admin_ssh_key { username = "admin" public_key = file("./ssh/id_rsa.pub") } source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-jammy" sku = "22_04-lts-gen2" version = "latest" } os_disk { name = "myOsDisk" caching = "ReadWrite" storage_account_type = "Standard_LRS" } } # create a storage account resource "azurerm_storage_account" "storage" { name = replace("${random_pet.rg_name.id}st01", "-", "") resource_group_name = azurerm_resource_group.rg.name location = azurerm_resource_group.rg.location account_tier = "Standard" account_replication_type = "LRS" } # create a blob container resource "azurerm_storage_container" "container" { name = "blob01" storage_account_name = azurerm_storage_account.storage.name container_access_type = "private" } # create a private endpoint for blob storage resource "azurerm_private_endpoint" "blob_endpoint" { name = "${random_pet.rg_name.id}-blob-endpoint" location = azurerm_resource_group.rg.location resource_group_name = azurerm_resource_group.rg.name subnet_id = azurerm_subnet.subnet.id private_service_connection { name = "${random_pet.rg_name.id}-blob-endpoint-connection" private_connection_resource_id = azurerm_storage_account.storage.id subresource_names = ["blob"] is_manual_connection = false } } # Create Azure Storage Account Network Rules # ref : https://gmusumeci.medium.com/using-private-endpoint-in-azure-storage-account-with-terraform-49b4734ada34 resource "azurerm_storage_account_network_rules" "rules" { storage_account_id = azurerm_storage_account.storage.id default_action = "Deny" bypass = ["Metrics", "Logging", "AzureServices"] }
ちなみに、今回 GitHub Copilot を使って書いたのですが、途中から「次はサブネットね」「次はストレージアカウントね」という感じで先回りして提示してくれました。恐ろしく、そしてスゴイと思いつつ、サンプル作成の生産性は爆上がりだなというのは改めて感じました。
動作確認として、Ubuntu に azcopy をインストールしてコピーしてみます。インストールは簡単で、バイナリを配置 (gzip ファイルをダウンロードして解凍) するだけです。
$ ./azcopy copy NOTICE.txt "https://<account>.blob.core.windows.net/blob01?<SAS Token>" INFO: Scanning... INFO: Any empty folders will not be processed, because source and/or destination doesn't have full folder support Job 3ab8a757-e3d3-8d47-423a-18cd10a39d63 has started Log file is located at: /home/sou/.azcopy/3ab8a757-e3d3-8d47-423a-18cd10a39d63.log 100.0 %, 1 Done, 0 Failed, 0 Pending, 0 Skipped, 1 Total, 2-sec Throughput (Mb/s): 2.0447 Job 3ab8a757-e3d3-8d47-423a-18cd10a39d63 summary Elapsed Time (Minutes): 0.0334 Number of File Transfers: 1 Number of Folder Property Transfers: 0 Number of Symlink Transfers: 0 Total Number of Transfers: 1 Number of File Transfers Completed: 1 Number of Folder Transfers Completed: 0 Number of File Transfers Failed: 0 Number of Folder Transfers Failed: 0 Number of File Transfers Skipped: 0 Number of Folder Transfers Skipped: 0 TotalBytesTransferred: 512602 Final Job Status: Completed
一点、名前解決として注意点があり、「hoge.privatelink.blob.core.windows.net」という名前ではなく、「hoge.blob.core.windows.net」という名前でプライベートエンドポイントの IP に解決するようにします。これは、接続時の SSL 証明書において前者の名前では有効な証明書と認識されないためです。今回は手抜きで hosts に書きました😅
Microsoft 社のプラクティスとしても、そのような名前解決とするよう示唆があります。
なお、今回は Private DNS まで作成しませんでしたが、それらを含めた参考情報は下記のサイトなどが参考になると思います。