Azure Hub-and-Spoke Architecture Explained and Automated with OpenTofu

This is my first blog of the new year (2026)! Since being re-awarded as a Microsoft MVP, Microsoft provided me with a fresh set of Azure credits. One of the first things I wanted to do was rebuild my Azure lab environment. This time, I wanted to do it the right way. I wanted it to mirror how I would design and deploy a real enterprise environment, including running fully on private endpoints and following a proper hub-and-spoke network model.

Just as importantly, I wanted everything defined in Infrastructure as Code (IaC) so I could spin environments up and down whenever I needed. That also aligns perfectly with what my team at Jamf is working on right now. We are making some changes to our underlying Azure architecture, including deeper network isolation, security controls, intergration with Jamf security cloud security products, and a shift from Bicep to OpenTofu. We will also be using AI agents to do a lot of the heavy lifting in that refactor. I will be sharing more about that in future blogs and talks as much as I am able to publicly.

Because OpenTofu is at the center of that work, I decided to build my entire Azure lab using OpenTofu and a full hub-and-spoke architecture. This gives my team a real, working reference base implementation that we can build on for production designs. I also want to share this with the larger tech community.

If you are note familiar with OpenTofu it is an open source infrastructure-as-code engine based on Terraform that lets you define, deploy, and manage cloud infrastructure using declarative configuration files, and you can learn more at https://opentofu.org.

You can access the GitHub Repository of my “OpenTofu Azure Hub and Spoke” solution here: https://github.com/Buchatech/OpenTofu-Azure-HubSpoke-public

Lets break down whats in the solution I built.


Solution Architecture

The solution deploys a production-style Azure network and platform foundation that includes:

  • Hub VNet with Azure Firewall, VPN Gateway, and DNS Private Resolver
  • Spoke VNet with peering and default routes through the firewall
  • Key Vault and Azure Container Registry using private endpoints
  • Optional Jumpbox VM for secure management access
  • GitHub Actions CI/CD pipeline using OIDC authentication

How the Automation Works

This is a multi-part solution built around a bootstrap Bash script (bootstrap.sh) and a fully generated OpenTofu repository.

The bootstrap script creates everything you need to get started:

  1. It creates an Azure Storage Account to store your OpenTofu remote state.
  2. It generates a complete OpenTofu project, including modules, variables, and environment structure.
  3. It configures the backend so OpenTofu uses Azure Storage for state.
  4. It creates a ready-to-use GitHub Actions pipeline for CI/CD.

Once the repository is generated, you can deploy your Azure environment by running OpenTofu locally or by pushing the repo to GitHub and letting the pipeline handle deployments for you. Within minutes, you can have a fully functional Azure hub-and-spoke environment up and running, and you can customize the generated modules to fit your own requirements.


Deployment Modes

The bootstrap bash script supports two deployment modes depending on how advanced and locked-down you want the environment to be.

FULL Mode (Default)
This is the enterprise-grade option.

  • Hub VNet with Azure Firewall, VPN Gateway, and DNS Private Resolver
  • Spoke VNet with peering and default route through the firewall
  • Private endpoints for Key Vault and Azure Container Registry
  • Optional Jumpbox VM for secure management
  • GitHub Actions CI/CD pipeline with OIDC authentication

BASIC Mode
This is a simpler version for learning or labs.

  • Hub VNet with Azure Firewall only
  • Spoke VNet with peering and default route through the firewall
  • Public access for Key Vault and Azure Container Registry
  • No Jumpbox, VPN Gateway, or DNS Private Resolver
  • GitHub Actions CI/CD pipeline with OIDC authentication

What the bootstrap.sh Script Does

When you run the bootstrap script, it will:

  1. Prompt you to select FULL or BASIC deployment mode
  2. Create an Azure Storage Account for OpenTofu remote state in rg-tfstate
  3. Generate the full OpenTofu repository structure based on your choice
  4. Configure the OpenTofu backend to use the storage account
  5. Create GitHub Actions workflow files for CI/CD
  6. Output the storage account details and the GitHub secrets you need to configure

From there, you are ready to deploy and customize the script and OpenTofu based on your Azure hub-and-spoke environment entirely through code.

