<?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>Real-World Solutions on Eriteach | Microsoft Cloud Tech</title><link>https://blog.eriteach.com/en/categories/real-world-solutions/</link><description>Recent content in Real-World Solutions on Eriteach | Microsoft Cloud Tech</description><generator>Hugo -- 0.155.1</generator><language>en</language><copyright>2024-2026 Robel Mehari. All rights reserved.</copyright><lastBuildDate>Thu, 23 Apr 2026 12:28:16 +0200</lastBuildDate><atom:link href="https://blog.eriteach.com/en/categories/real-world-solutions/index.xml" rel="self" type="application/rss+xml"/><item><title>Removing Firefox at Scale: My Intune Proactive Remediation Approach</title><link>https://blog.eriteach.com/en/posts/intune-proactive-remediation-remove-firefox/</link><pubDate>Mon, 02 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.eriteach.com/en/posts/intune-proactive-remediation-remove-firefox/</guid><description>My approach to detecting and removing Firefox using KQL for scoping and Intune for automated cleanup.</description><content:encoded><![CDATA[<p>When Mozilla Firefox is marked as unsanctioned in our environment, my next move is to clean up existing installs across all managed devices.</p>
<p>Before we standardized our browsers, users were free to install whatever they liked, which left Firefox scattered across hundreds of endpoints. I don&rsquo;t waste time with manual cleanup; I&rsquo;ve automated the entire process.</p>
<h2 id="scoping-at-scale-with-kql">Scoping at Scale with KQL</h2>
<p>I start by getting a clear picture of the &ldquo;infestation.&rdquo; I use <strong>KQL (Kusto Query Language)</strong> in Defender Advanced Hunting to see exactly which versions are out there and how many devices are affected:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// My Firefox Discovery Query
</span></span><span class="line"><span class="cl">DeviceTvmSoftwareInventory
</span></span><span class="line"><span class="cl">| where SoftwareName contains &#34;Firefox&#34;
</span></span><span class="line"><span class="cl">| summarize DeviceCount = dcount(DeviceName), Versions = make_set(SoftwareVersion) by SoftwareName
</span></span><span class="line"><span class="cl">| order by DeviceCount desc
</span></span></code></pre></div><p>This query gives me an immediate count and version list, which I use to target my Intune remediations effectively.</p>
<h2 id="the-strategy-automating-the-cleanup">The Strategy: Automating the Cleanup</h2>
<p>I use Intune Proactive Remediation with a two-script system:</p>
<ul>
<li><strong>Detection</strong>: Finds Firefox in the registry, Program Files, and user profiles.</li>
<li><strong>Remediation</strong>: Wipes everything—processes, files, shortcuts, services, and scheduled tasks.</li>
</ul>
<h3 id="detection-logic">Detection Logic</h3>
<p>My script scans three main areas where Firefox likes to hide:</p>
<ol>
<li><strong>Registry</strong> - 64-bit/32-bit uninstall keys and per-user installs.</li>
<li><strong>Program Files</strong> - Standard install locations.</li>
<li><strong>User Profiles</strong> - AppData folders.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$findings</span> <span class="p">=</span> <span class="vm">@</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="nv">$uninstallPaths</span> <span class="p">=</span> <span class="vm">@</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$path</span> <span class="k">in</span> <span class="nv">$uninstallPaths</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nb">Test-Path</span> <span class="nv">$path</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nv">$apps</span> <span class="p">=</span> <span class="nb">Get-ItemProperty</span> <span class="s2">&#34;</span><span class="nv">$path</span><span class="s2">\*&#34;</span> <span class="n">-ErrorAction</span> <span class="n">SilentlyContinue</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">            <span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="py">DisplayName</span> <span class="o">-like</span> <span class="s2">&#34;*Firefox*&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">foreach</span> <span class="p">(</span><span class="nv">$app</span> <span class="k">in</span> <span class="nv">$apps</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nv">$findings</span> <span class="p">+=</span> <span class="s2">&#34;Registry: </span><span class="p">$(</span><span class="nv">$app</span><span class="p">.</span><span class="n">DisplayName</span><span class="p">)</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nv">$findings</span><span class="p">.</span><span class="py">Count</span> <span class="o">-gt</span> <span class="mf">0</span><span class="p">)</span> <span class="p">{</span> <span class="n">exit</span> <span class="mf">1</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="n">exit</span> <span class="mf">0</span> <span class="p">}</span>
</span></span></code></pre></div><p>Full script: <a href="https://github.com/Thugney/eriteach-scripts/blob/main/intune/remediations/firefox-removal-detection.ps1">firefox-removal-detection.ps1</a></p>
<h2 id="the-kill-remediation">The Kill: Remediation</h2>
<p>My remediation script is designed to be thorough. It stops all active processes before attempting the uninstall to ensure nothing is locked.</p>
<ol>
<li><strong>Stop Processes</strong> - Firefox, plugin-container, and updaters.</li>
<li><strong>Uninstall</strong> - Uses the registry uninstall string (handles helper.exe and msiexec).</li>
<li><strong>Cleanup</strong> - Deletes directories in Program Files, ProgramData, and AppData.</li>
<li><strong>Final Polish</strong> - Removes shortcuts, the MozillaMaintenance service, and update tasks.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="c"># My Process Kill List</span>
</span></span><span class="line"><span class="cl"><span class="nv">$firefoxProcesses</span> <span class="p">=</span> <span class="vm">@</span><span class="p">(</span><span class="s2">&#34;firefox&#34;</span><span class="p">,</span> <span class="s2">&#34;firefox-esr&#34;</span><span class="p">,</span> <span class="s2">&#34;plugin-container&#34;</span><span class="p">,</span> <span class="s2">&#34;crashreporter&#34;</span><span class="p">,</span> <span class="s2">&#34;updater&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span> <span class="p">(</span><span class="nv">$proc</span> <span class="k">in</span> <span class="nv">$firefoxProcesses</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Get-Process</span> <span class="n">-Name</span> <span class="nv">$proc</span> <span class="n">-ErrorAction</span> <span class="n">SilentlyContinue</span> <span class="p">|</span> <span class="nb">Stop-Process</span> <span class="n">-Force</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Full script: <a href="https://github.com/Thugney/eriteach-scripts/blob/main/intune/remediations/firefox-removal-remediation.ps1">firefox-removal-remediation.ps1</a></p>
<h2 id="outcome-and-verification">Outcome and Verification</h2>
<p>I monitor the progress directly in the Intune portal under <strong>Devices</strong> &gt; <strong>Scripts and remediations</strong>.</p>
<p>Success looks like a &ldquo;fixed&rdquo; status on my devices. I also check my custom logs at <code>C:\ProgramData\Eriteach\Logs\</code> if I see any stubborn installs that require a manual look.</p>
<h2 id="lessons-learned">Lessons Learned</h2>
<ul>
<li><strong>User Profiles</strong> - The script cleans all profiles on the machine. I always warn users that bookmarks and saved passwords will be gone.</li>
<li><strong>Force Close</strong> - Since I force-close the process, I run this remediation during off-hours or maintenance windows to minimize disruption.</li>
</ul>
<h2 id="related-links">Related Links</h2>
<ul>
<li><a href="/en/posts/intune-proactive-remediation-firefox-update/">Auto-Update Firefox with Intune</a> - My workflow for when I need to keep Firefox but stay updated.</li>
<li><a href="https://learn.microsoft.com/en-us/mem/intune/fundamentals/remediations">Intune Remediations overview</a></li>
<li><a href="https://learn.microsoft.com/en-us/defender-endpoint/software-inventory">Microsoft Defender software inventory</a></li>
</ul>
]]></content:encoded></item><item><title>Removing Unauthorized Apps: My Intune Proactive Remediation Workflow</title><link>https://blog.eriteach.com/en/posts/remove-unauthorized-apps-intune-proactive-remediation/</link><pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate><guid>https://blog.eriteach.com/en/posts/remove-unauthorized-apps-intune-proactive-remediation/</guid><description>My workflow for detecting and removing unauthorized applications using KQL for discovery and Intune for automated cleanup.</description><content:encoded><![CDATA[<p>In my environment, the transition from local admin rights to standard users left behind a trail of &ldquo;shadow IT&rdquo;—Zoom, random personal tools, and legacy software that shouldn&rsquo;t be there.</p>
<p>I don&rsquo;t manually check every device. Instead, I&rsquo;ve built a workflow that finds these apps across the fleet and removes them automatically.</p>
<h2 id="the-discovery-fleet-wide-hunting-with-kql">The Discovery: Fleet-wide Hunting with KQL</h2>
<p>Before I can remove anything, I need to know exactly where the problems are. I use <strong>Microsoft Defender for Endpoint (MDE)</strong> to find these apps in seconds. Instead of browsing the portal, I run a KQL query in Advanced Hunting to identify high-risk software:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">// My Discovery Query (Sample)
</span></span><span class="line"><span class="cl">DeviceTvmSoftwareInventory
</span></span><span class="line"><span class="cl">| where SoftwareName has_any (&#34;utorrent&#34;, &#34;anydesk&#34;, &#34;ccleaner&#34;)
</span></span><span class="line"><span class="cl">| project DeviceName, SoftwareName, SoftwareVersion
</span></span></code></pre></div><p>This gives me my target list. For a more aggressive approach that handles EOL and EOS software, I use my <a href="https://blog.eriteach.com/en/posts/zombie-apps-removal-intune-proactive-remediation/">Zombie Software Hunting</a> workflow.</p>
<h2 id="the-automation-intune-proactive-remediation">The Automation: Intune Proactive Remediation</h2>
<p>Once I have my targets, I use Intune Proactive Remediation. My setup uses two scripts:</p>
<ol>
<li><strong>Detection</strong> - Checks if the app exists (exit 1 = found, triggering the kill)</li>
<li><strong>Remediation</strong> - Silent removal</li>
</ol>
<h3 id="my-configuration">My Configuration</h3>
<p>I target apps by registry, WMI, or the programs list. I typically configure my detection by setting these variables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$AppDisplayName</span> <span class="p">=</span> <span class="s2">&#34;Zoom&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$AppPublisher</span> <span class="p">=</span> <span class="s2">&#34;&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$AppProductCode</span> <span class="p">=</span> <span class="s2">&#34;{86B70A45-00A6-4CBD-97A8-464A1254D179}&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">$UsePartialMatch</span> <span class="p">=</span> <span class="vm">$true</span>
</span></span></code></pre></div><p>Full script: <a href="https://github.com/Thugney/eriteach-scripts/blob/main/proactive-remediation/Detect-UnwantedApp.ps1">Detect-UnwantedApp.ps1</a></p>
<h3 id="the-kill-remediation">The Kill (Remediation)</h3>
<p>Once detected, my remediation script pulls the uninstall string from the registry or uses the MSI product code to wipe the app silently.</p>
<p>Full script: <a href="https://github.com/Thugney/eriteach-scripts/blob/main/proactive-remediation/Remove-UnwantedApp.ps1">Remove-UnwantedApp.ps1</a></p>
<h2 id="deployment-details">Deployment Details</h2>
<p>This is how I set it up in my tenant:</p>
<ol>
<li><strong>Intune</strong> → <strong>Devices</strong> → <strong>Remediations</strong></li>
<li><strong>Create script package</strong>: &ldquo;Remove [AppName]&rdquo;</li>
<li><strong>Settings</strong>:
<ul>
<li>Run script in 64-bit PowerShell: <strong>Yes</strong></li>
<li>Run with logged-on credentials: <strong>No</strong> (runs as SYSTEM)</li>
</ul>
</li>
<li><strong>Schedule</strong>: Daily (I want these gone fast)</li>
</ol>
<h2 id="trade-offs-and-lessons-learned">Trade-offs and Lessons Learned</h2>
<ul>
<li><strong>Pilot first</strong> - I always run detection on a pilot group before enabling the remediation.</li>
<li><strong>Product codes change</strong> - I&rsquo;ve learned that different versions often have different codes; KQL helps me find all the variants first.</li>
<li><strong>Partial match risk</strong> - I use <code>$UsePartialMatch = $true</code> carefully to avoid catching plugins I might actually want to keep.</li>
</ul>
<h2 id="related-links">Related Links</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/mem/intune/fundamentals/remediations">Microsoft: Proactive Remediations</a></li>
<li><a href="https://learn.microsoft.com/en-us/mem/intune/apps/intune-management-extension">Intune script requirements</a></li>
</ul>
]]></content:encoded></item></channel></rss>