<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>JoshFridey.com - Blog Feed</title>
    <link>https://www.joshfridey.com</link>
    <description>Hands on IT notes, scripts, and walkthroughs</description>
    <language>en-us</language>
    <atom:link href="https://www.joshfridey.com/feed" rel="self" type="application/rss+xml" />
    
    <item>
      <title>I Found a  Remote Code Execution Attack on My Next.js Site</title>
      <link>https://www.joshfridey.com/blog/security/i-found-a-remote-code-execution-attack-on-my-next-js-site</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/security/i-found-a-remote-code-execution-attack-on-my-next-js-site</guid>
      <pubDate>Mon, 23 Feb 2026 08:39:57 GMT</pubDate>
      <description>I was working in VS Code when Git showed an untracked file in my project:

./upload-d1337.php
 
That stopped me immediately.

This is a Next.js app running on Node.js. There should never be a PHP file in the root of the project.

I opened it and found a full PHP webshell. A webshell is a backdoor. If someone can access it, they can run commands on your server, browse files, and upload more malicious code.

Now this was incident response.

 

How It Happened
The first shell was sitting at the project root.

After that, I searched the entire directory for *.php files and found another copy here:

./.next/static/chunks/upload-d1337.php
 
That folder is generated during the Next.js build process. It should only contain static JavaScript files. Seeing a PHP file there strongly suggested the application itself had been exploited.

I ran npm audit and found a critical advisory:

next 10.0.0 - 15.5.9
Next.js vulnerable to Remote Code Execution
https://github.com/advisories/GHSA-9qr9-h5gf-34mp
 
I was running version 15.5.2.

Remote Code Execution, or RCE, means an attacker can send a specially crafted request to your app and cause your server to execute code they control. In this case, that code wrote a PHP backdoor onto my server.

 

The Logs Confirmed It
From a single IP address, I saw this pattern:


00:23:32 - POST /  (303)
00:23:34 - POST /  (303)
00:23:36 - POST /  (500)
00:23:38 - POST /  (500)
00:23:39 - POST /  (303)
00:23:41 - GET /upload-d1337.php?key=d1337  (200)
 
Several POST requests hit the site first. A couple caused 500 errors, which usually happens while an exploit is being tuned.

Then immediately after, there was a successful request to the PHP file that had just been written.

This was almost certainly automated scanning. Bots continuously sweep the internet looking for known vulnerabilities in popular frameworks.

 

What I Did Immediately
I treated it as a real compromise.

Updated Next.js to the latest patched version

npm install next@latest
 
Rebuilt and redeployed the app
Removed all discovered PHP files
Rotated API keys and secrets
Changed hosting credentials
Enabled two-factor authentication
If there is any chance code execution occurred, assume secrets may have been exposed.

 

Lessons Worth Keeping
Keep dependencies updated. Public vulnerabilities are actively exploited.

Run npm audit regularly. At minimum:

Before production deployments
After installing new packages
As part of your CI pipeline using --audit-level=high
The goal is not to run it obsessively. The goal is to reduce how long you stay on a vulnerable version.

A clean Git working tree makes suspicious files obvious.

Deleting the malicious file is not enough. You have to patch the vulnerability that allowed it.

Limit what lives in your .env file. The fewer sensitive values your app holds, the smaller the blast radius.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/I_Found_a_RCE_Attack_09350739a7.webp" alt="I Found a RCE Attack" />
<p data-start="180" data-end="253">I was working in VS Code when Git showed an untracked file in my project:</p><div class="w-full my-4"><div><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="pointer-events-none absolute inset-x-px top-0 bottom-96"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-bg-elevated-secondary">&nbsp;</div></div></div><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" id="code-block-viewer" dir="ltr"><div class="cm-scroller"><pre><code class="language-plaintext">./upload-d1337.php</code></pre></div></div></div></div></div></div></div><div><div>&nbsp;</div></div></div></div></div><p data-start="283" data-end="311">That stopped me immediately.</p><p data-start="313" data-end="415">This is a Next.js app running on Node.js. There should never be a PHP file in the root of the project.</p><p data-start="417" data-end="595">I opened it and found a full PHP webshell. A webshell is a backdoor. If someone can access it, they can run commands on your server, browse files, and upload more malicious code.</p><p data-start="597" data-end="628">Now this was incident response.</p><p data-start="597" data-end="628">&nbsp;</p><h2>How It Happened</h2><p data-start="655" data-end="703">The first shell was sitting at the project root.</p><p data-start="705" data-end="795">After that, I searched the entire directory for <code data-start="753" data-end="760">*.php</code> files and found another copy here:</p><div class="w-full my-4"><div><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="pointer-events-none absolute inset-x-px top-0 bottom-96"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-bg-elevated-secondary">&nbsp;</div></div></div><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" id="code-block-viewer" dir="ltr"><div class="cm-scroller"><pre><code class="language-plaintext">./.next/static/chunks/upload-d1337.php</code></pre></div></div></div></div></div></div></div><div><div>&nbsp;</div></div></div></div></div><p data-start="845" data-end="1037">That folder is generated during the Next.js build process. It should only contain static JavaScript files. Seeing a PHP file there strongly suggested the application itself had been exploited.</p><p data-start="1039" data-end="1087">I ran <code data-start="1045" data-end="1056">npm audit</code> and found a critical advisory:</p><div class="w-full my-4"><div><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="pointer-events-none absolute inset-x-px top-0 bottom-96"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-bg-elevated-secondary">&nbsp;</div></div></div><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" id="code-block-viewer" dir="ltr"><div class="cm-scroller"><pre><code class="language-plaintext">next 10.0.0 - 15.5.9
Next.js vulnerable to Remote Code Execution
https://github.com/advisories/GHSA-9qr9-h5gf-34mp</code></pre></div></div></div></div></div></div></div><div><div>&nbsp;</div></div></div></div></div><p data-start="1213" data-end="1242">I was running version 15.5.2.</p><p data-start="1244" data-end="1454">Remote Code Execution, or RCE, means an attacker can send a specially crafted request to your app and cause your server to execute code they control. In this case, that code wrote a PHP backdoor onto my server.</p><p data-start="1244" data-end="1454">&nbsp;</p><p data-start="1244" data-end="1454">&nbsp;</p><h3>The Logs Confirmed It</h3><p data-start="1487" data-end="1532">From a single IP address, I saw this pattern:</p><div class="w-full my-4"><div><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light">&nbsp;</div></div></div><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" id="code-block-viewer" dir="ltr"><div class="cm-scroller"><pre><code class="language-plaintext">00:23:32 - POST /  (303)
00:23:34 - POST /  (303)
00:23:36 - POST /  (500)
00:23:38 - POST /  (500)
00:23:39 - POST /  (303)
00:23:41 - GET /upload-d1337.php?key=d1337  (200)</code></pre></div></div></div></div></div></div></div><div><div>&nbsp;</div></div></div></div></div><p data-start="1718" data-end="1842">Several POST requests hit the site first. A couple caused 500 errors, which usually happens while an exploit is being tuned.</p><p data-start="1844" data-end="1942">Then immediately after, there was a successful request to the PHP file that had just been written.</p><p data-start="1944" data-end="2083">This was almost certainly automated scanning. Bots continuously sweep the internet looking for known vulnerabilities in popular frameworks.</p><p data-start="1944" data-end="2083">&nbsp;</p><p data-start="1944" data-end="2083">&nbsp;</p><h3>What I Did Immediately</h3><p data-start="2117" data-end="2151">I treated it as a real compromise.</p><ol data-start="2153" data-end="2414"><li data-start="2153" data-end="2244"><p data-start="2156" data-end="2203">Updated Next.js to the latest patched version</p><div class="w-full my-4"><div><div class="relative"><div class="h-full min-h-0 min-w-0"><div class="h-full min-h-0 min-w-0"><div class="border corner-superellipse/1.1 border-token-border-light bg-token-bg-elevated-secondary rounded-3xl"><div class="pointer-events-none absolute inset-x-4 top-12 bottom-4"><div class="pointer-events-none sticky z-40 shrink-0 z-1!"><div class="sticky bg-token-border-light">&nbsp;</div></div></div><div class="corner-superellipse/1.1 rounded-3xl bg-token-bg-elevated-secondary"><div class="relative z-0 flex max-w-full"><div class="q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼk ͼy" id="code-block-viewer" dir="ltr"><div class="cm-scroller"><pre><code class="language-plaintext">npm install next@latest</code></pre></div></div></div></div></div></div></div><div><div>&nbsp;</div></div></div></div></div></li><li data-start="2245" data-end="2278"><p data-start="2248" data-end="2278">Rebuilt and redeployed the app</p></li><li data-start="2279" data-end="2314"><p data-start="2282" data-end="2314">Removed all discovered PHP files</p></li><li data-start="2315" data-end="2346"><p data-start="2318" data-end="2346">Rotated API keys and secrets</p></li><li data-start="2347" data-end="2377"><p data-start="2350" data-end="2377">Changed hosting credentials</p></li><li data-start="2378" data-end="2414"><p data-start="2381" data-end="2414">Enabled two-factor authentication</p></li></ol><p data-start="2416" data-end="2501">If there is any chance code execution occurred, assume secrets may have been exposed.</p><p data-start="2416" data-end="2501">&nbsp;</p><p data-start="2416" data-end="2501">&nbsp;</p><h3>Lessons Worth Keeping</h3><p data-start="2534" data-end="2607">Keep dependencies updated. Public vulnerabilities are actively exploited.</p><p data-start="2609" data-end="2647">Run <code data-start="2613" data-end="2624">npm audit</code> regularly. At minimum:</p><ul data-start="2649" data-end="2773"><li data-start="2649" data-end="2682"><p data-start="2651" data-end="2682">Before production deployments</p></li><li data-start="2683" data-end="2716"><p data-start="2685" data-end="2716">After installing new packages</p></li><li data-start="2717" data-end="2773"><p data-start="2719" data-end="2773">As part of your CI pipeline using <code data-start="2753" data-end="2773">--audit-level=high</code></p></li></ul><p data-start="2775" data-end="2878">The goal is not to run it obsessively. The goal is to reduce how long you stay on a vulnerable version.</p><p data-start="2880" data-end="2936">A clean Git working tree makes suspicious files obvious.</p><p data-start="2938" data-end="3033">Deleting the malicious file is not enough. You have to patch the vulnerability that allowed it.</p><p data-start="3035" data-end="3145">Limit what lives in your <code data-start="3060" data-end="3066">.env</code> file. The fewer sensitive values your app holds, the smaller the blast radius.</p><p data-start="3035" data-end="3145">&nbsp;</p><h2 data-start="3152" data-end="3165">References</h2><p data-start="3167" data-end="3246">Next.js Security Advisory<br><a target="_blank" rel="noopener noreferrer" href="https://github.com/advisories/GHSA-9qr9-h5gf-34mp">https://github.com/advisories/GHSA-9qr9-h5gf-34mp</a></p><p data-start="3167" data-end="3246"><a target="_blank" rel="noopener noreferrer" href="https://github.com/advisories/GHSA-9qr9-h5gf-34mp"><span class="ms-0.5 inline-block align-middle leading-none" aria-hidden="true"><svg class="block h-[0.75em] w-[0.75em] stroke-current stroke-[0.75]" xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" data-rtl-flip=""><use href="/cdn/assets/sprites-core-jxe2m7va.svg#304883" fill="currentColor"></use></svg></span></a></p><p data-start="3248" data-end="3316">Next.js Release Notes<br><a target="_blank" rel="noopener noreferrer" href="https://github.com/vercel/next.js/releases">https://github.com/vercel/next.js/releases</a></p>]]></content:encoded>
      <category>Security</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/I_Found_a_RCE_Attack_09350739a7.webp" length="19528" type="image/webp" />
    </item>
    <item>
      <title>Ripping CDs on Linux with abcde, Made Simple</title>
      <link>https://www.joshfridey.com/blog/scripts/ripping-c-ds-on-linux-with-abcde-made-simple</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/scripts/ripping-c-ds-on-linux-with-abcde-made-simple</guid>
      <pubDate>Sun, 01 Feb 2026 09:47:15 GMT</pubDate>
      <description>Ripping CDs on Linux with abcde, Made Simple