Here is the Readme from the repo. It goes even more in depth into my “OpenTofu Azure Hub and Spoke” solution. I hope you find it useful!

********************************************************************************

Azure Hub-Spoke with OpenTofu

Azure base network architecture solution

This repository contains a production-ready, modular OpenTofu configuration that deploys Azure hub-spoke network topology with two deployment modes (private or public) to match your requirements and budget.


Architecture Overview

This solution deploys a hub-and-spoke network architecture (visual shows full-private deployment):

Enterprise-grade Azure network architecture lab environment with Site-to-Site VPN, Azure Firewall, DNS Private Resolver, and core services

This repository contains a production-ready, modular OpenTofu (Terraform) configuration that deploys a complete Azure hub-spoke network topology designed for hybrid cloud scenarios, connecting your on-premises network (e.g., UniFi network) to Azure.

Architecture Overview

This lab deploys a hub-and-spoke network architecture following Azure best practices (visual shows full private deployment):

┌──────────────────────────────────────────────────────────────────────┐
│                            AZURE CLOUD                                │
│                                                                        │
│  ┌─── HUB VNet (rg-lab-hub-network) ────────────────────────┐        │
│  │ 10.10.0.0/16                                              │        │
│  │                                                            │        │
│  │  ┌──────────┐  ┌───────────┐  ┌────────────┐  ┌───────┐ │        │
│  │  │  Azure   │  │    VPN    │  │    DNS     │  │Jumpbox│ │        │
│  │  │ Firewall │  │  Gateway  │  │  Private   │  │  VM   │ │        │
│  │  │(10.10.1.0│  │(10.10.2.0)│  │  Resolver  │  │(Mgmt) │ │        │
│  │  │)+ DNAT   │  │           │  │(10.10.4-5.0│  │subnet │ │        │
│  │  │SSH:2222  │  │           │  │)           │  │       │ │        │
│  │  └─────┬────┘  └─────┬─────┘  └────────────┘  └───────┘ │        │
│  │        │             │                                     │        │
│  │        │             │  Site-to-Site VPN                  │        │
│  └────────┼─────────────┼─────────────────────────────────────┘        │
│           │             │                                               │
│           │  VNet Peering + Gateway Transit                            │
│           │             │                                               │
│  ┌────────▼─ SPOKE VNet (rg-lab-spoke1-network) ──────┐               │
│  │ 10.20.0.0/16                                        │               │
│  │                                                      │               │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────────┐ │               │
│  │  │   Apps   │  │   APIs   │  │   Data/Services  │ │               │
│  │  │ Subnet   │  │ Subnet   │  │     Subnet       │ │               │
│  │  │          │  │          │  │  - ACR (Private) │ │               │
│  │  │          │  │          │  │  - Key Vault     │ │               │
│  │  └──────────┘  └──────────┘  └──────────────────┘ │               │
│  │                                                      │               │
│  │  Traffic routed through Azure Firewall ─────────────┘               │
│  └──────────────────────────────────────────────────────               │
│                                                                         │
│  ┌─── Management RG (rg-lab-management) ────────────┐                 │
│  │  - Azure Container Registry (ACR)                 │                 │
│  │  - Azure Key Vault                                 │                 │
│  │  - Private Endpoints in Spoke Data subnet         │                 │
│  └────────────────────────────────────────────────────┘                 │
│                                                                         │
└─────────────────────────────┬───────────────────────────────────────────┘
                              │
                      S2S VPN Tunnel (IPsec)
                              │
              ┌───────────────▼──────────────┐
              │   ON-PREMISES NETWORK        │
              │   (e.g., UniFi Router)       │
              │   192.168.1.0/24             │
              │                              │
              │   SSH → Azure Firewall:2222  │
              │   → DNAT → Jumpbox:22        │
              └──────────────────────────────┘

Read more

Multiple Linux VM Deployment ARM Template

I looked for an existing ARM template that would create multiple Linux VM’s. I found only one that creates some in a scale set. The use case I was working with did not call for a scale set so I needed a different template.

