Infrastructure as Code (IaC) solution for deploying a complete Active Directory testing environment in Azure for testing, training, or development purposes.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Azure Cloud β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Virtual Network (10.0.0.0/16) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β
β β βββββββββββββββββββββ βββββββββββββββββββ β β
β β β 10.0.1.0/24 β β 10.0.2.0/24 β β β
β β β DC Subnet β β Workstation β β β
β β β β β Subnet β β β
β β β β β β β β
β β βββββββββββββββββββββ βββββββββββββββββββ β β
β β βββββββββββββββββββ β β
β β β 10.0.4.0/24 β β β
β β β Server β β β
β β β Subnet β β β
β β β β β β
β β βββββββββββββββββββ β β
β β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββ β β
β β β Jumpbox Subnet (10.0.3.0/24) β β β
β β β β β β
β β β βββββββββββββββββββββββ β β β
β β β β Jumpbox β β β β
β β β β Ubuntu 22.04 βββββββββββββββΌβββββΌβββΌβββββββ SSH (Your IP)
β β β β Ansible + Tools β β β β
β β β βββββββββββββββββββββββ β β β
β β βββββββββββββββββββββββββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Python 3.11+ - For the orchestration CLI
- Terraform 1.0+ - For infrastructure provisioning
- Azure CLI - For Azure authentication
- SSH Key Pair - For jumpbox access
- Poetry - Python dependency management
- Active Azure subscription
- Sufficient quota for:
- vCPUs: 2 per VM
- Public IPs: 1 (jumpbox only)
- Storage: ~128GB
git clone https://github.com/dejisec/ad-lab.git
cd ad-lab# Install Poetry if not already installed
curl -sSL https://install.python-poetry.org | python3 -
# Install Python dependencies
poetry install# Login to Azure
az login
# Set your subscription (if you have multiple)
az account set --subscription "Your-Subscription-Name"# Run the setup script
./setup.sh
# Edit the configuration
vim .envKey configuration options:
# Azure Configuration
LOCATION="West US"
RESOURCE_GROUP_NAME="ad-lab-rg"
# Domain Configuration
DOMAIN_NAME="company.local"
DOMAIN_NETBIOS_NAME="COMPANY"
ADMIN_USERNAME="ad-lab-admin"
ADMIN_PASSWORD="SecurePassword123!" # Change this!
# Network Configuration
SOURCE_SSH_IP="YOUR.PUBLIC.IP.HERE" # Your public IP for SSH access
# VM Configuration
DOMAIN_CONTROLLER_COUNT=1
WORKSTATION_COUNT=2
USE_SPOT_INSTANCES=false # Set to true for cost savings# Deploy with confirmation prompts
poetry run python -m ad_lab.cli deploy
# Or deploy without prompts
poetry run python -m ad_lab.cli deploy -yAfter deployment, you'll see connection instructions:
# Get connection information
poetry run python -m ad_lab.cli connect
# SSH to jumpbox
ssh azureuser@<jumpbox-public-ip>
# RDP to Domain Controller via SSH tunnel
ssh -L 3389:10.0.1.10:3389 azureuser@<jumpbox-public-ip>
# Then RDP to: localhost:3389
# RDP to Workstations via SSH tunnel
ssh -L 3390:10.0.2.5:3389 azureuser@<jumpbox-public-ip> # PC01
ssh -L 3391:10.0.2.4:3389 azureuser@<jumpbox-public-ip> # PC02
# Then RDP to: localhost:3390 or localhost:3391
# RDP to Server via SSH tunnel
ssh -L 3340:10.0.4.5:3389 azureuser@<jumpbox-public-ip> # Server01
ssh -L 3341:10.0.4.4:3389 azureuser@<jumpbox-public-ip> # Server02
# Then RDP to: localhost:3340 or localhost:3341| Command | Description | Options |
|---|---|---|
deploy |
Deploy the complete AD environment | -y (skip confirmation) |
destroy |
Destroy all resources and clean up | -y (skip confirmation) |
status |
Show current deployment status | - |
connect |
Display connection instructions | - |
validate |
Validate configuration and prerequisites | - |
# Deploy with custom configuration file
poetry run python -m ad_lab.cli --config-file custom.env deploy
# Deploy with debug logging
poetry run python -m ad_lab.cli --log-level DEBUG deploy
# Deploy with logging to file
poetry run python -m ad_lab.cli --log-file deployment.log deployConfigure VM sizes in .env:
# Primary VM sizes
DC_VM_SIZE="Standard_B2s"
WORKSTATION_VM_SIZE="Standard_B2s"
JUMPBOX_VM_SIZE="Standard_B1s"
# Fallback sizes (automatic failover)
DC_VM_SIZE_PRIORITY='["Standard_B2s", "Standard_B2ms", "Standard_D2s_v3"]'Adjust the number of VMs:
DOMAIN_CONTROLLER_COUNT=2 # HA domain controllers
WORKSTATION_COUNT=5 # Multiple workstations
SERVER_COUNT=3 # Additional servers
SERVER_ROLES='["web", "database", "file"]' # Server rolesVNET_ADDRESS_SPACE="10.0.0.0/16"
DC_SUBNET_PREFIX="10.0.1.0/24"
WORKSTATION_SUBNET_PREFIX="10.0.2.0/24"
JUMPBOX_SUBNET_PREFIX="10.0.3.0/24"
SERVER_SUBNET_PREFIX="10.0.4.0/24"# Check your quota
az vm list-usage --location "East US" --output table
# Solution: Use different VM sizes or regions# Check detailed logs
poetry run python -m ad_lab.cli --log-level DEBUG deploy
# Manual cleanup if needed
cd terraform
terraform destroy -auto-approve| Configuration | Estimated Cost |
|---|---|
| Basic (1 DC, 2 Workstations) | ~$150-200 |
| With Spot Instances | ~$50-75 |
| Extended (2 DCs, 5 Workstations) | ~$350-450 |
- Use Spot Instances: Set
USE_SPOT_INSTANCES=truefor up to 70% savings - Destroy When Not Needed: Always run
poetry run python -m ad_lab.cli destroyafter testing
- Isolated Network: All VMs are in a private network, accessible only via jumpbox
- NSG Rules: Strict firewall rules limit access to necessary ports only
- SSH Key Authentication: Jumpbox uses SSH keys, not passwords
- Source IP Restriction: SSH access limited to your configured IP
- No Public IPs: Only the jumpbox has a public IP address
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.