I still buy music.

 

Part of that is nostalgia, and part of it is a conscious choice. Music has always been a big part of my life. Punk rock, hardcore, metal, indie. I like owning the album, the artwork, the liner notes, and having a physical copy that lives on my shelf.

 

Because of that, I buy CDs and records, then rip my CDs into a lossless music library that I keep for myself. I stream that library to my devices so I can listen wherever I am, without depending on third-party services.

 

No piracy. No sketchy sources. Just music I bought and ripped for my own collection.

 

I already had a setup I liked. I just wanted to make it easier to use.

 

Why I use abcde on Linux
I really like abcde.

 

It is one of those Linux tools that has been around forever for a reason. It is reliable, flexible, and does exactly what it is supposed to do. Ripping, tagging, metadata lookups, encoding. It handles all of it without getting in the way.

 

I had no interest in replacing abcde or wrapping it in a heavy UI. I just wanted a better workflow when ripping more than one CD at a time.

 

A better multi-drive ripping workflow
I wrote a small wrapper script around abcde and called it ripcd.

From a terminal, I type:
 

Copy to Clipboard...
ripcd

The script checks all connected optical drives, detects which ones actually have discs loaded, and shows a simple menu. I can select a single drive or press a to rip all loaded CDs at once.

 

When ripping multiple discs, each CD opens in its own terminal tab. That keeps metadata prompts clean and makes it easy to work through several discs in parallel without mixing things up.

 

At that point, abcde takes over and does what it already does best.

FLAC only, by design
This script always rips CDs to lossless FLAC.
 

That choice is intentional. My goal is a clean archive, not a dozen encoding options. I keep one high-quality copy and convert formats later if I need to.


If you want different behavior, the abcde command is easy to change. The script is simple and readable on purpose so it can be adapted without much effort.

Why this setup works for me
I enjoy building and maintaining my own music library. I enjoy Linux tools that respect my time. I enjoy small bits of automation that remove friction instead of adding complexity.


Once ripping CDs became something I could start and mostly ignore, I knew the workflow was right.

Below this post you will find instructions for installing abcde on Fedora KDE, Ubuntu, and similar Linux setups, along with the full ripcd script. Everything is shared directly on the page so you can copy, paste, and tweak it for your own system.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/ripcd_w_abcde_cdeca6fdd1.jpg" alt="ripcd with abcde" />
<p data-start="1241" data-end="1259">I still buy music.</p><p data-start="1241" data-end="1259">&nbsp;</p><p data-start="1261" data-end="1511">Part of that is nostalgia, and part of it is a conscious choice. Music has always been a big part of my life. Punk rock, hardcore, metal, indie. I like owning the album, the artwork, the liner notes, and having a physical copy that lives on my shelf.</p><p data-start="1513" data-end="1734">&nbsp;</p><p data-start="1513" data-end="1734">Because of that, I buy CDs and records, then rip my CDs into a <em data-start="1576" data-end="1600"><i>lossless music library</i></em> that I keep for myself. I stream that library to my devices so I can listen wherever I am, without depending on third-party services.</p><p data-start="1736" data-end="1820">&nbsp;</p><p data-start="1736" data-end="1820">No piracy. No sketchy sources. Just music I bought and ripped for my own collection.</p><p data-start="1822" data-end="1892">&nbsp;</p><p data-start="1822" data-end="1892">I already had a setup I liked. I just wanted to make it easier to use.</p><p data-start="1822" data-end="1892">&nbsp;</p><blockquote><h3><strong data-start="243" data-end="290">A quick personal note on supporting artists</strong></h3><p data-start="292" data-end="510">I want to be explicit about something that matters to me. I believe in supporting artists and musicians directly. That means buying records and CDs, going to shows, and paying for the music that means something to you.</p><p data-start="512" data-end="733">Everything described here is about ripping music I already own for my personal library. No piracy, no file sharing, no gray areas. Just taking physical media I paid for and making it usable in a modern, self-hosted setup.</p><p data-start="735" data-end="1026">If you care about music, especially local scenes and independent artists, the best thing you can do is support them with your money and your time. Buy the album. Go to the show. Talk to the band at the merch table. The tools are just tools. The point is the music and the people who make it.</p><p data-start="735" data-end="1026">&nbsp;</p><h3><strong data-start="1153" data-end="1191">A few local bands worth supporting</strong></h3><p data-start="1200" data-end="1440">Since I’m talking about buying music and supporting artists, I’d be remiss not to call out a few local bands that have meant a lot to me as of recent. These are artists I’ve seen live, bought records from, and still listen to regularly.</p><p data-start="1200" data-end="1440">&nbsp;</p><p data-start="1447" data-end="1492"><a target="_blank" rel="noopener noreferrer" href="https://outpatientx.bandcamp.com/">Outpatient X&nbsp;</a> - Santa Cruz, CA<br><a target="_blank" rel="noopener noreferrer" href="https://bunkerclub805.bandcamp.com/">Bunker Club</a>&nbsp;- Paso Robles, CA<br><a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/@whatsgooddog831/">Whats Good</a> - Santa Cruz, CA</p></blockquote><p data-start="1142" data-end="1212">&nbsp;</p><h2>Why I use <strong data-start="1913" data-end="1922">abcde</strong> on Linux</h2><p data-start="1933" data-end="1957">I really like <strong data-start="1947" data-end="1956">abcde</strong>.</p><p data-start="1959" data-end="2197">&nbsp;</p><p data-start="1959" data-end="2197">It is one of those Linux tools that has been around forever for a reason. It is reliable, flexible, and does exactly what it is supposed to do. Ripping, tagging, metadata lookups, encoding. It handles all of it without getting in the way.</p><p data-start="2199" data-end="2338">&nbsp;</p><p data-start="2199" data-end="2338">I had no interest in replacing abcde or wrapping it in a heavy UI. I just wanted a better workflow when ripping more than one CD at a time.</p><p data-start="1513" data-end="1652">&nbsp;</p><h3 data-start="2345" data-end="2386">A better multi-drive ripping workflow</h3><p data-start="2388" data-end="2456">I wrote a small wrapper script around abcde and called it <strong data-start="2446" data-end="2455">ripcd</strong>.</p><p data-start="2458" data-end="2482">From a terminal, I type:<br>&nbsp;</p><pre><code class="language-plaintext">ripcd</code></pre><p data-start="2499" data-end="2690"><br>The script checks all connected optical drives, detects which ones actually have discs loaded, and shows a simple menu. I can select a single drive or press <code data-start="2656" data-end="2659">a</code> to rip all loaded CDs at once.</p><p data-start="2692" data-end="2879">&nbsp;</p><p data-start="2692" data-end="2879">When ripping multiple discs, each CD opens in its own terminal tab. That keeps metadata prompts clean and makes it easy to work through several discs in parallel without mixing things up.</p><p data-start="2881" data-end="2948">&nbsp;</p><p data-start="2881" data-end="2948">At that point, abcde takes over and does what it already does best.</p>
<h3 data-start="2955" data-end="2983"><strong data-start="2959" data-end="2972">FLAC only</strong>, by design</h3><p data-start="2985" data-end="3034">This script always rips CDs to <strong data-start="3016" data-end="3033">lossless FLAC</strong>.<br>&nbsp;</p><p data-start="3036" data-end="3192">That choice is intentional. My goal is a <em data-start="3077" data-end="3092"><i>clean archive</i></em>, not a dozen encoding options. I keep one high-quality copy and convert formats later if I need to.</p><p data-start="3194" data-end="3349"><br>If you want different behavior, the abcde command is easy to change. The script is simple and readable on purpose so it can be adapted without much effort.</p>
<h3 data-start="3356" data-end="3387">Why this setup works for me</h3><p data-start="3389" data-end="3569">I enjoy building and maintaining my own music library. I enjoy Linux tools that respect my time. I enjoy small bits of automation that remove friction instead of adding complexity.</p><p data-start="3571" data-end="3670"><br><em data-start="3571" data-end="3670"><i>Once ripping CDs became something I could start and mostly ignore, I knew the workflow was right.</i></em></p>
<p>Below this post you will find instructions for installing abcde on Fedora KDE, Ubuntu, and similar Linux setups, along with the full <strong data-start="3810" data-end="3819">ripcd</strong> script. Everything is shared directly on the page so you can copy, paste, and tweak it for your own system.</p>
<h2 data-start="176" data-end="197">Installing <code data-start="190" data-end="197">ripcd</code></h2><p data-start="199" data-end="313"><code data-start="199" data-end="206">ripcd</code> is a simple shell script. There is no installer. Copy it into place, make it executable, and you are done.</p><p data-start="315" data-end="358">You can install it per user or system-wide.</p><p data-start="315" data-end="358">&nbsp;</p><h3>The Script</h3><pre><code class="language-plaintext">#!/usr/bin/env bash
set -euo pipefail