I found a simple ARM template for creating multiple Windows VM’s on Azure here. It had exactly what I needed for my use case but did not cover Linux.

I modified the template and uploaded to Github in case this is helpful to anyone else. The repo has two templates. There is one for Ubuntu and one for SUSE. When you deploy the template it will need the following parameters:

The ARM template will create an availability set (AS) with N number of VM’s put in that AS, network interfaces, and public IP’s for each VM along with a VNet and Subnet as shown in the following screenshot:

Here is the link to download the ARM Template:

https://github.com/Buchatech/Multiple-Linux-VM-Deployment-ARM-Template

Read more

The “argument is null or empty” error in Azure Automation Runbook

I was recently working on an Azure Automation runbook that provisions an empty resource group in Azure. I was running into an issue when the runbook ran that the variable being used with New-AzureRmRoleAssignment was null. The errors I was receiving are:

New-AzureRmRoleAssignment : Cannot validate argument on parameter ‘SignInName’. The argument is null or empty. Provide
an argument that is not null or empty, and then try the command again.
At line:96 char:39
+ New-AzureRmRoleAssignment -SignInName $RequesterSignIn -RoleDefinitio …
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [New-AzureRmRoleAssignment], ParameterBindingValidationException
+ FullyQualifiedErrorId :
ParameterArgumentValidationError,Microsoft.Azure.Commands.Resources.NewAzureRoleAssignmentCommand

and

New-AzureRmRoleAssignment : Cannot validate argument on parameter ‘ObjectId’. Specify a parameter of type ‘System.Guid’
and try again.
At line:97 char:37
+ New-AzureRmRoleAssignment -ObjectID $RequesterID -RoleDefinitionName  …
+                                     ~~~~~~~~~~~~
+ CategoryInfo          : InvalidData: (:) [New-AzureRmRoleAssignment], ParameterBindingValidationException
+ FullyQualifiedErrorId :
ParameterArgumentValidationError,Microsoft.Azure.Commands.Resources.NewAzureRoleAssignmentCommand

It turned out to be a permission issue with AzureRM.Resources CMDLETS not being able to talk to AAD specifically Get-AzureRmADUser that I was using for a variable.

To fix this I had to give the following permissions for the AAD directory to the AzureServicePrincipal Run As Account:

Windows Azure Active Directory (AAD)
Application Permissions

·       Read/Write directory data
·       Read directory data

Delegated Permissions
·       Read directory data
·       Read all users’ full profiles
·       Read all users’ basic profiles

Microsoft Graph
App Permissions
·       Read directory data

In your runbook code you will typically have

# Authenticate to Azure resources
$connectionName = “AzureRunAsConnection”

# Get the connection “AzureRunAsConnection “
$servicePrincipalConnection = Get-AutomationConnection -Name $connectionName
“Logging in to Azure…”
Login-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint

You may have a some differences like the connection variable and the name of the runasconnection. The point here is that the runas connection is what needs to have the proper permissions. You can find this account here to get the name and ApplicationID:

To give the permissions go to Azure Active Directory>the directory you are using in this automation>App registrations>and search based on the ApplicationID. Don’t forget to select All apps in the drop down.

Click on Add first and add the AAD and then Microsoft Graph permissions.

After you add the proper permissions make sure you click on Grant Permissions. The permissions are not actually applied until you do this. Once you click on Grant permissions you will see the prompt shown in the screenshot. Click Yes.

Verify the permissions have been added properly. In AAD go to All applications>select All applications. Find your service principle application.

Click on the service principle applications permissions.

Verify the AAD and graph permissions are listed. If the AAD and graph permissions are listed then the runbook should be good to go.

Read more

4th book published (Service Manager 2016)

On March 2nd I became a 4 time author. With several talented co-authors we published the Microsoft System Center 2016 Service Manager Cookbook. It was great to work with the co-authors and I would like to thank each of them for their hard work. The co-authors are:

 

  • Microsoft MVP Anders Asp
  • Microsoft MVP Andreas Baumgarten
  • Microsoft MVP Steve Beaumont
  • Service Manager/System Center expert Dieter Gasser

