CloudExpert UP

Criar VM Windows com Bicep

Azure Virtual Machine Intermediário 25 min Gratuito

📋 Visão Geral

Neste workshop você aprenderá a provisionar uma máquina virtual Windows Server usando Bicep — a linguagem nativa de Infrastructure as Code da Microsoft para Azure.

Bicep é uma linguagem declarativa que simplifica a criação de templates ARM, oferecendo:

  • Sintaxe concisa e legível (vs. JSON ARM templates)
  • Suporte nativo no Azure CLI e Azure PowerShell
  • IntelliSense completo no VS Code
  • Deploy com Trusted Launch e segurança avançada
💡 Dica: Bicep é a linguagem oficial recomendada pela Microsoft para IaC no Azure. Dominar Bicep é essencial para as certificações AZ-104, AZ-400 e AZ-305.

✅ Pré-requisitos

Antes de começar, certifique-se de ter:

  • Uma subscrição Azure ativa
  • Azure CLI instalado (versão 2.20.0+) com Bicep CLI incluído
  • VS Code com extensão Bicep instalada (recomendado)
⚠️ Importante: Este workshop pode gerar custos na sua subscrição Azure. Lembre-se de limpar os recursos ao final.

🚀 Configuração do Ambiente

Prepare o ambiente para o deploy com Bicep:

1. Autenticar e criar Resource Group

# Login no Azure
az login

# Criar Resource Group
az group create --name myResourceGroupBicep --location eastus

2. Verificar instalação do Bicep

# Verificar versão do Bicep
az bicep version

# Instalar/atualizar se necessário
az bicep install
az bicep upgrade

⚙️ Implementação

Crie o arquivo main.bicep com toda a infraestrutura necessária:

Passo 1: Definir parâmetros

O arquivo Bicep começa com parâmetros configuráveis:

@description('Username for the Virtual Machine.')
param adminUsername string

@description('Password for the Virtual Machine.')
@minLength(12)
@secure()
param adminPassword string

@description('Unique DNS Name for the Public IP.')
param dnsLabelPrefix string = toLower('${vmName}-${uniqueString(resourceGroup().id, vmName)}')

@description('Name of the virtual machine.')
param vmName string = 'simple-vm'

@description('Location for all resources.')
param location string = resourceGroup().location

@description('Size of the virtual machine.')
param vmSize string = 'Standard_D2s_v5'

@description('Windows Server version.')
@allowed([
  '2016-datacenter-gensecond'
  '2016-datacenter-server-core-g2'
  '2019-datacenter-gensecond'
  '2019-datacenter-core-g2'
  '2022-datacenter-azure-edition'
  '2022-datacenter-azure-edition-core'
  '2022-datacenter-core-g2'
  '2022-datacenter-g2'
])
param OSVersion string = '2022-datacenter-azure-edition'

@description('Security Type of the Virtual Machine.')
@allowed([
  'Standard'
  'TrustedLaunch'
])
param securityType string = 'TrustedLaunch'

Passo 2: Definir variáveis e recursos de rede

var storageAccountName = 'bootdiags${uniqueString(resourceGroup().id)}'
var nicName = 'myVMNic'
var addressPrefix = '10.0.0.0/16'
var subnetName = 'Subnet'
var subnetPrefix = '10.0.0.0/24'
var virtualNetworkName = 'MyVNET'
var networkSecurityGroupName = 'default-NSG'
var securityProfileJson = {
  uefiSettings: {
    secureBootEnabled: true
    vTpmEnabled: true
  }
  securityType: securityType
}
var extensionName = 'GuestAttestation'
var extensionPublisher = 'Microsoft.Azure.Security.WindowsAttestation'
var extensionVersion = '1.0'
var maaTenantName = 'GuestAttestation'

// Storage Account
resource storageAccount 'Microsoft.Storage/storageAccounts@2022-05-01' = {
  name: storageAccountName
  location: location
  sku: { name: 'Standard_LRS' }
  kind: 'Storage'
}

// Public IP Address
resource publicIp 'Microsoft.Network/publicIPAddresses@2022-05-01' = {
  name: '${vmName}-PublicIP'
  location: location
  sku: { name: 'Basic' }
  properties: {
    publicIPAllocationMethod: 'Dynamic'
    dnsSettings: { domainNameLabel: dnsLabelPrefix }
  }
}

// Network Security Group
resource nsg 'Microsoft.Network/networkSecurityGroups@2022-05-01' = {
  name: networkSecurityGroupName
  location: location
  properties: {
    securityRules: [
      {
        name: 'default-allow-3389'
        properties: {
          priority: 1000
          access: 'Allow'
          direction: 'Inbound'
          protocol: 'Tcp'
          sourcePortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '3389'
        }
      }
      {
        name: 'default-allow-80'
        properties: {
          priority: 1001
          access: 'Allow'
          direction: 'Inbound'
          protocol: 'Tcp'
          sourcePortRange: '*'
          sourceAddressPrefix: '*'
          destinationAddressPrefix: '*'
          destinationPortRange: '80'
        }
      }
    ]
  }
}