# Detect optical drives with media inserted
get_drives_with_media() {
  for dev in /dev/sr*; do
    [[ -e "$dev" ]] || continue
    
    # Check if media is present
    if udevadm info -q property -n "$dev" 2&gt;/dev/null | grep -qx 'ID_CDROM_MEDIA=1'; then
      echo "$dev"
    fi
  done
}

# Detect available terminal emulator
detect_terminal() {
  if command -v konsole &gt;/dev/null 2&gt;&amp;1; then
    echo "konsole"
  elif command -v gnome-terminal &gt;/dev/null 2&gt;&amp;1; then
    echo "gnome-terminal"
  elif command -v xterm &gt;/dev/null 2&gt;&amp;1; then
    echo "xterm"
  else
    echo ""
  fi
}

# Find drives with media
mapfile -t drives &lt; &lt;(get_drives_with_media)

if (( ${#drives[@]} == 0 )); then
  echo "No discs detected in any optical drives."
  exit 1
fi

# Show menu
echo "Discs detected:"
echo
for i in "${!drives[@]}"; do
  echo "  $((i+1))) ${drives[$i]}"
done
echo
echo "  a) ALL (rip all drives simultaneously in tabs)"
echo "  q) Quit"
echo

read -rp "Select (1-${#drives[@]}, a, q): " choice

# Handle choice
case "${choice,,}" in
  q)
    echo "Quitting."
    exit 0
    ;;
    
  a|all)
    term="$(detect_terminal)"
    
    if [[ -z "$term" ]]; then
      echo "No supported terminal found. Cannot open tabs."
      exit 1
    fi
    
    case "$term" in
      konsole)
        echo "Opening Konsole with ${#drives[@]} tabs..."
        
        # Start an empty Konsole window
        konsole &amp;
        konsole_pid=$!
        sleep 1.5
        
        # Find the D-Bus service
        service="org.kde.konsole-$konsole_pid"
        
        # Check if the service exists
        if ! qdbus | grep -q "$service"; then
          echo "Could not connect to Konsole via D-Bus, opening separate windows instead..."
          for dev in "${drives[@]}"; do
            konsole -e bash -lc "abcde -d '$dev' -o flac; echo; echo 'Done. Press Enter to close.'; read" &amp;
            sleep 0.3
          done
          exit 0
        fi
        
        # Process each drive
        for i in "${!drives[@]}"; do
          dev="${drives[$i]}"
          
          if (( i == 0 )); then
            # First drive: use the existing session
            session=$(qdbus $service /Windows/1 currentSession 2&gt;/dev/null)
          else
            # Additional drives: create new tabs
            session=$(qdbus $service /Windows/1 newSession 2&gt;/dev/null)
            sleep 0.5
          fi
          
          if [[ -n "$session" ]]; then
            # Set tab title
            qdbus $service /Sessions/$session setTitle 1 "abcde $(basename "$dev")" 2&gt;/dev/null
            
            # Send the command
            qdbus $service /Sessions/$session sendText "abcde -d '$dev' -o flac" 2&gt;/dev/null
            qdbus $service /Sessions/$session sendText "$(printf '\r')" 2&gt;/dev/null
            
            echo "Started ripping $dev in tab $((i+1))"
          fi
        done
        
        echo ""
        echo "All tabs created! You can:"
        echo "  - Press Ctrl+Shift+* to split tabs side-by-side"
        echo "  - Use Ctrl+Shift+Left/Right to switch between tabs"
        ;;
        
      gnome-terminal)
        echo "Opening ${#drives[@]} tabs in GNOME Terminal..."
        cmd="gnome-terminal"
        for dev in "${drives[@]}"; do
          cmd="$cmd --tab --title='abcde $(basename "$dev")' -- bash -lc \"abcde -d '$dev' -o flac; echo; echo 'Done. Press Enter to close.'; read\""
        done
        eval "$cmd" &amp;
        ;;
        
      xterm)
        echo "Opening ${#drives[@]} separate xterm windows..."
        for dev in "${drives[@]}"; do
          xterm -title "abcde $(basename "$dev")" -e bash -lc "abcde -d '$dev' -o flac; echo; echo 'Done. Press Enter to close.'; read" &amp;
        done
        ;;
    esac
    
    echo "All ripping processes started!"
    exit 0
    ;;
    
  *)
    # Check if it's a valid number
    if [[ "$choice" =~ ^[0-9]+$ ]] &amp;&amp; (( choice &gt;= 1 &amp;&amp; choice &lt;= ${#drives[@]} )); then
      dev="${drives[$((choice-1))]}"
      echo "Ripping ${dev}..."
      exec abcde -d "$dev" -o flac
    else
      echo "Invalid selection."
      exit 1
    fi
    ;;
esac
</code></pre>
<h3 data-start="365" data-end="410">Option A: User-only install (recommended)</h3><p data-start="412" data-end="431">Copy the script to:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">~/.local/bin/ripcd
</code></pre></div><p data-start="461" data-end="480">&nbsp;</p><p data-start="461" data-end="480">Make it executable:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">chmod +x ~/.local/bin/ripcd
</code></pre></div><p data-start="519" data-end="583">Most modern distros already include <code data-start="555" data-end="569">~/.local/bin</code> in your PATH.</p>
<h3 data-start="590" data-end="623">Option B: System-wide install</h3><p data-start="625" data-end="644">Copy the script to:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">/usr/local/bin/ripcd
</code></pre></div><p data-start="676" data-end="695">&nbsp;</p><p data-start="676" data-end="695">Make it executable:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">sudo chmod +x /usr/local/bin/ripcd
</code></pre></div><p data-start="741" data-end="783">This makes <code data-start="752" data-end="759">ripcd</code> available to all users.</p>
<h2 data-start="790" data-end="831">Optional: Close the launching terminal</h2><p data-start="833" data-end="924">If you want the terminal you run <code data-start="866" data-end="873">ripcd</code> from to close after launching abcde, add an alias</p>
<h3 data-start="926" data-end="945">User-only alias</h3><p data-start="947" data-end="966">Add to <code data-start="954" data-end="965">~/.bashrc</code>:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">alias ripcd='~/.local/bin/ripcd &amp;&amp; exit' </code></pre></div><p data-start="1018" data-end="1036">&nbsp;</p><p data-start="1018" data-end="1036">Reload your shell:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">source ~/.bashrc</code></pre></div>
<h3 data-start="1069" data-end="1090">System-wide alias</h3><p data-start="1092" data-end="1099">Create:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">sudo nano /etc/profile.d/ripcd.sh
</code></pre></div><p data-start="1144" data-end="1148">&nbsp;</p><p data-start="1144" data-end="1148">Add:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">alias ripcd='ripcd &amp;&amp; exit'</code></pre></div>
<h2 data-start="1192" data-end="1208">Using <code data-start="1201" data-end="1208">ripcd</code></h2><p data-start="1210" data-end="1214">Run:</p><div class="contain-inline-size rounded-2xl corner-superellipse/1.1 relative bg-token-sidebar-surface-primary"><pre><code class="language-plaintext">ripcd </code></pre></div><p data-start="1231" data-end="1247">&nbsp;</p><p data-start="1231" data-end="1247">The script will:</p><ul data-start="1249" data-end="1366"><li data-start="1249" data-end="1288"><p data-start="1251" data-end="1288">Detect all connected optical drives</p></li><li data-start="1289" data-end="1329"><p data-start="1291" data-end="1329">Check which drives have discs loaded</p></li><li data-start="1330" data-end="1366"><p data-start="1332" data-end="1366">Show a menu of available options</p></li></ul><p data-start="1368" data-end="1433">Select a single drive or press <code data-start="1399" data-end="1402">a</code> to rip all loaded CDs at once.</p><p data-start="1435" data-end="1573">When ripping multiple discs, each CD opens in its own terminal tab or window. abcde then runs normally and prompts for metadata as needed.</p><p data-start="1435" data-end="1573">&nbsp;</p><h3>Notes</h3><ul data-start="1590" data-end="1816"><li data-start="1590" data-end="1638"><p data-start="1592" data-end="1638"><code data-start="1592" data-end="1599">abcde</code> must already be installed and working.</p></li><li data-start="1639" data-end="1676"><p data-start="1641" data-end="1676">All rips are done in lossless FLAC.</p></li><li data-start="1677" data-end="1731"><p data-start="1679" data-end="1731">Supported terminals: Konsole, GNOME Terminal, xterm.</p></li><li data-start="1732" data-end="1816"><p data-start="1734" data-end="1816">The abcde command inside the script can be modified if you want different options.</p></li></ul>]]></content:encoded>
      <category>Scripts</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/ripcd_w_abcde_cdeca6fdd1.jpg" length="81848" type="image/jpeg" />
    </item>
    <item>
      <title>We’ve Run Into an Issue Download Edge WebView2</title>
      <link>https://www.joshfridey.com/blog/scripts/we-ve-run-into-an-issue-download-edge-web-view2</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/scripts/we-ve-run-into-an-issue-download-edge-web-view2</guid>
      <pubDate>Tue, 20 Jan 2026 04:08:39 GMT</pubDate>
      <description>I’ve been running into this issue repeatedly for a few clients lately, usually showing up as Microsoft Teams or OneDrive throwing a vague “We’ve run into an issue” message and prompting the user to download Edge WebView2. After fixing it manually more times than I care to admit, I finally got annoyed with the repetitiveness and decided to automate the fix into a single batch script you can run with a double click.

In most cases, reinstalling Teams or OneDrive does nothing. That’s because the underlying problem usually isn’t the app itself.

What’s Actually Breaking

In my experience, this issue almost always comes down to a broken or partially corrupted Microsoft Edge WebView2 runtime. WebView2 is a shared system-level component that Microsoft uses to render UI inside apps like Teams and OneDrive. When it’s missing, corrupted, or stuck in a bad state, multiple apps can fail in similar and confusing ways.

Common causes I’ve seen include:

Interrupted or failed WebView2 updates

Corrupted installer files

Pathing issues where WebView2 exists on disk but apps can’t resolve it correctly

Because WebView2 is installed separately from Teams and OneDrive, reinstalling those apps usually leaves the broken runtime untouched.

Why This Is Annoying to Fix Manually

One of the more frustrating parts of this issue is that WebView2 installs itself into a versioned folder under its application directory. The folder name is a numeric version string, for example 144.0.3719.82, and that version changes constantly as updates roll out.

That means:

You can’t reliably hardcode the path

The registry is not always trustworthy when the runtime is partially broken

The installer you need is buried under a version-specific folder

This is exactly the kind of problem that’s easy to fix once, but painful to repeat across multiple machines.

What the Script Does

This script is designed to fix the “We’ve Run Into an Issue” / “Download Edge WebView2” error in a repeatable way.

At a high level, it does three things:

Allows WebView2 to be repaired

It updates two registry values that control whether the Edge WebView2 runtime can be reinstalled or repaired.

In certain failure states, those flags prevent the installer from doing anything useful.

Automatically finds the installed WebView2 version

Instead of relying on the registry or hardcoded paths, the script locates the active WebView2 version folder directly on disk.

It identifies the numeric version directory dynamically, so it works regardless of the installed version.

Runs a proper system-level repair

It executes the correct setup.exe from the discovered version folder.

This forces a clean, system-level repair install of the WebView2 runtime.

The end result is a repaired WebView2 runtime without having to uninstall Edge, reinstall Teams, or manually hunt through folders.

Usage Notes

The script must be run as administrator.

It will close Edge and WebView2 processes to avoid file locks.

It returns an exit code of 0 when the repair completes successfully.

It’s designed to be portable, you can drop it onto an affected machine and run it directly.

I’ve tried to keep this as universal as possible so it can be used across different systems without modification. If you do run into edge cases or failures in your environment, let me know, but this approach has been reliable for me and has saved a lot of repetitive cleanup work.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Web_View2_ee50b8e26a.webp" alt="We’ve Run Into an Issue Download Edge WebView2 " />
<p data-start="518" data-end="943">I’ve been running into this issue repeatedly for a few clients lately, usually showing up as Microsoft Teams or OneDrive throwing a vague <strong data-start="656" data-end="685">“We’ve run into an issue”</strong> message and prompting the user to <strong data-start="720" data-end="746">download Edge WebView2</strong>. After fixing it manually more times than I care to admit, I finally got annoyed with the repetitiveness and decided to automate the fix into a single batch script you can run with a double click.</p><p data-start="945" data-end="1072">In most cases, reinstalling Teams or OneDrive does nothing. That’s because the underlying problem usually isn’t the app itself.</p><p data-start="945" data-end="1072">&nbsp;</p><h2 data-start="1074" data-end="1101">What’s Actually Breaking</h2><p data-start="1103" data-end="1451">In my experience, this issue almost always comes down to a broken or partially corrupted <strong data-start="1192" data-end="1227">Microsoft Edge WebView2 runtime</strong>. WebView2 is a shared system-level component that Microsoft uses to render UI inside apps like Teams and OneDrive. When it’s missing, corrupted, or stuck in a bad state, multiple apps can fail in similar and confusing ways.</p><p data-start="1103" data-end="1451">&nbsp;</p><p data-start="1453" data-end="1485">Common causes I’ve seen include:</p><p data-start="1453" data-end="1485">&nbsp;</p><ul data-start="1486" data-end="1643"><li data-start="1486" data-end="1528"><p data-start="1488" data-end="1528">Interrupted or failed WebView2 updates</p></li><li data-start="1529" data-end="1558"><p data-start="1531" data-end="1558">Corrupted installer files</p></li><li data-start="1559" data-end="1643"><p data-start="1561" data-end="1643">Pathing issues where WebView2 exists on disk but apps can’t resolve it correctly</p></li></ul><p data-start="1645" data-end="1779">&nbsp;</p><p data-start="1645" data-end="1779">Because WebView2 is installed separately from Teams and OneDrive, reinstalling those apps usually leaves the broken runtime untouched.</p><p data-start="1645" data-end="1779">&nbsp;</p><h2 data-start="1781" data-end="1820">Why This Is Annoying to Fix Manually</h2><p data-start="1822" data-end="2094">One of the more frustrating parts of this issue is that WebView2 installs itself into a <strong data-start="1910" data-end="1930">versioned folder</strong> under its application directory. The folder name is a numeric version string, for example <code data-start="2021" data-end="2036">144.0.3719.82</code>, and that version changes constantly as updates roll out.</p><p data-start="1822" data-end="2094">&nbsp;</p><p data-start="2096" data-end="2107">That means:</p><p data-start="2096" data-end="2107">&nbsp;</p><ul data-start="2108" data-end="2291"><li data-start="2108" data-end="2146"><p data-start="2110" data-end="2146">You can’t reliably hardcode the path</p></li><li data-start="2147" data-end="2224"><p data-start="2149" data-end="2224">The registry is not always trustworthy when the runtime is partially broken</p></li><li data-start="2225" data-end="2291"><p data-start="2227" data-end="2291">The installer you need is buried under a version-specific folder</p></li></ul><p data-start="2293" data-end="2401">&nbsp;</p><p data-start="2293" data-end="2401">This is exactly the kind of problem that’s easy to fix once, but painful to repeat across multiple machines.</p><p data-start="2293" data-end="2401">&nbsp;</p><h2 data-start="2403" data-end="2426">What the Script Does</h2><p data-start="2428" data-end="2546">This script is designed to fix the <strong data-start="2463" data-end="2519">“We’ve Run Into an Issue” / “Download Edge WebView2”</strong> error in a repeatable way.</p><p data-start="2428" data-end="2546">&nbsp;</p><p data-start="2548" data-end="2586">At a high level, it does three things:</p><p data-start="2548" data-end="2586">&nbsp;</p><ol data-start="2588" data-end="3334"><li data-start="2588" data-end="2834"><p data-start="2591" data-end="2625"><strong data-start="2591" data-end="2625">Allows WebView2 to be repaired</strong></p><ul data-start="2629" data-end="2834"><li data-start="2629" data-end="2740"><p data-start="2631" data-end="2740">It updates two registry values that control whether the Edge WebView2 runtime can be reinstalled or repaired.</p></li><li data-start="2744" data-end="2834"><p data-start="2746" data-end="2834">In certain failure states, those flags prevent the installer from doing anything useful.</p></li></ul></li><li data-start="2836" data-end="3136"><p data-start="2839" data-end="2893"><strong data-start="2839" data-end="2893">Automatically finds the installed WebView2 version</strong></p><ul data-start="2897" data-end="3136"><li data-start="2897" data-end="3025"><p data-start="2899" data-end="3025">Instead of relying on the registry or hardcoded paths, the script locates the active WebView2 version folder directly on disk.</p></li><li data-start="3029" data-end="3136"><p data-start="3031" data-end="3136">It identifies the numeric version directory dynamically, so it works regardless of the installed version.</p></li></ul></li><li data-start="3138" data-end="3334"><p data-start="3141" data-end="3178"><strong data-start="3141" data-end="3178">Runs a proper system-level repair</strong></p><ul data-start="3182" data-end="3334"><li data-start="3182" data-end="3255"><p data-start="3184" data-end="3255">It executes the correct <code data-start="3208" data-end="3219">setup.exe</code> from the discovered version folder.</p></li><li data-start="3259" data-end="3334"><p data-start="3261" data-end="3334">This forces a clean, system-level repair install of the WebView2 runtime.</p></li></ul></li></ol><p data-start="3336" data-end="3466">&nbsp;</p><p data-start="3336" data-end="3466">The end result is a repaired WebView2 runtime without having to uninstall Edge, reinstall Teams, or manually hunt through folders.</p><p data-start="3336" data-end="3466">&nbsp;</p><h3>Usage Notes</h3><ul data-start="3484" data-end="3758"><li data-start="3484" data-end="3526"><p data-start="3486" data-end="3526">The script must be run as administrator.</p></li><li data-start="3527" data-end="3591"><p data-start="3529" data-end="3591">It will close Edge and WebView2 processes to avoid file locks.</p></li><li data-start="3592" data-end="3664"><p data-start="3594" data-end="3664">It returns an exit code of <code data-start="3621" data-end="3624">0</code> when the repair completes successfully.</p></li><li data-start="3665" data-end="3758"><p data-start="3667" data-end="3758">It’s designed to be portable, you can drop it onto an affected machine and run it directly.</p></li></ul><p data-start="3760" data-end="4040">&nbsp;</p><p data-start="3760" data-end="4040">I’ve tried to keep this as universal as possible so it can be used across different systems without modification. If you do run into edge cases or failures in your environment, let me know, but this approach has been reliable for me and has saved a lot of repetitive cleanup work.</p><p data-start="3760" data-end="4040">&nbsp;</p><p>&nbsp;</p><pre><code class="language-plaintext">@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
setlocal enabledelayedexpansion
COLOR 09
TITLE JOSHFRIDEY.COM WEBVIEWW2 FIX
:: --- Elevation Check ---
:: Check if script is running as admin (no redirection)
NET SESSION
IF %ERRORLEVEL% NEQ 0 (
   ECHO Requesting administrative privileges...
   PowerShell -Command "Start-Process '%~f0' -ArgumentList '/elevated' -Verb RunAs"
   EXIT /B
)
:: Check for elevation flag
IF "%1"=="/elevated" SHIFT
set "REGKEY=HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView"
set "VALNAME=SystemComponent"
set "VALNAME2=NoRemove"
echo Setting %REGKEY% %VALNAME% to 0...
reg add "%REGKEY%" /v "%VALNAME%" /t REG_DWORD /d 0 /f
if errorlevel 1 (
 echo Failed to set registry value %VALNAME%.
)
reg add "%REGKEY%" /v "%VALNAME2%" /t REG_DWORD /d 0 /f
if errorlevel 1 (
 echo Failed to set registry value %VALNAME2%.
)
REM --- Determine WebView2 folder version from disk ---
set "APPROOT=C:\Program Files (x86)\Microsoft\EdgeWebView\Application"
set "RAW="
REM Find the only folder whose name contains only digits and dots, like 144.0.3719.82
for /d %%D in ("%APPROOT%\*") do (
 set "NAME=%%~nxD"
 set "TMP=!NAME!"
 REM Strip digits and dots
 for %%C in (0 1 2 3 4 5 6 7 8 9 .) do set "TMP=!TMP:%%C=!"
 REM If nothing remains, NAME was only digits and dots
 if "!TMP!"=="" (
   set "RAW=!NAME!"
   goto :FoundVersion
 )
)

:FoundVersion
Echo Version %RAW%

set "EXE=C:\Program Files (x86)\Microsoft\EdgeWebView\Application\!RAW!\Installer\setup.exe"
echo Stopping WebView2 and Edge processes..
taskkill /f /im msedgewebview2.exe
taskkill /f /im msedge.exe
start "" /wait "%EXE%" --msedgewebview --system-level --verbose-logging
set "EC=%ERRORLEVEL%"
echo WebView2 reinstall exit code: %EC%
ECHO === WEBVIEW2 FIX COMPLETE ===
ECHO Press any key to exit...
PAUSE
EXIT
</code></pre>]]></content:encoded>
      <category>Scripts</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Web_View2_ee50b8e26a.webp" length="55808" type="image/webp" />
    </item>
    <item>
      <title>When Windows 11 Decided I Didn’t Need Printers Today</title>
      <link>https://www.joshfridey.com/blog/printers/when-windows-11-decided-i-didn-t-need-printers-today</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/printers/when-windows-11-decided-i-didn-t-need-printers-today</guid>
      <pubDate>Fri, 12 Sep 2025 04:00:20 GMT</pubDate>
      <description>Ran into a strange printer issue the other day that had me mutter a few things I probably shouldn’t repeat here. Thought I’d share it, just in case it saves someone else the same hassle. Windows Protected Print Mode.