It was an honor to work with them. Also a shout out to Microsoft MVP Sam Erskine for writing up the foreword and helping with the technical review. Last I want to thank Rafael Delgado who also was a technical review on the book. This book is an update to the Microsoft System Center 2012 Service Manager Cookbook. In this new book you will read the new updated recipes for 2016, how to upgrade from 2012 R2 to 2016 and about the new HTML 5 portal.

Official book description:

System Center Service Manager (SCSM) is an integrated platform that offers a simplified data center management experience by implementing best practices such as Incident Management, Service Request, and Change control to achieve efficient service delivery across your organization.

This book provides you with real-world recipes that can be used immediately and will show you how to configure and administer SCSM 2016. You’ll also find out how to solve particular problems and scenarios to take this tool further. You’ll start with recipes on implementing ITSM frameworks and processes and configuring Service Level Agreements (SLAs). Then, you’ll work through deploying and configuring the HTML5 Self-Service Portal, configuring Incident and Problem Management, and designing and configuring change and release management. You’ll also learn about security roles and overall Microsoft SCSM 2016 administration.

Toward the end of the book, we’ll look at advanced topics, such as presenting the wealth of information stored within the Service Manager Data Warehouse, standardizing SCSM deployments, and implementing automation.

What you will learn:

  • See a practical implementation of the ITSM framework and processes based on ITIL
  • Deploy and configure the new Service Manager HTML5 Self-Service Portal along with Service Catalog design and configuration
  • Get to know about Incident, Problem, and Change Management processes and configuration
  • Get to grips with performing advanced personalization in Service Manager
  • Discover how to set up and use automation with and within Service Manager 2016
  • Work with Service Manager Data Warehouse
  • Find out what Security Roles are and how to implement them
  • Learn how to upgrade from SCSM 2012 R2 to SCSM 2016

The book can be ordered here:

https://www.amazon.com/dp/B01N5FL2SK

I also want to call out this is the 4th book that I have authored or co-authored. Here is a shot of all 4.

I have also been fortunate to be a technical reviewer on 5 other books. Here is a shot of them.

These books have all been on System Center products. I am stepping into a new era. Be on the lookout for more of a focus on cloud based solutions and know there is exciting stuff coming in the near future!

Read more

Service Manager vs. ServiceNow

I am often asked how does Service Manager compare to ServiceNow. I don’t have a solid canned response for this. I often respond that you really have to compare System Center to ServiceNow because you get the entire suite when you buy System Center not just Service Manager. Also it would be a bad decision to not consider using the other components such as Operations Manager, Orchestrator, and Configuration Manager given the tight out of the box integration with Service Manager and these components.

With ServiceNow you get an ITSM solution but have to pay additional monthly fee’s when you want to add on other functionality such as automation, event management (monitoring), CMDB, or asset management. With System Center you get all of this for the price of System Center and you simply have to turn and configure the additional functionality you want. One more point is that many organizations own and utilize Configuration Manager and or Operations Manager and will often already own the licensing they need to deploy Service Manager.

On December 9th 2015 System Center MVP’s Chris Ross and Pete Zerger held an awesome webinar on System Center + Cireson vs ServiceNow. This was a must see webinar. It covered the often asked about topic of “Service Manager vs ServiceNow“.

These guys did a great job covering the topic. One of the most important areas they covered was Real-world Total Cost of Ownership (TCO) Comparison. TCO is one of the top data points that matter to businesses when they are considering a new ITSM solution.

In this blog post I am going to look at some of the key topics that stuck out to me from the webinar in regards to Service Manager vs ServiceNow. Keep in mind that these comparisons also include Cireson’s software.

One of the setbacks for some organizations on going with Service Manager is that they believe there is no cloud option for it. That is wrong Service Manager can be deployed in Azure cloud. Also there are a couple of companies that have a SaaS offering for Service Manager. The following graphic looks at the different types of Service Manager deployments and their options.

clip_image001

This first chart looks the numbers of the TCO of Service Manager vs ServiceNow if you don’t already own the System Center ECAL licensing.

clip_image002

