Suntory Azure Managed Service Standard Document

Azure Virtual Machine
Build Procedure
Document IDAZ-VM-BUILD-001
Version1.0
StatusRELEASED
CreatedMay 18, 2026
RevisedMay 18, 2026
CompanySuntory Holdings Limited
DivisionDigital & AI Global ITG
AuthorTomoki Koyama
Target Proceduresβ‘  Azure Portal (Manual Build)γ€€β‘‘ Terraform Build

This document defines the build procedure for Azure Virtual Machines.
Before starting the build, ensure that AZ-VM-PARAM-001 (Parameter Sheet) has been filled in and approved by the approver.
For the design rationale of each parameter, refer to AZ-VM-DESIGN-001 (Design Document).

Revision History

Ver.Revision Date AuthorDescriptionApprover
1.02026-05-18Tomoki Koyama Initial release (Azure Portal manual build procedure and Terraform build procedure)β€”
πŸ“‹ Related Documents
Document IDDocument Name TypeNotes
AZ-VM-OVERVIEW-001Azure Virtual Machine Service OverviewService Overviewβ€”
AZ-VM-DESIGN-001Azure Virtual Machine Design DocumentDesign DocumentDesign rationale and standard value details
AZ-VM-PARAM-001Azure Virtual Machine Parameter SheetParameter SheetMust be filled in and approved before build
AZ-VM-BUILD-001Azure Virtual Machine Build Procedure (This Document)Build Procedureβ€”

Prerequisites and Pre-work Preparation

⚠️ Do not start the build until the Parameter Sheet (AZ-VM-PARAM-001) has been fully completed and approved by the approver.

Common Prerequisites

No. Checklist Item Details
1Parameter Sheet filled in and approvedAll fields in AZ-VM-PARAM-001 have been filled in and approver approval has been obtained
2Hostname duplication checkConfirm no duplicates via nslookup <hostname> and ServiceNow CMDB
3Access permissions to target SubscriptionConfirm that a Contributor role or higher required for VM creation has been granted
4Confirm existing VNets / SubnetsConfirm that the target VNets and Subnets exist. Refer to Suntory VNets / Network Security Zones
5Confirm Common NSGConfirm that the Common NSG to be assigned (e.g., si2-securitygroup-shd-cs-tokyo-cmn-01) exists
6Confirm Recovery Services VaultConfirm that the Vault for backup exists and that a backup policy has been configured
7Prepare temporary passwordThe temporary password used only during setup should have been received via a separate channel (e.g., Teams DM). Do not record it in this document or email

Terraform-Specific Prerequisites

No.Checklist ItemDetails
1Terraform CLI installationVersion 1.7.0 or higher must be installed (verify with terraform version)
2Azure CLI installation and loginMust be logged in to the target Subscription via az login (verify with az account show)
3Storage Account for Terraform StateThe Storage Account and container for storing tfstate must already exist
4Secure method for passing admin_passwordPass the password via the TF_VAR_admin_password environment variable or Azure Key Vault Data Source. Direct entry in terraform.tfvars is prohibited

Step 1: Azure Portal Manual Build

In Azure Portal (portal.azure.com), navigate to "Virtual machines" β†’ "οΌ‹ Create" β†’ "Azure virtual machine".