Everything Worked—At First

I was setting up two network printers on a Windows 11 machine. Nothing fancy—just your standard TCP/IP installs. They tested fine, so I handed the machine off and went on my way.

Not long after, I checked in—and both printers were showing as offline. No changes made, nothing unplugged, just… not working.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Windows_Protected_Print_Mode_c13f6d49fb_b48f8db40a.webp" alt="Windows Protected Print Mode" />
<p data-start="150" data-end="340">Ran into a strange printer issue the other day that <strong data-start="202" data-end="265">had me mutter a few things I probably shouldn’t repeat here</strong>. Thought I’d share it, just in case it saves someone else the same hassle.</p><p data-start="150" data-end="340">&nbsp;</p><h2>Everything Worked—At First</h2><p data-start="379" data-end="557">I was setting up two network printers on a Windows 11 machine. Nothing fancy—just your standard TCP/IP installs. They tested fine, so I handed the machine off and went on my way.</p><p data-start="559" data-end="685">Not long after, I checked in—and both printers were showing as offline. No changes made, nothing unplugged, just… not working.</p><p data-start="559" data-end="685">&nbsp;</p><h3 data-start="692" data-end="732">Manual Printer Setup Was Locked Down</h3><p data-start="734" data-end="935">I went to remove and re-add the printers like I’ve done a hundred times before. But when I got to the install screen, the usual options—<strong data-start="870" data-end="910">manual setup, finding older printers</strong>—were all <strong data-start="920" data-end="935">greyed out.</strong></p><p data-start="937" data-end="983">That’s when I knew something odd was going on.</p><p data-start="937" data-end="983">&nbsp;</p><h3 data-start="990" data-end="1032">The Fix: Turn Off Protected Print Mode</h3><p data-start="1034" data-end="1199">After a bunch of digging, I found a setting under <strong data-start="1084" data-end="1140">Settings &gt; Bluetooth &amp; devices &gt; Printers &amp; scanners</strong> called <strong data-start="1148" data-end="1181">Windows Protected Print Mode.</strong> It was turned on.</p><p data-start="1201" data-end="1356">I switched it off—and boom, everything started working again. The manual printer options came back, and I could reinstall both printers without any issues.</p><p data-start="1201" data-end="1356">&nbsp;</p><h3 data-start="1363" data-end="1396">What Is Protected Print Mode?</h3><p data-start="1398" data-end="1543">Basically, it’s a newer Windows security feature that blocks most third-party printer drivers and only allows Microsoft’s built-in print driver.</p><p data-start="1545" data-end="1650">The idea is to make printing more secure—but the tradeoff is that you lose a lot of useful features like:</p><ul data-start="1651" data-end="1722"><li data-start="1651" data-end="1667"><p data-start="1653" data-end="1667">Tray selection</p></li><li data-start="1668" data-end="1693"><p data-start="1670" data-end="1693">Advanced print settings</p></li><li data-start="1694" data-end="1722"><p data-start="1696" data-end="1722">Some multifunction options</p></li></ul><p data-start="1724" data-end="1771">For most users, that’s a pretty big limitation.</p><p data-start="1724" data-end="1771">&nbsp;</p><h3 data-start="1778" data-end="1830">Microsoft Plans to Make This the Default by 2027</h3><p data-start="1832" data-end="2020">Yep, this setting isn’t just some random toggle—it’s part of Microsoft’s longer-term plan. Protected Print Mode is expected to be the default print setup starting sometime around <strong data-start="2011" data-end="2019">2027</strong>.</p><p data-start="2022" data-end="2124">We’ll see if that timeline sticks, but it’s something IT folks should start getting familiar with now.</p><p data-start="2022" data-end="2124">&nbsp;</p><h2>Final Thoughts</h2><p data-start="2151" data-end="2437">I’m still not sure how the setting got turned on—could’ve been a Windows update, a vendor tool, or maybe I just clicked too fast. But if your printers randomly stop working and the manual install options are missing, <strong data-start="2368" data-end="2412">check that Protected Print Mode setting.</strong> It might be the culprit.</p>]]></content:encoded>
      <category>Printers</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Windows_Protected_Print_Mode_c13f6d49fb_b48f8db40a.webp" length="92508" type="image/webp" />
    </item>
    <item>
      <title>Maintenance Batch Script</title>
      <link>https://www.joshfridey.com/blog/scripts/maintenance-batch-script</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/scripts/maintenance-batch-script</guid>
      <pubDate>Thu, 11 Sep 2025 23:33:15 GMT</pubDate>
      <description>I put off building a proper all in one Windows maintenance batch script for years. I had smaller scripts here and there to speed things up, but nothing consolidated. Starting this blog finally gave me a good reason to sit down, build a single script I actually use, and share it so anyone else can grab it.



