<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Peter Upfold</title>
	<atom:link href="https://peter.upfold.org.uk/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://peter.upfold.org.uk/blog</link>
	<description>My personal site and blog</description>
	<lastBuildDate>Sat, 27 Sep 2025 08:35:05 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>
	<item>
		<title>Computing.co.uk&#8217;s Rising Stars 2025</title>
		<link>https://peter.upfold.org.uk/blog/2025/09/27/computing-co-uks-rising-stars-2025/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sat, 27 Sep 2025 08:04:14 +0000</pubDate>
				<category><![CDATA[Life]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3151</guid>

					<description><![CDATA[I&#8217;m really delighted and honoured to have been included in this list &#8212; Computing.co.uk&#8217;s Rising Stars 2025! Over the past year, he has revolutionised the charity’s observability, teaching himself Zabbix, an open source monitoring tool. His custom scripts now proactively detect and resolve issues before they can have an impact, significantly reducing both downtime and [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><img decoding="async" src="https://media.upfold.org.uk/pwdb50/images/computing_2025_30risingstars.20250927090838.png" alt="Computing 30 Rising Stars 2025" /></p>
<p>I&#8217;m really delighted and honoured to have been included in this list &#8212; <a href="https://www.computing.co.uk/profile/2025/rising-stars-2025/peter-upfold">Computing.co.uk&#8217;s Rising Stars 2025</a>!</p>
<blockquote><p>Over the past year, he has revolutionised the charity’s observability, teaching himself Zabbix, an open source monitoring tool. His custom scripts now proactively detect and resolve issues before they can have an impact, significantly reducing both downtime and the risk to [the organisation]. Peter extended monitoring for domains and certificates across the infrastructure to prevent outages caused by expired certificates, all while managing a full workload that included optimising cloud costs, saving&#8230; £150,000 annually.</p>
<p>Beyond technical achievements, he has transformed team culture, fostering better communication and aligning technology solutions with the organisation’s mission.</p></blockquote>
<p>The recognition is really nice to have. What I am most grateful for is that, in a <em>very</em> uncertain and challenging world, I&#8217;m in the really fortunate position to be able to spend most of my time contributing to an organisation that shares my values. Technology&#8217;s job should be to make people&#8217;s lives better. It should empower all of us. It should be a tool to help us maintain the delicate balances of power that keep a society healthy and just. It&#8217;s very clear that there&#8217;s a lot of work to do on that in 2025.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Zabbix: Marking switch ports as &#8220;not important&#8221; in bulk</title>
		<link>https://peter.upfold.org.uk/blog/2025/02/15/zabbix-marking-switch-ports-as-not-important-in-bulk/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sat, 15 Feb 2025 14:02:30 +0000</pubDate>
				<category><![CDATA[Zabbix]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[scripting]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3145</guid>

					<description><![CDATA[When monitoring network switches that have end-user-facing ports, such as desk ports, it is excessively noisy to monitor these for conditions like &#8220;this port has gone down!&#8221;. There are a few approaches one can take to addressing this issue, one of which is to set macros on the host which mark each port as &#8220;not [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>When monitoring network switches that have end-user-facing ports, such as desk ports, it is excessively noisy to monitor these for conditions like &#8220;this port has gone down!&#8221;. There are a few approaches one can take to addressing this issue, one of which is to set macros on the host which mark each port as &#8220;not important&#8221;: <strong>{$IFCONTROL:&#8221;portname&#8221;}</strong> set to <strong>0</strong>. However, adding this macro in bulk in the interface is not fun!</p>
<p>So, I&#8217;ve written a script that will set these &#8220;not important&#8221; macros against a number of hosts and interfaces provided in a CSV file.</p>
<p><a href="https://github.com/PeterUpfold/zabbix-api-scripts">You can find the script here</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Accessing Resources via Private Endpoint in Azure Hub-and-Spoke Virtual Network with Basic SKU VPN Gateway</title>
		<link>https://peter.upfold.org.uk/blog/2024/11/13/accessing-resources-via-private-endpoint-in-azure-hub-and-spoke-virtual-network-with-basic-sku-vpn-gateway/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Wed, 13 Nov 2024 17:53:04 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Hub-and-spoke]]></category>
		<category><![CDATA[Private DNS Zones]]></category>
		<category><![CDATA[Virtual Network Gateway]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3070</guid>

					<description><![CDATA[In this blog post, we’ll be: configuring a virtual network topology in Azure in the “hub and spoke model” deploying an example resource (a Key Vault) in our spoke network restricting access to the Key Vault using a private endpoint connection so that it is only accessible inside the vnet configuring a DNS forwarder running [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>In this blog post, we’ll be:</p>
<ul>
<li>configuring a virtual network topology in Azure in the “hub and spoke model”</li>
<li>deploying an example resource (a Key Vault) in our spoke network
<ul>
<li>restricting access to the Key Vault using a private endpoint connection so that it is only accessible inside the vnet</li>
</ul>
</li>
<li>configuring a DNS forwarder running Debian + Unbound in the hub network for resolving the private DNS name of the Key Vault</li>
<li>configuring a Basic SKU Virtual Network Gateway</li>
<li>configuring a Windows client to connect to the Basic VPN Gateway in a point-to-site configuration so it has access to the Key Vault through the private endpoint</li>
</ul>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/hubandspoke.png"><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-3120" style="border: 1px solid #000; padding: 10px; background:#fff" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/hubandspoke.png" alt="Diagram of the architecture. A key vault, KVNEHubAndSpokeTest, is at the left of the diagram, connected to a virtual network vnetHSTestDev (172.16.1.0/24). This is peered with vnetHSTestConnectivity (172.16.0.0/24). This vnet contains private DNS zones, a virtual machine VM-NE-ConnectivityDNS (172.16.0.4), and the basic SKU virtual network gateway, vpng-HSTestConnectivity. On the right, the internet, and a VPN client connected through it. The VPN client has a line connecting it, via the internet, to the VPN gateway" width="819" height="459" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/hubandspoke.png 819w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/hubandspoke-300x168.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/hubandspoke-768x430.png 768w" sizes="(max-width: 819px) 100vw, 819px" /></a></p>
<h2 class="western">Why?</h2>
<p>A hub and spoke network with private endpoints for restricting access to various Azure PaaS resources is a fairly common architecture, but there are a few parts of it that lead to unnecessary costs: namely the PaaS private DNS resolver and the Virtual Network Gateway in its non-Basic SKUs, such as <strong>VpnGw1</strong>.</p>
<p>The primary purpose of this post is to document how I’ve achieved this architecture using the Virtual Network Gateway Basic SKU, which saves ~£80/month over the <b>VpnGw1</b> SKU. It also saves the <a href="https://azure.microsoft.com/en-in/pricing/details/dns/">PaaS private DNS resolver costs</a> by using a lightweight VM.</p>
<h2 class="western">Create hub network</h2>
<p>We’ll start by creating our “hub” network, called <b>vnetHSTestConnectivity</b> in my case.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00-create-hub-network.png"><img decoding="async" class="aligncenter size-full wp-image-3072" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00-create-hub-network.png" alt="Create virtual network screen. The virtual network name is vnetHSTestConnectivity" width="758" height="752" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00-create-hub-network.png 758w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00-create-hub-network-300x298.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00-create-hub-network-150x150.png 150w" sizes="(max-width: 758px) 100vw, 758px" /></a></p>
<p>We’ll be using the 172.16.0.0/24 range for this network.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00a-hub-network-ip-addressing.png"><img decoding="async" class="aligncenter size-full wp-image-3073" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00a-hub-network-ip-addressing.png" alt="Create virtual network IP addressing configuration. We have chosen 172.16.0.0/24" width="762" height="612" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00a-hub-network-ip-addressing.png 762w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/00a-hub-network-ip-addressing-300x241.png 300w" sizes="(max-width: 762px) 100vw, 762px" /></a></p>
<p>We will make the <strong>default</strong> subnet smaller, a /26, and add a gateway subnet after it:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3074" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets-1024x346.png" alt="Add a subnet: we are adding 172.16.0.64/27 as the GatewaySubnet. The default subnet is configured to 172.16.0.0/26" width="1024" height="346" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets-1024x346.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets-300x101.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets-768x260.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets-1536x519.png 1536w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/01-hub-network-subnets.png 1893w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<table class="organic-table" width="100%" cellspacing="0" cellpadding="4">
<tbody>
<tr valign="top">
<th width="50%">
<p align="left">Subnet Name</p>
</th>
<th width="50%">
<p align="left">Range</p>
</th>
</tr>
<tr valign="top">
<td width="50%">
<p align="left">default</p>
</td>
<td width="50%">
<p align="left">172.16.0.0/26</p>
</td>
</tr>
<tr valign="top">
<td width="50%">
<p align="left">GatewaySubnet</p>
</td>
<td width="50%">
<p align="left">172.16.0.64/27</p>
</td>
</tr>
</tbody>
</table>
<h2>Create &#8220;Dev&#8221; spoke network</h2>
<p>Our spoke network will be considered the “Dev” network, and called <b>vnetHSTestDev</b>.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/02-create-dev-network.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3076" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/02-create-dev-network.png" alt="Create virtual network screen. It is called vnetHSTestDev" width="751" height="635" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/02-create-dev-network.png 751w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/02-create-dev-network-300x254.png 300w" sizes="auto, (max-width: 751px) 100vw, 751px" /></a></p>
<p>Here, we’ll use the next /24 block for IP addressing, 172.16.1.0/24.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/03-dev-network-ip-addressing.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3077" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/03-dev-network-ip-addressing.png" alt="IP address screen. We are using 172.16.1.0/24" width="772" height="581" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/03-dev-network-ip-addressing.png 772w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/03-dev-network-ip-addressing-300x226.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/03-dev-network-ip-addressing-768x578.png 768w" sizes="auto, (max-width: 772px) 100vw, 772px" /></a></p>
<h2 class="western">Peer the Connectivity (hub) and Dev (spoke) networks</h2>
<p>We need to peer the two networks. In the vnet configuration, go to <b>Peerings</b> and <b>Add</b>:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-overview.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3078" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-overview.png" alt="Peering overview screen, showing no peerings yet configured" width="711" height="665" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-overview.png 711w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-overview-300x281.png 300w" sizes="auto, (max-width: 711px) 100vw, 711px" /></a></p>
<p>We need to do this on both ends of the connection, so for both vnets:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-configuration.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3079" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-configuration.png" alt="peerConnectivityDev, showing the local virtual network peering settings" width="773" height="902" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-configuration.png 773w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-configuration-257x300.png 257w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/04-connectivity-peering-configuration-768x896.png 768w" sizes="auto, (max-width: 773px) 100vw, 773px" /></a><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/05-dev-peering-configuration.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3080" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/05-dev-peering-configuration.png" alt="peerDevConnectivity, showing the local virtual network peering settings" width="750" height="901" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/05-dev-peering-configuration.png 750w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/05-dev-peering-configuration-250x300.png 250w" sizes="auto, (max-width: 750px) 100vw, 750px" /></a></p>
<h2 class="western">Deploy a Basic SKU VPN Gateway in the Connectivity (Hub) network</h2>
<p>The Basic SKU cannot be selected in the Azure Portal UI, so we’ll need to deploy by PowerShell. Let’s deploy this via the cloud shell. We will need to select <strong>switch to PowerShell</strong> if the shell starts in bash.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/06-deploy-vpngw-cloudshell.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3083" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/06-deploy-vpngw-cloudshell.png" alt="Azure cloud PowerShell, running ./build-vpngw.ps1" width="831" height="339" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/06-deploy-vpngw-cloudshell.png 831w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/06-deploy-vpngw-cloudshell-300x122.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/06-deploy-vpngw-cloudshell-768x313.png 768w" sizes="auto, (max-width: 831px) 100vw, 831px" /></a></p>
<p>The <a href="https://raw.githubusercontent.com/PeterUpfold/azure-hub-and-spoke-network-with-basic-vng/refs/heads/main/New-BasicVPNGateway.ps1">script to run is in my GitHub repository for this post</a>, and is adapted from <a href="https://github.com/MicrosoftDocs/azure-docs/issues/114852#issuecomment-1728303343">this comment in the azure-docs repository</a> (thanks to <a href="https://github.com/GitaraniSharma-MSFT">Gitarani Sharma</a>).</p>
<p>(I quickly used <b>vim</b> to paste in the script contents and run it, but you can also use the cloud shell’s <b>Manage files</b> &gt; <b>Upload</b> to get the PowerShell script into the cloud shell environment so that you can run it.)</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3084" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress-1024x176.png" alt="Azure cloud PowerShell, showing verbose progress &quot;performing the operation Creating Resource&quot;..." width="1024" height="176" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress-1024x176.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress-300x52.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress-768x132.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress-1536x264.png 1536w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-deploy-vpngw-cloudshell-progress.png 1785w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<h2 class="western">Build a VPN client machine</h2>
<p>The machine that will connect into this virtual network and access the key vault will also be another Azure VM. This doesn&#8217;t need to be in Azure at all &#8212; I&#8217;ve done it just for convenience’s sake; it&#8217;s a mighty quick way to build a Windows VM! In a real environment, this is likely to be your development machine that you&#8217;re using to develop whatever Azure solution you&#8217;re building.</p>
<p>Note that the VPN client machine lives <strong>entirely outside the vnets</strong> we have created &#8212; the point is that this machine is <em>remote</em> and connects via the VPN!</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-Z-deploy-vpnclient.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3085" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-Z-deploy-vpnclient.png" alt="Create virtual machine screen, showing no infrastructure redundancy required and the smalldisk Windows Server 2025 image" width="769" height="593" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-Z-deploy-vpnclient.png 769w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/07-Z-deploy-vpnclient-300x231.png 300w" sizes="auto, (max-width: 769px) 100vw, 769px" /></a></p>
<h2 class="western">Generate VPN Gateway certificates</h2>
<p>For the purposes of authenticating to the VPN Gateway, we will use self-signed certificates <a href="https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site">as per Microsoft’s guidance</a>. In a real environment, of course, we would want to use a proper PKI!</p>
<p>We will misuse the VPN client machine to generate these for our lab environment. In PowerShell:</p>
<p><code>$params = @{<br />
Type = 'Custom'<br />
Subject = 'CN=P2SRootCert'<br />
KeySpec = 'Signature'<br />
KeyExportPolicy = 'Exportable'<br />
KeyUsage = 'CertSign'<br />
KeyUsageProperty = 'Sign'<br />
KeyLength = 2048<br />
HashAlgorithm = 'sha256'<br />
NotAfter = (Get-Date).AddMonths(24)<br />
CertStoreLocation = 'Cert:\CurrentUser\My'<br />
}<br />
$cert = New-SelfSignedCertificate @params</code></p>
<p><code>$params = @{<br />
Type = 'Custom'<br />
Subject = 'CN=P2SChildCert'<br />
DnsName = 'P2SChildCert'<br />
KeySpec = 'Signature'<br />
KeyExportPolicy = 'Exportable'<br />
KeyLength = 2048<br />
HashAlgorithm = 'sha256'<br />
NotAfter = (Get-Date).AddMonths(18)<br />
CertStoreLocation = 'Cert:\CurrentUser\My'<br />
Signer = $cert<br />
TextExtension = @(<br />
'2.5.29.37={text}1.3.6.1.5.5.7.3.2')<br />
}<br />
New-SelfSignedCertificate @params</code></p>
<p>Make a note of the 24 month and 18 month certificate expiry, if this environment is likely to last that long!</p>
<p>Now, we need to export the <strong>P2SRootCert</strong> root certificate. Open <b>mmc</b> and add the Certificates snap-in for the current user.</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/08-export-root-cert.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3086" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/08-export-root-cert.png" alt="MMC certificates snap-in, focusing on the context menu of P2SRootCert, where we are about to click Export" width="991" height="380" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/08-export-root-cert.png 991w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/08-export-root-cert-300x115.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/08-export-root-cert-768x294.png 768w" sizes="auto, (max-width: 991px) 100vw, 991px" /></a></p>
<p>We don’t need to export the private key in this case, as we won’t be signing any new certificates or using this PKI outside of this test environment.</p>
<p>Export as base 64 format:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/09-export-root-cert-base64.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3087" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/09-export-root-cert-base64.png" alt="Export certificate wizard, showing us selecting Base-64 encoded X.509 (.cer)" width="547" height="519" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/09-export-root-cert-base64.png 547w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/09-export-root-cert-base64-300x285.png 300w" sizes="auto, (max-width: 547px) 100vw, 547px" /></a></p>
<p>Open the exported file in Notepad and copy the base 64 certificate content. Exclude the beginning and end lines; you should have just the base 64 characters themselves.</p>
<h2 class="western">Set up VPN Gateway point-to-site configuration</h2>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/10-point-to-site-not-configured.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3088" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/10-point-to-site-not-configured.png" alt="VPN Gateway point-to-site configuration -- not yet configured" width="999" height="198" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/10-point-to-site-not-configured.png 999w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/10-point-to-site-not-configured-300x59.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/10-point-to-site-not-configured-768x152.png 768w" sizes="auto, (max-width: 999px) 100vw, 999px" /></a></p>
<p>The address pool needs to be a range specifically for VPN clients and must be <b>outside</b> the ranges of the networks that those clients seek to access through the VPN.</p>
<p>We will choose <b>192.168.240.0/24</b>, as this is well outside the 172.16 ranges we are using for the hub and spoke networks, and is less likely to conflict with domestic local networks that use low numbers in the 192.168.* space!</p>
<p>Paste the base64 content of the root certificate into <b>public certificate data</b> and give it a name.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3089" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert-1024x370.png" alt="Point-to-site configuration, showing address pool 192.168.240.0/24 and the P2SRootCert base 64 content pasted in to Public certificate data." width="1024" height="370" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert-1024x370.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert-300x108.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert-768x278.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/11-point-to-site-ip-space-and-cert.png 1485w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Add the additional routes of the peered networks to <b>Additional routes to advertise</b>. In our case, that’s the IP range of the Dev network, 172.16.1.0/24.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/12-additional-routes-to-advertise.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3090" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/12-additional-routes-to-advertise.png" alt="Additional routes to advertise, showing 172.16.1.0/24" width="695" height="69" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/12-additional-routes-to-advertise.png 695w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/12-additional-routes-to-advertise-300x30.png 300w" sizes="auto, (max-width: 695px) 100vw, 695px" /></a></p>
<p>This may take <i>some time</i> to save the new configuration, so while we wait for <b>Updating</b> to disappear from the VPN Gateway’s <b>Overview</b> page, we’ll set up our private DNS zone for the Key Vault. (We haven’t created the vault yet!)</p>
<h1 class="western">Create private DNS zone for Key Vault</h1>
<p>Deploy a new <b>Private DNS Zone</b>. We don’t need to do anything in the Private DNS Zone Editor, just deploy it as is:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/13-private-dns-zone.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3091" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/13-private-dns-zone.png" alt="Create Private DNS Zone screen, with instance name privatelink.vault.azure.net" width="621" height="452" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/13-private-dns-zone.png 621w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/13-private-dns-zone-300x218.png 300w" sizes="auto, (max-width: 621px) 100vw, 621px" /></a></p>
<p>Link the Private DNS Zone to the connectivity (hub) network:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/14-link-private-dns-zone.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3092" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/14-link-private-dns-zone.png" alt="Add Virtual Network Link to Private DNS zone, showing that it is linked to the vnet vnetHSTestConnectivity" width="749" height="432" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/14-link-private-dns-zone.png 749w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/14-link-private-dns-zone-300x173.png 300w" sizes="auto, (max-width: 749px) 100vw, 749px" /></a></p>
<h1 class="western">Deploy DNS server in the Connectivity (hub) network</h1>
<p>So that we can resolve the name of the Key Vault to its internal IP address in our Dev (spoke) network, we need a DNS server that’s in the Connectivity (hub) network.</p>
<p>The VPN client machine will query this DNS server for the specific namespaces that my connection script will configure, including <b>.vault.azure.net</b>.</p>
<p>You can of course do this with Azure Private DNS Resolver, but at a much higher cost!</p>
<p>I will deploy a Debian VM in this instance, and use the Unbound DNS server.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3093" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config-1024x502.png" alt="Create a virtual machine for the DNS resolver machine, showing name VM-NE-ConnectivityDNS, Debian 12 bookworm arm64 image and the size Standard_P2pts_v2" width="1024" height="502" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config-1024x502.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config-300x147.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config-768x376.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/15-private-dns-resolver-vm-config.png 1420w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>It will be deployed into the default subnet of <b>vnetHSTestConnectivity</b>:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/16-private-dns-resolver-vm-config-network.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3094" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/16-private-dns-resolver-vm-config-network.png" alt="Create a virtual machine networking screen for DNS resolver VM, showing the vnetHSTestConnectivity virtual network, default VLAN and no NIC network security group" width="742" height="739" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/16-private-dns-resolver-vm-config-network.png 742w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/16-private-dns-resolver-vm-config-network-300x300.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/16-private-dns-resolver-vm-config-network-150x150.png 150w" sizes="auto, (max-width: 742px) 100vw, 742px" /></a></p>
<p>Once deployed, connect over SSH to the new VM.</p>
<p>This new DNS server has been assigned<b> 172.16.0.4</b> in my example.</p>
<p>We will install and configure Unbound:</p>
<pre class="western">sudo apt update
sudo systemctl disable --now systemd-resolved
sudo apt install unbound
sudo vim /etc/unbound/unbound.conf</pre>
<p>&nbsp;</p>
<p>I’ll edit the config file to add <strong>access-control</strong> entries for both the private ranges of the hub and spoke network, and also for the VPN connection IP address range 192.168.240.0/24.</p>
<p>Finally, the <b>forward-zone</b> stanza ensures that all DNS queries are forwarded via the Azure DNS resolver inside the Connectivity network. Because the private DNS zone is linked to this network, our DNS VM and its clients can resolve the private names.</p>
<pre class="western">server:
  interface: 0.0.0.0
  access-control: 172.16.0.0/16 allow
  access-control: 192.168.240.0/24 allow

forward-zone:
  name: <span style="font-family: Liberation Mono, monospace;">"</span>.<span style="font-family: Liberation Mono, monospace;">"</span>
  forward-addr: 168.63.129.16</pre>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/17-unbound-configuration.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3095" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/17-unbound-configuration.png" alt="Unbound configuration, showing the access-control and forward-addr stanzas" width="735" height="812" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/17-unbound-configuration.png 735w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/17-unbound-configuration-272x300.png 272w" sizes="auto, (max-width: 735px) 100vw, 735px" /></a></p>
<p>Let’s restart Unbound:</p>
<pre class="western">sudo systemctl restart unbound</pre>
<h2 class="western">Deploy Key Vault with private link</h2>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/18-keyvault-deploy.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3096" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/18-keyvault-deploy.png" alt="Key Vault in the Marketplace. We are about to click Key Vault to create it" width="429" height="719" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/18-keyvault-deploy.png 429w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/18-keyvault-deploy-179x300.png 179w" sizes="auto, (max-width: 429px) 100vw, 429px" /></a></p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/19-keyvault-name.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3097" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/19-keyvault-name.png" alt="Create key vault screen, showing the name KVNEHubAndSpokeTestDev" width="759" height="740" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/19-keyvault-name.png 759w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/19-keyvault-name-300x292.png 300w" sizes="auto, (max-width: 759px) 100vw, 759px" /></a></p>
<p>My Key Vault is called <b>KVNEHubAndSpokeTestDev</b>.</p>
<p>&nbsp;</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/20-keyvault-access-config.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3098" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/20-keyvault-access-config.png" alt="Key Vault Access Config screen, where we leave the defaults" width="584" height="743" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/20-keyvault-access-config.png 584w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/20-keyvault-access-config-236x300.png 236w" sizes="auto, (max-width: 584px) 100vw, 584px" /></a></p>
<p>On the <b>Networking</b> tab, we will disable public access and create a private endpoint linked to <strong>vnetHSTestDev</strong>:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3099" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint-1024x549.png" alt="Key vault create private endpoint screen, showing that we are connecting the private endpoint to vnetHSTestDev" width="1024" height="549" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint-1024x549.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint-300x161.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint-768x412.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/21-keyvault-private-endpoint.png 1416w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Once created, we will be unable to list keys or secrets in the portal, as we’re not yet transiting the VPN to access the Key Vault via the private endpoint:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3100" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png" alt="Key Vault Keys screen, showing that &quot;public network access is disabled and request is not from a trusted service nor via an approved private link&quot;" width="897" height="673" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png 897w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access-300x225.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access-768x576.png 768w" sizes="auto, (max-width: 897px) 100vw, 897px" /></a></p>
<p>Let’s make sure the user we are using has the appropriate RBAC permissions to work with the data in the Key Vault, and it’s only the private endpoint issue that’s preventing us from having access. (Even an Owner will not have access to the Key Vault data plane by default.)</p>
<p>We will add the<b> Key Vault Administrator</b> RBAC role:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3101" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac-1024x507.png" alt="RBAC screen for Key Vault, adding Key Vault Administrator" width="1024" height="507" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac-1024x507.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac-300x148.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac-768x380.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/23-keyvault-rbac.png 1225w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>We need to link the private DNS zone for <b>privatelink.vaultcore.azure.net</b> into the Connectivity vnet:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/24-linkvaultcore.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3102" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/24-linkvaultcore.png" alt="Add Virtual Network Link for privatelink.vaultcore.azure.net into the vnetHSTestConnectivity network" width="804" height="552" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/24-linkvaultcore.png 804w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/24-linkvaultcore-300x206.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/24-linkvaultcore-768x527.png 768w" sizes="auto, (max-width: 804px) 100vw, 804px" /></a></p>
<h2 class="western">Test: look up the Key Vault name on the DNS server</h2>
<p>To test that the private DNS zone is working, I’ll log onto my DNS server VM and query it directly for the name of the Key Vault.</p>
<pre class="western">sudo apt install dnsutils
dig @127.0.0.1 kvnehubandspoketestdev.vault.azure.net</pre>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3103" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server-1024x433.png" alt="Using dig to resolve the Key Vault on the DNS server. We see the result of dig command -- an address of 172.16.1.5" width="1024" height="433" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server-1024x433.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server-300x127.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server-768x325.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-look-up-on-dns-server.png 1128w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>We know this is working here as it resolves to a private IP address in the Dev network range: 172.16.1.5.</p>
<h2 class="western">Connect from isolated VM into the connectivity network via the VNG</h2>
<p>Everything is looking good, but we haven’t yet connected our VPN client to the network!</p>
<p>Back in the VPN Gateway Overview page, download the connection profile by pressing the <b>Download VPN client </b>button.</p>
<p>In the zip file that you’ll get, we will transfer the script in the <b>WindowsPowerShell</b> folder across to the VPN client machine.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-addvpnconnection.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3104" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-addvpnconnection.png" alt="Add VPN connection PowerShell script, showing that it begins with an embedded XML document with an EapHostConfig root node" width="634" height="336" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-addvpnconnection.png 634w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-addvpnconnection-300x159.png 300w" sizes="auto, (max-width: 634px) 100vw, 634px" /></a></p>
<p>Run this PowerShell script. If successful, you’ll be able to go to Windows Settings &gt; Network &gt; VPN and see the new profile <b>vnetHSTestConnectivity</b>. Don’t connect to it just yet.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-vpn-connection-in-settings.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3105" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-vpn-connection-in-settings.png" alt="Windows Settings app &gt; network &amp; internet &gt; VPN, showing that a connection called vnetHSTestConnectivity exists, but is not connected" width="509" height="319" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-vpn-connection-in-settings.png 509w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/25-Z-vpn-connection-in-settings-300x188.png 300w" sizes="auto, (max-width: 509px) 100vw, 509px" /></a></p>
<p>When we connect to the VPN, we need to also add rules so that Windows directs DNS requests for the private resources that need to be resolved inside the VPN to our DNS server in that network. This can be handled automatically by OpenVPN etc. using the higher end VPN Gateway SKU, but not the Basic SKU.</p>
<p>I’ve written a PowerShell script for connecting and disconnecting from the network that automates the management of these DNS policy rules to let us resolve the private names. Let’s take a look.</p>
<p>The scripts and configuration are in <a href="https://github.com/PeterUpfold/azure-hub-and-spoke-network-with-basic-vng/tree/main">the GitHub repository that accompanies this post</a>.</p>
<p><a href="https://raw.githubusercontent.com/PeterUpfold/azure-hub-and-spoke-network-with-basic-vng/refs/heads/main/config.example.json">config.json</a> contains the private DNS zones that will be resolved via the private DNS resolver, and the IP address of this VM inside the vnet.</p>
<pre class="western">{
    "VPNConfigurationName": "vnetHSTestConnectivity",
    "VPNGatewayRange": "192.168.240.0/24",
    "DNSClientNRPTRules": [
        ".vault.azure.net",
        ".table.core.windows.net",
        ".vaultcore.azure.net",
        ".servicebus.windows.net",
        ".queue.core.windows.net",
        ".file.core.windows.net",
        ".blob.core.windows.net",
        ".azurewebsites.net"
    ],
    "DNSServer": "172.16.0.4"
}</pre>
<p>(We’re only using .vault.azure.net / .vaultcore.azure.net in this post, but this configuration is flexible!)</p>
<p>We will connect to the VPN by running <a href="https://github.com/PeterUpfold/azure-hub-and-spoke-network-with-basic-vng/blob/main/Connect-BasicVPNGatewayWithPrivateDNS.ps1">Connect-BasicVPNGatewayWithPrivateDNS.ps1</a></p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3106" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn.png" alt="PowerShell - Connect-BasicVPNGatewayWithPrivateDNS.ps1. The command has not yet run" width="705" height="253" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn.png 705w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-300x108.png 300w" sizes="auto, (max-width: 705px) 100vw, 705px" /></a></p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3107" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress-1024x598.png" alt="PowerShell - Connect-BasicVPNGatewayWithPrivateDNS.ps1. We can see verbose output for rthe script adding various DNS policy rules &quot;adding NRPT rule for namespace&quot;..." width="1024" height="598" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress-1024x598.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress-300x175.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress-768x449.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/26-connect-powershell-vpn-progress.png 1075w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>We should now be connected to the VPN. We should be able to ping the DNS server at <b>172.16.0.4</b>.</p>
<p>Let’s verify that we can resolve the private endpoint DNS name of the Key Vault to its internal IP address:</p>
<pre class="western">Resolve-DnsName kvnehubandspoketestdev.vault.azure.net</pre>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3108" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn-1024x568.png" alt="Reolsve-DnsName kvneuhubandspoketestdev.privatelink.vaultcore.azure.net resolves to 172.16.1.5" width="1024" height="568" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn-1024x568.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn-300x166.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn-768x426.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/27-resolve-private-name-over-vpn.png 1044w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><br />
Great! We can see the IP4Address returned is in the spoke network range: <b>172.16.1.5</b>.</p>
<p>Now let’s access the vault over the private endpoint by means of our VPN connection. For demonstration purposes, we will do this using a browser on our VPN client machine and accessing the Azure Portal.</p>
<p>Remember that before we saw this error?</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3100" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png" alt="Key Vault Keys screen, showing that &quot;public network access is disabled and request is not from a trusted service nor via an approved private link&quot;" width="897" height="673" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access.png 897w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access-300x225.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/22-keyvault-no-access-768x576.png 768w" sizes="auto, (max-width: 897px) 100vw, 897px" /></a></p>
<p>We should now be able to generate a key:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/28-key-vault-gen-key.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3109" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/28-key-vault-gen-key.png" alt="Azure Key Vavult Generate Key screen. We are creating a key called test" width="612" height="615" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/28-key-vault-gen-key.png 612w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/28-key-vault-gen-key-300x300.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/28-key-vault-gen-key-150x150.png 150w" sizes="auto, (max-width: 612px) 100vw, 612px" /></a></p>
<p>and add a secret:</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/29-key-vault-create-secret.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3110" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/29-key-vault-create-secret.png" alt="Azure Key Vault Create secret screen. We are creating a secret called testsecret" width="553" height="576" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/29-key-vault-create-secret.png 553w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/29-key-vault-create-secret-288x300.png 288w" sizes="auto, (max-width: 553px) 100vw, 553px" /></a></p>
<p>If we disconnect from the VPN, we should be unable to list the secrets again, due to the vnet restriction.</p>
<p>It’s important to use <a href="https://github.com/PeterUpfold/azure-hub-and-spoke-network-with-basic-vng/blob/main/Disconnect-BasicVPNGatewayWithPrivateDNS.ps1">the corresponding disconnect script</a> to disconnect, so that the DNS policy rules are removed and those Azure DNS namespaces don’t continue to try and resolve through an inaccessible DNS server!</p>
<pre class="western">.\Disconnect-BasicVPNGatewayWithPrivateDNS.ps1

</pre>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/30-script-disconnect-dns.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3111" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/30-script-disconnect-dns.png" alt="Disconnect script, showing the removal of the DNS NRPT rules that were added by the connection script" width="686" height="636" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/30-script-disconnect-dns.png 686w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/30-script-disconnect-dns-300x278.png 300w" sizes="auto, (max-width: 686px) 100vw, 686px" /></a></p>
<p>Once disconnected from the VPN, we’re locked out of listing the secrets again!</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3112" style="border: 1px solid #000;" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list-1024x530.png" alt="Key Vault, again showing that we cannot list secrets" width="1024" height="530" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list-1024x530.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list-300x155.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list-768x397.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/32-key-vault-unable-to-list.png 1257w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>“Could not import package. Warning SQL72012 / Error SQL72014” when importing a .bacpac from a blob</title>
		<link>https://peter.upfold.org.uk/blog/2024/11/09/could-not-import-package-warning-sql72012-when-importing-a-bacpac-from-a-blob/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sat, 09 Nov 2024 07:48:32 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Azure SQL]]></category>
		<category><![CDATA[bacpac]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3063</guid>

					<description><![CDATA[Azure SQL Database’s point in time restore and long term retention are solid backup options, of which you’d have every reasonable expectation for a PaaS service! However, Microsoft’s documentation is abundantly clear that, at the time of writing, there is no support for immutable backups via this method. If you actually need to achieve immutable [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Azure SQL Database’s point in time restore and long term retention are solid backup options, of which you’d have every reasonable expectation for a PaaS service!</p>
<p>However, Microsoft’s documentation is abundantly clear that, at the time of writing, <a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/automated-backups-overview?view=azuresql">there is no support for immutable backups</a> via this method.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-sql-backups-not-supported.png"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-3064" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-sql-backups-not-supported.png" alt="&quot;Configure backups as immutable&quot; stated as &quot;not supported&quot; for Azure SQL in a table on Microsoft's documentation site" width="665" height="117" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-sql-backups-not-supported.png 665w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-sql-backups-not-supported-300x53.png 300w" sizes="auto, (max-width: 665px) 100vw, 665px" /></a></p>
<p>If you actually need to achieve immutable backup storage for Azure SQL database, you’ll need a different approach.</p>
<p><a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/database-export?view=azuresql">The <b>Export</b> button within Azure SQL Database</a> can be used to export a .bacpac file. If this is stored in a <a href="https://learn.microsoft.com/en-us/azure/storage/blobs/immutable-storage-overview">storage account with immutability locked</a>, you have a copy of your data that will be resilient, even to a Global Administrator compromise.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3065" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage-1024x411.png" alt="Microsoft Azure Portal -- the Access policy page for a blob container, showing the immutable blob storage options" width="1024" height="411" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage-1024x411.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage-300x120.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage-768x308.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/11/immutable-blob-storage.png 1402w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>With regard to .bacpac exports, Microsoft helpfully reminds us that:</p>
<blockquote><p>BACPACs are not intended to be used for backup and restore operations. Azure automatically creates backups for every user database. For details, see <a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/business-continuity-high-availability-disaster-recover-hadr-overview?view=azuresql">business continuity overview</a> and <a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/automated-backups-overview?view=azuresql-db&amp;preserve-view=true">Automated backups in Azure SQL Database</a> or <a href="https://learn.microsoft.com/en-us/azure/azure-sql/managed-instance/automated-backups-overview?view=azuresql-mi&amp;preserve-view=true">Automated backups in Azure SQL Managed Instance</a>.</p></blockquote>
<p>However, that leads me right back to “immutability is not supported” point regarding the backups they’re mentioning here. It seems remarkable that “business continuity” is mentioned in the context of backups that are very vulnerable in many BCP scenarios, given the world of ransomware we face today (and will face in the future!)</p>
<p>A .bacpac file held in immutable storage can be imported back into a new Azure SQL database to restore it, but it’s important to note Microsoft’s warning:</p>
<blockquote><p>For an export to be transactionally consistent, you must ensure either that no write activity is occurring during the export, or that you&#8217;re exporting from a <a href="https://learn.microsoft.com/en-us/azure/azure-sql/database/database-copy?view=azuresql">transactionally consistent copy</a> of your database.</p></blockquote>
<p>This is indeed critical. A copy can be made simply with the <b>Copy </b>button within Azure SQL Database. Once complete, press <b>Export</b> on the copy of the database. You can delete the copied database once the export is complete.</p>
<p>The truncated error message I received (and the reason for this blog post) when trying to import a .bacpac that was <b>not</b> transactionally consistent is as follows:</p>
<pre class="western"><a name="code"></a><code class="western">The ImportExport operation with Request Id failed due to 'Could not import package. Warning SQL72012: The object [data_0] exists in the target, but it will not be dropped even though you selected the 'Generate drop statements for objects that are in the target database but that are not in the source' check box. Warning SQL72012: The object [log] exists in the target, but it will not be dropped even though you selected the 'Generate drop statements for objects that are in the target database but that are not in the source' check box. Error SQL72014: Framework Mi'.</code></pre>
<p>If you see this, you’ll need to export a <i>copy</i> of the database, as above, so that no transactions are occurring on that database copy for the duration of the export operation.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Office will not find Current Channel (Preview) or Beta updates</title>
		<link>https://peter.upfold.org.uk/blog/2024/10/09/office-will-not-find-current-channel-preview-or-beta-updates/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Wed, 09 Oct 2024 20:09:47 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[Microsoft Office]]></category>
		<category><![CDATA[Office]]></category>
		<category><![CDATA[Office 365]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3057</guid>

					<description><![CDATA[If you have at some point enabled Microsoft’s Cloud Update in the Microsoft 365 Apps admin center, and later want to update a device managed in this way to Current Channel (Preview) or the beta channel, you may find yourself tearing your hair out wondering why a separate Group Policy/Intune/registry based instruction to switch to [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>If you have at some point <a href="https://learn.microsoft.com/en-us/microsoft-365-apps/admin-center/cloud-update">enabled Microsoft’s Cloud Update in the Microsoft 365 Apps admin center</a>, and later want to update a device managed in this way to Current Channel (Preview) or the beta channel, you may find yourself tearing your hair out wondering why a separate Group Policy/Intune/registry based instruction to switch to a different channel doesn’t work immediately. Or perhaps why you can’t quickly take a machine out of cloud update control, or indeed deploy the preview channels via the cloud update mechanism!</p>
<p>To be fair, Cloud Update taking precedence and causing other policies to be ignore<a href="https://learn.microsoft.com/en-us/microsoft-365-apps/admin-center/cloud-update"> is documented</a>, although how to override this if you do need to wrestle control back from the Cloud Updates is <a href="https://learn.microsoft.com/en-us/microsoft-365-apps/admin-center/cloud-update">buried a little</a> in Microsoft’s document.</p>
<p>I had a test machine that was enrolled in the Cloud Update, but I now needed to be on Current Channel (Preview), so I couldn’t have it be controlled by Cloud Update anymore, as these preview channels aren’t an option in that portal. The test machine was added to the scope of <a href="https://learn.microsoft.com/en-us/microsoft-365-apps/updates/change-update-channels#change-the-update-channel-with-microsoft-intune-administrative-templates">an Intune Configuration Profile which set the update channel via Administrative Templates</a>.</p>
<p>A simple check for updates in any Office app happily informed me I was already up to date. 🙁</p>
<p>I could get the device on the beta channel by running <b>officesetup.exe /configure &lt;xml&gt;</b>, with an XML file as follows:</p>
<p><code>&lt;Configuration&gt;<br />
&lt;Updates Channel=<span style="font-family: Source Sans Pro, sans-serif;">"</span>BetaChannel<span style="font-family: Source Sans Pro, sans-serif;">"</span> /&gt;<br />
&lt;/Configuration&gt;</code></p>
<p>However, this wouldn’t stick – the next Office update would roll it right back to Current Channel.</p>
<p>I excluded an Entra ID group containing the primary user and the device from Cloud Update, but this didn’t help immediately. I didn’t have 24 hours available to me to wait for the exclusion to take effect.</p>
<div id="attachment_3058" style="width: 831px" class="wp-caption aligncenter"><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image001.png"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-3058" class="size-full wp-image-3058" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image001.png" alt="Screenshot of Microsoft 365 apps admin center, showing &quot;Updates Overview&quot;, where in &quot;Tenant Settings&quot; you can set an exclusion group" width="821" height="416" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image001.png 821w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image001-300x152.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image001-768x389.png 768w" sizes="auto, (max-width: 821px) 100vw, 821px" /></a><p id="caption-attachment-3058" class="wp-caption-text">In Tenant Settings, you can exclude a group from cloud update management, but it may take some &#8220;cloud time&#8221; to apply!</p></div>
<p>It turns out that once in Cloud Update, if we want to force Cloud Update off without waiting for the machine to drop out of the inventory, we must set this registry value:</p>
<p><b>HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\cloud\office\16.0\Common\officeupdate</b></p>
<p>Value: <b>ignoregpo</b>, Type: <b>DWORD</b>, Value data: <b>0</b></p>
<div id="attachment_3059" style="width: 1034px" class="wp-caption aligncenter"><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box.png"><img loading="lazy" decoding="async" aria-describedby="caption-attachment-3059" class="size-large wp-image-3059" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box-1024x299.png" alt="Windows Registry Editor, with ignoregpo = 0 highlighted, in the pathHKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\cloud\office\16.0\Common\officeupdate" width="1024" height="299" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box-1024x299.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box-300x88.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box-768x224.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/10/image002_reg_box.png 1153w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a><p id="caption-attachment-3059" class="wp-caption-text">Change ignoregpo from 1 to 0 here!</p></div>
<p>Now, the updates setting from <b>HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\office </b>will actually be honoured, and an update check should find the right channel!</p>
<p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Sentinel KQL Queries: Detecting a Lack of Zscaler Data Ingestion</title>
		<link>https://peter.upfold.org.uk/blog/2024/08/18/sentinel-kql-queries-detecting-a-lack-of-zscaler-data-ingestion/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sun, 18 Aug 2024 07:41:35 +0000</pubDate>
				<category><![CDATA[Security]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[KQL]]></category>
		<category><![CDATA[Log Analytics]]></category>
		<category><![CDATA[Sentinel]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3044</guid>

					<description><![CDATA[Ingesting data from Zscaler Internet Access and Zscaler Private Access into a SIEM is a valuable technique for identifying risky endpoint activity or system compromise. It also gives you a (hopefully) immutable1 copy of this audit data to support a post-incident investigation. I’ve been able to configure both Zscaler Internet Access and Zscaler Private Access [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="wp-block-image"><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/08/2024-08-18-zscaler-ingestion.png"><img loading="lazy" decoding="async" class="alignright wp-image-3047 size-medium" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/08/2024-08-18-zscaler-ingestion-228x300.png" alt="Screenshot of the &quot;PIU - No Zscaler Internet Access data received in 2 hours&quot; Sentinel rule in situ in the Azure Portal." width="228" height="300" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/08/2024-08-18-zscaler-ingestion-228x300.png 228w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/08/2024-08-18-zscaler-ingestion.png 573w" sizes="auto, (max-width: 228px) 100vw, 228px" /></a></div>
<p>Ingesting data from Zscaler Internet Access and Zscaler Private Access into a SIEM is a valuable technique for identifying risky endpoint activity or system compromise. It also gives you a (hopefully) immutable<sup><a href="#3044-foonote1">1</a></sup> copy of this audit data to support a post-incident investigation.</p>
<p>I’ve been able to configure <a href="https://help.zscaler.com/zscaler-technology-partners/zscaler-and-microsoft-sentinel-deployment-guide">both Zscaler Internet Access and Zscaler Private Access data to be ingested into Microsoft Sentinel</a><sup><a href="#3044-foonote2">2</a></sup>, but occasionally have found that the somewhat circuitous path that ZIA data takes into the SIEM (NSS VM, to another Linux VM over syslog, then to Azure Monitor Agent, and finally to Sentinel) can be brittle. A reboot of the collector VMs has always fixed this, but you have to know that the data flow has stopped!</p>
<p>I have written a couple of KQL queries for this scenario – one for Zscaler Internet Access (ZIA) and one for Zscaler Private Access (ZPA).</p>
<p>Each will trigger an incident if zero log entries are received for the relevant service within a 2 hour period.</p>
<p>You can import these to your own Sentinel environment by clicking <b>Import</b> in <b>Analytics rules</b> and providing the ARM template files linked below.</p>
<h2 class="western">Zscaler Internet Access</h2>
<p><a class="button" href="https://peter.upfold.org.uk/files/2024-08-zia-no-data.json">Download as ARM template</a></p>
<p><code>CommonSecurityLog | where DeviceVendor == "Zscaler" and DeviceProduct == "NSSWeblog"<br />
| where TimeGenerated &gt; ago(30d)<br />
| summarize last_log = datetime_diff("second",now(), max(TimeGenerated))<br />
| where last_log &gt;= 7200</code></p>
<p>Set this to run against data for the last 2 hours, with the maximum “look back” period. The query will return 0 results if data ingestion is occurring correctly, so you will want to alert on >0 log entries.</p>
<h2 class="western">Zscaler Private Access</h2>
<p><a class="button" href="https://peter.upfold.org.uk/files/2024-08-zpa-no-data.json">Download as ARM template</a></p>
<p><code>ZPAUserActivity<br />
| where LogTimestamp &gt; ago(30d)<br />
| summarize last_log = datetime_diff("second",now(), max(LogTimestamp))<br />
| where last_log &gt;= 7200</code></p>
<p>Set this to run against data for the last 2 hours, with the maximum “look back” period.</p>
<p>You will get an error <i>“Failed to run the analytics rule query. One of the tables does not exist”</i> if you have not completely configured the ZPA log ingestion, including adding the custom <b>ZPAUserActivity </b>function to your Sentinel workspace. Follow the <a href="https://help.zscaler.com/zscaler-technology-partners/zscaler-and-microsoft-sentinel-deployment-guide">Zscaler and Microsoft Sentinel Deployment Guide</a> (“Configuring NSS VM-Based Log Ingestion for ZPA”, page 126).</p>
<p>&nbsp;</p>
<p><a name="3044-foonote1"></a>1: The keys to the kingdom are then the Sentinel/LA workspace, which hopefully your attacker has not escalated privileges to be able to delete. There’s nothing like “immutable vaults” in Azure Recovery Vaults for Sentinel or Log Analytics workspaces. You can set a standard Azure lock, but a privileged attacker could just delete the lock!</p>
<p><a name="3044-foonote2"></a>2: Zscaler recently (just in time) updated the ZPA ingestion workflow to use the Azure Monitor Agent rather than the deprecated Log Analytics Agent. This took a little reconfiguring and was quite an involved process!</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Word hangs on saving: App Control for Business and WebClnt.dll</title>
		<link>https://peter.upfold.org.uk/blog/2024/07/28/word-hangs-on-saving-app-control-for-business-and-webclnt-dll/</link>
					<comments>https://peter.upfold.org.uk/blog/2024/07/28/word-hangs-on-saving-app-control-for-business-and-webclnt-dll/#comments</comments>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sun, 28 Jul 2024 09:13:28 +0000</pubDate>
				<category><![CDATA[Windows]]></category>
		<category><![CDATA[App Control for Business]]></category>
		<category><![CDATA[ETW]]></category>
		<category><![CDATA[WDAC]]></category>
		<category><![CDATA[Web Client]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3032</guid>

					<description><![CDATA[The Symptom Users were experiencing a 5-15 second delay when saving a document to OneDrive or SharePoint, during which Word would show as &#8220;not responding&#8221;. All machines in question use App Control for Business (WDAC). The Cause During the &#8220;not responding&#8221; period, Word is attempting to start the Web Client service, which is set to [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>The Symptom</h2>
<p>Users were experiencing a 5-15 second delay when saving a document to OneDrive or SharePoint, during which Word would show as &#8220;not responding&#8221;.</p>
<p>All machines in question use App Control for Business (WDAC).</p>
<h2>The Cause</h2>
<p>During the &#8220;not responding&#8221; period, Word is attempting to start the Web Client service, which is set to <strong>Manual</strong>.</p>
<p><strong>svchost.exe</strong> launches, tries to load the <strong>WebClnt.dll</strong> library, but this is blocked by App Control for Business.</p>
<p>Word has to wait for this attempt to time out, before then giving up and saving anyway.</p>
<h2>The Fix</h2>
<p>Setting the Web Client service start type to <strong>Disabled</strong> prevents Word from attempting to start the service, fixing the delay.</p>
<h2>The Diagnosis Process</h2>
<p><a href="https://randomascii.wordpress.com/">Event Tracing for Windows</a> and <a href="https://github.com/google/UIforETW">UIforETW</a> strike <a href="https://peter.upfold.org.uk/blog/2023/12/22/adventures-in-etw-slow-comment/">again</a>, along with the incredible utility of the public symbols for Office.</p>
<p>The process was similar to that which I documented <a href="https://peter.upfold.org.uk/blog/2023/12/22/adventures-in-etw-slow-comment/">here</a>, where the <a href="https://peter.upfold.org.uk/blog/2023/12/22/adventures-in-etw-slow-comment/">process of saving comments in Word caused a hang</a>. The key is to click <strong>Trace </strong>&gt; <strong>Load Symbols </strong>to ensure we have function names in the trace.</p>
<p>Drilling down to the problem time area and then digging into the amount of time we spend in each function revealed that the time was being spent in <strong>davhlpr.dll!TriggerStartWebclientServiceIfNotRunning</strong>.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice.png"><img loading="lazy" decoding="async" class="size-large wp-image-3034 aligncenter" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice-1024x407.png" alt="A screenshot of Windows Performance Analyzer, showing a stack trace where a significant wait was being incurred in davhlpr.dll!TriggerWebClientServiceIfNotRunning" width="1024" height="407" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice-1024x407.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice-300x119.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice-768x305.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice-1536x611.png 1536w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-trigger-webclientservice.png 1795w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3039" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt-1024x374.png" alt="Stack trace from Windows Performance Analyzer from WWLIB.DLL!CmdSaveAs to davhlpr.dll!TriggerStartWebclientServiceIfNotRunning" width="1024" height="374" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt-1024x374.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt-300x109.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt-768x280.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt-1536x560.png 1536w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-stack-trace-winword-webclnt.png 1623w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Well this does indeed sound like something to do with the Web Client service!</p>
<p>I&#8217;d also separately noticed frequent events in the <strong>CodeIntegrity</strong> Windows Event Log relating to <strong>WebClnt.dll</strong>. This library is blocked by the default rule set for App Control for Business, as <a href="https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control/design/applications-that-can-bypass-wdac">apparently it can be used as a bypass for the feature</a>.</p>
<blockquote><p>Unless your use scenarios explicitly require them, Microsoft recommends that you block the following applications. An attacker can use these applications or files to circumvent application allow policies, including WDAC:</p>
<p>&#8230;</p>
<ul>
<li>webclnt.dll/davsvc.dll</li>
</ul>
</blockquote>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll.png"><img loading="lazy" decoding="async" class="aligncenter size-large wp-image-3038" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll-1024x421.png" alt="CodeIntegrity logs, showing that WebClnt.dll is blocked" width="1024" height="421" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll-1024x421.png 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll-300x123.png 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll-768x316.png 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-codeintegrity-webclntdll.png 1101w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p><code>Code Integrity determined that a process \Device\HarddiskVolume4\Windows\System32\svchost.exe) attempted to load \Device\HarddiskVolume4\Windows\System32\WebClnt.dll that did not meet the Enterprise signing level requirements.</code></p>
<p>The error message is misleading &#8212; the DLL is signed (although I believe via a .cat file buried somewhere, as it&#8217;s part of the operating system). It&#8217;s actually this <a href="https://learn.microsoft.com/en-us/windows/security/application-security/application-control/windows-defender-application-control/design/applications-that-can-bypass-wdac">default rule set that explicitly blocks it</a>, despite it being part of the OS!</p>
<p>Fair enough, I suppose. The difficulty here is that the process of Word trying to start the service, spawning a <strong>svchost.exe</strong>, only for it to be unable to to load <strong>webclnt.dll</strong> is taking seconds and seconds, while the user is unable to do any work. This adds up if it happens on every save of a document.</p>
<p>I had the idea perhaps to enable this DLL via an App Control rule, but this didn&#8217;t go anywhere, despite a test to attempt to exclude <strong>webclnt.dll</strong> from the ruleset.</p>
<p>Instead, I set the service to <strong>Disabled</strong> and tried to reproduce the issue.</p>
<p><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-disable-webclient-service.png"><img loading="lazy" decoding="async" class="size-full wp-image-3037 aligncenter" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-disable-webclient-service.png" alt="services.msc, showing the WebClient service and its StartupType set to Disabled" width="587" height="512" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-disable-webclient-service.png 587w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/07/2024-07-28-disable-webclient-service-300x262.png 300w" sizes="auto, (max-width: 587px) 100vw, 587px" /></a></p>
<p>Suddenly, instant save!</p>
<p>The service being disabled doesn&#8217;t seem to have any negative impact on Office applications &#8212; and indeed I can be quite confident this won&#8217;t break any other functionality our users rely on, since the Web Client DLL has been consistently blocked in all other contexts too!</p>
<p>It&#8217;s another W for Event Tracing for Windows, and the publication of the symbols for Microsoft Office. Thank you, Microsoft!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://peter.upfold.org.uk/blog/2024/07/28/word-hangs-on-saving-app-control-for-business-and-webclnt-dll/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Adventures with Arch Packages: Exercise Caution with Exclude</title>
		<link>https://peter.upfold.org.uk/blog/2024/05/20/adventures-with-arch-packages-exercise-caution-with-exclude/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Mon, 20 May 2024 06:46:21 +0000</pubDate>
				<category><![CDATA[Completely Random]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Rants & Ramblings]]></category>
		<category><![CDATA[Arch]]></category>
		<category><![CDATA[pacman]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3020</guid>

					<description><![CDATA[Running a bleeding-edge rolling release Linux distribution like Arch Linux has its challenges and risks, but there is perhaps no greater feeling of absolute control over your own operating system! It also leads to the opportunity to have “adventures” like the one described here. While “adventure” might be tongue-in-cheek, the truth is that there is [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Running a bleeding-edge rolling release Linux distribution like Arch Linux has its challenges and risks, but there is perhaps no greater feeling of absolute control over your own operating system! It also leads to the opportunity to have “adventures” like the one described here. While “adventure” might be tongue-in-cheek, the truth is that there is great educational value in breaking something and thus being forced to fix it!</p>
<p>I recently made the mistake of seeing that a <b>pacman</b> upgrade to <b>icu</b> (International Components for Unicode) would break a few Electron packages, which I needed for the open source build of VS Code, among a few other options. (I generally like to avoid Electron apps, but VS Code is a rare exception!)</p>
<p>I was notified by <b>pacman</b> that there was a dependency issue here that could not be solved, so that the entire transaction was not possible.</p>
<p>My mistake was thinking “oh, I’ll just exclude <b>icu</b> from the upgrade, as it’s causing the dependency issue”. Usually these issues are fixed in a day or two, so I’d upgrade it later.</p>
<p>This was indeed a mistake.</p>
<p>Somehow, apps were now referencing the new <b>icu</b> that wasn’t there already. I rebooted, to discover that the GUI wouldn’t start, and I couldn’t even use <b>pacman</b> in single-user mode to roll back the previous transaction, as <b>pacman</b> itself depended upon<b> icu</b>.</p>
<p>I’d broken the very package manager that I needed to roll back. Oops!</p>
<h2 class="western">Install media to the rescue</h2>
<p>I booted into the Arch install media, and set about to address the issue.</p>
<p><b>chroot</b>ing into the target filesystem and using <b>pacman</b> wasn’t an option, as running the package manager from the target filesystem failed with the same dependency issue.</p>
<p>I had a few false starts with trying to run <b>pacman </b>from the install medium, pointing it at the mounted OS partition with <b>&#45;&#45;sysroot</b>.</p>
<p>While this didn’t pan out, I remembered that <b>pacstrap</b>, while designed for install-time, presumably could install packages in the target filesystem without needing <b>pacman</b> itself. I was concerned that this might reinstall some base packages, but it turns out you can specify which packages to install manually on its command line.</p>
<p>So, I gathered the list of packages that needed upgrading by using <b>pacman -Syu &#45;&#45;sysroot /mnt</b> against the target filesystem, and then supplied this package list to <b>pacstrap</b>:<br />
<code>pacstrap -G -i -M /mnt icu brltty electron27 electron28 electron29 electron30 freerdp freerdp2 harfbuzz-icu raptor</code><br />
And… we’re back!</p>
<h2 class="western">Lessons learned</h2>
<ul>
<li>The <a href="https://wiki.archlinux.org/title/System_maintenance#Partial_upgrades_are_unsupported">manual isn’t joking about “Partial upgrades are unsupported”</a>.</li>
<li>Considerable caution is required when using <b>pacman &#45;&#45;exclude</b></li>
<li>“The OS is ephemeral and I can rebuild it” is a bit too relaxed an attitude when you don’t really<i> want</i> to have to rebuild the OS at short notice. Back up the OS packages and libraries too.</li>
<li>Really understanding the package manager and install process gives you the tools to pull yourself out of the holes you dig for yourself!<i> </i></li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Reminding myself which machine I am authenticating to with a sudo &#8220;lecture&#8221;</title>
		<link>https://peter.upfold.org.uk/blog/2024/04/26/reminding-myself-which-machine-i-am-authenticating-to-with-a-sudo-lecture/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Fri, 26 Apr 2024 08:09:17 +0000</pubDate>
				<category><![CDATA[Completely Random]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[lecture]]></category>
		<category><![CDATA[sudo]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3010</guid>

					<description><![CDATA[Image credit: https://unsplash.com/photos/person-wearing-black-leather-shoes-VUWDlBXGogg by Fallon Michael I frequently SSH into various systems from my primary Linux machine. There is an analogous issue to &#8220;too many browser tabs&#8221; that exists here &#8212; having too many SSH sessions open in different terminal tabs! There is a risk in these cases of accidentally typing a higher-privileged sudo password [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="wp-block-image">
<figure class="alignright size-medium"><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-scaled.jpg"><img loading="lazy" decoding="async" width="300" height="200" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-300x200.jpg" alt="Person wearing black leather shoes standing on a painted circle with &quot;You Are Here&quot; written on the asphalt ground" class="wp-image-3012" srcset="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-300x200.jpg 300w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-1024x683.jpg 1024w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-768x512.jpg 768w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-1536x1024.jpg 1536w, https://peter.upfold.org.uk/blog/wp-content/uploads/2024/04/fallon-michael-VUWDlBXGogg-unsplash-2048x1365.jpg 2048w" sizes="auto, (max-width: 300px) 100vw, 300px" /></a><figcaption class="wp-element-caption">Image credit: <a href="https://unsplash.com/photos/person-wearing-black-leather-shoes-VUWDlBXGogg">https://unsplash.com/photos/person-wearing-black-leather-shoes-VUWDlBXGogg</a> by Fallon Michael</figcaption></figure>
</div>


<p>I frequently SSH into various systems from my primary Linux machine. There is an analogous issue to &#8220;too many browser tabs&#8221; that exists here &#8212; having too many SSH sessions open in different terminal tabs!</p>



<p>There is a risk in these cases of accidentally typing a higher-privileged<strong> sudo</strong> password into a lower security system by typing into the wrong terminal. There are various approaches that can help here; I have used <strong>screen</strong> banners with different colours before.</p>



<p>A good &#8220;last line of defence&#8221; approach to this risk that I have settled on is to make use of <strong>sudo</strong>&#8216;s &#8220;lectures&#8221;. You will have seen the default:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p></p>
<cite>We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things:<br>#1) Respect the privacy of others.<br>#2) Think before you type.<br>#3) With great power comes great responsibility.</cite></blockquote>



<p>We can customise this, and also set it to always show, rather than just the first time you ever use <strong>sudo</strong> on that machine. We&#8217;ll create a custom lecture file with our desired text &#8212; in my case, the hostname I&#8217;m logged into, so I&#8217;m sure where I am before I type the password!</p>



<p>Then, use <strong>visudo</strong> to set these options:</p>



<pre class="wp-block-code"><code>Defaults lecture=always
Defaults lecture_file=/etc/custom_sudo_lecture</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Installing the Zabbix Agent 2 on Windows with Minimal Privileges (LocalService)</title>
		<link>https://peter.upfold.org.uk/blog/2024/02/18/installing-the-zabbix-agent-2-on-windows-with-minimal-privileges-localservice/</link>
		
		<dc:creator><![CDATA[Peter]]></dc:creator>
		<pubDate>Sun, 18 Feb 2024 12:18:05 +0000</pubDate>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[Zabbix]]></category>
		<guid isPermaLink="false">https://peter.upfold.org.uk/blog/?p=3004</guid>

					<description><![CDATA[The Zabbix Agent 2 on Linux uses a non-root account by default (“zabbix”), and thus provides some protection against the worst outcomes of a potential vulnerability in the agent, or perhaps a takeover of a Zabbix server that monitors that agent. The Agent on Windows, however, runs with NT AUTHORITY\SYSTEM, which has extensive privileges on [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-full"><a href="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/02/zabbix.png"><img loading="lazy" decoding="async" width="168" height="44" src="https://peter.upfold.org.uk/blog/wp-content/uploads/2024/02/zabbix.png" alt="" class="wp-image-3007"/></a></figure>



<p>The Zabbix Agent 2 on Linux uses a non-root account by default (“zabbix”), and thus provides some protection against the worst outcomes of a potential vulnerability in the agent, or perhaps a takeover of a Zabbix server that monitors that agent.</p>



<p>The Agent on Windows, however, runs with <em>NT AUTHORITY\SYSTEM</em>, which has extensive privileges on the monitored system.</p>



<p>I have put together a little wrapper script around the Zabbix Agent 2 MSI installer which runs the installer, then reconfigures it to run as <em>NT AUTHORITY\LocalService</em>, which is <a href="https://learn.microsoft.com/en-us/windows/win32/services/localservice-account">a minimally privileged account</a>.</p>



<p>You can <a href="https://github.com/PeterUpfold/ZabbixAgent2WinLowPrivInstaller/tree/main">find the script on GitHub</a>. You’ll need to also grab the Zabbix Agent 2 MSI installer, rename it to <strong>zabbix-agent2.msi</strong> and provide that MSI in the same directory when you deploy.</p>



<p>It goes without saying that this is not officially supported, but I have not experienced any issues monitoring the standard items that are in the <strong>Windows by Zabbix Agent</strong> template. It is possible you will run into issues with unsupported items if the item in question does in fact require elevated permissions on the monitored host!</p>



<p>Hopefully this will be useful to others looking to monitor Windows systems with Zabbix, while maintaining as much of the principle of least privilege as possible!</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