☐ The ☐ column is a checkbox. Check each row as you complete the task.
1Basics tab
☐ No. Parameter Suntory Standard Value Reference / Notes
☐1Subscription Select the Subscription specified in the Parameter Sheet Refer to Suntory Subscriptions mapping
☐2Resource group Select existing RG or create a new one
rgp-<region>-<sub>-<env>-<app>-<seq>
Refer to Design doc No.2
☐3Virtual machine name Enter the hostname following the naming convention
e.g., JZJP1WAPSP001
Confirm no duplicates via nslookup / CMDB
☐4Region Japan East (for SJP)
☐5Availability options Production: Availability zone β†’ Self-selected zone
Dev / Test: No infrastructure redundancy required
Enter zone number (1 / 2 / 3). Use different zones for multiple VMs
☐6Security type Trusted launch virtual machines Confidential only for sites handling general customer information
☐7Image (OS) Select the OS required by the system (latest SKU)
e.g. Win: Windows Server 2025 Datacenter Azure Edition
Refer to Design doc No.7 Publisher/Offer/SKU
☐8VM architecture x64
☐9Run with Azure Spot discount Unchecked (OFF) Risk of forced shutdown. OFF by default
☐10VM Size (SKU) Enter/select the SKU specified in the Parameter Sheet
e.g., Standard_D2s_v5
Refer to Design doc No.10 workload-based recommendation table
☐11Enable Hibernation Unchecked (OFF) Stateless design principle
☐12Authentication type Password
Username: AzureVmAdmin
Password: Enter the temporary password received via a separate channel
Change after registering in CyberArk. Do not record the password in this document
☐13Public inbound ports None Public IP is held on the LB side
☐14Licensing (Azure Hybrid Benefit) Unchecked (OFF)
2Disks tab
☐No. ParameterSuntory Standard ValueReference / Notes
☐15VM disk encryption OFF (unchecked) SSE is enabled by default
☐16OS disk size Image default (127 GiB) Select a larger size if 128 GiB or more is required for C drive / root
☐17OS disk type Standard SSD (LRS) Standard SSD is the only option for the OS volume
☐18Delete with VM (OS disk) Checked (ON)
☐19Key management Platform-managed key
☐20Enable Ultra Disk compatibility OFF (enable only if Ultra Disk attachment is planned)
☐21γ€œ26Data disk (only when needed) Click "οΌ‹ Create and attach a new disk"
Name: <hostname>_data01
Source type: None (empty disk)
Size: Specify according to requirements
Storage type: Premium SSD (DB) / Standard SSD (others)
Key management: Platform-managed key
Shared disk: No
Delete with VM: ON
On Windows, use D drive; one disk per drive
3Networking tab
☐No. ParameterSuntory Standard ValueReference / Notes
☐27Virtual network Select existing VNets
e.g., vnt-jp1-sjp-bp-infra-01
Refer to Suntory VNets / Network Security Zones
☐28Subnet Select existing subnet
e.g. (internal): snt-jp1-sjp-bn-infra-tst-01
Determine DMZ or internal based on requirements
☐29Public IP None (select None from the dropdown) Per security policy, no Public IP is assigned to the VM
☐30NIC network security group Select Advanced β†’ Select existing Common NSG under "Configure network security group"
e.g., si2-securitygroup-shd-cs-tokyo-cmn-01
Creating a new NSG is not permitted
☐31Delete NIC when VM is deleted Checked (ON)
☐32Enable accelerated networking Checked (ON) Performance improvement via SR-IOV
☐33Load balancing None LB is requested separately
4Management tab
☐No. ParameterSuntory Standard ValueReference / Notes
☐34Microsoft Defender for Cloud Confirm the "Foundational CSPM Free Plan" message (automatically enabled) No configuration change required
☐35Metadata Security Protocol – IMDS Enabled (ON)
☐36Metadata Security Protocol – WireServer Disabled (OFF) Avoid impact on agent communication
☐37System assigned managed identity Unchecked (OFF)
☐38Login with Microsoft Entra ID Unchecked (OFF) For CyberArk PAM management
☐39Enable auto-shutdown Unchecked (OFF)
☐40Enable backup Checked (ON) Compliant with Suntory backup policy
☐41Recovery Services vault Select the Vault confirmed in consultation with the backup operations team If undecided, select Default and change later
☐42Backup policy subtype Enhanced (mandatory when Trusted launch is selected)
☐43Enable Disaster Recovery Unchecked (OFF) DR to be considered separately
☐44Enable periodic assessment Checked (ON) Periodic check of patch status
☐45Enable hotpatch Dev / Test: ONγ€€Production: OFF
☐46Patch orchestration options Azure-orchestrated (only when No.45 is ON)
☐47Reboot setting Reboot if required (only when No.45 is ON)
5Monitoring tab
☐No. ParameterSuntory Standard ValueReference / Notes
☐48Enable recommended alert rules Checked (ON) Detailed monitoring to be set up separately with NewRelic
☐50Boot diagnostics Disable
☐51Enable OS guest diagnostics Production: ONγ€€Dev / Test: OFF Additional cost incurred
☐52Enable application health monitoring OFF
6Advanced tab
☐No. ParameterSuntory Standard ValueReference / Notes
☐53γ€œ55Extensions / VM applications / Custom dataAll not required (do not configure)Handled via IaC
☐56Performance (NVMe)Enable only when using Premium SSD / Ultra Disk
☐57γ€œ59Host / Capacity reservations / Proximity placement groupAll not required in principle
7Tags tab
Tag keys and values must fully comply with Suntory Azure Foundation Tag Standards.xlsx.
☐No. Tag Key (Tag name)ValueCategory
☐60SubsidiaryCompany abbreviation (e.g., SJP)Required
☐61BusinessUnite.g., SPS (Required for SJP)Optional (Required for SJP)
☐62ServiceNamee.g., Beer Production Planning System (Required for SJP)Required
☐63SystemIDe.g., aaa (Required for SJP)Optional (Required for SJP)
☐64Environmentprod or nonprodRequired
☐65BCPRanke.g., 3Required
☐66Responsibilitye.g., TransformationG (Required for SJP)Optional (Required for SJP)
8Review + Create β†’ Post-Build Tasks
☐ Task Details / Notes
☐ Confirm "Validation passed" on the Review + create tab If there are errors, go back to the relevant tab and fix them
☐ Click the "Create" button to start deployment Deployment takes approximately 3–10 minutes
☐ Confirm deployment completion Confirm the "Deployment complete" message. Open the VM resource via "Go to resource"
☐ Confirm and record the VM's private IP VM blade β†’ "Overview" β†’ record the Private IP address
☐ Confirm Defender for Cloud is enabled VM blade β†’ Defender for Cloud β†’ confirm "Foundational CSPM Free Plan"
☐ Verify tag accuracy Confirm all tags are correctly set on the VM blade β†’ "Tags"
☐ Register in CyberArk Register the VM in CyberArk and force-change the admin password (AzureVmAdmin)
☐ Register in ServiceNow CMDB Register the hostname, IP, and configuration information in CMDB