This script is simple, practical, and includes a few small touches that make it easy to run again later.



Key Features

1 Automatic admin elevation

If you forget to run it as Administrator, it restarts itself with elevated permissions. No extra steps.



2 Clears conflicting processes

Right up front it stops common Windows servicing processes like DISM and TiWorker so maintenance tasks are not fighting for the same resources.



3 Flexible startup options

On launch you choose whether to continue with a reboot, without a reboot, or exit. You stay in control.



4 Live status updates

Every step reports what it is doing so you are not guessing about progress behind the scenes.



What It Does

After you pick your preferences, the script runs a set of routine Windows maintenance tasks:



Defrags and optimizes all hard drives

Cleans the TEMP directory

Checks for and installs Windows Updates

Performs a DISM cleanup

Runs sfc /scannow twice for good measure



The Script

The full batch script is below. Copy it into Notepad, save it as a .bat file, and run it when you need a quick tune up. Modify it to fit your workflow. It is a solid foundation for keeping a system in good shape with minimal effort.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Maintenance_Batch_Script_f9f9f60edd.webp" alt="Maintenance Batch Script" />
<p data-start="118" data-end="432">I put off building a proper all in one <strong data-start="157" data-end="193">Windows maintenance batch script</strong> for years. I had smaller scripts here and there to speed things up, but nothing consolidated. Starting this blog finally <strong data-start="315" data-end="340">gave me a good reason</strong> to sit down, build a single script I actually use, and share it so anyone else can grab it.</p><p data-start="118" data-end="432">&nbsp;</p><p data-start="434" data-end="538">This script is simple, practical, and includes a few small touches that make it easy to run again later.</p><p data-start="434" data-end="538">&nbsp;</p><h2 data-start="540" data-end="559"><strong data-start="543" data-end="559">Key Features</strong></h2><h3>1 Automatic admin elevation</h3><p>If you forget to run it as Administrator, it restarts itself with elevated permissions. No extra steps.</p><p data-start="561" data-end="699">&nbsp;</p><h3>2 Clears conflicting processes</h3><p data-start="701" data-end="890">Right up front it stops common Windows servicing processes like <strong data-start="803" data-end="811">DISM</strong> and <strong data-start="816" data-end="828">TiWorker</strong> so maintenance tasks are not fighting for the same resources.</p><p data-start="701" data-end="890">&nbsp;</p><h3>3 Flexible startup options</h3><p data-start="892" data-end="1041">On launch you choose whether to continue <strong data-start="967" data-end="984">with a reboot</strong>, <strong data-start="986" data-end="1006">without a reboot</strong>, or <strong data-start="1011" data-end="1019">exit</strong>. You stay in control.</p><p data-start="892" data-end="1041">&nbsp;</p><h3>4 Live status updates</h3><p data-start="1043" data-end="1165">Every step reports what it is doing so you are not guessing about progress behind the scenes.</p><p data-start="1043" data-end="1165">&nbsp;</p><h2 data-start="1167" data-end="1186"><strong data-start="1170" data-end="1186">What It Does</strong></h2><p data-start="1188" data-end="1284">After you pick your preferences, the script runs a set of routine <strong data-start="1254" data-end="1277">Windows maintenance</strong> tasks:</p><p data-start="1188" data-end="1284">&nbsp;</p><ul data-start="1286" data-end="1488"><li data-start="1286" data-end="1327">Defrags and optimizes all hard drives</li><li data-start="1328" data-end="1361">Cleans the <strong data-start="1341" data-end="1349">TEMP</strong> directory</li><li data-start="1362" data-end="1409">Checks for and installs <strong data-start="1388" data-end="1407">Windows Updates</strong></li><li data-start="1410" data-end="1441">Performs a <strong data-start="1423" data-end="1431">DISM</strong> cleanup</li><li data-start="1442" data-end="1488">Runs <strong data-start="1449" data-end="1465">sfc /scannow</strong> twice for good measure</li></ul><p>&nbsp;</p><h2 data-start="1490" data-end="1507"><strong data-start="1493" data-end="1507">The Script</strong></h2><p data-start="1509" data-end="1743">The full batch script is below. Copy it into Notepad, save it as a <code data-start="1576" data-end="1582">.bat</code> file, and run it when you need a quick tune up. Modify it to fit your workflow. It is a solid foundation for keeping a system in good shape with minimal effort.</p><p data-start="1685" data-end="1731">&nbsp;</p><pre><code class="language-plaintext">@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
COLOR 09
TITLE JOSHFRIDEY.COM SYSTEM MAINTENANCE BATCH
:: --- Elevation Check ---
:: Check if script is running as admin
NET SESSION &gt;NUL 2&gt;&amp;1
IF %ERRORLEVEL% NEQ 0 (
    ECHO Requesting administrative privileges...
    PowerShell -Command "Start-Process '%~f0' -ArgumentList '/elevated' -Verb RunAs"
    EXIT /B
)
:: Check for elevation flag
IF "%1"=="/elevated" SHIFT
:: -- Show Menu --
:MENU
CLS
ECHO ====================================
ECHO # SYSTEM MAINTENANCE OPTIONS
ECHO ====================================
ECHO.
ECHO [1] WINDOWS UPDATES WITHOUT REBOOT
ECHO [2] WINDOWS UPDATES WITH REBOOT
ECHO [3] EXIT SCRIPT
ECHO.
CHOICE /C 123 /N /M "Choose an option: "
IF ERRORLEVEL 3 GOTO :EOF
IF ERRORLEVEL 2 SET FLAG=REBOOT&amp;GOTO MAINTENANCE
IF ERRORLEVEL 1 SET FLAG=NOREBOOT&amp;GOTO MAINTENANCE
:: --- Maintenance Logic ---
:MAINTENANCE
CLS
ECHO ====================================
ECHO # REBOOT DISM and TIWORKER
ECHO ====================================
TASKLIST | FINDSTR "Dism.exe TiWorker.exe" &gt;NUL &amp;&amp; TASKKILL /F /IM "Dism.exe" /IM "TiWorker.exe" /T &gt;NUL 2&gt;&amp;1
ECHO.
ECHO ====================================
ECHO # Disk Cleanup Automation
ECHO ====================================
ECHO # Clearing CleanMgr.exe automation settings.
powershell -Command "Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\*' -Name StateFlags0001 -ErrorAction SilentlyContinue | Remove-ItemProperty -Name StateFlags0001 -ErrorAction SilentlyContinue"
ECHO # Enabling Update Cleanup. This is done automatically in Windows 10 via a scheduled task.
powershell -Command "New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Update Cleanup' -Name StateFlags0001 -Value 2 -PropertyType DWord"
ECHO # Enabling Temporary Files Cleanup.
powershell -Command "New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\VolumeCaches\Temporary Files' -Name StateFlags0001 -Value 2 -PropertyType DWord"
ECHO # Starting CleanMgr.exe...
powershell -Command "Start-Process -FilePath CleanMgr.exe -ArgumentList '/sagerun:1' -Wait"
ECHO # Waiting for CleanMgr and DismHost processes. Second wait neccesary as CleanMgr.exe spins off separate processes.
powershell -Command "Get-Process -Name cleanmgr,dismhost -ErrorAction SilentlyContinue | Wait-Process"
ECHO.
ECHO ====================================
ECHO # INSTALLING PSWindowsUpdate MODULE
ECHO ====================================
ECHO # Setting Execution Policy to Remote Signed...
powershell -Command "Set-ExecutionPolicy RemoteSigned -Force -ErrorAction SilentlyContinue"
ECHO # Installing Package Provider NuGet...
powershell -Command "Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -ErrorAction SilentlyContinue"
ECHO # Installing Module PSWindowsUpdate...
powershell -Command "Install-Module PSWindowsUpdate -Force -ErrorAction SilentlyContinue"
ECHO # Importing Module PSWindowsUpdate...
powershell -Command "Import-Module PSWindowsUpdate -Force -ErrorAction SilentlyContinue"
ECHO.
ECHO ====================================
ECHO # CHECKING FOR WINDOWS UPDATES
ECHO ====================================
powershell -Command "Get-WindowsUpdate -ErrorAction SilentlyContinue"
ECHO.
ECHO ====================================
ECHO # RESTART &amp; CLEAR PRINT SPOOLER
ECHO ====================================
net stop spooler
taskkill.exe /f /im printfilterpipelinesvc.exe
del /F /Q %systemroot%\System32\spool\PRINTERS\*
del /F /Q %systemroot%\System32\spool\SERVERS\*
net start spooler
ECHO ====================================
ECHO # RUNNING DISM CLEANUP
ECHO ====================================
DISM /Online /Cleanup-Image /RestoreHealth /StartComponentCleanup
ECHO.
ECHO ====================================
ECHO # RUNNING DISM RESTOREHEALTH
ECHO ====================================
DISM /Online /Cleanup-Image /RestoreHealth
ECHO ====================================
ECHO # RUNNING SFC
ECHO ====================================
SFC /SCANNOW
SFC /SCANNOW
ECHO.
ECHO ====================================
ECHO # RUNNING DEFRAG
ECHO ====================================
DEFRAG /C /O /U
ECHO.
ECHO ====================================
ECHO # INSTALLING APP UPDATES
ECHO ====================================
winget update --force --all
ECHO ====================================
ECHO # INSTALLING WINDOWS UPDATES
ECHO ====================================
IF "%FLAG%"=="REBOOT" (
    powershell -Command "Install-WindowsUpdate -AcceptAll -Install -AutoReboot -ErrorAction SilentlyContinue"
    shutdown -r -t 00
) ELSE (
    powershell -Command "Install-WindowsUpdate -AcceptAll -Install -IgnoreReboot -ErrorAction SilentlyContinue"
)
ECHO.
:FINISH
ECHO.
ECHO === MAINTENANCE COMPLETE ===
ECHO Press any key to exit...
PAUSE &gt;NUL
EXIT /B
</code></pre>]]></content:encoded>
      <category>Scripts</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Maintenance_Batch_Script_f9f9f60edd.webp" length="46377" type="image/webp" />
    </item>
    <item>
      <title>Why Microsoft Teams Is a Smart Alternative for File Management</title>
      <link>https://www.joshfridey.com/blog/data/why-microsoft-teams-is-a-smart-alternative-for-file-management</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/data/why-microsoft-teams-is-a-smart-alternative-for-file-management</guid>
      <pubDate>Thu, 11 Sep 2025 23:08:54 GMT</pubDate>
      <description>This might be a bit of a hot take, but it’s worth saying: while a well-maintained on-prem Windows file server—or even a NAS—can offer excellent stability and performance, cloud-based tools are quickly becoming the go-to for modern businesses.
