Entra Cloud Sync + Hybrid Join: A Step-by-Step Guide

I run a little Active Directory domain in my home lab, and for the longest time it lived completely on its own island. Local logins, local DNS, local everything. Meanwhile every cloud thing I touched — Microsoft 365, Azure, random SaaS apps — wanted me to log in with a different identity. Two usernames, two passwords, two worlds. That’s the exact problem Microsoft built hybrid identity to solve, and I wanted to actually understand how it works instead of just reading about it.

So I built it. This post is the result: what Entra Connect actually is, the difference between the two sync engines Microsoft gives you, what Hybrid Join means, and a full best-practices walkthrough of deploying Entra Cloud Sync and then Hybrid Join on top of it. (There’s a twist on the Hybrid Join side for Cloud Sync setups — I’ll get to it.) I’ll link the Microsoft Learn docs at every step so you’re never taking my word for it.

If you’ve got an on-prem domain and a Microsoft tenant and you want them to feel like one thing — this is the post.

Read more…

Upgrading Proxmox VE 8 to 9: A Real-World Walkthrough

I’ll be honest — I’ve been putting this off for a while.

Proxmox VE 9 dropped earlier this year, and every time I looked at the upgrade guide I thought “yeah, I’ll get to that.” This weekend I finally ran out of excuses and carved out some time to tackle it. Three nodes, a hyper-converged Ceph cluster, and a handful of HA-managed VMs. Nothing exotic, but enough moving parts to make it interesting.

This post is meant to complement the official Proxmox upgrade documentation — not replace it. If you want the authoritative source, go there. What I’m documenting here is what the upgrade actually looked like in practice on a real homelab cluster, including the things pve8to9 flagged and how I resolved them.

Read more…

Building an Ubuntu 26.04 LTS Cloud-Init Template in Proxmox

Ubuntu 26.04 LTS dropped last month, and instead of doing what I’d normally do — build a template, snapshot it, and slowly watch it age — I decided it was finally time to actually dig into Cloud-Init.

If you’ve built Proxmox templates before, you know the drill: create a VM, install the OS, install your packages, sysprep it, convert it to a template, and then clone it every time you need a new box. It works, but every clone inherits whatever state the template was in the day you built it. Packages drift. You’re manually cleaning up machine IDs and SSH host keys after every clone. Spin up ten VMs for a lab build and you’ve got ten rounds of post-boot cleanup ahead of you.

Cloud-Init sidesteps all of that. Instead of a fat, opinionated image, you start with a minimal cloud image — the same kind AWS, Azure, and GCP use under the hood. The image carries no fixed identity. When you clone it and boot it, Proxmox injects your config (username, SSH key, hostname, network settings) via a virtual CD-ROM drive, and Cloud-Init configures the VM from the inside on first boot. Clean machine ID, unique SSH host keys, fully configured — before you even open a terminal.

The real unlock is that your config is a YAML file. Want ten identical VMs? Same file, ten times. Want to add a package or a user? Edit the file. Your infrastructure becomes something you can version, reuse, and actually reason about — instead of a template you’re scared to touch because you don’t remember what’s in it.

That’s why I built this. Here’s exactly how I did it on Proxmox VE 9.2.3 with Ubuntu 26.04 LTS “Resolute Raccoon.” Still on Proxmox 8? I’d get the cluster current first — here’s how I upgraded my Proxmox cluster from version 8 to 9.

What is Cloud-Init, Actually?

Cloud-Init is an industry-standard first-boot initialization system — the same one AWS, Azure, and GCP use under the hood. Instead of manually clicking through an Ubuntu installer and then scrubbing machine IDs and SSH host keys from the image, you start with a pre-built minimal “cloud image” designed to be generic and stateless.

When you clone it and boot it, Cloud-Init reads a small config — hostname, username, SSH key, network settings — that Proxmox injects via a virtual CD-ROM drive. The VM configures itself automatically in seconds. No manual post-boot setup, no configuration drift, no forgotten hostname changes causing weird network issues two weeks later.

