<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Alfred van Ster</title><link>https://avanster.tech/</link><description>Recent content on Alfred van Ster</description><generator>Hugo -- 0.161.1</generator><language>en-us</language><lastBuildDate>Sun, 03 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://avanster.tech/index.xml" rel="self" type="application/rss+xml"/><item><title>Architecting Resilient TS Plus Environments for Remote Workforces</title><link>https://avanster.tech/posts/tsplus-high-availability/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/posts/tsplus-high-availability/</guid><description>&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;Delivering remote applications seamlessly requires more than just opening an RDP port. In a modern Managed Service Provider (MSP) landscape, exposing internal servers directly to the internet is a critical security failure.&lt;/p&gt;
&lt;p&gt;This guide breaks down the architecture required to build a highly available, secure TS Plus environment that guarantees uptime while strictly controlling access via a centralized gateway and external MFA.&lt;/p&gt;
&lt;h3 id="the-architecture"&gt;The Architecture&lt;/h3&gt;
&lt;p&gt;A resilient TS Plus deployment separates the access layer from the execution layer. This ensures that a spike in user traffic or a targeted attack on the gateway does not crash the underlying application servers.&lt;/p&gt;</description></item><item><title>Cloud Identity: Entra ID Stale Guest Reaper</title><link>https://avanster.tech/library/script-entra-guest-reaper/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-entra-guest-reaper/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;External guest accounts are frequently provisioned for vendors but rarely decommissioned, creating a lingering attack surface. This script uses the Microsoft Graph API to identify and remove guest accounts that have been inactive for over 90 days.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Uses App Registration credentials to silently authenticate to MS Graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Filtering:&lt;/strong&gt; Queries all users where &lt;code&gt;userType&lt;/code&gt; equals &amp;ldquo;Guest&amp;rdquo;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluation:&lt;/strong&gt; Checks the &lt;code&gt;signInActivity&lt;/code&gt; property against a 90-day threshold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Purge:&lt;/strong&gt; Identifies stale guest accounts to close the security loop.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ThresholdDate = (Get-Date).AddDays(&lt;span style="color:#ae81ff"&gt;-90&lt;/span&gt;).ToString(&lt;span style="color:#e6db74"&gt;&amp;#34;yyyy-MM-ddTHH:mm:ssZ&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Url = &lt;span style="color:#e6db74"&gt;&amp;#34;[https://graph.microsoft.com/beta/users](https://graph.microsoft.com/beta/users)?&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;`$&lt;/span&gt;&lt;span style="color:#e6db74"&gt;filter=userType eq &amp;#39;Guest&amp;#39;&amp;amp;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;`$&lt;/span&gt;&lt;span style="color:#e6db74"&gt;select=displayName,userPrincipalName,signInActivity&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Guests = Invoke-RestMethod -Uri $Url -Method Get -Headers $Headers
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; ($Guest &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; $Guests.value) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ([&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;]::IsNullOrEmpty($Guest.signInActivity.lastSignInDateTime) &lt;span style="color:#f92672"&gt;-or&lt;/span&gt; ($Guest.signInActivity.lastSignInDateTime &lt;span style="color:#f92672"&gt;-lt&lt;/span&gt; $ThresholdDate)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Host &lt;span style="color:#e6db74"&gt;&amp;#34;Stale Guest Found: &lt;/span&gt;$($Guest.displayName)&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Endpoint Security: BitLocker Key Escrow to IT Glue</title><link>https://avanster.tech/library/script-bitlocker-itglue/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-bitlocker-itglue/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Relying on manual documentation for BitLocker recovery keys often results in locked data when an endpoint fails. This script automates the escrow process, pulling the active numeric password from the local disk and pushing it directly into an IT Glue configuration record via their REST API.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Extraction:&lt;/strong&gt; Queries the WMI namespace for the active, 48-digit BitLocker Numeric Password on the OS drive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Connects to the IT Glue API using a secure organizational API key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload Delivery:&lt;/strong&gt; Matches the local Hostname to the IT Glue Configuration ID and PATCHes the &amp;ldquo;BitLocker Key&amp;rdquo; custom field with the extracted key.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# IT Glue API Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ITGKey = &lt;span style="color:#e6db74"&gt;&amp;#34;YOUR_ITGLUE_API_KEY&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ITGBaseUrl = &lt;span style="color:#e6db74"&gt;&amp;#34;[https://api.itglue.com](https://api.itglue.com)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Header = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;x-api-key&amp;#34;&lt;/span&gt; = $ITGKey
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;application/vnd.api+json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$BitLocker = Get-BitLockerVolume -MountPoint $env:SystemDrive
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$RecoveryKey = ($BitLocker.KeyProtector | Where-Object { $_.KeyProtectorType &lt;span style="color:#f92672"&gt;-eq&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;RecoveryPassword&amp;#39;&lt;/span&gt; }).RecoveryPassword
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$SearchUri = &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$ITGBaseUrl&lt;span style="color:#e6db74"&gt;/configurations?filter[name]=&lt;/span&gt;$env:COMPUTERNAME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ConfigRecord = Invoke-RestMethod -Uri $SearchUri -Method Get -Headers $Header
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($ConfigRecord.data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $Payload = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; data = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; type = &lt;span style="color:#e6db74"&gt;&amp;#34;configurations&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attributes = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;custom-fields&amp;#34;&lt;/span&gt; = @{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;bitlocker-recovery-key&amp;#34;&lt;/span&gt; = $RecoveryKey
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } | ConvertTo-Json -Depth &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Invoke-RestMethod -Uri &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$ITGBaseUrl&lt;span style="color:#e6db74"&gt;/configurations/&lt;/span&gt;$($ConfigRecord.data[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].id)&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -Method Patch -Headers $Header -Body $Payload
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Enforcing Zero-Trust on macOS via Jamf and SentinelOne</title><link>https://avanster.tech/posts/zero-trust-macos-jamf/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/posts/zero-trust-macos-jamf/</guid><description>&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;Managing Apple devices in a predominantly Windows-centric MSP environment is often treated as an afterthought. However, relying on basic MDM profiles is no longer sufficient. To achieve true Zero-Trust, macOS fleets require the same stringent Endpoint Detection and Response (EDR) and identity controls as their Windows counterparts.&lt;/p&gt;
&lt;p&gt;This guide details the architectural implementation of enforcing Zero-Trust on macOS using Jamf Pro for orchestration, SentinelOne for threat hunting, and Keeper for MFA-backed identity management.&lt;/p&gt;</description></item><item><title>Infrastructure: Hyper-V Orphaned Snapshot Monitor</title><link>https://avanster.tech/library/script-hyperv-snapshot-monitor/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-hyperv-snapshot-monitor/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A common L3 infrastructure issue is technicians taking a VM checkpoint before an update and forgetting to delete it. This script scans for stale snapshots and fires an alert to a Microsoft Teams or Slack webhook.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Discovery:&lt;/strong&gt; Scans the local Hyper-V host for all active checkpoints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluation:&lt;/strong&gt; Compares the creation date of the snapshot against a 7-day threshold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alerting:&lt;/strong&gt; If stale snapshots exist, it constructs a JSON payload and POSTs it to a designated webhook URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ThresholdDate = (Get-Date).AddDays(&lt;span style="color:#ae81ff"&gt;-7&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$StaleSnapshots = Get-VM | Get-VMSnapshot | Where-Object { $_.CreationTime &lt;span style="color:#f92672"&gt;-lt&lt;/span&gt; $ThresholdDate }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($StaleSnapshots) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $Payload = @{ text = &lt;span style="color:#e6db74"&gt;&amp;#34;⚠️ Orphaned Snapshots Detected on &lt;/span&gt;$env:COMPUTERNAME&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; } | ConvertTo-Json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Invoke-RestMethod -Uri &lt;span style="color:#e6db74"&gt;&amp;#34;[https://your-webhook.url](https://your-webhook.url)&amp;#34;&lt;/span&gt; -Method Post -Body $Payload -ContentType &lt;span style="color:#e6db74"&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Infrastructure: Windows Server DNS Stale Record Scavenger</title><link>https://avanster.tech/library/script-dns-stale-scavenger/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-dns-stale-scavenger/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In dynamic DHCP environments, DNS zones become polluted with stale A-records. This script provides a surgical, auditable way to identify and purge stale DNS records older than a defined threshold.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Targeting:&lt;/strong&gt; Selects a specific internal DNS zone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluation:&lt;/strong&gt; Pulls all A records and compares the Timestamp against a 14-day threshold.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execution:&lt;/strong&gt; Exports a CSV log of the stale records before actively removing them from the server.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ZoneName = &lt;span style="color:#e6db74"&gt;&amp;#34;internal.avanster.tech&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ThresholdDate = (Get-Date).AddDays(&lt;span style="color:#ae81ff"&gt;-14&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Records = Get-DnsServerResourceRecord -ZoneName $ZoneName -RRType &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; ($Record &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; $Records) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($Record.Timestamp &lt;span style="color:#f92672"&gt;-ne&lt;/span&gt; $null &lt;span style="color:#f92672"&gt;-and&lt;/span&gt; $Record.Timestamp &lt;span style="color:#f92672"&gt;-lt&lt;/span&gt; $ThresholdDate) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Remove-DnsServerResourceRecord -ZoneName $ZoneName -InputObject $Record -Force
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Host &lt;span style="color:#e6db74"&gt;&amp;#34;[-] Removed: &lt;/span&gt;$($Record.HostName)&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>M365 Security: Unauthorized Mailbox Forwarding Auditor</title><link>https://avanster.tech/library/script-exchange-forwarding-auditor/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-exchange-forwarding-auditor/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A classic BEC tactic involves configuring an inbox rule to forward emails to an external address. This script audits an entire Exchange Online tenant for any mailboxes with active forwarding rules to external domains.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Connection:&lt;/strong&gt; Authenticates to Exchange Online via module parameters.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Auditing:&lt;/strong&gt; Iterates through all user mailboxes checking forwarding properties.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Evaluation:&lt;/strong&gt; Compares the forwarding destination against the tenant&amp;rsquo;s accepted domains.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Alerting:&lt;/strong&gt; Outputs a high-priority warning if external exfiltration is detected.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Connect-ExchangeOnline -ShowBanner:$false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$AcceptedDomains = (Get-AcceptedDomain).Name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;foreach&lt;/span&gt; ($Mailbox &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; (Get-Mailbox -ResultSize Unlimited)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($Mailbox.ForwardingSmtpAddress) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $ForwardDestination = $Mailbox.ForwardingSmtpAddress.Replace(&lt;span style="color:#e6db74"&gt;&amp;#34;smtp:&amp;#34;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($ForwardDestination &lt;span style="color:#f92672"&gt;-notmatch&lt;/span&gt; ($AcceptedDomains -join &lt;span style="color:#e6db74"&gt;&amp;#34;|&amp;#34;&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Host &lt;span style="color:#e6db74"&gt;&amp;#34;⚠️ EXTERNAL FORWARD: &lt;/span&gt;$($Mailbox.UserPrincipalName)&lt;span style="color:#e6db74"&gt; -&amp;gt; &lt;/span&gt;$ForwardDestination&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -ForegroundColor Red
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Networking: Cisco Meraki Automated Configuration Backup</title><link>https://avanster.tech/library/script-meraki-config-backup/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/library/script-meraki-config-backup/</guid><description>&lt;ol&gt;
&lt;li&gt;The Workflow&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Meraki dashboards are convenient, but if an admin accidentally modifies a critical firewall rule, rolling back is a nightmare. This Python script uses the Meraki Dashboard API to serialize your network configurations into a secure JSON format.&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Initializes the Meraki SDK using a read-only API key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iteration:&lt;/strong&gt; Loops through the Organization to find all active Networks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extraction:&lt;/strong&gt; Pulls VLAN subnets, SSID configurations, and MX L3 Firewall rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Serialization:&lt;/strong&gt; Dumps the state into a structured JSON file.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; meraki
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&lt;/span&gt; json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; datetime &lt;span style="color:#f92672"&gt;import&lt;/span&gt; datetime
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;API_KEY &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;YOUR_MERAKI_API_KEY&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ORG_ID &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;YOUR_ORG_ID&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dashboard &lt;span style="color:#f92672"&gt;=&lt;/span&gt; meraki&lt;span style="color:#f92672"&gt;.&lt;/span&gt;DashboardAPI(API_KEY, suppress_logging&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;backup_network_config&lt;/span&gt;():
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; networks &lt;span style="color:#f92672"&gt;=&lt;/span&gt; dashboard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;organizations&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getOrganizationNetworks(ORG_ID)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backup_data &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; net &lt;span style="color:#f92672"&gt;in&lt;/span&gt; networks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; net_id &lt;span style="color:#f92672"&gt;=&lt;/span&gt; net[&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; net_name &lt;span style="color:#f92672"&gt;=&lt;/span&gt; net[&lt;span style="color:#e6db74"&gt;&amp;#39;name&amp;#39;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backup_data[net_name] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;vlans&amp;#39;&lt;/span&gt;: dashboard&lt;span style="color:#f92672"&gt;.&lt;/span&gt;appliance&lt;span style="color:#f92672"&gt;.&lt;/span&gt;getNetworkApplianceVlans(net_id)}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; filename &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;f&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;meraki_backup_&lt;/span&gt;&lt;span style="color:#e6db74"&gt;{&lt;/span&gt;datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;now()&lt;span style="color:#f92672"&gt;.&lt;/span&gt;strftime(&lt;span style="color:#e6db74"&gt;&amp;#39;%Y%m&lt;/span&gt;&lt;span style="color:#e6db74"&gt;%d&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt;)&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;with&lt;/span&gt; open(filename, &lt;span style="color:#e6db74"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; f:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; json&lt;span style="color:#f92672"&gt;.&lt;/span&gt;dump(backup_data, f, indent&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Zero-Touch M365 Offboarding with n8n, Docker, and PowerShell</title><link>https://avanster.tech/posts/zero-touch-m365-offboarding/</link><pubDate>Sun, 03 May 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/posts/zero-touch-m365-offboarding/</guid><description>&lt;h3 id="overview"&gt;Overview&lt;/h3&gt;
&lt;p&gt;In a Managed Service Provider (MSP) environment, manual offboarding is a massive liability. Missing a step when revoking access can lead to data breaches, compliance violations, and wasted licensing costs.&lt;/p&gt;
&lt;p&gt;This guide outlines an architectural approach to &amp;ldquo;Zero-Touch&amp;rdquo; offboarding, leveraging a self-hosted n8n instance running in Docker to trigger a robust PowerShell workflow that interacts directly with the Microsoft Graph API.&lt;/p&gt;
&lt;h3 id="the-architecture"&gt;The Architecture&lt;/h3&gt;
&lt;p&gt;Relying on technicians to manually run scripts on their local machines creates bottlenecks. By containerizing the automation engine, we achieve predictable, auditable execution.&lt;/p&gt;</description></item><item><title>The Global Troubleshooting Framework: Identity vs. Platform</title><link>https://avanster.tech/posts/global-troubleshooting-framework/</link><pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate><guid>https://avanster.tech/posts/global-troubleshooting-framework/</guid><description>A strategic diagnostic approach to cut through the noise of L2 escalations by distinguishing between User Identity and System Platform domains.</description></item><item><title>The MSP Triage Map: Which Logs to Check (And When)</title><link>https://avanster.tech/posts/msp-log-triage-map/</link><pubDate>Thu, 15 Jan 2026 12:00:00 +0200</pubDate><guid>https://avanster.tech/posts/msp-log-triage-map/</guid><description>A master reference guide for identifying the correct logs to resolve common MSP service desk escalations.</description></item><item><title>NAT Demystified: The Engine of Modern MSP Networking</title><link>https://avanster.tech/posts/understanding-nat/</link><pubDate>Thu, 15 Jan 2026 11:00:00 +0200</pubDate><guid>https://avanster.tech/posts/understanding-nat/</guid><description>A comprehensive guide to NAT types, use cases, and common MSP troubleshooting scenarios like Double NAT and Hairpinning.</description></item><item><title>The VPN 'Ping Paradox': Solving IP Subnet Collisions</title><link>https://avanster.tech/posts/vpn-subnet-collisions/</link><pubDate>Thu, 15 Jan 2026 10:00:00 +0200</pubDate><guid>https://avanster.tech/posts/vpn-subnet-collisions/</guid><description>Why can you ping a server but not access its data? A deep dive into IP subnet collisions in remote work environments.</description></item><item><title>SecOps: SentinelOne Global Threat Scraper</title><link>https://avanster.tech/library/s1-threat-scraper/</link><pubDate>Sat, 10 Jan 2026 11:00:00 +0000</pubDate><guid>https://avanster.tech/library/s1-threat-scraper/</guid><description>&lt;p&gt;When a new 0-day vulnerability or a suspicious file hash is identified, waiting for a scheduled scan is not an option. This PowerShell tool utilizes the SentinelOne Management API to &amp;ldquo;scrape&amp;rdquo; the entire fleet for specific indicators of compromise (IOCs).&lt;/p&gt;
&lt;h3 id="1-the-workflow"&gt;1. The Workflow&lt;/h3&gt;
&lt;p&gt;The script performs the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Authentication:&lt;/strong&gt; Connects via API Token to the S1 Management Console.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Query:&lt;/strong&gt; Requests a list of all endpoints where a specific file hash or process has been detected in the last 24 hours.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reporting:&lt;/strong&gt; Generates a CSV list of infected Hostnames, IP addresses, and the &amp;ldquo;Detection State&amp;rdquo; (Mitigated vs. Active).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-implementation"&gt;2. The Implementation&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# S1 API Configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ApiToken = &lt;span style="color:#e6db74"&gt;&amp;#34;YOUR_API_TOKEN&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$BaseUrl = &lt;span style="color:#e6db74"&gt;&amp;#34;[https://your-console.sentinelone.net/web/api/v2.1](https://your-console.sentinelone.net/web/api/v2.1)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Header = @{ &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;Token &lt;/span&gt;$ApiToken&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Define the Threat Hash to hunt for&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$TargetHash = &lt;span style="color:#e6db74"&gt;&amp;#34;e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Search for the hash across the site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$Response = Invoke-RestMethod -Uri &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$BaseUrl&lt;span style="color:#e6db74"&gt;/threats?contentHashes=&lt;/span&gt;$TargetHash&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -Method Get -Headers $Header
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; ($Response.data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Host &lt;span style="color:#e6db74"&gt;&amp;#34;ALERT: Threat detected on &lt;/span&gt;$($Response.data.count)&lt;span style="color:#e6db74"&gt; endpoints!&amp;#34;&lt;/span&gt; -ForegroundColor Red
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; $Response.data | Select-Object computerName, lastActiveDate, threatName | Export-Csv -Path &lt;span style="color:#e6db74"&gt;&amp;#34;./ThreatReport.csv&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;} &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Write-Host &lt;span style="color:#e6db74"&gt;&amp;#34;Clear: No matches found for the target hash.&amp;#34;&lt;/span&gt; -ForegroundColor Green
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>The Human Factor: Why Great Backups Fail During Disasters</title><link>https://avanster.tech/posts/human-factor-in-dr/</link><pubDate>Sat, 10 Jan 2026 10:00:00 +0000</pubDate><guid>https://avanster.tech/posts/human-factor-in-dr/</guid><description>&lt;p&gt;You can have the most expensive, lightning-fast immutable backup array in the world, but if your lead engineer is panicking and your documentation is trapped inside the server that just went down, your architecture has failed.&lt;/p&gt;
&lt;p&gt;In my experience as an L2 Escalation specialist, I’ve seen that the &amp;ldquo;Human Element&amp;rdquo; is the most unpredictable variable in any Disaster Recovery (DR) plan.&lt;/p&gt;
&lt;h3 id="1-the-paradox-of-digital-documentation"&gt;1. The Paradox of Digital Documentation&lt;/h3&gt;
&lt;p&gt;Many teams store their &amp;ldquo;How-to-Recover&amp;rdquo; guides on the very infrastructure they are trying to recover. If the SAN is dead, your recovery PDF is dead too.
&lt;strong&gt;The Fix:&lt;/strong&gt; I advocate for &amp;ldquo;Out-of-Band&amp;rdquo; documentation—secure, offline, or cloud-native copies (like an encrypted Git repository or a physical &amp;ldquo;Break-Glass&amp;rdquo; binder) that are accessible even when the primary network is dark.&lt;/p&gt;</description></item><item><title>Resilient Data: Architecting a 3-2-1-1 Backup Strategy for MSPs</title><link>https://avanster.tech/posts/msp-backup-strategy/</link><pubDate>Mon, 05 Jan 2026 10:00:00 +0000</pubDate><guid>https://avanster.tech/posts/msp-backup-strategy/</guid><description>&lt;p&gt;In modern infrastructure, &amp;ldquo;Backup&amp;rdquo; is not a task—it is a foundational pillar of security. For an MSP managing hundreds of endpoints, a simple file-copy isn&amp;rsquo;t enough. Here is how I architect systems to survive ransomware and site-wide disasters.&lt;/p&gt;
&lt;h3 id="1-the-3-2-1-1-framework"&gt;1. The 3-2-1-1 Framework&lt;/h3&gt;
&lt;p&gt;I advocate for an evolved version of the classic 3-2-1 rule, specifically designed for remote-first workforces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3 Copies of Data:&lt;/strong&gt; Primary, local secondary, and offsite tertiary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;2 Different Media:&lt;/strong&gt; Utilizing localized NAS storage for fast LAN recovery and cloud-native repositories.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 Offsite Location:&lt;/strong&gt; Ensuring data is physically separated from the primary site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 Immutable Copy:&lt;/strong&gt; Utilizing &lt;strong&gt;S3 Object Lock&lt;/strong&gt; or Air-gapping to ensure backups cannot be deleted by compromised credentials.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-the-infrastructure-stack"&gt;2. The Infrastructure Stack&lt;/h3&gt;
&lt;p&gt;My preferred approach utilizes a unified management plane to reduce &amp;ldquo;Shadow Data&amp;rdquo;:&lt;/p&gt;</description></item><item><title>Securing the Perimeter: Why I chose a VPS over Shared Hosting</title><link>https://avanster.tech/posts/securing-the-perimeter/</link><pubDate>Wed, 01 Jan 2025 17:00:00 +0000</pubDate><guid>https://avanster.tech/posts/securing-the-perimeter/</guid><description>&lt;p&gt;Most portfolios live on shared hosting—cheap, easy, but restricted. For my infrastructure, I chose a &lt;strong&gt;Virtual Private Server (VPS)&lt;/strong&gt;. Here’s why a Systems Engineer treats their &amp;ldquo;home on the web&amp;rdquo; like a production environment.&lt;/p&gt;
&lt;h3 id="1-the-isolation-advantage"&gt;1. The Isolation Advantage&lt;/h3&gt;
&lt;p&gt;On shared hosting, you are at the mercy of your &amp;ldquo;neighbors.&amp;rdquo; If another site on the same IP gets hit with a DDoS or runs a malicious script, your site slows down or goes dark. On my VPS, my &lt;strong&gt;vCPU and RAM&lt;/strong&gt; are mine alone.&lt;/p&gt;</description></item><item><title>Contact</title><link>https://avanster.tech/contact/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://avanster.tech/contact/</guid><description>&lt;div id="contact-intro"&gt;
Feel free to reach out using the form below!
&lt;/div&gt;
&lt;div id="contact-wrapper"&gt;
&lt;form id="ajax-form" action="https://formspree.io/f/mreezkkz" method="POST" style="display: flex; flex-direction: column; gap: 15px; max-width: 500px;"&gt;
&lt;input type="text" name="name" placeholder="Your Name" required style="padding: 12px; border-radius: 5px; border: 1px solid #ccc; background: white; color: black; font-family: inherit; font-size: 1rem;"&gt;
&lt;input type="email" name="email" placeholder="Your Email" required style="padding: 12px; border-radius: 5px; border: 1px solid #ccc; background: white; color: black; font-family: inherit; font-size: 1rem;"&gt;
&lt;textarea name="message" placeholder="How can I help?" rows="5" required style="padding: 12px; border-radius: 5px; border: 1px solid #ccc; background: white; color: black; font-family: inherit; font-size: 1rem;"&gt;&lt;/textarea&gt;
&lt;button type="submit" id="submit-btn" style="padding: 12px; background: #222; color: #fff; border: none; border-radius: 5px; cursor: pointer; font-weight: bold; font-family: inherit; font-size: 1rem;"&gt;
Send Message
&lt;/button&gt;
&lt;p id="form-status" style="margin-top: 10px; font-family: inherit;"&gt;&lt;/p&gt;</description></item><item><title>Resume</title><link>https://avanster.tech/resume/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://avanster.tech/resume/</guid><description>&lt;div style="margin-bottom: 30px; text-align: center;"&gt;
&lt;a href="https://avanster.tech/Resume_A_v_Ster.pdf" download="Resume_A_v_Ster.pdf" style="background-color: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: bold; display: inline-block; transition: 0.3s;"&gt;
📥 Download Full PDF Resume
&lt;/a&gt;
&lt;/div&gt;
&lt;h1 id="alfred-van-ster"&gt;ALFRED VAN STER&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;Systems Engineer | SME | Service Engagement Manager&lt;/strong&gt;
Cape Town, South Africa (Remote | 100% Uptime UPS)
&lt;a href="mailto:alfred@avanster.tech"&gt;alfred@avanster.tech&lt;/a&gt; | &lt;a href="https://avanster.tech"&gt;avanster.tech&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="professional-summary"&gt;PROFESSIONAL SUMMARY&lt;/h3&gt;
&lt;p&gt;Performance-driven Systems Engineer and Subject Matter Expert (SME) with over 5 years of experience in technical troubleshooting, infrastructure automation, and high-level escalations. Specialized in managing MSP infrastructure and serving as a Service Engagement Manager (SEM) for enterprise clients. Expert in maintaining high-availability environments and securing identity-focused cloud architectures.&lt;/p&gt;</description></item></channel></rss>