Solutions like Microsoft SharePoint bring a lot to the table: remote access, collaboration tools, and seamless integration with the Microsoft 365 ecosystem. But that doesn’t mean they’re always easy to manage—especially without an experienced IT team.

Where Microsoft Teams Comes In
Over the years, I’ve seen many clients run into challenges managing folder structures and permissions within SharePoint. It’s a powerful tool, no doubt—but it’s not the most intuitive. Without proper guidance or support, it’s easy to end up with a cluttered mess of disconnected sites and folders that eventually get lost in the shuffle.
Microsoft Teams offers a more streamlined, user-friendly approach. It allows clients to manage their file structure in a more organized and accessible way. Teams Channels act like folders, and with built-in permission controls, it’s much easier to restrict access when needed. Plus, everything ties directly into other Microsoft tools like OneNote, Planner, Tasks, and Lists—making collaboration much more cohesive.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Microsoft_Teams_File_Share_d87e526e76_547a4cf50a.webp" alt="Microsoft Teams File Share" />
<p>This might be a bit of a hot take, but it’s worth saying: while a well-maintained on-prem Windows file server—or even a NAS—can offer excellent stability and performance, cloud-based tools are quickly becoming the go-to for modern businesses.</p><p>&nbsp;</p><p>Solutions like Microsoft SharePoint bring a lot to the table: remote access, collaboration tools, and seamless integration with the Microsoft 365 ecosystem. But that doesn’t mean they’re always easy to manage—especially without an experienced IT team.</p><p>&nbsp;</p><p>&nbsp;</p><h2><span style="font-size:14.0pt;"><span style="line-height:115%;"><strong>Where Microsoft Teams Comes In</strong></span></span></h2><p>Over the years, I’ve seen many clients run into challenges managing folder structures and permissions within SharePoint. It’s a powerful tool, no doubt—but it’s not the most intuitive. Without proper guidance or support, it’s easy to end up with a cluttered mess of disconnected sites and folders that eventually get lost in the shuffle.</p><p>&nbsp;</p><p>Microsoft Teams offers a more streamlined, user-friendly approach. It allows clients to manage their file structure in a more organized and accessible way. Teams Channels act like folders, and with built-in permission controls, it’s much easier to restrict access when needed. Plus, everything ties directly into other Microsoft tools like OneNote, Planner, Tasks, and Lists—making collaboration much more cohesive.</p><p>&nbsp;</p><p>&nbsp;</p><h2><span style="font-size:14.0pt;"><span style="line-height:115%;"><strong>How I Recommend Setting Up a Team for File Sharing</strong></span></span></h2><p>If you’re looking to move your file sharing into Microsoft Teams, here’s a setup process I often recommend:</p><p>&nbsp;</p><h3>1 Open Microsoft Teams</h3><p><span style="font-family:&quot;Aptos&quot;,sans-serif;font-size:12.0pt;"><span style="line-height:115%;">navigate to either the <strong>Teams</strong> or <strong>Chat</strong> section, depending on your setup.</span></span></p><p>&nbsp;</p><h3>2 Click Create a New Team.&nbsp;</h3><p><span style="font-family:&quot;Aptos&quot;,sans-serif;font-size:12.0pt;"><span style="line-height:115%;">I usually suggest setting it up as a <strong>Public</strong> team so that all staff members can join and access shared files without needing an invite.</span></span></p><p>&nbsp;</p><h3>3 Give the default channel a clear and relevant name</h3><p>something like “<strong>General</strong>”, “<strong>Shared Files</strong>”, or a department-specific name like “<strong>Accounting</strong>”.</p><p>&nbsp;</p><h3>4 Use additional Channels</h3><p>To create top-level folders within the team. Each channel will have its own files section, and you can configure permissions separately to control access based on roles or teams.</p>
<img src="https://api.joshfridey.com/uploads/Create_Team_bc02a466af_b966c2e5b8.png" alt="Create New Team" />
<img src="https://api.joshfridey.com/uploads/Create_Private_Channel_6c4ace6cce_4574b90b1d.png" alt="Create Private Channel" />
<p>By using Teams for file sharing, you’re giving your organization a cleaner, more manageable solution that supports collaboration—without the complexity that sometimes comes with SharePoint.</p>]]></content:encoded>
      <category>Data</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Microsoft_Teams_File_Share_d87e526e76_547a4cf50a.webp" length="57518" type="image/webp" />
    </item>
    <item>
      <title>Connect to Exchange Online for Microsoft 365 Using PowerShell</title>
      <link>https://www.joshfridey.com/blog/power-shell/connect-to-exchange-online-for-microsoft-365-using-power-shell</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/power-shell/connect-to-exchange-online-for-microsoft-365-using-power-shell</guid>
      <pubDate>Thu, 11 Sep 2025 23:05:20 GMT</pubDate>
      <description>If you&apos;re managing Microsoft 365 for Business, you may eventually need to perform tasks in Exchange Online that aren’t available through the standard web interface. To do this, you’ll need to connect via PowerShell, which gives you direct access to advanced configuration options through command-line commands.