// Virtual Network
resource vnet 'Microsoft.Network/virtualNetworks@2022-05-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: { addressPrefixes: [ addressPrefix ] }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: subnetPrefix
          networkSecurityGroup: { id: nsg.id }
        }
      }
    ]
  }
}

// Network Interface
resource nic 'Microsoft.Network/networkInterfaces@2022-05-01' = {
  name: nicName
  location: location
  properties: {
    ipConfigurations: [
      {
        name: 'ipconfig1'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: { id: publicIp.id }
          subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnet.name, subnetName) }
        }
      }
    ]
  }
}

Passo 3: Definir a Virtual Machine com Trusted Launch

// Windows Virtual Machine
resource vm 'Microsoft.Compute/virtualMachines@2022-03-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: 'StandardSSD_LRS' }
      }
      dataDisks: [
        {
          diskSizeGB: 1023
          lun: 0
          createOption: 'Empty'
        }
      ]
    }
    networkProfile: {
      networkInterfaces: [
        { id: nic.id }
      ]
    }
    diagnosticsProfile: {
      bootDiagnostics: {
        enabled: true
        storageUri: storageAccount.properties.primaryEndpoints.blob
      }
    }
    securityProfile: ((securityType == 'TrustedLaunch') ? securityProfileJson : null)
  }
}

// Guest Attestation Extension (Trusted Launch)
resource vmExtension 'Microsoft.Compute/virtualMachines/extensions@2022-03-01' = if ((securityType == 'TrustedLaunch') && ((securityProfileJson.uefiSettings.secureBootEnabled == true) && (securityProfileJson.uefiSettings.vTpmEnabled == true))) {
  parent: vm
  name: extensionName
  location: location
  properties: {
    publisher: extensionPublisher
    type: extensionName
    typeHandlerVersion: extensionVersion
    autoUpgradeMinorVersion: true
    enableAutomaticUpgrade: true
    settings: {
      AttestationConfig: {
        MaaSettings: { maaEndpoint: '', maaTenantName: maaTenantName }
      }
    }
  }
}

// Install IIS Extension
resource iisExtension 'Microsoft.Compute/virtualMachines/extensions@2022-03-01' = {
  parent: vm
  name: 'installIIS'
  location: location
  properties: {
    publisher: 'Microsoft.Compute'
    type: 'CustomScriptExtension'
    typeHandlerVersion: '1.10'
    autoUpgradeMinorVersion: true
    settings: {
      commandToExecute: 'powershell -ExecutionPolicy Unrestricted Install-WindowsFeature -name Web-Server -IncludeManagementTools;'
    }
  }
}

// Outputs
output hostname string = publicIp.properties.dnsSettings.fqdn
output publicIPAddress string = publicIp.properties.ipAddress
📝 Nota: Este template Bicep cria uma VM com Trusted Launch habilitado, incluindo Secure Boot e vTPM para segurança avançada. Também instala IIS automaticamente via Custom Script Extension.

Passo 4: Fazer o deploy com Azure CLI

# Deploy com Bicep
az deployment group create \
  --resource-group myResourceGroupBicep \
  --template-file main.bicep \
  --parameters adminUsername=azureuser

Você será solicitado a fornecer a senha (adminPassword) durante o deploy. Use uma senha com pelo menos 12 caracteres, incluindo maiúsculas, minúsculas, números e caracteres especiais.

✅ Validação

Verifique se o deploy foi concluído com sucesso:

1. Verificar outputs do deploy

# Ver resultados do deploy
az deployment group show \
  --resource-group myResourceGroupBicep \
  --name main \
  --query properties.outputs

2. Obter IP público da VM

# Obter IP público
az vm show \
  --resource-group myResourceGroupBicep \
  --name simple-vm \
  --show-details \
  --query publicIps \
  --output tsv

3. Testar o IIS no navegador

Acesse o IP público retornado ou o hostname DNS:

http://<PUBLIC_IP_ADDRESS>
🎉 Parabéns! Se a página padrão do IIS apareceu, você deployou com sucesso uma VM Windows Server usando Bicep, com Trusted Launch, NSG configurado e IIS instalado automaticamente!

🧹 Limpeza

Remova todos os recursos criados para evitar cobranças:

# Deletar o resource group e todos os recursos
az group delete \
  --name myResourceGroupBicep \
  --yes --no-wait
⚠️ Atenção: Este comando apaga o resource group e todos os recursos dentro dele (VM, VNet, NSG, Storage, IP público). A operação é irreversível.