diff --git a/modules/azure/azure-virtual-machine-starterkit/backplane/README.md b/modules/azure/azure-virtual-machine-starterkit/backplane/README.md deleted file mode 100644 index 8b9713fb..00000000 --- a/modules/azure/azure-virtual-machine-starterkit/backplane/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Azure Virtual Machine Starterkit Backplane - -There is no terraform for starterkit backplane. - -You need to manually create an API Key in meshStack and fill in the variables in the imported definition. - -## How to create an API Key - -> **Note**: you need to have Organization Admin permission in meshStack to create an API Key with admin rights. - -1. In the Admin Area, go to "Access Control" > "API Keys" -2. Create a new API Key with the required permissions for managing: - - Projects - - Tenants - - Building Blocks -3. Copy the key ID to MESHSTACK_API_KEY and secret to MESHSTACK_API_SECRET - -## Required Building Block Definitions - -This starterkit requires the following building block definition to be configured in your meshStack: - -1. **Azure Virtual Machine Building Block**: The actual VM provisioning building block - - Ensure it's configured to work with your Azure platform - - Note the definition version UUID for the starterkit configuration - -## Configuration - -When configuring the starterkit as a building block definition in meshStack: - -1. Set the appropriate platform support (Azure) -2. Configure all required input variables -3. Link to the correct Azure VM building block definition diff --git a/modules/azure/azure-virtual-machine-starterkit/buildingblock/README.md b/modules/azure/azure-virtual-machine-starterkit/buildingblock/README.md index 12c52cf7..f885b670 100644 --- a/modules/azure/azure-virtual-machine-starterkit/buildingblock/README.md +++ b/modules/azure/azure-virtual-machine-starterkit/buildingblock/README.md @@ -21,7 +21,7 @@ The Azure VM Starterkit building block automates the creation of a complete Azur ## Features - Single unified project (no dev/prod separation) -- Flexible VM configuration (Linux or Windows) +- Linux VM with SSH key authentication - Optional public IP assignment - Automatic project admin assignment for the creator - Customizable project tags @@ -64,13 +64,11 @@ No modules. | [landing\_zone\_identifier](#input\_landing\_zone\_identifier) | Azure Landing zone identifier for the tenant. | `string` | n/a | yes | | [name](#input\_name) | This name will be used for the created project and VM | `string` | n/a | yes | | [project\_tags\_yaml](#input\_project\_tags\_yaml) | YAML configuration for project tags. Expected structure:
yaml| `string` | `"{}"` | no | -| [vm\_admin\_password](#input\_vm\_admin\_password) | The admin password for Windows VM (required for Windows). | `string` | `null` | no | | [vm\_admin\_username](#input\_vm\_admin\_username) | The admin username for the VM. | `string` | `"azureuser"` | no | | [vm\_enable\_public\_ip](#input\_vm\_enable\_public\_ip) | Whether to create and assign a public IP address to the VM. | `bool` | `false` | no | | [vm\_location](#input\_vm\_location) | The Azure region where the VM will be deployed. | `string` | `"westeurope"` | no | -| [vm\_os\_type](#input\_vm\_os\_type) | The operating system type (Linux or Windows). | `string` | `"Linux"` | no | | [vm\_size](#input\_vm\_size) | The size of the virtual machine. | `string` | `"Standard_B1s"` | no | -| [vm\_ssh\_public\_key](#input\_vm\_ssh\_public\_key) | SSH public key for Linux VM authentication (required for Linux). | `string` | `null` | no | +| [vm\_ssh\_public\_key](#input\_vm\_ssh\_public\_key) | SSH public key used to authenticate as the VM's admin user. | `string` | n/a | yes | | [workspace\_identifier](#input\_workspace\_identifier) | The identifier of the meshStack workspace | `string` | n/a | yes | ## Outputs @@ -106,7 +104,6 @@ module "vm_starterkit" { username = "jdoe" } - vm_os_type = "Linux" vm_size = "Standard_B2s" vm_location = "westeurope" vm_ssh_public_key = file("~/.ssh/id_rsa.pub") @@ -114,39 +111,9 @@ module "vm_starterkit" { } ``` -### Windows VM - -```hcl -module "vm_starterkit" { - source = "./modules/azure/azure-virtual-machine/starterkit/buildingblock" - - workspace_identifier = "my-workspace" - name = "my-win-vm" - full_platform_identifier = "azure.my-platform" - landing_zone_identifier = "my-landing-zone" - - # Building block UUID - azure_vm_definition_version_uuid = "..." - - creator = { - type = "User" - identifier = "user456" - displayName = "Jane Smith" - username = "jsmith" - } - - vm_os_type = "Windows" - vm_size = "Standard_D2s_v3" - vm_location = "northeurope" - vm_admin_password = var.windows_admin_password - vm_enable_public_ip = true -} -``` - ## Notes - The resource group will be automatically created by the Azure VM building block -- Ensure SSH public key is provided for Linux VMs -- Ensure admin password is provided for Windows VMs +- An SSH public key is required (the VM uses SSH key authentication) - Public IP is disabled by default for security - Project tags can be customized using YAML format diff --git a/modules/azure/azure-virtual-machine-starterkit/buildingblock/logo.png b/modules/azure/azure-virtual-machine-starterkit/buildingblock/logo.png new file mode 100644 index 00000000..7a659778 Binary files /dev/null and b/modules/azure/azure-virtual-machine-starterkit/buildingblock/logo.png differ diff --git a/modules/azure/azure-virtual-machine-starterkit/buildingblock/main.tf b/modules/azure/azure-virtual-machine-starterkit/buildingblock/main.tf index c9b232ff..b6ab924d 100644 --- a/modules/azure/azure-virtual-machine-starterkit/buildingblock/main.tf +++ b/modules/azure/azure-virtual-machine-starterkit/buildingblock/main.tf @@ -61,41 +61,26 @@ resource "meshstack_building_block" "azure_vm" { uuid = meshstack_tenant_v4.vm_tenant.metadata.uuid } display_name = "Azure Virtual Machine" - inputs = merge( - { - vm_name = { - value = jsonencode(local.identifier) - } - location = { - value = jsonencode(var.vm_location) - } - os_type = { - value = jsonencode(var.vm_os_type) - } - vm_size = { - value = jsonencode(var.vm_size) - } - admin_username = { - value = jsonencode(var.vm_admin_username) - } - enable_public_ip = { - value = jsonencode(var.vm_enable_public_ip) - } - }, - # Only send the OS-specific credential input for the matching OS. This mirrors - # the pre-v3 behavior where a null `value_string` omitted the input entirely; - # under v3 a `jsonencode(... : null)` would instead send an explicit JSON null. - var.vm_os_type == "Linux" ? { - ssh_public_key = { - value = jsonencode(var.vm_ssh_public_key) - } - } : {}, - var.vm_os_type == "Windows" ? { - admin_password = { - value = jsonencode(var.vm_admin_password) - } - } : {}, - ) + inputs = { + vm_name = { + value = jsonencode(local.identifier) + } + location = { + value = jsonencode(var.vm_location) + } + vm_size = { + value = jsonencode(var.vm_size) + } + admin_username = { + value = jsonencode(var.vm_admin_username) + } + enable_public_ip = { + value = jsonencode(var.vm_enable_public_ip) + } + ssh_public_key = { + value = jsonencode(var.vm_ssh_public_key) + } + } } } diff --git a/modules/azure/azure-virtual-machine-starterkit/buildingblock/outputs.tf b/modules/azure/azure-virtual-machine-starterkit/buildingblock/outputs.tf index 354c6986..794dc61c 100644 --- a/modules/azure/azure-virtual-machine-starterkit/buildingblock/outputs.tf +++ b/modules/azure/azure-virtual-machine-starterkit/buildingblock/outputs.tf @@ -32,14 +32,14 @@ This starter kit has set up the following resources in workspace `${var.workspac - **Azure Project**: A dedicated project for your virtual machine resources - **Azure Tenant**: An Azure subscription tenant with your chosen landing zone -- **Virtual Machine**: ${var.vm_os_type} VM (${var.vm_size}) in ${var.vm_location} +- **Virtual Machine**: Linux VM (${var.vm_size}) in ${var.vm_location} --- ## VM Details - **VM Name**: ${local.identifier} -- **Operating System**: ${var.vm_os_type} +- **Operating System**: Linux - **Size**: ${var.vm_size} - **Region**: ${var.vm_location} - **Public IP**: ${var.vm_enable_public_ip ? "Enabled" : "Disabled"} @@ -50,9 +50,7 @@ This starter kit has set up the following resources in workspace `${var.workspac ## Next Steps ### 1. Access Your VM -${var.vm_os_type == "Linux" && var.vm_enable_public_ip ? "- Connect via SSH using your provided SSH key" : ""} -${var.vm_os_type == "Windows" && var.vm_enable_public_ip ? "- Connect via RDP using the admin credentials" : ""} -${!var.vm_enable_public_ip ? "- Connect through Azure Bastion or VPN (no public IP assigned)" : ""} +${var.vm_enable_public_ip ? "- Connect via SSH using your provided SSH key" : "- Connect through Azure Bastion or VPN (no public IP assigned)"} ### 2. View Azure Resources - [Access Azure Tenant](/#/w/${var.workspace_identifier}/p/${meshstack_project.vm_project.metadata.name}/i/${var.full_platform_identifier}/overview) diff --git a/modules/azure/azure-virtual-machine-starterkit/buildingblock/variables.tf b/modules/azure/azure-virtual-machine-starterkit/buildingblock/variables.tf index 99f51169..69ac9ff7 100644 --- a/modules/azure/azure-virtual-machine-starterkit/buildingblock/variables.tf +++ b/modules/azure/azure-virtual-machine-starterkit/buildingblock/variables.tf @@ -63,16 +63,6 @@ variable "vm_location" { default = "westeurope" } -variable "vm_os_type" { - type = string - description = "The operating system type (Linux or Windows)." - default = "Linux" - validation { - condition = contains(["Linux", "Windows"], var.vm_os_type) - error_message = "vm_os_type must be either 'Linux' or 'Windows'" - } -} - variable "vm_size" { type = string description = "The size of the virtual machine." @@ -87,15 +77,7 @@ variable "vm_admin_username" { variable "vm_ssh_public_key" { type = string - description = "SSH public key for Linux VM authentication (required for Linux)." - default = null -} - -variable "vm_admin_password" { - type = string - description = "The admin password for Windows VM (required for Windows)." - default = null - sensitive = true + description = "SSH public key used to authenticate as the VM's admin user." } variable "vm_enable_public_ip" { diff --git a/modules/azure/azure-virtual-machine-starterkit/e2e/main.tf b/modules/azure/azure-virtual-machine-starterkit/e2e/main.tf new file mode 100644 index 00000000..e1779b84 --- /dev/null +++ b/modules/azure/azure-virtual-machine-starterkit/e2e/main.tf @@ -0,0 +1,116 @@ +variable "test_context" { + type = object({ + hub_git_ref = string + workspace = string + name_suffix = string + + # Mode discriminator: set in foundation mode to order an already-deployed BBD version; + # null in build-from-source mode, which builds the child + starter-kit BBDs from hub source. + bbd_version_ref = optional(object({ + uuid = string + })) + + # Azure platform coordinates. Needed in build-from-source mode to provision the child VM + # backplane and to let the starter kit create its project/tenant on a landing zone. + fixtures = optional(object({ + azure = object({ + entra_tenant_id = string + subscription_uuid = string + full_platform_identifier = string + landing_zone_identifier = string + }) + })) + }) + nullable = false +} + +locals { + build_from_source = var.test_context.bbd_version_ref == null + + # Role definitions/assignments for the child VM backplane are scoped here. In the smoke-test + # environment this is the fixture subscription; a real foundation scopes its backplane at the + # management group that parents all landing-zone subscriptions (see the foundation deploy unit). + azure_scope = local.build_from_source ? "/subscriptions/${var.test_context.fixtures.azure.subscription_uuid}" : null + + # VM project/name must satisfy ^[a-zA-Z0-9-]{0,24}$. name_suffix is a 14-digit timestamp. + vm_name = "vm${var.test_context.name_suffix}" +} + +# A throwaway SSH key so the Linux VM can be created (azurerm requires a valid RSA public key and +# disables password auth for Linux). The private key is never used — the smoke test only asserts the +# building block reaches SUCCEEDED. +resource "tls_private_key" "vm" { + count = local.build_from_source ? 1 : 0 + algorithm = "RSA" + rsa_bits = 4096 +} + +# Child Azure VM building block definition (composed by the starter kit). +module "azure_virtual_machine" { + count = local.build_from_source ? 1 : 0 + source = "../../azure-virtual-machine" + + meshstack = { + owning_workspace_identifier = var.test_context.workspace + tags = {} + } + hub = { + git_ref = var.test_context.hub_git_ref + bbd_draft = true + } + + azure_tenant_id = var.test_context.fixtures.azure.entra_tenant_id + azure_scope = local.azure_scope + + # Unique backplane name per run so role definitions don't clash across concurrent/retried runs. + backplane_name = "hub-e2e-vm-${var.test_context.name_suffix}" +} + +# Azure VM starter-kit building block definition (the module under test). +module "starterkit" { + count = local.build_from_source ? 1 : 0 + source = "../" + + meshstack = { + owning_workspace_identifier = var.test_context.workspace + tags = {} + } + hub = { + git_ref = var.test_context.hub_git_ref + bbd_draft = true + } + + full_platform_identifier = var.test_context.fixtures.azure.full_platform_identifier + landing_zone_identifier = var.test_context.fixtures.azure.landing_zone_identifier + azure_vm_definition_version_uuid = module.azure_virtual_machine[0].building_block_definition.version_ref.uuid +} + +locals { + version_ref = local.build_from_source ? module.starterkit[0].building_block_definition.version_ref : var.test_context.bbd_version_ref +} + +resource "meshstack_building_block" "this" { + # Force the whole composition (both BBDs + child backplane) to exist before the run, and to be + # torn down only after the building block's delete run completes. + depends_on = [module.starterkit, module.azure_virtual_machine] + + wait_for_completion = true + + spec = { + building_block_definition_version_ref = { uuid = local.version_ref.uuid } + + display_name = "smoke-test-azure-vm-starterkit-${var.test_context.name_suffix}" + target_ref = { + kind = "meshWorkspace" + name = var.test_context.workspace + } + + inputs = { + name = { value = jsonencode(local.vm_name) } + vm_location = { value = jsonencode("westeurope") } + vm_size = { value = jsonencode("Standard_B1s") } + vm_enable_public_ip = { value = jsonencode(false) } + vm_ssh_public_key = { value = jsonencode(local.build_from_source ? tls_private_key.vm[0].public_key_openssh : "") } + } + } +} diff --git a/modules/azure/azure-virtual-machine-starterkit/e2e/terraform.tf b/modules/azure/azure-virtual-machine-starterkit/e2e/terraform.tf new file mode 100644 index 00000000..db5c2761 --- /dev/null +++ b/modules/azure/azure-virtual-machine-starterkit/e2e/terraform.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + meshstack = { + source = "meshcloud/meshstack" + } + azurerm = { + source = "hashicorp/azurerm" + } + azuread = { + source = "hashicorp/azuread" + } + tls = { + source = "hashicorp/tls" + } + } +} diff --git a/modules/azure/azure-virtual-machine-starterkit/e2e/tests/azure_virtual_machine_starterkit_hub.tftest.hcl b/modules/azure/azure-virtual-machine-starterkit/e2e/tests/azure_virtual_machine_starterkit_hub.tftest.hcl new file mode 100644 index 00000000..0dcb21c9 --- /dev/null +++ b/modules/azure/azure-virtual-machine-starterkit/e2e/tests/azure_virtual_machine_starterkit_hub.tftest.hcl @@ -0,0 +1,11 @@ +run "azure_virtual_machine_starterkit_hub" { + assert { + condition = meshstack_building_block.this.status.status == "SUCCEEDED" + error_message = "azure/azure-virtual-machine-starterkit hub building block expected SUCCEEDED, got ${meshstack_building_block.this.status.status}" + } + + assert { + condition = length(jsondecode(meshstack_building_block.this.status.outputs["summary"].value)) > 0 + error_message = "expected a non-empty summary output from the starter kit" + } +} diff --git a/modules/azure/azure-virtual-machine-starterkit/meshstack_integration.tf b/modules/azure/azure-virtual-machine-starterkit/meshstack_integration.tf new file mode 100644 index 00000000..1a4a3ebc --- /dev/null +++ b/modules/azure/azure-virtual-machine-starterkit/meshstack_integration.tf @@ -0,0 +1,233 @@ +variable "full_platform_identifier" { + type = string + description = "Full identifier of the Azure platform (example: `azure.westeurope`)." +} + +variable "landing_zone_identifier" { + type = string + description = "Azure landing zone identifier for the created tenant." +} + +variable "azure_vm_definition_version_uuid" { + type = string + description = "Version UUID of the Azure Virtual Machine building block definition that this starter kit composes." +} + +variable "project_tags" { + type = map(list(string)) + default = {} + description = "Tags applied to the created meshProject." +} + +variable "notification_subscribers" { + type = list(string) + default = [] +} + +variable "meshstack" { + type = object({ + owning_workspace_identifier = string + + tags = optional(map(list(string)), {}) + }) + description = "Shared meshStack context. Tags are optional and propagated to building block definition metadata." +} + +variable "hub" { + type = object({ + git_ref = optional(string, "main") + bbd_draft = optional(bool, true) + }) + const = true + default = { + git_ref = "main" + bbd_draft = true + } + description = <<-EOT + `git_ref`: Hub release reference. Set to a tag (e.g. 'v1.2.3') or branch or commit sha of the meshstack-hub repo. + `bbd_draft`: If true, the building block definition version is kept in draft mode, which allows changing it (useful during development in LCF/ICF). + EOT +} + +output "building_block_definition" { + description = "BBD is consumed in building block compositions." + value = { + uuid = meshstack_building_block_definition.this.metadata.uuid + version_ref = var.hub.bbd_draft ? meshstack_building_block_definition.this.version_latest : meshstack_building_block_definition.this.version_latest_release + } +} + +locals { + name_regex = "^[a-zA-Z0-9-]{0,24}$" # keep aligned with the other starter kits (project/name length limits) +} + +resource "meshstack_building_block_definition" "this" { + metadata = { + owned_by_workspace = var.meshstack.owning_workspace_identifier + tags = var.meshstack.tags + } + + spec = { + description = "The Azure Virtual Machine Starterkit provisions a dedicated meshProject and Azure tenant, then composes an Azure Virtual Machine building block to deliver a ready-to-use VM." + display_name = "Azure Virtual Machine Starterkit" + notification_subscribers = var.notification_subscribers + symbol = "https://raw.githubusercontent.com/meshcloud/meshstack-hub/${var.hub.git_ref}/modules/azure/azure-virtual-machine-starterkit/buildingblock/logo.png" + + readme = chomp(<<-EOT + The **Azure Virtual Machine Starterkit** gives application teams a pre-configured Azure environment with a single virtual machine, following best practices. It creates a dedicated meshProject and Azure tenant and composes the Azure Virtual Machine building block to provision the VM. + + ## 🎯 When to use it + + This building block is ideal for teams that: + + - Need a quick, governed Azure VM without assembling project, tenant and VM wiring by hand. + - Want a Linux VM in a landing-zone-compliant tenant. + + ## Resources Created + + - **Azure Project**: A dedicated meshProject for your virtual machine resources. + - **Azure Tenant**: An Azure subscription tenant on your chosen landing zone. + - **Virtual Machine**: A Linux VM (composed via the Azure Virtual Machine building block). + + ## Shared Responsibilities + + | Responsibility | Platform Team | Application Team | + | ----------------------------------------- | ------------- | ---------------- | + | Provide the Azure platform + landing zone | ✅ | ❌ | + | Provision project, tenant and VM | ✅ | ❌ | + | Manage workloads on the VM | ❌ | ✅ | + | Rotate SSH keys / credentials | ❌ | ✅ | + + --- + EOT + ) + run_transparency = true + } + + version_spec = { + draft = var.hub.bbd_draft + implementation = { + terraform = { + repository_url = "https://github.com/meshcloud/meshstack-hub.git" + terraform_version = "1.11.5" + async = false + ref_name = var.hub.git_ref + repository_path = "modules/azure/azure-virtual-machine-starterkit/buildingblock" + use_mesh_http_backend_fallback = true + } + } + inputs = { + "creator" = { + assignment_type = "AUTHOR" + description = "Information about the creator of the resources who will be assigned Project Admin role." + display_name = "Creator" + type = "CODE" + } + "workspace_identifier" = { + assignment_type = "WORKSPACE_IDENTIFIER" + display_name = "Workspace Identifier" + type = "STRING" + } + "full_platform_identifier" = { + argument = jsonencode(var.full_platform_identifier) + assignment_type = "STATIC" + display_name = "Full Platform Identifier" + type = "STRING" + } + "landing_zone_identifier" = { + argument = jsonencode(var.landing_zone_identifier) + assignment_type = "STATIC" + display_name = "Landing Zone Identifier" + type = "STRING" + } + "azure_vm_definition_version_uuid" = { + argument = jsonencode(var.azure_vm_definition_version_uuid) + assignment_type = "STATIC" + display_name = "Azure VM Definition Version UUID" + type = "STRING" + } + "project_tags_yaml" = { + # buildingblock expects a YAML string it yamldecodes; jsonencode wraps the YAML string as the STATIC argument. + argument = jsonencode(yamlencode(var.project_tags)) + assignment_type = "STATIC" + description = "Tags for the created project (YAML)." + display_name = "Project Tags" + type = "STRING" + } + "name" = { + assignment_type = "USER_INPUT" + description = "Used for the created project and VM." + display_name = "Name" + type = "STRING" + value_validation_regex = local.name_regex + validation_regex_error_message = "No underscore/dots/spaces are allowed. A maximum length of 25 characters is allowed." + } + "vm_location" = { + assignment_type = "USER_INPUT" + description = "Azure region where the VM is deployed." + display_name = "VM Location" + type = "STRING" + default_value = jsonencode("westeurope") + } + "vm_size" = { + assignment_type = "USER_INPUT" + description = "Size of the virtual machine." + display_name = "VM Size" + type = "STRING" + default_value = jsonencode("Standard_B1s") + } + "vm_admin_username" = { + assignment_type = "USER_INPUT" + description = "Admin username for the VM." + display_name = "VM Admin Username" + type = "STRING" + default_value = jsonencode("azureuser") + } + "vm_ssh_public_key" = { + assignment_type = "USER_INPUT" + description = "SSH public key used to authenticate as the VM's admin user." + display_name = "VM SSH Public Key" + type = "STRING" + } + "vm_enable_public_ip" = { + assignment_type = "USER_INPUT" + description = "Whether to assign a public IP to the VM." + display_name = "Enable Public IP" + type = "BOOLEAN" + default_value = jsonencode(false) + } + } + outputs = { + "summary" = { + assignment_type = "SUMMARY" + display_name = "Summary" + type = "STRING" + } + } + permissions = [ + "BUILDINGBLOCK_DELETE", + "BUILDINGBLOCK_LIST", + "BUILDINGBLOCK_SAVE", + "PROJECTPRINCIPALROLE_DELETE", + "PROJECTPRINCIPALROLE_LIST", + "PROJECTPRINCIPALROLE_SAVE", + "PROJECT_DELETE", + "PROJECT_LIST", + "PROJECT_SAVE", + "TENANT_DELETE", + "TENANT_LIST", + "TENANT_SAVE", + ] + } +} + +terraform { + required_version = ">= 1.12.0" + + required_providers { + meshstack = { + source = "meshcloud/meshstack" + version = ">= 0.21.0" + } + } +} diff --git a/modules/azure/azure-virtual-machine/buildingblock/APP_TEAM_README.md b/modules/azure/azure-virtual-machine/buildingblock/APP_TEAM_README.md deleted file mode 100644 index e25ad761..00000000 --- a/modules/azure/azure-virtual-machine/buildingblock/APP_TEAM_README.md +++ /dev/null @@ -1,120 +0,0 @@ -# Azure Virtual Machine - -## Description -An Azure Virtual Machine (VM) is an on-demand, scalable computing resource that provides the flexibility of virtualization without the need to buy and maintain physical hardware. Azure VMs support both Linux and Windows operating systems and can be configured with various sizes and capabilities to meet specific workload requirements. - -Key features include: -- **Flexible Sizing**: Choose from a wide range of VM sizes optimized for different workloads (general purpose, compute-intensive, memory-optimized, etc.) -- **Multiple OS Support**: Run Linux distributions or Windows Server -- **Managed Disks**: Persistent storage for OS and data with various performance tiers -- **Network Isolation**: Deploy VMs in virtual networks with network security groups for enhanced security -- **Managed Identity**: System-assigned identities for secure authentication to Azure services without storing credentials - -## Usage Motivation -This building block provisions Azure Virtual Machines to provide isolated, dedicated compute resources for various workloads. VMs are ideal when you need: -- Full control over the operating system and software stack -- Ability to install custom applications or legacy software -- Dedicated compute resources with predictable performance -- Migration of on-premises workloads to the cloud (lift-and-shift) - -## Usage Examples - -### Development and Testing Environments -Deploy VMs for development teams to create isolated environments for building and testing applications. Each team can have their own VM with specific configurations, tools, and dependencies without affecting other teams. - -### Application Hosting -Host web applications, APIs, or microservices on VMs when containerization isn't feasible or when you need full OS control. The VM can run application servers like Apache, Nginx, IIS, or custom software stacks. - -### Database Servers -Deploy VMs to host database management systems like PostgreSQL, MySQL, SQL Server, or MongoDB when managed database services don't meet specific requirements or when migrating existing database installations. - -### Build and CI/CD Agents -Use VMs as build agents for CI/CD pipelines, providing isolated environments for compiling code, running tests, and creating deployment artifacts. - -### Data Processing -Deploy VMs for batch processing jobs, data transformation tasks, or computational workloads that require specific software or configurations. - -## Shared Responsibility - -| Responsibility | Platform Team | Application Team | -|------------------------|--------------|----------------| -| Provisioning and configuring VM infrastructure | ✅ | ❌ | -| Managing virtual networks and subnets | ✅ | ❌ | -| Providing secure access methods (Bastion, VPN) | ✅ | ❌ | -| Installing and configuring applications | ❌ | ✅ | -| Managing OS updates and patches | ❌ | ✅ | -| Configuring firewall rules and NSG policies | ✅ | ⚠️ (within team's NSG) | -| Backup and disaster recovery configuration | ⚠️ | ✅ | -| Monitoring application performance | ❌ | ✅ | -| Managing user access and SSH keys | ❌ | ✅ | - -## VM Size Selection Guide - -Choose the appropriate VM size based on your workload requirements: - -| VM Series | Use Case | Example Sizes | -|-----------|----------|---------------| -| **B-Series** | Burstable, cost-effective for low CPU utilization | B1s, B2s, B2ms | -| **D-Series** | General purpose, balanced CPU-to-memory | D2s_v3, D4s_v3, D8s_v3 | -| **E-Series** | Memory-optimized, high memory-to-CPU ratio | E4s_v3, E8s_v3, E16s_v3 | -| **F-Series** | Compute-optimized, high CPU-to-memory ratio | F4s_v2, F8s_v2, F16s_v2 | - -Start with smaller sizes (e.g., B2s for dev/test) and scale up as needed. - -## Recommendations for Secure and Efficient VM Usage - -### Security Best Practices -- **Avoid Public IPs**: Use Azure Bastion or VPN for remote access instead of exposing VMs to the internet -- **Use SSH Keys**: For Linux VMs, always use SSH key authentication instead of passwords -- **Strong Passwords**: For Windows VMs, use complex passwords and consider Azure AD integration -- **Managed Identity**: Leverage system-assigned managed identities to authenticate to Azure services -- **Network Security Groups**: Configure NSG rules to allow only necessary traffic -- **Regular Updates**: Keep the OS and applications updated with the latest security patches -- **Azure Security Center**: Enable for security recommendations and threat detection - -### Performance and Cost Optimization -- **Right-Size VMs**: Monitor resource utilization and adjust VM size accordingly -- **Use Premium SSD**: For production workloads requiring consistent performance -- **Spot Instances**: Enable spot instances for dev/test and non-critical workloads to save up to 90% on costs - - Be aware that spot VMs can be evicted when Azure needs capacity - - Best for stateless workloads and batch processing - - Not suitable for production or databases with local data -- **Reserved Instances**: Purchase reserved instances for long-running production VMs to save costs -- **Auto-Shutdown**: Configure automatic shutdown schedules for non-production VMs -- **Azure Hybrid Benefit**: Use existing Windows Server licenses to reduce costs - -### Operational Best Practices -- **Backup Strategy**: Implement regular backups using Azure Backup -- **Monitoring**: Enable Azure Monitor and configure alerts for critical metrics -- **Tagging**: Apply consistent tags for cost tracking and resource management -- **Documentation**: Document VM purpose, configurations, and dependencies -- **Disaster Recovery**: Plan for disaster recovery with Azure Site Recovery if needed - -### Data Disk Best Practices -- **Separate OS and Data**: Use separate data disks for application data -- **Choose Right Storage Type**: Use Premium SSD for I/O intensive workloads, Standard SSD for regular workloads -- **Plan Disk Size**: Provision appropriate disk size upfront as resizing requires downtime - -## Getting Started - -### For Linux VMs -1. Generate an SSH key pair if you don't have one: `ssh-keygen -t rsa -b 4096` -2. Request VM provisioning with your public key -3. Connect via SSH: `ssh azureuser@
key1:
- "value1"
- "value2"
key2:
- "value3"