Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,8 @@ override.tf.json
.terraformrc
terraform.rc

.vscode/settings.json
.vscode/settings.json

# Bicep build artifacts (ARM templates)
infra/bicep/**/*.json
!infra/bicep/**/*.parameters.json
229 changes: 228 additions & 1 deletion infra/bicep/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,228 @@
# Bicep IaC
# Bicep IaC - Azure VM + Network

This directory contains Azure Bicep Infrastructure as Code (IaC) for deploying a demo Windows Server virtual machine with associated networking resources in Azure.

## What's Deployed

The infrastructure includes:

- **Resource Group**: Container for all Azure resources
- **Virtual Network (VNet)**: 10.0.0.0/16 address space
- **Subnet**: 10.0.0.0/24 address space (default subnet)
- **Network Security Group (NSG)**: With inbound rules for RDP (3389), HTTP (80), and HTTPS (443)
- **Public IP Address**: Static IP with DNS name for accessing the VM
- **Network Interface (NIC)**: Connects the VM to the VNet and public IP
- **Virtual Machine**: Windows Server 2022 Datacenter
- **Managed OS Disk**: StandardSSD_LRS for the OS disk

All resources are deployed to **Sweden Central** region by default.

## Prerequisites

Before deploying, ensure you have:

1. **Azure CLI** installed and authenticated
```bash
az login
az account show # Verify you're in the correct subscription
```

2. **Bicep CLI** installed (included with Azure CLI 2.20.0+)
```bash
bicep --version
```

3. **Azure Subscription** with appropriate permissions (Contributor or Owner role at subscription level)

## File Structure

```
infra/bicep/
├── main.bicep # Main template (subscription scope)
├── main.bicepparam # Parameter file with example values
├── modules/
│ ├── network.bicep # Network infrastructure (VNet, Subnet, NSG)
│ └── vm.bicep # Virtual machine resources (VM, NIC, Public IP)
└── README.md # This file
```

## Deployment

### Option 1: Deploy with Parameter File

The easiest way to deploy is using the parameter file. You'll need to provide the admin password:

```bash
# Navigate to the bicep directory
cd infra/bicep

# Deploy (will prompt for adminPassword)
az deployment sub create \
--location swedencentral \
--template-file main.bicep \
--parameters main.bicepparam

# Or provide password inline (for automation)
az deployment sub create \
--location swedencentral \
--template-file main.bicep \
--parameters main.bicepparam \
--parameters adminPassword='YourSecurePassword123!'
```

### Option 2: Deploy with Individual Parameters

You can override any parameters at deployment time:

```bash
az deployment sub create \
--location swedencentral \
--template-file main.bicep \
--parameters resourceGroupName='rg-my-demo' \
--parameters adminUsername='myadmin' \
--parameters adminPassword='MySecurePassword123!' \
--parameters resourcePrefix='myvm'
```

### Option 3: Deploy with What-If (Preview Changes)

Preview what resources will be created before deploying:

```bash
az deployment sub what-if \
--location swedencentral \
--template-file main.bicep \
--parameters main.bicepparam \
--parameters adminPassword='YourSecurePassword123!'
```

## Validation

### Local Validation (Fast)

Validate Bicep syntax and compile to ARM template:

```bash
# Validate main template
bicep build main.bicep

# Validate network module
bicep build modules/network.bicep

# Validate VM module
bicep build modules/vm.bicep
```

### Azure Validation (Comprehensive)

Validate against Azure APIs (checks permissions, quotas, API versions):

```bash
az deployment sub validate \
--location swedencentral \
--template-file main.bicep \
--parameters main.bicepparam \
--parameters adminPassword='YourSecurePassword123!'
```

## Connecting to Your VM

After deployment completes, you'll receive outputs including the public IP address:

1. **Get deployment outputs:**
```bash
az deployment sub show \
--name main \
Comment on lines +132 to +135
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs show az deployment sub show --name main, but the earlier az deployment sub create examples don't specify --name main, so this command likely won't find the deployment. Either add --name main to the create/what-if/validate commands or update the "show outputs" step to use the actual deployment name.

Suggested change
1. **Get deployment outputs:**
```bash
az deployment sub show \
--name main \
1. **Get deployment outputs** (replace `<deploymentName>` with the name you used in `az deployment sub create`, for example `main`):
```bash
az deployment sub show \
--name <deploymentName> \

Copilot uses AI. Check for mistakes.
--query properties.outputs
```

2. **Connect via RDP:**
- Open Remote Desktop Connection (mstsc.exe on Windows)
- Enter the public IP address or FQDN
- Login with the credentials you provided during deployment

## Customization

You can customize the deployment by modifying parameters:

| Parameter | Default | Description |
|-----------|---------|-------------|
| `location` | `swedencentral` | Azure region for resources |
| `resourceGroupName` | `rg-demo-vm` | Name of the resource group |
| `adminUsername` | `azureuser` | VM administrator username |
| `adminPassword` | *(required)* | VM administrator password (min 12 chars) |
| `vmSize` | `Standard_B2s` | VM size (2 vCPU, 4 GB RAM) |
| `osVersion` | `2022-Datacenter` | Windows Server version (2019 or 2022) |
| `resourcePrefix` | `demo` | Prefix for resource naming |

## Clean Up

To delete all deployed resources:

```bash
# Delete the resource group (deletes all resources within it)
az group delete --name rg-demo-vm --yes --no-wait
```

## Cost Considerations

This demo configuration uses low-cost resources:

- **VM Size**: Standard_B2s (burstable B-series, ~$30-40/month)
- **OS Disk**: StandardSSD_LRS (128 GB, ~$5/month)
- **Public IP**: Standard SKU Static (~$3/month)
- **Networking**: VNet and NSG have no additional cost

**Total estimated cost**: ~$40-50 USD/month when running continuously.

To minimize costs:
- Stop (deallocate) the VM when not in use: `az vm deallocate --resource-group rg-demo-vm --name <vm-name>`
- Delete resources when no longer needed

## Security Notes

⚠️ **This is a demo configuration**. For production use:

1. **Restrict NSG rules**: Don't allow RDP from `*` (any source). Limit to your IP address or corporate network.
2. **Use Azure Bastion**: Instead of exposing RDP publicly, use Azure Bastion for secure access.
3. **Enable JIT Access**: Use Azure Security Center's Just-In-Time VM access.
4. **Key-based authentication**: Consider using Azure Key Vault for secrets management.
5. **Monitoring**: Enable Azure Monitor and diagnostic settings.
6. **Backup**: Configure Azure Backup for production VMs.

## Troubleshooting

### Bicep Build Errors

```bash
# Check Bicep version
bicep --version

# Upgrade Bicep
az bicep upgrade

# Validate specific file
bicep build <file>.bicep
```

### Deployment Errors

```bash
# View deployment logs
az deployment sub show --name main

# List all subscription deployments
az deployment sub list --output table
```

### Common Issues

1. **Password complexity**: Ensure password meets Azure requirements (min 12 chars, includes uppercase, lowercase, number, special char)
2. **Quota limits**: Check your subscription quotas for the selected VM size and region
3. **Permissions**: Ensure you have Contributor or Owner role at subscription level

## Additional Resources

- [Azure Bicep Documentation](https://learn.microsoft.com/azure/azure-resource-manager/bicep/)
- [Azure VM Sizes](https://learn.microsoft.com/azure/virtual-machines/sizes)
- [Azure Naming Conventions](https://learn.microsoft.com/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming)
87 changes: 87 additions & 0 deletions infra/bicep/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Main Bicep template for Azure VM + Network infrastructure
// Deployment scope: subscription (creates resource group)
targetScope = 'subscription'

// Parameters
@description('Azure region for all resources')
param location string = 'swedencentral'

@description('Name of the resource group to create')
@minLength(1)
@maxLength(90)
param resourceGroupName string = 'rg-demo-vm'

@description('Administrator username for the VM')
@minLength(1)
@maxLength(20)
param adminUsername string = 'azureuser'

@description('Administrator password for the VM')
@secure()
@minLength(12)
param adminPassword string

@description('Virtual machine size')
param vmSize string = 'Standard_B2s'

@description('Windows Server OS version')
@allowed([
'2019-Datacenter'
'2022-Datacenter'
])
param osVersion string = '2022-Datacenter'

@description('Prefix for naming resources')
@minLength(3)
@maxLength(10)
param resourcePrefix string = 'demo'

// Variables
var uniqueSuffix = uniqueString(subscription().subscriptionId, resourceGroupName)

// Create resource group
resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = {
name: resourceGroupName
location: location
}

// Deploy network infrastructure
module network 'modules/network.bicep' = {
scope: resourceGroup
name: 'networkDeployment'
params: {
location: location
resourcePrefix: resourcePrefix
uniqueSuffix: uniqueSuffix
}
}

// Deploy virtual machine
module virtualMachine 'modules/vm.bicep' = {
scope: resourceGroup
name: 'vmDeployment'
params: {
location: location
resourcePrefix: resourcePrefix
uniqueSuffix: uniqueSuffix
adminUsername: adminUsername
adminPassword: adminPassword
vmSize: vmSize
osVersion: osVersion
subnetId: network.outputs.subnetId
nsgId: network.outputs.nsgId
}
}

// Outputs
@description('Resource group name')
output resourceGroupName string = resourceGroup.name

@description('Virtual machine name')
output vmName string = virtualMachine.outputs.vmName

@description('Public IP address')
output publicIpAddress string = virtualMachine.outputs.publicIpAddress

@description('Virtual network name')
output vnetName string = network.outputs.vnetName
26 changes: 26 additions & 0 deletions infra/bicep/main.bicepparam
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Parameter file for main.bicep
// This file contains example parameter values for demo deployment
using './main.bicep'

// Azure region for resources
param location = 'swedencentral'

// Resource group name
param resourceGroupName = 'rg-demo-vm'

// VM administrator username
param adminUsername = 'azureuser'

// VM administrator password - MUST be provided at deployment time
// Use: az deployment sub create --parameters main.bicepparam --parameters adminPassword='YourSecurePassword123!'
// Or use: az deployment sub create --parameters main.bicepparam (will prompt for password)
param adminPassword = readEnvironmentVariable('ADMIN_PASSWORD', '')
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adminPassword is set in the .bicepparam file with a default of empty string when ADMIN_PASSWORD isn't present. This prevents the CLI from prompting and will fail the template's @minLength(12) validation (and Azure password rules). Remove adminPassword from the parameter file (provide via CLI at deployment time), or make missing env var an explicit error rather than defaulting to ''.

Suggested change
param adminPassword = readEnvironmentVariable('ADMIN_PASSWORD', '')

Copilot uses AI. Check for mistakes.

// VM size - Standard_B2s is a low-cost general purpose size
param vmSize = 'Standard_B2s'

// Windows Server version
param osVersion = '2022-Datacenter'

// Resource naming prefix
param resourcePrefix = 'demo'
Loading