Step 2: Terraform Build

πŸ“¦ Features of Terraform-based Build

Directory Structure

azure-vm/ β”œβ”€β”€ providers.tf # Azure provider & backend configuration β”œβ”€β”€ variables.tf # Input variable definitions β”œβ”€β”€ main.tf # Main resource definitions (NIC / VM / Disk / Backup) β”œβ”€β”€ outputs.tf # Output value definitions └── terraform.tfvars # Variable values (one file per environment)

providers.tf

πŸ“„ providers.tf Terraform
terraform { required_version = ">= 1.7.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 4.0" } } # Manage Terraform State in Azure Blob Storage backend "azurerm" { resource_group_name = "rgp-jp1-shd-bn-tfstate-001" storage_account_name = "stsjptfstate001" container_name = "tfstate" key = "vm/<vm_name>.tfstate" # Separate file per VM } } provider "azurerm" { features { virtual_machine { # Automatically delete OS disk on VM deletion (Design doc No.18) delete_os_disk_on_deletion = true graceful_shutdown = false skip_shutdown_and_force_delete = false } } }

variables.tf

πŸ“„ variables.tf Terraform
# ─── Basics ──────────────────────────────────────────────────────────────── variable "resource_group_name" { description = "Resource group name (e.g., rgp-jp1-sjp-bn-aaa-001)" type = string } variable "location" { description = "Azure region" type = string default = "japaneast" } variable "vm_name" { description = "VM name (naming convention: <Co><Z><Region><OS><Role><Env><Seq> e.g., JZJP1WAPSP001)" type = string } variable "vm_size" { description = "VM SKU (e.g., Standard_D2s_v5)" type = string default = "Standard_D2s_v5" } variable "os_type" { description = "OS type: windows or linux" type = string default = "windows" validation { condition = contains(["windows", "linux"], var.os_type) error_message = "os_type must be 'windows' or 'linux'." } } variable "availability_zone" { description = "Availability zone (1 / 2 / 3)" type = string default = "1" validation { condition = contains(["1", "2", "3"], var.availability_zone) error_message = "availability_zone must be 1, 2, or 3." } } variable "hotpatch_enabled" { description = "Enable hotpatch (Dev/Test: true / Production: false)" type = bool default = false } # ─── Disks ───────────────────────────────────────────────────────────────── variable "os_disk_size_gb" { description = "OS disk size (0 = Image default / 127 GiB)" type = number default = 0 } variable "data_disks" { description = "List of additional data disks" type = list(object({ name = string # e.g., JZJP1WAPSP001_data01 size_gb = number storage_type = string # StandardSSD_LRS / Premium_LRS / UltraSSD_LRS lun = number # 0, 1, 2, ... (unique number) })) default = [] } # ─── Networking ──────────────────────────────────────────────────────────── variable "subnet_id" { description = "Resource ID of the existing subnet" type = string } variable "nsg_id" { description = "Resource ID of the Common NSG" type = string } # ─── Management ──────────────────────────────────────────────────────────── variable "recovery_vault_name" { description = "Recovery Services Vault name" type = string } variable "backup_policy_id" { description = "Resource ID of the backup policy" type = string } variable "admin_password" { description = "Admin password (temporary; change after registering in CyberArk)" type = string sensitive = true # ⚠️ Do NOT write this value directly in terraform.tfvars. # Pass it via the TF_VAR_admin_password environment variable. } # ─── Tags ────────────────────────────────────────────────────────────────── variable "tags" { description = "Resource tags (compliant with Tag Standards)" type = map(string) default = {} }