You will notice that over a 5 year period System Center including Cireson and Azure is lower TCO over ServiceNow. Wow. If this did not include Cireson or Azure the TCO of System Center would be even lower compared to ServiceNow.

This next chart looks at the TCO of System Center vs ServiceNow if your organization already owns the ECAL licenses or has an Enterprise Agreement (EA) with Microsoft.

clip_image003

This has even deeper savings compared to the first chart. Now remember this includes Cireson software and having Service Manager deployed in Azure.

This chart looks at the TCO of System Center vs ServiceNow with System Center being deployed on-premises.

clip_image004

Again the TCO savings with System Center goes even lower. This chart still includes Cireson with System Center. This does not include the cost of the data center fabric storage, VM’s etc… which would typically already be in place before deploying System Center.

The following table compares feature sets of System Center and ServiceNow.

clip_image005

Notice ServiceNow does offer features such as automation and system management but they come at an additional monthly cost. One more item to point out from this slide is that System Center offers functionality that ServiceNow does not such as enterprise and cloud backup through Data Protection Manager and Virtualization and Private cloud Management through Virtual Machine Manager and Azure Pack. ServiceNow does offer Event Management it requires an additional purchase and plugin install.

So I pulled out information from the webinar that stuck out to me. There is much more information in the webinar and context behind each of the charts I included in this blog post. I recommend you watch the full webinar. You can watch the entire webinar right here:

System Center + Cireson versus ServiceNow: A Head-to-Head Comparison from Team Cireson on Vimeo.

NOTE: Below is a link to another blog that covers Service Manager vs ServiceNow.

http://blog.navantis.com/reduce-it-spend-and-increase-performance-choosing-the-right-it-service-desk-tool/

Read more

System Center Futures 2016 and Beyond

UPDATE 9-4-2015:

***There is an upcoming FREE event covering the Future of System Center. This will be held on Sep 25, 2015 at the Microsoft MTC in Minnesota (http://www.microsoft.com/en-us/mtc/locations/minneapolis.aspx). This is a must attend event for any company running System Center. For more info on this event visit: http://bit.ly/1JIHS48***

Last week I was able to attend the first ever Microsoft Ignite conference in Chicago. There was a lot of exciting news announced at this conference around the many Microsoft products and technologies. Everything was covered from SharePoint, Exchange, Unified Communications, Office, Windows server, Windows 10, all things Azure and more. This post is focused for any System Center professional that was unable to attend the MS Ignite 2015 conference but what’s to know what’s up with System Center. If you had any concern about System Center going away or just want to know about the future of System Center in general this post is for you.

During conference there were many sessions related to the various System Center components however there were a couple of critical sessions that covered the future of System Center. These are the Platform Vision & Strategy sessions. These are titled:

Windows Server & System Center Futures—Bring Azure to your Datacenter (Platform Vision & Strategy)

And

Platform Vision & Strategy (6 of 7): What’s New in System Center for Management

These sessions are important because they featured System Centers top guy Jeremy Winter and he talked about future direction of the management solutions. In this post I will sum up key information from each of these sessions.

NOTE: This post is my perspective on the Platform Vision & Strategy sessions from Ignite and do not represent the opinions of Microsoft.

Traditionally System Center has been a complete management stack for IT Operations. This is not going to change but will continue to get better. The stack consists of: Managing endpoints (PC’s/Mobile device/servers) – *SCCM/Intune* | Monitor – *SCOM* | Automation – *Orchestrator (SMA)* | Provision – *VMM* | Service Management – *SCSM* | Protection – *Data Protection Manager* | Self-service – *Azure Pack* also represented in the following screenshot from one of the session slides.

clip_image001[4]

So we are now in the year 2015 and have not had a new major version of the entire stack since 2012. However since the release of System Center 2012 we have seen a steady progression of enhancement to the stack. We have seen it move from SP1 to R2 and now updates and new features through update rollups.

These update rollups have been released on a faster cadence at a speed we have not seen from Microsoft before. In fact we have recently seen a round of new features in update rollup 6 and more announced at Ignite. Below is a list of key features that stuck out to me along with slides from one of the Platform Vision & Strategy sessions giving insight into where the System Center components are headed next.

Read more