One important thing to understand upfront: Cloud-Init only runs once — on first boot. It’s not a service that re-applies config every time the VM starts. After that first boot, it stamps a flag on the VM and never runs again. Rebooting a running VM doesn’t pull fresh packages or re-apply settings. If you want a fresh, fully updated machine, the workflow is to clone the template again — not reboot an existing one. That distinction shapes everything about how you operate day-to-day with Cloud-Init.

Read more…

WS-Management: Configuration Refresh Failed — Fixing the MaxEnvelopeSizekb Limit

I installed Server Manager on my laptop to make it easier to manage my Active Directory Domain Controllers remotely. When I added two of my domain controllers I got the following error message:

After some searching I discovered that the response the server tried to send back was bigger than WinRM was allowed to carry. The management tool asked for a configuration refresh, the server started building the response, and WinRM threw it in the trash before it ever arrived.


Why It’s Happening

WS-Management (WinRM) communicates using SOAP over HTTP — basically XML envelopes sent back and forth. There’s a setting called MaxEnvelopeSizekb that caps how large any single response envelope can be. Out of the box, that limit is 512 KB (512,000 bytes).

The problem is that as your environment grows — more AD objects, more roles, more configuration data — the payload for a full configuration refresh eventually exceeds that cap. The two domain controllers I added were returning 527 KB and 526 KB respectively. Close, but just over the line, and WinRM doesn’t negotiate. It just refuses.

This is especially common on Domain Controllers with large Active Directory configurations, or any server that has accumulated a lot of roles, features, and policies over time.

Read more…

Azure Private Endpoints, DNS Private Resolver, and Conditional Forwarders: Private Access End-to-End

Diagram of Azure DNS with Private Resolver, Private DNS Zone, and Conditional Forwarder

By default, most Azure services — storage accounts, key vaults, SQL databases, you name it — are reachable over the public internet. That’s fine for a quick lab, but not great when you want your on-premises workloads to talk to Azure resources without any of that traffic ever leaving your private network.

That’s where Private Endpoints come in. A Private Endpoint drops a Network Interface Card (NIC) directly inside your Virtual Network, giving your Azure resource a private IP address that’s only reachable from within your network. No public internet required.

The catch? DNS. Your on-premises DNS servers still resolve that storage account’s hostname to its old public IP. We need to teach them to ask Azure’s private DNS instead. To bridge that gap we’ll use Azure’s DNS Private Resolver — a fully managed, serverless DNS forwarder that lives inside your VNet — and then configure a Conditional Forwarder on your Windows DNS server to point traffic for Azure domains at it.

Here’s what we’re building end-to-end:

  • Create a Private Endpoint for an Azure Storage Account (file share).
  • Create a DNS Private Resolver with an inbound endpoint so on-prem DNS can reach it.
  • Configure a Conditional Forwarder in Windows DNS to send *.file.core.windows.net queries to that resolver.

There are multiple scenarios for using these services. The scenario I am deploying is called Azure Private Resolver for virtual network and on-premises workloads. Check that link for additional scenario types!

Let’s jump in!

Read more…

Deploying Azure Virtual Network Gateway Basic: A How-To Guide

If you’ve got an Azure subscription through Visual Studio Enterprise, you’ve basically been handed the perfect playground for testing and learning in the cloud. But once you start building resources inside a private Azure network, you quickly hit the next challenge: how do you securely connect it back to your home lab without exposing everything to the internet? In this post, I’m going to go through the process of how I deployed a Basic Azure Virtual Network Gateway and connected it to my Ubiquiti Dream Machine, creating a secure site-to-site VPN tunnel between my Azure VNet and my home network. I wanted to document what that process looked like for me and share it in case it helps someone else doing the same thing.

What is a Virtual Network Gateway?

TL;DR: A managed VPN concentrator and router for your Azure VNet!