main.tf

πŸ“„ main.tf Terraform
locals { admin_username = "AzureVmAdmin" # Design doc No.12 - fixed value is_windows = var.os_type == "windows" # VM resource ID (ternary expression for Windows / Linux) vm_id = local.is_windows ? azurerm_windows_virtual_machine.this[0].id : azurerm_linux_virtual_machine.this[0].id } # ─────────────────────────────────────────────────────────────── # NIC (Design doc No.31 Delete NIC with VM / No.32 Accelerated Networking) # ─────────────────────────────────────────────────────────────── resource "azurerm_network_interface" "this" { name = "${var.vm_name}-nic-01" location = var.location resource_group_name = var.resource_group_name accelerated_networking_enabled = true # No.32 Enabled by default ip_configuration { name = "ipconfig1" subnet_id = var.subnet_id private_ip_address_allocation = "Dynamic" # public_ip_address_id is not set (No.29 – None by default) } tags = var.tags } # Assign Common NSG to NIC (Design doc No.30) resource "azurerm_network_interface_security_group_association" "this" { network_interface_id = azurerm_network_interface.this.id network_security_group_id = var.nsg_id } # ─────────────────────────────────────────────────────────────── # Windows VM # ─────────────────────────────────────────────────────────────── resource "azurerm_windows_virtual_machine" "this" { count = local.is_windows ? 1 : 0 name = var.vm_name resource_group_name = var.resource_group_name location = var.location size = var.vm_size zone = var.availability_zone # No.5 Availability zone admin_username = local.admin_username admin_password = var.admin_password network_interface_ids = [azurerm_network_interface.this.id] # OS disk (No.16–19) os_disk { name = "${var.vm_name}-osdisk" caching = "ReadWrite" storage_account_type = "StandardSSD_LRS" # No.17 Standard SSD disk_size_gb = var.os_disk_size_gb == 0 ? null : var.os_disk_size_gb # OS disk deletion is controlled by features.virtual_machine in providers.tf } # OS image (No.7 - Windows Server 2025 latest) source_image_reference { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" sku = "2025-datacenter-azure-edition" version = "latest" } # Trusted Launch (No.6 Security type = Trusted launch) vtpm_enabled = true secure_boot_enabled = true # Guest OS update settings (No.44–47) patch_assessment_mode = "AutomaticByPlatform" # No.44 periodic assessment ON patch_mode = "AutomaticByPlatform" # No.46 Azure-orchestrated hotpatching_enabled = var.hotpatch_enabled # No.45 Dev/Test: true / Production: false # Azure Hybrid Benefit disabled (No.14) license_type = "None" # Managed Identity disabled (No.37) # Disabled by omitting the identity block tags = var.tags } # ─────────────────────────────────────────────────────────────── # Linux VM (when os_type = "linux") # ─────────────────────────────────────────────────────────────── resource "azurerm_linux_virtual_machine" "this" { count = local.is_windows ? 0 : 1 name = var.vm_name resource_group_name = var.resource_group_name location = var.location size = var.vm_size zone = var.availability_zone admin_username = local.admin_username admin_password = var.admin_password disable_password_authentication = false network_interface_ids = [azurerm_network_interface.this.id] os_disk { name = "${var.vm_name}-osdisk" caching = "ReadWrite" storage_account_type = "StandardSSD_LRS" disk_size_gb = var.os_disk_size_gb == 0 ? null : var.os_disk_size_gb } # OS image (RHEL 9 latest - add an image variable in variables.tf to change) source_image_reference { publisher = "RedHat" offer = "RHEL" sku = "9-lvm-gen2" version = "latest" } vtpm_enabled = true secure_boot_enabled = true patch_assessment_mode = "AutomaticByPlatform" patch_mode = "AutomaticByPlatform" tags = var.tags } # ─────────────────────────────────────────────────────────────── # Data disks (No.21–26) # ─────────────────────────────────────────────────────────────── resource "azurerm_managed_disk" "data" { for_each = { for d in var.data_disks : d.name => d } name = each.value.name resource_group_name = var.resource_group_name location = var.location zone = var.availability_zone storage_account_type = each.value.storage_type create_option = "Empty" # No.22 Source type = None (empty disk) disk_size_gb = each.value.size_gb tags = var.tags } resource "azurerm_virtual_machine_data_disk_attachment" "data" { for_each = { for d in var.data_disks : d.name => d } managed_disk_id = azurerm_managed_disk.data[each.key].id virtual_machine_id = local.vm_id lun = each.value.lun caching = "ReadWrite" } # ─────────────────────────────────────────────────────────────── # Azure Backup (Design doc No.40–42) # ─────────────────────────────────────────────────────────────── resource "azurerm_backup_protected_vm" "this" { resource_group_name = var.resource_group_name recovery_vault_name = var.recovery_vault_name source_vm_id = local.vm_id backup_policy_id = var.backup_policy_id }

outputs.tf

πŸ“„ outputs.tf Terraform
output "vm_id" { description = "Virtual Machine resource ID" value = local.vm_id } output "vm_private_ip" { description = "VM private IP address" value = azurerm_network_interface.this.private_ip_address } output "nic_id" { description = "NIC resource ID" value = azurerm_network_interface.this.id } output "os_disk_id" { description = "OS disk resource ID" value = local.is_windows ? azurerm_windows_virtual_machine.this[0].os_disk[0].name : azurerm_linux_virtual_machine.this[0].os_disk[0].name }

terraform.tfvars (example)

⚠️ Do not include admin_password in this file. Pass it via the TF_VAR_admin_password environment variable or retrieve it from Azure Key Vault.
πŸ“„ terraform.tfvars (example) Terraform
# ─── Basics ─────────────────────────────────────────────────── resource_group_name = "rgp-jp1-sjp-bn-aaa-001" location = "japaneast" vm_name = "JZJP1WAPSP001" vm_size = "Standard_D2s_v5" os_type = "windows" availability_zone = "1" hotpatch_enabled = false # false for Production / true for Dev/Test # ─── Disks ──────────────────────────────────────────────────── os_disk_size_gb = 0 # 0 = Image default (127 GiB) data_disks = [ { name = "JZJP1WAPSP001_data01" size_gb = 100 storage_type = "StandardSSD_LRS" lun = 0 } ] # ─── Networking ─────────────────────────────────────────────── subnet_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgp-jp1-sjp-bn-infra-001/providers/Microsoft.Network/virtualNetworks/vnt-jp1-sjp-bp-infra-01/subnets/snt-jp1-sjp-bn-infra-tst-01" nsg_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgp-jp1-shd-cs-nsg-001/providers/Microsoft.Network/networkSecurityGroups/si2-securitygroup-shd-cs-tokyo-cmn-01" # ─── Management ─────────────────────────────────────────────── recovery_vault_name = "rsv-jp1-sjp-bn-001" backup_policy_id = "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/rgp-jp1-sjp-bn-backup-001/providers/Microsoft.RecoveryServices/vaults/rsv-jp1-sjp-bn-001/backupPolicies/EnhancedPolicy" # ─── Tags (Tag Standards compliant) ────────────────────────── tags = { Subsidiary = "SJP" ServiceName = "Beer Production Planning System" Environment = "nonprod" BCPRank = "3" BusinessUnit = "SPS" SystemID = "aaa" Responsibility = "TransformationG" }

Terraform Execution Steps

1Authentication and Initialization
Terminal
# Log in to Azure (confirm the target Subscription) az login az account show # Switch to the target Subscription az account set --subscription "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # Initialize Terraform (loads backend configuration) terraform init
2Set Password (Environment Variable)
⚠️ Do not include the password in tfvars; always pass it via an environment variable.
Terminal (Windows PowerShell)
# Set environment variable in PowerShell (session-scoped) $env:TF_VAR_admin_password = "<temporary-password>"
Terminal (bash / macOS / Linux)
# Set environment variable in bash export TF_VAR_admin_password="<temporary-password>"
3Review Plan
Terminal
# Preview changes (nothing is actually created) terraform plan -var-file="terraform.tfvars" # Output example (review resources to be added) # Plan: 6 to add, 0 to change, 0 to destroy. # + azurerm_network_interface.this # + azurerm_network_interface_security_group_association.this # + azurerm_windows_virtual_machine.this[0] # + azurerm_managed_disk.data["JZJP1WAPSP001_data01"] # + azurerm_virtual_machine_data_disk_attachment.data["JZJP1WAPSP001_data01"] # + azurerm_backup_protected_vm.this
4Apply (Deploy)
Terminal
# Create resources (-auto-approve applies without confirmation; omit for the first run) terraform apply -var-file="terraform.tfvars" # Enter "yes" at the confirmation prompt to start deployment # Output values (outputs) are displayed upon completion # Output example # Apply complete! Resources: 6 added, 0 changed, 0 destroyed. # Outputs: # vm_id = "/subscriptions/.../virtualMachines/JZJP1WAPSP001" # vm_private_ip = "10.0.1.10" # nic_id = "/subscriptions/.../networkInterfaces/JZJP1WAPSP001-nic-01"
5Post-Build Tasks (Terraform)
☐ Task Details
☐ Confirm and record the private IP Record the IP obtained with terraform output vm_private_ip
☐ Verify deployment in Azure Portal Confirm that VM, NIC, disk, NSG assignment, and backup are correctly configured
☐ Confirm Defender for Cloud is enabled Confirm "Foundational CSPM Free Plan" on the VM blade in Portal
☐ Verify tag accuracy Check all tags on the VM "Tags" tab in Portal
☐ Register in CyberArk Register the VM in CyberArk and force-change the admin password
☐ Register in ServiceNow CMDB Register the hostname, IP, and configuration information in CMDB
☐ Confirm state file backup Confirm that the tfstate file on Azure Blob Storage has been updated

Reference: Resource Deletion Procedure

⚠️ Deletion is an irreversible operation. Obtain approver permission before proceeding.

Deletion via Azure Portal

  1. Open the target VM under "Virtual machines"
  2. Click "Delete"
  3. Confirm the "Apply force delete" checkbox
  4. Confirm that the resources to be deleted (VM, NIC, OS disk) are checked
  5. Enter the VM name and click "Delete"
  6. Remove the VM from CyberArk
  7. Delete or update the corresponding record in ServiceNow CMDB

Deletion via Terraform

Terminal
# Delete all deployed resources (targets resources recorded in State) terraform destroy -var-file="terraform.tfvars" # Enter "yes" at the confirmation prompt # VM, NIC, disk, backup protection, and NSG assignment will be deleted