When I was first learning this process, I had no problem finding documentation on the individual commands for Exchange—but I struggled to find clear instructions on how to connect to a client’s Microsoft 365 tenant using PowerShell. This guide breaks down that connection process step by step. In future posts, I’ll dive deeper into useful Exchange Online commands, and I’ll be linking back to this post so it’s easy to follow along.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Power_Shell_Exchange_Online_a4f5eada57.webp" alt="Power Shell Exchange Online" />
<p data-start="180" data-end="502">If you're managing <strong data-start="199" data-end="229">Microsoft 365 for Business</strong>, you may eventually need to perform tasks in <strong data-start="275" data-end="294">Exchange Online</strong> that aren’t available through the standard web interface. To do this, you’ll need to connect via <strong data-start="392" data-end="406">PowerShell</strong>, which gives you direct access to advanced configuration options through command-line commands.</p><p data-start="180" data-end="502">&nbsp;</p><p data-start="504" data-end="950">When I was first learning this process, I had no problem finding documentation on the individual commands for Exchange—but I struggled to find clear instructions on <strong data-start="669" data-end="696">how to connect</strong> to a client’s Microsoft 365 tenant using PowerShell. This guide breaks down that connection process step by step. In future posts, I’ll dive deeper into useful Exchange Online commands, and I’ll be linking back to this post so it’s easy to follow along.</p><p data-start="504" data-end="950">&nbsp;</p><p data-start="504" data-end="950">&nbsp;</p><h2>Connect to Exchange Online with PowerShell, step by step</h2><p data-start="504" data-end="950">&nbsp;</p><h3>1 Open PowerShell</h3><p data-start="504" data-end="950"><i><strong>Note:</strong> Be sure to run PowerShell as Administrator</i></p><p data-start="504" data-end="950">&nbsp;</p><p data-start="504" data-end="950">&nbsp;</p><h3>2 Set the Execution Policy</h3><p data-start="504" data-end="950"><i>Before installing modules, you'll want to ensure <strong>PowerShell </strong>allows trusted remote scripts to run:</i></p><pre><code class="language-plaintext">Set-ExecutionPolicy RemoteSigned</code></pre><p>&nbsp;</p><p>&nbsp;</p><h3>3 Install the Exchange Online Management Module</h3><p><i>This command installs the module that gives you access to Exchange Online cmdlets:</i></p><pre><code class="language-plaintext">Import-Module ExchangeOnlineManagement</code></pre><blockquote><p><strong>💡 If prompted, go ahead and accept installation of NuGet and trust the repository.</strong></p></blockquote><p>&nbsp;</p><p>&nbsp;</p><h3>4 Connect to Exchange Online</h3><p><i>Now you're ready to sign in using your Microsoft 365 admin credentials:</i></p><pre><code class="language-plaintext">Connect-ExchangeOnline -UserPrincipalName "admin-user@microsoftaccount-clientdomain.com"</code></pre><p>Once connected, you're ready to start issuing Exchange Online commands.</p>]]></content:encoded>
      <category>PowerShell</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Power_Shell_Exchange_Online_a4f5eada57.webp" length="30249" type="image/webp" />
    </item>
    <item>
      <title>Windows 11 24H2 Insecure Guest Logins</title>
      <link>https://www.joshfridey.com/blog/microsoft/windows-11-24-h2-insecure-guest-logins</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/microsoft/windows-11-24-h2-insecure-guest-logins</guid>
      <pubDate>Mon, 08 Sep 2025 02:06:44 GMT</pubDate>
      <description>Recently, while working with businesses of various sizes, I’ve encountered issues with password less file shares on systems running Windows 11 24H2. This problem has appeared on both Windows file shares and NAS devices that support SMB.

The Best Solution? Secure Your File Shares
Ideally, the best approach is to update your file share security to require usernames and passwords for access. Implementing proper authentication helps protect sensitive data and reduces security risks.

However, I know that not every environment is ready for that change right away. Whether you’re waiting to plan a security upgrade or dealing with clients reluctant to make changes, I get it. Sometimes, you just need a quick fix to keep things running smoothly.