An Azure Virtual Network Gateway is used when you need secure, private connectivity into an Azure Virtual Network without exposing resources to the public internet. It acts like a managed VPN router inside your VNet, enabling encrypted connections such as site-to-site VPNs (linking your on-premises network to Azure), point-to-site VPNs (allowing individual users to securely connect to Azure), or VNet-to-VNet connections (linking multiple Azure networks). It’s most valuable for hybrid cloud scenarios, remote administration, and keeping systems reachable over private IP addresses while maintaining strong network security and controlled access. Azure supports a few common VPN connection models, each designed for a specific access pattern:

  • Site-to-Site (S2S) VPN: Creates an always-on encrypted tunnel between an on-premises network (office/data center) and an Azure VNet, making it ideal for hybrid connectivity where entire networks need to communicate over private IPs.
  • Point-to-Site (P2S) VPN: Connects individual users/devices directly into an Azure VNet, which is perfect for remote admin access, developer labs, or secure access to private Azure resources without exposing them publicly.
  • VNet-to-VNet: Connects two Azure VNets using VPN tunnels, typically used when networks must remain isolated (different environments/regions/subscriptions) but still require secure private communication—though VNet peering is often simpler for Azure-to-Azure connectivity.

Read more…

How to Takeover an Unmanaged Directory in Azure

When trying to create an Azure subscription I received the following error:

This tenant is viral. If you are an IT admin, you can take over the directory.
This tenant is viral. If you are an IT admin, you can take over the directory.

After some reading a viral tenant is another meaning for an unmanaged directory. An unmanaged directory is a directory that was automatically created when a user with @virtuallyboring.com created an Azure account. Its a directory that has no global administrator. Microsoft created a Azure Active Directory (AAD) tenant in the background and is sitting there unmanaged. This is so users can create an account and use resources without it being a blocker that the domain isn’t claimed in AAD.

There are two ways to take over an unmanaged directory:

  • Internal admin takeover:
    • Your account gets elevated to global administrator
    • No users, domains, or service plans are migrated
  • External Admin Takeover:
    • Add the unmanaged domain name to a tenant where you are a Global Administrator
    • A mapping of users to resources is created in your managed Azure Active Directory
      • Users can continue to access services without interruption

Source: Admin takeover of an unmanaged directory – Azure AD | Microsoft Docs

For my example I will be using a internal admin takeover.

Read more…

Migrate GoDaddy Domain and DNS to AWS Route 53

I started this blog back in June of 2014 to play around with platforms like Joomla and WordPress. I wouldn’t be truthful if I didn’t say the GoDaddy Superbowl commercials didn’t sell me to start with GoDaddy to register my domain name and provide hosting. Over the years I haven’t had any major issues that caused long term outages, only a few hours sprinkled in every month with their Linux Hosting Essential with CPanel plan. With 2020 being my first year working in AWS I felt strongly to move my domain over to AWS Route 53 so I can start playing around with the AWS suite of services! That is the goal of this post is to walk through the transfer my domain and DNS from GoDaddy to AWS Route 53!

These instructions are specifically for GoDaddy but would work for any domain registrar provider you are using today. The screenshots would be different but the task would be the same. You have to switch back and forth between GoDaddy console and AWS Console quite a bit. I try to start the task stating if a console swap is need and where you should be. Working with DNS can be tricky as it can be a waiting game especially when dealing with external DNS replicated across the world (Time To Live – TTL). If doing this in a production environment make sure you do this during a low peak time and give yourself extra window of time to troubleshoot (and wait for DNS replication). It’s pretty easy and straight forward, but it’s DNS.

What exactly is Route 53? Route 53 is Amazon Web Services (AWS) highly available and scalable Domain Name System (DNS) service launched back in 2010. It has powerful traffic routing policies and health checks that you use depending on your use case. Route 53 has a default limit of 50 domain names however this limit can be increased by contacting AWS support.

Table of Contents:

Part 1: Migrate from GoDaddy DNS to AWS Route 53

Part 2: Migrate domain registered with GoDaddy to AWS Route 53

Part 1 – Migrate from GoDaddy DNS to AWS Route 53:

Step 1: Create AWS Route 53 Hosted Zone

What is a AWS Hosted Zone? Here is a snippet from the Route 53 FAQ:

A hosted zone is an Amazon Route 53 concept. A hosted zone is analogous to a traditional DNS zone file; it represents a collection of records that can be managed together, belonging to a single parent domain name. All resource record sets within a hosted zone must have the hosted zone’s domain name as a suffix. For example, the amazon.com hosted zone may contain records named www.amazon.com, and www.aws.amazon.com, but not a record named www.amazon.ca. You can use the Route 53 Management Console or API to create, inspect, modify, and delete hosted zones. You can also use the Management Console or API to register new domain names and transfer existing domain names into Route 53’s management.

First we must create the Hosted Zone in Route 53. This is so we can get our Amazon Name Servers for use in a later step. Go to Route 53 in the AWS console, then click Hosted Zones on the left column, then Create Hosted Zone:

Read more…

Enable SSH Service on ESXi hosts using PowerShell

I found myself wanting to enable the SSH service on my ESXi hosts. I could use Host Profiles to enable it but I decided to PowerShell script it! To enable SSH there are three parts to it:

You will need to start the SSH service and set it to Start and Stop with Host:

And you will need to suppress the SSH is enabled warning message:

esxi-hosts-ssh-warning

This script does all of the above to an entire cluster. Let’s see it in action!

######################################################################
# Start SSH Service, change Startup Policy, and Suppress SSH Warning #
######################################################################

#Variables
$vCenter = "LABVC01.virtuallyboring.com"
$Cluster = "Nested ESXi Cluster"

### Start of Script
# Load VMware Cmdlet and connect to vCenter
Add-PSSnapin vmware.vimautomation.core
connect-viserver -server $vCenter

$VMHost = Get-Cluster -Name $Cluster | Get-VMhost

# Start SSH Server on a Cluster
ForEach ($VMhost in $Cluster){
Write-Host -ForegroundColor GREEN "Starting SSH Service on " -NoNewline
Write-Host -ForegroundColor YELLOW "$VMhost"
Get-VMHost | Get-VMHostService | ? {($_.Key -eq "TSM-ssh") -and ($_.Running -eq $False)} | Start-VMHostService
}

# Change Startup Policy
ForEach ($VMhost in $Cluster){
Write-Host -ForegroundColor GREEN "Setting Startup Policy on " -NoNewline
Write-Host -ForegroundColor YELLOW "$VMhost"
Get-VMHost | Get-VMHostService | where { $_.key -eq "TSM-SSH" } | Set-VMHostService -Policy "On" -Confirm:$false -ea 1
}

# Surpress SSH Warning
ForEach ($VMhost in $Cluster){
Write-Host -ForegroundColor GREEN "Setting UserVar to supress Shell warning on " -NoNewline
Write-Host -ForegroundColor YELLOW "$VMhost"
Get-VMhost | Get-AdvancedSetting | Where {$_.Name -eq "UserVars.SuppressShellWarning"} | Set-AdvancedSetting -Value "1" -Confirm:$false
}
### End of Script

Read more…

How to Add ESXi Hosts to vCenter using PowerShell

There are quite a few clicks needed to add a host to vCenter. If you are deploying multiple hosts to your environment you have many clicks ahead. You should script it! This PowerShell script will make adding multiple hosts to vCenter easy!

Let’s see it in action!

#####################################################################
# Load VMware Plugins and connect to vCenter
#####################################################################

Add-PSSnapin vmware.vimautomation.core
## Enter your vCenter here
connect-viserver -server LABVC01.virtuallyboring.com

########################################################################
# Add Multiple Hosts to vCenter
########################################################################

# Variables
## You can use comma separated names or change to pull from a text file. Your pick.
$ESXiHosts = "ESXi01.virtuallyboring.com" , "ESXi02.virtuallyboring.com" , "ESXi03.virtuallyboring.com"
## Enter the name of a Data Center or Host Cluster
$ESXiLocation = "Nested ESXi Cluster"

# Start Script
$credentials = Get-Credential -UserName root -Message "Enter the ESXi root password"

Foreach ($ESXiHosts in $ESXiHosts) {
Add-VMHost -Name $ESXiHosts -Location $ESXiLocation -User $credentials.UserName -Password $credentials.GetNetworkCredential().Password -RunAsync -force
Write-Host -ForegroundColor GREEN "Adding ESXi host $ESXiHosts to vCenter"
}
# End Script

Read more…