In the next section, I’ll go over some temporary workarounds to help you bypass this issue while you work toward a long-term solution.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/Windows1124_H2_Guest_Access_a8dbf55357.webp" alt="Windows 11 24H2 Insecure Guest Access" />
<p>Recently, while working with businesses of various sizes, I’ve encountered issues with password less file shares on systems running Windows 11 24H2. This problem has appeared on both Windows file shares and NAS devices that support SMB.</p><p>&nbsp;</p><h2>The Best Solution?</h2><p>Secure Your File Shares Ideally, the best approach is to update your file share security to require usernames and passwords for access. Implementing proper authentication helps protect sensitive data and reduces security risks. However, I know that not every environment is ready for that change right away. Whether you’re waiting to plan a security upgrade or dealing with clients reluctant to make changes, I get it. Sometimes, you just need a quick fix to keep things running smoothly. In the next section, I’ll go over some temporary workarounds to help you bypass this issue while you work toward a long-term solution.</p><p>&nbsp;</p><h2>Temporary Workarounds</h2><p>If you need to access password less file shares on Windows 11 24H2, here are a few potential workarounds:</p><p>&nbsp;</p><h3>1 Enable Guest Access in Group Policy (Not Recommended for Security Reasons)&nbsp;</h3><p><i>While enabling guest access isn't ideal, it may help in situations where legacy devices or specific workflows require it.</i></p><p>&nbsp;</p><ol><li>Open Group Policy Editor (gpedit.msc).</li><li><p>Navigate to:</p><pre><code class="language-plaintext">Computer Configuration → Administrative Templates → Network → Lanman Workstation</code></pre></li><li>Locate and Enable the policy: "Enable insecure guest logons"</li><li>Restart your computer for the changes to take effect.</li></ol><p>&nbsp;</p><p><strong data-start="1531" data-end="1542">Warning</strong>: This reduces security and should only be used as a temporary fix.</p><p>&nbsp;</p><h3>2 Adjust Windows Registry (If Group Policy Is Unavailable)</h3><p><i>If you don’t have access to Group Policy on certain Windows editions, you can modify the registry:</i></p><p>&nbsp;</p><ol><li>Open <strong>Registry Editor</strong> (regedit).</li><li><p>Navigate to:</p><pre><code class="language-plaintext">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters</code></pre><p>&nbsp;</p></li><li>Look for a DWORD entry named <strong data-start="1982" data-end="2010">"AllowInsecureGuestAuth"</strong> (or create it if missing).</li><li>Set its value to <strong>1</strong>.</li><li>Restart your computer.</li></ol><p>&nbsp;</p><h3>3 Check SMB Protocol Version</h3><p><i>If you're connecting to a NAS or older file server, it may not support newer SMB versions. To check and enable older SMB versions:</i></p><p>&nbsp;</p><ol><li><p>Open PowerShell (Admin) and run:</p><pre><code class="language-plaintext">Get-SmbServerConfiguration | Select EnableSMB1Protocol, EnableSMB2Protocol</code></pre></li><li><p>If SMB1 is disabled but required, enable it temporarily:</p><pre><code class="language-plaintext">Set-SmbServerConfiguration -EnableSMB1Protocol $true -Force</code></pre></li><li>Restart your computer.</li></ol><p>&nbsp;</p><p><strong>Security Note:</strong> SMB1 is deprecated due to security vulnerabilities. If possible, update your NAS or file server to support SMB2/SMB3 instead.</p>]]></content:encoded>
      <category>Microsoft</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/Windows1124_H2_Guest_Access_a8dbf55357.webp" length="69356" type="image/webp" />
    </item>
    <item>
      <title>Remote Desktop Access to Microsoft Entra ID Joined Device</title>
      <link>https://www.joshfridey.com/blog/microsoft/remote-desktop-access-to-microsoft-entra-id-joined-device</link>
      <guid isPermaLink="true">https://www.joshfridey.com/blog/microsoft/remote-desktop-access-to-microsoft-entra-id-joined-device</guid>
      <pubDate>Fri, 05 Sep 2025 05:54:07 GMT</pubDate>
      <description>Remote Desktop Protocol (RDP) is an essential tool for IT professionals and end users alike, allowing seamless remote management and access to multiple devices. However, with the transition to Microsoft Entra ID (formerly Azure Active Directory), the traditional process of accessing remote devices has changed slightly, introducing a few additional steps.

In this guide, I’ll share my preferred method for accessing Microsoft Entra ID-joined devices via RDP. Keep in mind that there are multiple ways to accomplish this, some potentially easier. However, this method works effectively for both onsite and offsite configurations.</description>
      <content:encoded><![CDATA[<img src="https://api.joshfridey.com/uploads/RDP_Entra_ID_a9577deaf4_86e9f418ce.webp" alt="Remote Desktop on Microsoft Entra ID joined Device" />
<p data-start="173" data-end="535">Remote Desktop Protocol (RDP) is an essential tool for IT professionals and end users alike, allowing seamless remote management and access to multiple devices. However, with the transition to <strong data-start="366" data-end="388">Microsoft Entra ID</strong> (formerly Azure Active Directory), the traditional process of accessing remote devices has changed slightly, introducing a few additional steps.</p><p data-start="173" data-end="535">In this guide, I’ll share my <strong data-start="566" data-end="586">preferred method</strong> for accessing Microsoft Entra ID-joined devices via RDP. Keep in mind that there are <strong data-start="672" data-end="689">multiple ways</strong> to accomplish this, some potentially easier. However, this method works effectively for both <strong data-start="783" data-end="820">onsite and offsite configurations</strong>.</p><p>&nbsp;</p><h2>Prerequisites</h2><p><i>To ensure a successful remote connection, you’ll need the following:</i></p><ul style="list-style-type:square;"><li>VPN Connection if wanting off-site access.</li></ul><p>&nbsp;</p><h3><strong data-start="987" data-end="1020">Configuring the Remote Device</strong> (The device you want to access)</h3><ol><li data-start="1056" data-end="1197"><p data-start="1059" data-end="1094"><strong data-start="1059" data-end="1092">Set a Static Local IP Address</strong></p><ol><li data-start="1098" data-end="1197">This helps maintain a consistent connection and prevents address changes from affecting access.</li></ol></li><li data-start="1199" data-end="1327"><p data-start="1202" data-end="1244"><strong data-start="1202" data-end="1242">Enable Remote Desktop Protocol (RDP)</strong></p><ol><li data-start="1248" data-end="1327">Go to <strong data-start="1256" data-end="1294">Settings &gt; System &gt; Remote Desktop</strong> and enable <strong data-start="1306" data-end="1324">Remote Desktop</strong>.</li></ol></li><li data-start="1329" data-end="1561"><p data-start="1332" data-end="1380"><strong data-start="96" data-end="156">Network Level Authentication (NLA), recommended enabled.</strong> Keep this checked for security. If you are having trouble connecting and suspect NLA is the blocker, you can temporarily disable it to test.</p><ol><li data-start="1384" data-end="1439">Open <strong data-start="307" data-end="328">System Properties</strong> › <strong data-start="331" data-end="341">Remote</strong>.</li><li data-start="1443" data-end="1561">Under <strong data-start="353" data-end="371">Remote Desktop</strong>, uncheck <strong data-start="381" data-end="485">Allow connections only from computers running Remote Desktop with Network Level Authentication (NLA)</strong>.</li><li data-start="1443" data-end="1561">Try the connection again. If it works, address the root cause, then re-enable NLA.</li></ol></li></ol><p>&nbsp;</p><h3><strong data-start="1571" data-end="1603">Configuring the Local Device</strong> (The device you’re accessing from)</h3><ol><li data-start="1642" data-end="1763"><p data-start="1645" data-end="1686"><strong data-start="1645" data-end="1684">(For Offsite Access) Connect to VPN</strong></p><ol><li data-start="1690" data-end="1763">Ensure you’re connected to your organization’s VPN before proceeding.</li></ol></li><li data-start="1765" data-end="2113"><p data-start="1768" data-end="1794"><strong data-start="1768" data-end="1792">Modify the Host File</strong></p><ol><li data-start="1798" data-end="1835">Open <strong data-start="1805" data-end="1833">Notepad as Administrator</strong></li><li data-start="1839" data-end="1896">Navigate to <strong data-start="1853" data-end="1894">C:\Windows\System32\drivers\etc\hosts</strong></li><li data-start="1900" data-end="1995">Add the <strong data-start="1910" data-end="1931">static IP address</strong> and <strong data-start="1936" data-end="1959">exact computer name</strong> of the remote device to the file.</li><li data-start="1999" data-end="2113"><em data-start="2001" data-end="2008"><i>Note:</i></em> The <strong data-start="2013" data-end="2044">computer name must be exact</strong>, as Microsoft Entra ID requires an exact match for authentication.</li></ol></li><li data-start="2115" data-end="2356"><p data-start="2118" data-end="2161"><strong data-start="2118" data-end="2159">Launch the Remote Desktop Application</strong></p><ol><li data-start="2165" data-end="2251">Open the <strong data-start="2176" data-end="2198">Remote Desktop app</strong> and enter the <strong data-start="2213" data-end="2226">host name</strong> of your remote device.</li><li data-start="2255" data-end="2356"><em data-start="2257" data-end="2269"><i>Important:</i></em> <strong data-start="2270" data-end="2300">IP addresses will not work</strong> with this method; you must use the <strong data-start="2336" data-end="2353">computer name</strong>.</li></ol></li><li data-start="2358" data-end="2606"><p data-start="2361" data-end="2390"><strong data-start="2361" data-end="2388">Enter Login Credentials</strong></p><ol><li data-start="2394" data-end="2450">Username: Enter your <strong data-start="2417" data-end="2448">Microsoft 365 email address</strong></li><li data-start="2454" data-end="2524">Navigate to the <strong data-start="2472" data-end="2488">Advanced tab</strong> &gt; <strong data-start="2491" data-end="2522">User Authentication section</strong></li><li data-start="2528" data-end="2606">Check the box for <strong data-start="2548" data-end="2604">“Use a web account to sign into the remote computer”</strong></li></ol></li></ol><p>&nbsp;</p><h2>Conclusion</h2><p>By following these steps, you can successfully remote into a Microsoft Entra ID joined device using RDP. This method ensures compatibility with both onsite and offsite (with VPN) setups, allowing seamless access across different environments.</p>]]></content:encoded>
      <category>Microsoft</category>
      <author>Josh Fridey</author>
      <enclosure url="https://api.joshfridey.com/uploads/RDP_Entra_ID_a9577deaf4_86e9f418ce.webp" length="45251" type="image/webp" />
    </item>
  </channel>
</rss>