<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Powershell on Nicola Suter</title><link>https://tech.nicolonsky.ch/tags/powershell/</link><description>Recent content in Powershell on Nicola Suter</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>© 2026 Nicola Suter</copyright><lastBuildDate>Wed, 22 Feb 2023 00:00:00 +0000</lastBuildDate><atom:link href="https://tech.nicolonsky.ch/tags/powershell/rss.xml" rel="self" type="application/rss+xml"/><item><title>Optimising Microsoft Graph PowerShell scripts</title><link>https://tech.nicolonsky.ch/optimising-microsoft-graph-powershell-scripts/</link><pubDate>Wed, 22 Feb 2023 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/optimising-microsoft-graph-powershell-scripts/</guid><description>&lt;p&gt;We all have probably been there and developed a PowerShell script that took some fair amount of time until the execution completed, weren’t we? Of course one could argue and say that as long a script ‘works’ it is good enough but depending on the use case and environment a PowerShell script that runs 30 to 60 minutes exceeds the patience of most (IT) people and can also lead to increased costs. But what makes those kinds of scripts that awfully slow and can’t we just tweak them to run faster?&lt;/p&gt;
&lt;p&gt;The following examples and script will be related to PowerShell and the Microsoft Graph API but the mentioned approaches can be adapted for any kind of RESTful API and scripting language. For all interactions with the Graph API I use the &lt;a href="https://github.com/microsoftgraph/msgraph-sdk-powershell" target="_blank" rel="noreferrer"&gt;Microsoft Graph PowerShell SDK&lt;/a&gt; which is available as a PowerShell module from the PowerShell gallery.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Reasons why your script is slow
 &lt;div id="reasons-why-your-script-isslow" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#reasons-why-your-script-isslow" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;I would say I have already read and (tried to) understand hundreds of PowerShell scripts out there and I often notice the following flaws which lead to poor performance:&lt;/p&gt;

&lt;h4 class="relative group"&gt;&lt;strong&gt;Slow algorithms and wrong data structures&lt;/strong&gt;
 &lt;div id="slow-algorithms-and-wrong-data-structures" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#slow-algorithms-and-wrong-data-structures" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;p&gt;Slow algorithms are extricably linked to the understanding of data structures when it comes to PowerShell scripting. Here the top two cases I often observe:&lt;/p&gt;</description></item><item><title>The easiest way to work with the Microsoft Graph PowerShell SDK</title><link>https://tech.nicolonsky.ch/graph-powershell-sdk-kickstart/</link><pubDate>Fri, 09 Sep 2022 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/graph-powershell-sdk-kickstart/</guid><description>&lt;p&gt;When you are new to RESTful APIs and want to start with Microsoft Graph to automate tasks in your Endpoint Manager tenant all the stuff about app registrations, access tokens, pagination and request headers can be quite confusing. In this post I want to show you a quick tip to kickstart your Microsoft Graph API experience.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Requirements
 &lt;div id="requirements" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#requirements" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Cloud admin account with Intune Administrator role assigned&lt;/li&gt;
&lt;li&gt;Ability to install Modules from the PowerShell gallery&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class="relative group"&gt;JWT
 &lt;div id="jwt" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#jwt" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Just because you can&amp;rsquo;t see it&amp;hellip; doesn&amp;rsquo;t mean it isn&amp;rsquo;t there: Due to the naturality of OAuth 2.0 and OpenID connect (these are the protocols involved for authorization and authentication in a cloud environment) we can capture short lived access tokens, also called Json Web Tokens (JWTs) directly from a browser. Tokens are usually valid between 50 and 60 minutes - just what we need to get some hands on with Microsoft Graph API and MEM automation.&lt;/p&gt;
&lt;p&gt;The cool thing is actually that we don&amp;rsquo;t need any kind of app registration or additional permissions which can be quite some blocker in certain restricted environments (or staff unfamiliar with those technologies 😉).&lt;/p&gt;</description></item><item><title>Intune app protection policy report</title><link>https://tech.nicolonsky.ch/app-protection-policy-report/</link><pubDate>Mon, 13 Dec 2021 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/app-protection-policy-report/</guid><description>&lt;p&gt;App protection (also called MAM) policies have been around for a couple of years within MEM and I already used them in various projects to protect company data on unmanaged iOS and Android devices. One of the drawbacks with this approach is that we do not have full visibility about the usage and I tried to shed some light about this with a PowerShel reporting script that pulls data from the Microsoft Graph API.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Information visible within the portal
 &lt;div id="information-visible-within-the-portal" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#information-visible-within-the-portal" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;In the MEM portal we can find report data about the number of users that have checked-in to any MAM policy grouped by the respective app.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2021/12/Intune_MAM_Report_Portal_View.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Intune MAM insights"
 src="https://tech.nicolonsky.ch/content/images/2021/12/Intune_MAM_Report_Portal_View.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If we want to perform a wipe we will also be able to see the devices a user has registered:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2021/12/Intune_MAM_Report_Wipe_Request.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="MAM wipe"
 src="https://tech.nicolonsky.ch/content/images/2021/12/Intune_MAM_Report_Wipe_Request.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course I was curious which additional data is available on the Microsoft Graph API and found the following resource storing app protection policy check in details: &lt;code&gt;/users/{ID}/managedAppRegistrations&lt;/code&gt;.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Script
 &lt;div id="script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The script uses the &lt;code&gt;Intune PowerShell SDK&lt;/code&gt; (could easily be ported to MSAL.PS because I wrote it already a couple of months ago) to enumerate all internal users within the tenant and will check the above mentioned &lt;code&gt;managedAppRegistrations&lt;/code&gt; resource.
At the end you are presented a flattened CSV report containig the following details:&lt;/p&gt;</description></item><item><title>Automatically sign your PowerShell scripts with GitHub actions</title><link>https://tech.nicolonsky.ch/github-actions-powershell-signing/</link><pubDate>Fri, 09 Jul 2021 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/github-actions-powershell-signing/</guid><description>&lt;p&gt;Based on one of my older posts about &lt;a href="https://tech.nicolonsky.ch/sign-powershell-az-devops" &gt;PowerShell script signing with Azure DevOps&lt;/a&gt; I recently implemented a PowerShell script signing workflow with GitHub actions I wanted to share with the community.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Prerequisites
 &lt;div id="prerequisites" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#prerequisites" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;For this post the following prerequisites are required:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Code signing certificate in PFX format&lt;/li&gt;
&lt;li&gt;GitHub account&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 class="relative group"&gt;Add variables to GitHub actions
 &lt;div id="add-variables-to-github-actions" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-variables-to-github-actions" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Because GitHub variables can only be of string content we need to get the contents of the pfx file as base64 encoded string:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$pfxCertFilePath = &amp;#34;~\Downloads\CodeSigningCertificate.pfx&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$pfxContent = Get-Content $pfxCertFilePath -Encoding Byte
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[System.Convert]::ToBase64String($pfxContent) | Set-Clipboard&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;GitHub action variables
 &lt;div id="github-action-variables" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-action-variables" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Add two variables as actions secrets:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BASE64_PFX&lt;/code&gt;: Base 64 encoded string of the PFX (automatically copied into your clipboard with the above commands)
&lt;code&gt;PFX_PASSWORD&lt;/code&gt;: Password for the private key of the pfx file&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/32899754/120798485-b6791e80-c53d-11eb-851f-dcb27a08b567.png" target="_blank" rel="noreferrer"&gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt=""
 src="https://user-images.githubusercontent.com/32899754/120798485-b6791e80-c53d-11eb-851f-dcb27a08b567.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3 class="relative group"&gt;GitHub action workflow
 &lt;div id="github-action-workflow" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#github-action-workflow" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Place the following workflow file within your git repository:
&lt;code&gt;.github/workflows/SignPowerShell.yaml&lt;/code&gt; whereas the name of the YAML file can be freely chosen.&lt;/p&gt;</description></item><item><title>Securely sending emails from PowerShell scripts with modern authentication enforced</title><link>https://tech.nicolonsky.ch/sending-emails-with-modern-auth/</link><pubDate>Fri, 19 Mar 2021 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/sending-emails-with-modern-auth/</guid><description>&lt;p&gt;The &lt;code&gt;Send-MailMessage&lt;/code&gt; cmdlet has been around for a couple of years and is mostly used to send email messages from PowerShell. But with the deprecation and security flaws of legacy authentication it&amp;rsquo;s time for a better option which actually supports modern authentication. For this purpose we can use the Microsoft Graph API and the Microsoft Graph PowerShell SDK. The best thing is that this solution works without any service account and does not need any exclusions from conditional access.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Microsoft Graph resource
 &lt;div id="microsoft-graph-resource" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#microsoft-graph-resource" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;To send a mail we simply specify the user account from which we want to send the email:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;POST: https://graph.microsoft.com/v1.0/users/sean.connery@dev.nicolonsky.ch/sendMail&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 class="relative group"&gt;Create an app registration
 &lt;div id="create-an-app-registration" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#create-an-app-registration" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Simply create a new app registration with the &lt;code&gt;Mail.Send&lt;/code&gt; permissions and use a certificate for the authentication.&lt;/p&gt;
&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="New App registration"
 src="https://tech.nicolonsky.ch/content/images/2021/03/App-Permissions.png"
 &gt;&lt;/figure&gt;
&lt;p&gt;We need to take additional steps to limit the permissions of the app registration. Otherwise the app can send mails on behalf of &lt;strong&gt;any user&lt;/strong&gt; in your tenant. To limit the permissions we leverage &lt;a href="https://docs.microsoft.com/en-us/powershell/module/exchange/new-applicationaccesspolicy?view=exchange-ps" target="_blank" rel="noreferrer"&gt;exchange application access policies&lt;/a&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Connect to Exchange Online with the ExchangeOnlineManagement PowerShell module
&lt;code&gt;Connect-ExchangeOnline&lt;/code&gt;&lt;/p&gt;</description></item><item><title>Dealing with Intune OMA-URI encoding and applocker rules</title><link>https://tech.nicolonsky.ch/intune-oma-uri-encoding/</link><pubDate>Tue, 16 Feb 2021 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/intune-oma-uri-encoding/</guid><description>&lt;p&gt;While fine-tuning and adjusting applocker policies for co-managed Windows 10 clients I got really annoyed by special characters commonly used in the German/Swiss language. The Intune portal seemed to use different encoding and didn&amp;rsquo;t allow me to just copy/paste the currently deployed policy and extend it with a new rule. I needed to request the original file that was uploaded to the tenant in order to adjust the rule. Instead of just accepting this I decided that it is time for an easier approach which I will share with you.&lt;/p&gt;

&lt;h2 class="relative group"&gt;The actual issue
 &lt;div id="the-actual-issue" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-actual-issue" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;After uploading the XML with the applocker policies (in my case EXE rules), special characters like &amp;lsquo;ö&amp;rsquo; or &amp;lsquo;ü&amp;rsquo; have a weird encoding displayed in the portal. The next person that wants to edit the policy needs to take care to fix the unrecognized characters otherwise the publisher rule won&amp;rsquo;t work anymore.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2021/02/Applocker-OMA-URI.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="OMA-URI"
 src="https://tech.nicolonsky.ch/content/images/2021/02/Applocker-OMA-URI.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Top: Portal view of the special characters, bottom: original file.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2021/02/Encoding-Issue.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Encoding issue"
 src="https://tech.nicolonsky.ch/content/images/2021/02/Encoding-Issue.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 class="relative group"&gt;Fixing things
 &lt;div id="fixing-things" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#fixing-things" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;

&lt;h3 class="relative group"&gt;Save file with UTF-8 encoding
 &lt;div id="save-file-with-utf-8-encoding" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#save-file-with-utf-8-encoding" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;First, make sure that you saved an uploaded the file with UTF-8 encoding as this introduces support for most character sets. You can do this by selecting the encoding box in the right bottom of Visual Studio Code:&lt;/p&gt;</description></item><item><title>Microsoft Graph Access Token Acquisition with PowerShell explained in depth</title><link>https://tech.nicolonsky.ch/explaining-microsoft-graph-access-token-acquisition/</link><pubDate>Mon, 04 Jan 2021 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/explaining-microsoft-graph-access-token-acquisition/</guid><description>&lt;p&gt;When working with the Microsoft Graph API or introducing the API to colleagues I often get asked about the steps required to obtain an access token for the API with PowerShell. Out in the wild, I&amp;rsquo;ve spotted many different ways and lots of implementations still relying on the ADAL (Active Directory Authentication Library) despite the fact that this client library is superseded by MSAL (Microsoft Authentication Library). So let&amp;rsquo;s talk about acquiring access token &amp;ldquo;in stile&amp;rdquo; with the most simple method available.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Why do we need an access token?
 &lt;div id="why-do-we-need-an-access-token" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-do-we-need-an-access-token" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;When talking about the Microsoft Graph API an access token fulfills two roles, first: prove authentication (proof of identity) second prove authorization (permissions). Each request needs to submit a request-header that contains the access token.&lt;/p&gt;
&lt;p&gt;For an API it&amp;rsquo;s crucial to validate the authentication and authorization for every request. Otherwise, requests could be made to resources the actor has no access to.&lt;/p&gt;

&lt;h2 class="relative group"&gt;What&amp;rsquo;s inside the access token
 &lt;div id="whats-inside-the-access-token" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#whats-inside-the-access-token" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;You did probably stumble over the terms &amp;ldquo;bearer authentication&amp;rdquo; or &amp;ldquo;bearer token&amp;rdquo; these describe a mechanism within the OAuth 2.0 Authorization framework to authenticate requests with access tokens. Tokens are issued by the authorization server (Azure AD) and contain a server-generated string in the format of a JSON Web Token (JWT) with the following information (the list is not exhaustive and truncated to only contain the most interesting parts):&lt;/p&gt;</description></item><item><title>Build an Azure DevOps pipeline to automatically sign your PowerShell scripts</title><link>https://tech.nicolonsky.ch/sign-powershell-az-devops/</link><pubDate>Thu, 01 Oct 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/sign-powershell-az-devops/</guid><description>&lt;p&gt;Too lazy to sign your PowerShell scripts? Yes of course it provides security benefits but performing the steps manually can be easily forgotten and re-signing needs to happen after every script change. Because I like CI/CD topics and have not found a solution on the internet I decided to build a solution based on Azure capabilities. Furthermore, I wanted a solution which does not require to hand out the code signing certificate to the respective script author which can be useful if you have a bunch of people writing PowerShell scripts.&lt;/p&gt;
&lt;p&gt;From a personal perspective, I would also recommend signing scripts you hand over to customers to ensure the integrity of the scripts because as soon as the script gets changed the signature is invalid.&lt;/p&gt;
&lt;p&gt;You can find more general recommendations about script signing in the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_signing" target="_blank" rel="noreferrer"&gt;PowerShell docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Solution overview
 &lt;div id="solution-overview" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#solution-overview" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The key vault will store the code signing certificate with an access policy that allows access from the Azure DevOps pipeline.&lt;/p&gt;
&lt;p&gt;The pipeline consists of the following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Import code signing certificate
&lt;ul&gt;
&lt;li&gt;The certificate is supplied as secret in a variable group which is linked to the key vault&lt;/li&gt;
&lt;li&gt;Access to the key vault is granted with a service connection (service principal)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Sign PowerShell scripts which contain a &amp;ldquo;magic token&amp;rdquo;
&lt;ul&gt;
&lt;li&gt;All &lt;code&gt;*.ps1&lt;/code&gt; within the attached repository will be enumerated&lt;/li&gt;
&lt;li&gt;To control which scripts will be signed only those with the &amp;ldquo;magic token&amp;rdquo; get processed&lt;/li&gt;
&lt;li&gt;The &amp;ldquo;magic token&amp;rdquo; gets removed before signing the script&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Publish signed PowerShell scripts as pipeline artifacts
&lt;ul&gt;
&lt;li&gt;To make them available for download&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/10/PoshSigning_DevOps.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Solution overview"
 src="https://tech.nicolonsky.ch/content/images/2020/10/PoshSigning_DevOps.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Ensuring regular Defender Quick scans with Microsoft Endpoint Manager proactive remediations</title><link>https://tech.nicolonsky.ch/defender-scan-endpoint-analytics/</link><pubDate>Mon, 28 Sep 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/defender-scan-endpoint-analytics/</guid><description>&lt;p&gt;While looking into the new Microsoft Defender Antivirus report available in MEM (Intune) I discovered some machines which did not report any recent Defender antimalware scans, despite configured via configuration profile. Of course, AV scans are kinda old-fashioned against rapidly evolving threats but a regular quick scan won&amp;rsquo;t hurt anyone. Instead of having a look at every single machine affected, I decided to try out the new proactive remediations feature which went globally available last week and let endpoint analytics do the detection and remediation work for me. As a reference, I used the &lt;a href="https://docs.microsoft.com/en-us/mem/analytics/proactive-remediations" target="_blank" rel="noreferrer"&gt;Tutorial: Proactive remediations&lt;/a&gt; from Microsoft which covers the process quite well.&lt;/p&gt;

&lt;h2 class="relative group"&gt;PowerShell scrips
 &lt;div id="powershell-scrips" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#powershell-scrips" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;For Endpoint analytics / Proactive remediations we need two PowerShell scripts. The first script is used as a detection script and determines whether remediation is necessary based on the exit code. Exit code &lt;code&gt;0&lt;/code&gt; indicates a healthy status and exit code &lt;code&gt;1&lt;/code&gt; indicates remediation necessary. Remediation occurs with a second PowerShell script.&lt;/p&gt;
&lt;p&gt;To detect the most recent Defender scan I used the Windows Eventlog. Event ID&amp;rsquo;s are documented &lt;a href="https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-antivirus/troubleshoot-microsoft-defender-antivirus#to-view-a-microsoft-defender-antivirus-event" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Detection script
 &lt;div id="detection-script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#detection-script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;script src="https://gist.github.com/nicolonsky/ac80d1cc89c9b42c2ed8099592e5c2bc.js"&gt;&lt;/script&gt;

&lt;h3 class="relative group"&gt;Remediation script
 &lt;div id="remediation-script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#remediation-script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;The remediation script is just about a one-liner to trigger a quick scan. You can extend this based on your requirements and respective to your Intune settings. E.g. triggering a signature update for a scan or adding additional steps.&lt;/p&gt;</description></item><item><title>Discover the Microsoft Graph API with the Microsoft Endpoint Manager Portal</title><link>https://tech.nicolonsky.ch/discover-mem-graph-urls/</link><pubDate>Tue, 08 Sep 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/discover-mem-graph-urls/</guid><description>&lt;p&gt;You always wanted to automate a specific action within Intune / the Microsoft Endpoint Manager Portal (MEM) but were afraid of the complexity? The Microsoft Graph API docs deliver you more questions instead of answers? Automating tasks within the MEM portal could be very easy, couldn&amp;rsquo;t it? I promise it will be much simpler with this magician trick.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Microsoft Endpoint Manager Portal
 &lt;div id="microsoft-endpoint-manager-portal" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#microsoft-endpoint-manager-portal" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The MEM Portal UI relies on the Microsoft Graph API. This means that the UI where you create new settings and policies and the Intune backend are encapsulated with different layers. Communication between the UI and the backend happens with the Microsoft Graph API. With the developer tools we can trace network traffic and discover the request URLs and request body payload which are required to interact with the API.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Architecture"
 src="https://tech.nicolonsky.ch/content/images/2020/09/MEM-Portal-Automation.svg"
 &gt;&lt;/figure&gt;
{: .align-center}&lt;/p&gt;

&lt;h2 class="relative group"&gt;Example about how to capture URLs and build a PowerShell script
 &lt;div id="example-about-how-to-capture-urls-and-build-a-powershell-script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#example-about-how-to-capture-urls-and-build-a-powershell-script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/k6ZCSJVXaOI?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;p&gt;Original request body:&lt;/p&gt;</description></item><item><title>Access has been blocked by Conditional Access policies when using device code flow</title><link>https://tech.nicolonsky.ch/device-code-auth-ca/</link><pubDate>Thu, 03 Sep 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/device-code-auth-ca/</guid><description>&lt;p&gt;When using device code authentication for PowerShell modules with conditional access you might receive prompts like: &amp;ldquo;&lt;code&gt;Access has been blocked by Conditional Access policies. The access policy does not allow token issuance&lt;/code&gt;&amp;rdquo; or &amp;ldquo;&lt;code&gt;AADSTS50097: Device authentication is required&lt;/code&gt;&amp;rdquo;. But what&amp;rsquo;s the reason for this error and is there a solution available?&lt;/p&gt;

&lt;h2 class="relative group"&gt;Examples from the field
 &lt;div id="examples-from-the-field" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#examples-from-the-field" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Device code flow is quite a convenient way to sign-in for an app within the web browser - at least if it works. If not you have to consider other options and that&amp;rsquo;s probably the reason why you&amp;rsquo;re reading this blog article.&lt;/p&gt;
&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Device Code Auth"
 src="https://tech.nicolonsky.ch/content/images/2020/09/device-code-auth-conditional-access.png"
 &gt;&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;Az PowerShell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Running the Az PowerShell module on PowerShell 7 uses device code flow to authenticate against your Azure tenant and might fail:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Connect-AzAccount: AADSTS50097: Device authentication is required.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timestamp: 2020-08-17 13:36:31Z: Response status code does not indicate success: 401 (Unauthorized).&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The sign-in to Azure is tied to the &amp;ldquo;Microsoft Azure Management&amp;rdquo; app that you can select within Conditional Access.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microsoft Graph PowerShell&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The same applies for the new Microsoft.Graph PowerShell modules - but here we receive a more detailed error message:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Connect-Graph: AADSTS53003: Access has been blocked by Conditional Access policies. The access policy does not allow token issuance.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Timestamp: 2020-08-17 13:37:12Z&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The sign-in to the new Microsoft Graph Modules is tied to the &amp;ldquo;Microsoft Graph PowerShell (Preview)&amp;rdquo; app and some more apps I couldn&amp;rsquo;t determine.&lt;/p&gt;</description></item><item><title>Bulk create Intune mobile app deployment groups and assignments</title><link>https://tech.nicolonsky.ch/intune-mobile-app-assignment-bulk/</link><pubDate>Wed, 19 Aug 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/intune-mobile-app-assignment-bulk/</guid><description>&lt;p&gt;Creating assignments and software deployment groups for Intune mobile apps is quite a repetitive and manual task. Because of that, I want to share a PowerShell script with you which allows you to automatically create software deployment groups in Azure AD and the assignments for various intents.&lt;/p&gt;
&lt;p&gt;The script allows you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create Azure AD groups (install uninstall purpose)
&lt;ul&gt;
&lt;li&gt;Pick existing groups based on displayName&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Assign Intune mobile apps (tested for Win32 and MSI LOB apps)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/08/intune-mobile-app-assignment-bulk.gif" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Script in Action"
 src="https://tech.nicolonsky.ch/content/images/2020/08/intune-mobile-app-assignment-bulk.gif"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nicolonsky/Techblog/tree/master/IntuneMobileAppAssignment" target="_blank" rel="noreferrer"&gt;You can find the script on my techblog GitHub repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Because of the &lt;a href="https://github.com/nicolonsky/Techblog/blob/master/IntuneMobileAppAssignment/New-IntuneAppAssignment.ps1#L220" target="_blank" rel="noreferrer"&gt;configurable group prefixes&lt;/a&gt; the script helps you to keep your Intune environment clean and implement a standard app assignment configuration.&lt;/p&gt;
&lt;p&gt;The script uses the Microsoft Graph API and the following resources&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://graph.microsoft.com/beta/deviceAppmanagement/mobileApps&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://graph.microsoft.com/beta/deviceAppmanagement/mobileApps/{AppID}/Assignments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://graph.microsoft.com/beta/groups&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It uses the preregistered app &amp;ldquo;Microsoft Intune PowerShell&amp;rdquo; which exists by default in all tenants. If you want to run the Script with PowerShell 7 you need to create an adjust the MSAL token section with the &lt;code&gt;-DeviceCode&lt;/code&gt; parameter.&lt;/p&gt;
&lt;p&gt;You can bulk select the apps you want to create the assignment and AAD deployment groups:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/08/intune-mobile-app-assignment-bulk-select.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="App assignment"
 src="https://tech.nicolonsky.ch/content/images/2020/08/intune-mobile-app-assignment-bulk-select.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hope this saves you some time.&lt;/p&gt;</description></item><item><title>Add PowerShell modules to Azure functions</title><link>https://tech.nicolonsky.ch/azure-functions-powershell-modules/</link><pubDate>Mon, 17 Aug 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/azure-functions-powershell-modules/</guid><description>&lt;p&gt;Azure functions for PowerShell natively ship without additional cmdlets or PowerShell modules. In this post, I will show you how to add both public modules from the PowerShell gallery with automatic dependency management and custom modules.&lt;/p&gt;
&lt;p&gt;For both options, we use the Kudu tools to adjust the configuration of our function app. You can launch them from the &lt;em&gt;&amp;ldquo;Advanced Tools&amp;rdquo;&lt;/em&gt; section of your function app:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/08/add-powershell-modules-to-azure-functions-kudu.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Kudu tools"
 src="https://tech.nicolonsky.ch/content/images/2020/08/add-powershell-modules-to-azure-functions-kudu.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Afterwards, launch the PowerShell debug console and navigate to the &lt;code&gt;wwwroot&lt;/code&gt; folder of your app:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/08/add-powershell-modules-to-azure-functions-kudu.gif" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Kudu debug console"
 src="https://tech.nicolonsky.ch/content/images/2020/08/add-powershell-modules-to-azure-functions-kudu.gif"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 class="relative group"&gt;Option 1: Automatic dependency management
 &lt;div id="option-1-automatic-dependency-management" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#option-1-automatic-dependency-management" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Installing modules which require license acceptance (e.g. the MSAL.PS module) currently cannot be installed with automatic dependency management.
You can track the issue status &lt;a href="https://github.com/Azure/azure-functions-powershell-worker/issues/417" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt; and &lt;a href="https://github.com/MicrosoftDocs/azure-docs/issues/60701" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;.
{: .notice&amp;ndash;warning}&lt;/p&gt;
&lt;p&gt;Azure function apps running PowerShell come with a nice feature called managed dependencies. You can specify the modules you want to import from the PowerShell Gallery and the function app host will automatically process the dependencies.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;requirements.psd1&lt;/code&gt; add the module details:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# This file enables modules to be automatically managed by the Functions service.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# See https://aka.ms/functionsmanageddependency for additional information.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c"&gt;# For latest supported version, go to &amp;#39;https://www.powershellgallery.com/packages/Az&amp;#39;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Az&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;4.*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;Microsoft.Graph.Authentication&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;0.*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;You can either specify an exact version available from the PowerShell Gallery or specify the major version with a wildcard. With the wildcard option it will use the latest version available.&lt;/p&gt;</description></item><item><title>Playing around with the Office 365 Service Communications API</title><link>https://tech.nicolonsky.ch/microsoft365-service-status/</link><pubDate>Mon, 10 Aug 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/microsoft365-service-status/</guid><description>&lt;p&gt;The Office 365 Service Communications API provides information about Microsoft 365 service status for your tenant including service messages. I built a little PowerShell module to access the API with PowerShell cmdlets. In this post I want to show you some examples which help you to use the API.&lt;/p&gt;

&lt;h2 class="relative group"&gt;PowerShell Module
 &lt;div id="powershell-module" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#powershell-module" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;I built a PowerShell module to access Microsoft 365 service status details natively with PowerShell. The PowerShell module and documentation is available on the &lt;a href="https://www.powershellgallery.com/packages/Microsoft365ServiceStatus" target="_blank" rel="noreferrer"&gt;PowerShell Gallery&lt;/a&gt; and on &lt;a href="https://github.com/nicolonsky/Microsoft365ServiceStatus" target="_blank" rel="noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Before using the module an app registration is required. Setup instructions are also provided on GitHub.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/32899754/89341042-fe6eb100-d6a0-11ea-80a7-e7357f9717c5.gif" target="_blank" rel="noreferrer"&gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Module"
 src="https://user-images.githubusercontent.com/32899754/89341042-fe6eb100-d6a0-11ea-80a7-e7357f9717c5.gif"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h3 class="relative group"&gt;CI/CD
 &lt;div id="cicd" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#cicd" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;By leveraging Azure DevOps I created a build and release pipeline which automatically builds the PowerShell module with Plaster.&lt;/p&gt;
&lt;p&gt;Builds are only created if the commit on GitHub includes a version tag. This version tag gets automatically populated to the module manifest.&lt;/p&gt;
&lt;p&gt;The build artifact gets then automatically published to the PowerShell Gallery as a new version. Furthermore, a new GitHub release including the module artifact is added to the project.&lt;/p&gt;
&lt;p&gt;This process fully automates the publishing and build process for the module. For local development and maintenance, the module can also be built with &lt;a href="https://github.com/nightroman/Invoke-Build" target="_blank" rel="noreferrer"&gt;&lt;code&gt;Invoke-Build&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Azure AD guest user review solution</title><link>https://tech.nicolonsky.ch/azure-ad-guest-user-review-solution/</link><pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/azure-ad-guest-user-review-solution/</guid><description>&lt;p&gt;Azure Active Directory guest users really simplify the process to collaborate with external users. Although keeping a good governance on guest accounts can become quite a challenge.
The two biggest challenges I often observe are: &lt;em&gt;&amp;ldquo;Who invited that guest user?&amp;rdquo;&lt;/em&gt; and &lt;em&gt;&amp;ldquo;Does this guest user still need access to our infrastructure?&amp;rdquo;&lt;/em&gt;. Inspired by a recent post of Thomas Kurth regarding &lt;a href="https://www.wpninjas.ch/2020/06/azure-ad-guest-account-governance-and-cleanup/" target="_blank" rel="noreferrer"&gt;Azure AD Guest Account - Governance and Cleanup&lt;/a&gt; I also developed a solution which comes quite close to an &amp;ldquo;Azure AD Access review&amp;rdquo; like user experience.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Notable features
 &lt;div id="notable-features" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#notable-features" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &amp;lsquo;Manager&amp;rsquo; attribute of your guest users get&amp;rsquo;s automatically populated with the identity of the inviter&lt;/li&gt;
&lt;li&gt;All Azure AD app registration information is stored in Azure Key Vault&lt;/li&gt;
&lt;li&gt;Almost zero touch deployment with ARM templates&lt;/li&gt;
&lt;li&gt;You can integrate existing guest users into this solution by populating the manager attribute in Azure AD&lt;/li&gt;
&lt;li&gt;You can configure the approval frequency for guest accounts&lt;/li&gt;
&lt;li&gt;Approval frequency respects last approval date for each guest account&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class="relative group"&gt;Architecture
 &lt;div id="architecture" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#architecture" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/content/images/2020/07/AzureADGuestReview-AzScheme.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Azure AD Guest User Review"
 src="https://tech.nicolonsky.ch/content/images/2020/07/AzureADGuestReview-AzScheme.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The solution leverages function of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Azure Logic App&lt;/p&gt;</description></item><item><title>10 suggestions to improve your next PowerShell script</title><link>https://tech.nicolonsky.ch/10-suggestions-to-improve-your-next-powershell-script/</link><pubDate>Wed, 08 Jul 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/10-suggestions-to-improve-your-next-powershell-script/</guid><description>&lt;p&gt;Most of the time PowerShell is my favourite choice to automate processes and tasks. In order to improve the maintainability of my scripts I usually try to focus on some standards combined with a clean scripting style. In this post I want to show you 10 suggestions to improve your next PowerShell script. I&amp;rsquo;ve tried to order the suggestions according to an actual PowerShell starting from the very first line till the last line.&lt;/p&gt;

&lt;h2 class="relative group"&gt;1. Script prerequisites
 &lt;div id="1-script-prerequisites" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-script-prerequisites" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Your PowerShell script might need specific modules or elevated user rights to run. The &lt;code&gt;#Requires&lt;/code&gt; statement ensures that these prerequisites are met before the actual script get&amp;rsquo;s executed. So you don&amp;rsquo;t need to implement your own checks to verify prerequisites.&lt;/p&gt;
&lt;p&gt;Simply use the &lt;code&gt;#Requires&lt;/code&gt; statement at the very first line of your script. &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_requires" target="_blank" rel="noreferrer"&gt;Find out more about #Requires statement&lt;/a&gt;.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Modules
 &lt;div id="modules" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#modules" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Another benefit of specifying the modules within the requires statement is that scripts hosted on the PowerShell Gallery automatically install the modules mentioned in the &lt;code&gt;#Requires&lt;/code&gt; list.&lt;/p&gt;
&lt;p&gt;To make sure a specific module is installed use:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;#&lt;/span&gt;&lt;span class="k"&gt;Requires&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;-module&lt;/span&gt;&lt;span class="na"&gt; &amp;#34;Microsoft.Graph.Intune&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;To ensure a module with a specific version is available:&lt;/p&gt;</description></item><item><title>Remove Azure AD direct License Assignments with PowerShell</title><link>https://tech.nicolonsky.ch/remove-azure-ad-direct-license-assignments-with-powershell/</link><pubDate>Wed, 08 Jul 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/remove-azure-ad-direct-license-assignments-with-powershell/</guid><description>&lt;p&gt;Who doesn&amp;rsquo;t love a clean and tidy environment, do you? This also applies for your license assignments in Office 365 and Azure AD. As time passess it is likely to have users with direct license assignments or users which still have old trial licenses assigned. To get rid of those assignments I created a PowerShell script with removal and reporting functionality.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nicolonsky/Techblog/tree/master/CleanupAzureADLicensing" target="_blank" rel="noreferrer"&gt;Direct link to the script&lt;/a&gt;.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Identify direct license assignments
 &lt;div id="identify-direct-license-assignments" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#identify-direct-license-assignments" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;In the Azure Portal we recognize direct license assignments on a user account by viewing the &amp;ldquo;Assignment Paths&amp;rdquo;:
&lt;a href="https://tech.nicolonsky.ch/content/images/2020/07/Remove-Azure-AD-direct-License-Assignments-with-PowerShell-Portal-View.png" &gt;&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Azure AD Direct License Assignment Portal View"
 src="https://tech.nicolonsky.ch/content/images/2020/07/Remove-Azure-AD-direct-License-Assignments-with-PowerShell-Portal-View.png"
 &gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the MSOnline PowerShell module we can view the &lt;code&gt;Licenses&lt;/code&gt; property of a user and retrieve a nested property called: &lt;code&gt;GroupsAssigningLicense&lt;/code&gt;. The &lt;code&gt;GroupsAssigningLicense&lt;/code&gt; property contains either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An empty array if the license was not inherited from a group -&amp;gt; direct assignment&lt;/li&gt;
&lt;li&gt;An array with objectId&amp;rsquo;s
&lt;ul&gt;
&lt;li&gt;If the array contains the user&amp;rsquo;s objectId -&amp;gt; direct assignment&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example 1: User with objectId &lt;code&gt;36c9b091-fe88-4dc2-a9e1-2662020b4bab&lt;/code&gt; has group based license assignment and direct assignment:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AccountSkuId : nicolasuter:SPE_E5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GroupsAssigningLicense : {0a918505-d0d5-4078-9891-0e8bec67cb65, 36c9b091-fe88-4dc2-a9e1-2662020b4bab}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Example 2: User has no inherited licenses from a group:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AccountSkuId : nicolasuter:SPE_E5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GroupsAssigningLicense : {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 class="relative group"&gt;PowerShell Script
 &lt;div id="powershell-script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#powershell-script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;You find the PowerShell script &lt;a href="https://github.com/nicolonsky/Techblog/tree/master/CleanupAzureADLicensing" target="_blank" rel="noreferrer"&gt;on my techblog GitHub repository&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Exploring the new Microsoft Graph PowerShell Module(s)</title><link>https://tech.nicolonsky.ch/exploring-the-new-microsoft-graph-powershell-modules/</link><pubDate>Tue, 12 May 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/exploring-the-new-microsoft-graph-powershell-modules/</guid><description>&lt;p&gt;Microsoft is working on a new set of PowerShell modules grouped under the umbrella of &lt;a href="https://github.com/microsoftgraph/msgraph-sdk-powershell" target="_blank" rel="noreferrer"&gt;Microsoft.Graph&lt;/a&gt; that will (hopefully) cover all the Microsoft Graph resources available. I&amp;rsquo;ve already used some of them for my &lt;a href="https://github.com/nicolonsky/ConditionalAccessDocumentation" target="_blank" rel="noreferrer"&gt;Conditional Access Documentation Script&lt;/a&gt; and thought they have some notable features worth sharing.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Advantages and changes
 &lt;div id="advantages-and-changes" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#advantages-and-changes" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;The Microsoft Graph modules use the new Microsoft Authentication Library (MSAL) instead of the old Azure AD Authentication Library (ADAL). The MSAL library in the modules implements a token cache which persists the access and refresh tokens.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;MSAL caches a token after it has been acquired. Application code should try to get a token silently (from the cache), first, before acquiring a token by other means. - &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-acquire-cache-tokens" target="_blank" rel="noreferrer"&gt;Microsoft docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;The token cache persists system reboots and re-opening PowerShell sessions. The module allows you to obtain tokens either for authentication via &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-authentication-flows#client-credentials" target="_blank" rel="noreferrer"&gt;client credentials&lt;/a&gt; (certificate only) or &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code" target="_blank" rel="noreferrer"&gt;device code flow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Furthermore, the new modules support a really broad spectrum of available entities on the Graph API. From an EM+S perspective this means for example: groups, users, identity protection, conditional access, and some of the Intune app management commands are also starting to appear.&lt;/p&gt;
&lt;p&gt;Just be aware that the modules are currently published as pre-release. If you encounter any issues share them with the development team on GitHub and submit issues or even better contribute directly to the project.&lt;/p&gt;</description></item><item><title>Validating a GUID with PowerShell</title><link>https://tech.nicolonsky.ch/validating-a-guid-with-powershell/</link><pubDate>Tue, 05 May 2020 00:00:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/validating-a-guid-with-powershell/</guid><description>&lt;p&gt;For some recent Microsoft Graph scripts I wanted to translate some Azure AD Object ID / GUID entries to their respective display name. The array with the GUID&amp;rsquo;s contained already some readable text. Of course I only wanted to translate the GUID entries with according Graph API requests. Otherwise the Graph requests would fail. Google offered only some fancy regex functions and helpers but I had that .NET function in my mind which looks much nicer compared to whatever regex pattern that I don&amp;rsquo;t understand.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;applications&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;includeApplications&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;797f4846-ba00-4fd7-ba43-dac1f8f63013&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;Office365&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;So I needed a way to test a string for a valid GUID and only invoking the Graph calls for GUID values.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Matching a GUID with a regex
 &lt;div id="matching-a-guid-with-a-regex" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-a-guid-with-a-regex" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;I found the following regex on &lt;a href="https://pscustomobject.github.io/powershell/functions/PowerShell-Validate-Guid-copy" target="_blank" rel="noreferrer"&gt;this site&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;d815c3bc-9c49-4633-9d16-29808242d063&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;-match&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;(?im)^[{(]?[0-9A-F]{8}[-]?(?:[0-9A-F]{4}[-]?){3}[0-9A-F]{12}[)}]?$&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;Matching a GUID with the .NET method
 &lt;div id="matching-a-guid-with-the-net-method" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#matching-a-guid-with-the-net-method" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Instead of the complex regex we can invoke this nice .NET member method which returns true or false based on the input:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;guid&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;TryParse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;d815c3bc-9c49-4633-9d16-29808242d063&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vm"&gt;$&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="no"&gt;ref][guid&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I guess that&amp;rsquo;s much more convenient.&lt;/p&gt;

&lt;h3 class="relative group"&gt;Helper Function
 &lt;div id="helper-function" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#helper-function" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s a helper function which can be used in PowerShell scripts:&lt;/p&gt;</description></item><item><title>Creating desktop shortcuts with Intune</title><link>https://tech.nicolonsky.ch/intune-create-desktop-shortcut/</link><pubDate>Tue, 09 Jul 2019 23:22:24 +0000</pubDate><guid>https://tech.nicolonsky.ch/intune-create-desktop-shortcut/</guid><description>&lt;p&gt;Why want you to create desktop shortcuts with Intune? Business specific apps may require special shortcuts in order to launch the application with the right parameters. Or you need to create a shortcut for an application which is stored on your on premises fileserver. For this purpose I created a little solution which closes the gap between the modern cloud and on premises world. In comparison with other solutions this one works if you have redirected the users desktop with OneDrive Known Folder Move and automatically remediates missing shortcuts if they got deleted.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/nicolonsky/Techblog/tree/master/IntuneShortcut" target="_blank" rel="noreferrer"&gt;Direct link to the GitHub repository.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Browser links:&lt;/strong&gt; Instead of placing shortcuts to websites on the desktop I would recommend you to use managed bookmarks which can be directly provisioned within the web browser. I documented this for the new Microsoft Edge based on chromium &lt;a href="https://tech.nicolonsky.ch/managing-the-new-microsoft-edge-browser-with-intune/#provision-managed-favorites" &gt;here&lt;/a&gt;.
{: .notice}&lt;/p&gt;

&lt;h2 class="relative group"&gt;Features
 &lt;div id="features" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#features" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This solution works when the desktop is redirected with OneDrive Known Folder Move&lt;/li&gt;
&lt;li&gt;Everything is user based (local userprofile)&lt;/li&gt;
&lt;li&gt;If the shortcut is missing or deleted it gets automatically (re)created&lt;/li&gt;
&lt;li&gt;Possibility to remove shortcut via Intune Win32 app uninstall&lt;/li&gt;
&lt;li&gt;Shortcut can point to: URL, File (UNC) or Folder (UNC)&lt;/li&gt;
&lt;li&gt;Ability to pass shortcut arguments&lt;/li&gt;
&lt;li&gt;Ability to specify shortcut icon (UNC/URL)&lt;/li&gt;
&lt;li&gt;Ability to deploy shortcut together with an app using Intune Win32 app dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class="relative group"&gt;Architecture
 &lt;div id="architecture" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#architecture" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;A simple PowerShell script which does all the shortcut stuff is wrapped as Intune Win32 App. This adds possibility to detect the presence of the shortcut and if required to uninstall it with Intune. In order to work with the redirected desktop to OneDrive with Known Folder Move we can take advantage of the &lt;code&gt;[Environment]::GetFolderPath(&amp;quot;Desktop&amp;quot;)&lt;/code&gt; method to resolve the desktop location. Based on the Win32 app configuration the shortut get&amp;rsquo;s either created on the users personal desktop or on the allusers desktop.&lt;/p&gt;</description></item><item><title>Calling the Microsoft Graph API via PowerShell without a user</title><link>https://tech.nicolonsky.ch/calling-the-microsoft-graph-api/</link><pubDate>Mon, 17 Jun 2019 18:47:47 +0000</pubDate><guid>https://tech.nicolonsky.ch/calling-the-microsoft-graph-api/</guid><description>&lt;p&gt;A colleague recently asked me how to access the Microsoft Graph API using PowerShell without specifying his user account or credentials. So here&amp;rsquo;s a little post about the required configuration to authenticate against the OAuth 2.0 endpoint of Azure AD with an app registration. This is especially useful for automation services like Azure automation.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tech.nicolonsky.ch/calling-the-microsoft-graph-api/#powershell-template" &gt;At the end of this post you&amp;rsquo;ll find a PowerShell template.&lt;/a&gt;&lt;/p&gt;

&lt;h2 class="relative group"&gt;Gather application information
 &lt;div id="gather-application-information" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#gather-application-information" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Create a new client secret for your app and note down the following values:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Client Secret&lt;/li&gt;
&lt;li&gt;Application ID (client ID)&lt;/li&gt;
&lt;li&gt;Directory ID (tenant ID)&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="kg-card kg-image-card kg-card-hascaption"&gt;&lt;img src="https://tech.nicolonsky.ch/content/images/2019/06/image-1.png" class="kg-image"&gt;&lt;figcaption&gt;Azure AD App registration&lt;/figcaption&gt;&lt;/figure&gt;
## PowerShell code

&lt;h3 class="relative group"&gt;Request authentication token
 &lt;div id="request-authentication-token" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#request-authentication-token" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;In order to use the Graph API we need an authentication token. The information we gathered before is now sent to the Azure AD OAuth 2.0 endpoint.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;https://login.microsoftonline.com/{TenantID}/oauth2/v2.0/token&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the request supply a body with the following content:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8f9f420d-606c-4e13-889e-837072dbfb42&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;BlaBlaExampleSecret&amp;#34;&lt;/span&gt; &lt;span class="c"&gt;#Generated secret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://graph.microsoft.com/.default&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;em&gt;scope&lt;/em&gt; and &lt;em&gt;grant_type&lt;/em&gt; are required attributes. A full example looks like:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$body&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8f9f420d-606c-4e13-889e-837072dbfb42&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;BlaBlaExampleSecret&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;https://graph.microsoft.com/.default&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;grant_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;client_credentials&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;7955e1b3-cbad-49eb-9a84-e14aed7f3400&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;Invoke-WebRequest&lt;/span&gt; &lt;span class="n"&gt;-Uri&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;https://login.microsoftonline.com/&lt;/span&gt;&lt;span class="nv"&gt;$tenantId&lt;/span&gt;&lt;span class="s2"&gt;/oauth2/v2.0/token&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-ContentType&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;application/x-www-form-urlencoded&amp;#34;&lt;/span&gt; &lt;span class="n"&gt;-Body&lt;/span&gt; &lt;span class="nv"&gt;$body&lt;/span&gt; &lt;span class="n"&gt;-Method&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If everything works we receive an access token object as HTML 200/ok response:&lt;/p&gt;</description></item><item><title>Introducing the OneDrive AutoMountTeamSites setting</title><link>https://tech.nicolonsky.ch/onedrive-automountteamsites/</link><pubDate>Sun, 17 Mar 2019 16:03:09 +0000</pubDate><guid>https://tech.nicolonsky.ch/onedrive-automountteamsites/</guid><description>&lt;p&gt;Reviewing the latest OneDrive features I wanted to try the new &lt;em&gt;AutoMountTeamSites&lt;/em&gt; setting which lets you preconfigure SharePoint online sites to sync automatically for defined users and devices.&lt;/p&gt;
&lt;p&gt;&lt;mark&gt;&lt;strong&gt;Updated on 12.07.2019&lt;/strong&gt;: &lt;a href="#intune-administrative-template-configuration"&gt;Included the Intune administrative template configuration&lt;/a&gt;&lt;/mark&gt;&lt;/p&gt;
&lt;p&gt;The setting is officially described as follow:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This setting lets you specify SharePoint team site libraries to sync automatically the next time users sign in to the OneDrive sync client. (Microsoft)&lt;/p&gt;
&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;If you enable this setting, the OneDrive sync client will automatically download the contents of the libraries you specified as online-only files the next time the user signs in. The user won&amp;rsquo;t be able to stop syncing the libraries. (Microsoft)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 class="relative group"&gt;Prerequisites
 &lt;div id="prerequisites" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#prerequisites" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;In order to get things up an running we need at least:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OneDrive sync client version 19.012.0121.0011 or newer&lt;/li&gt;
&lt;li&gt;Windows 10 Version 1709 or newer&lt;/li&gt;
&lt;li&gt;OneDrive Files On-Demand enabled (&lt;a href="https://tech.nicolonsky.ch/onedrive-automountteamsites/#intune-administrative-template-configuration" &gt;described below&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be aware that this feature is not supported with on-premises SharePoint sites and not recommended to enable this setting for more than 1'000 devices. The device limit is related to the Windows Push Notification Service which tells the OneDrive clients when a file change occurs on a server side. When you exceed that limit clients will find themselves in a polling mode. &lt;a href="https://hansbrender.com/2019/04/04/onedrive-update-for-gpo-team-site-libraries-to-sync-automatically/" target="_blank" rel="noreferrer"&gt;Hans Brender explains this behavior well on his blog&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Intune map network drives and execute PowerShell script on each user logon</title><link>https://tech.nicolonsky.ch/intune-execute-powershell-script-on-each-user-logon/</link><pubDate>Fri, 11 Jan 2019 20:51:36 +0000</pubDate><guid>https://tech.nicolonsky.ch/intune-execute-powershell-script-on-each-user-logon/</guid><description>&lt;p&gt;Recently a customer needed a drive mapping solution to access his on premise file shares during his transition phase to a cloud-only workplace. I wanted to share the solution with you because it&amp;rsquo;s  a frequently asked question around a modern workplace migration. The following solution can also be extended or modified for a printer mapping or other PowerShell scripts which need to run on each user logon.&lt;/p&gt;
&lt;!--kg-card-begin: markdown--&gt;
&lt;p&gt;&lt;mark&gt;&lt;strong&gt;Updated 04.08.2019&lt;/strong&gt;: I&amp;rsquo;ve developed an automated solution to generate network drive mapping configurations with an online tool which also migrates group policy network drive mappings. See: &lt;a href="https://tech.nicolonsky.ch/next-level-network-drive-mapping-with-intune"&gt;next-level-network-drive-mapping-with-intune&lt;/a&gt;.&lt;/mark&gt;&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;!--kg-card-begin: markdown--&gt;
&lt;p&gt;&lt;a href="https://github.com/nicolonsky/Techblog/tree/master/IntuneNetworkDrives" target="_blank" rel="noreferrer"&gt;Direct link to the final scripts&lt;/a&gt;&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;
&lt;p&gt;Lets assume we have the following scenario:&lt;/p&gt;
&lt;figure class="kg-card kg-image-card"&gt;&lt;img src="https://tech.nicolonsky.ch/content/images/2019/01/Hybrid-AAD.png" class="kg-image"&gt;&lt;/figure&gt;
- Customer with hybrid user-identities (Azure AD Connect)
- On premise ressources with legacy file shares
- Devices are Azure AD joined &amp;nbsp;( **not** hybrid joined)
- MDM managed with Intune
- [Optional] Always on VPN for external on-premise resource access
- [Optional] Windows Hello for Business deployment as described [here](https://docs.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/hello-hybrid-aadj-sso) 

&lt;h2 class="relative group"&gt;Architecture
 &lt;div id="architecture" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#architecture" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;With my colleague &lt;a href="https://blog.alschneiter.com/" target="_blank" rel="noreferrer"&gt;Alain Schneiter&lt;/a&gt; I designed the following solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Main PowerShell script stored on Azure blob storage which handles the drive mapping -  driveletters, UNC paths and descriptions can be configured within the script  &lt;/li&gt;
&lt;li&gt;Client side script deployed with Intune which triggers the main script during logon.  The main script is not stored locally which makes it easy to customize (no updates oder changes needed on client side)&lt;/li&gt;
&lt;li&gt;Deployment is user targeted via Azure AD group and Intune&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class="relative group"&gt;Azure blob storage configuration
 &lt;div id="azure-blob-storage-configuration" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#azure-blob-storage-configuration" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;We wanted to store the script within Azure because the customer was already using Azure blob storage. It&amp;rsquo;s also possible to store the PowerShell script on GitHub if you don&amp;rsquo;t want to use Azure.&lt;/p&gt;</description></item><item><title>Clean up stale Azure AD devices</title><link>https://tech.nicolonsky.ch/clean-up-azure-ad-devices/</link><pubDate>Thu, 10 Jan 2019 22:25:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/clean-up-azure-ad-devices/</guid><description>&lt;p&gt;If you are using Azure AD and the time passes you&amp;rsquo;ll have a lot of old device entries. If you enable the automatic device cleanup rule in Microsoft Intune the device is only removed within MDM and the Azure AD entry still exists.&lt;/p&gt;
&lt;figure class="kg-card kg-image-card kg-card-hascaption"&gt;&lt;img src="https://tech.nicolonsky.ch/content/images/2019/01/image-1.png" class="kg-image"&gt;&lt;figcaption&gt;Intune device cleanup rule&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;For this reason I created a tiny PowerShell snippet to create a report with all devices which didn&amp;rsquo;t contact your Azure AD tenant since the treshold date specified. If you confirm the operation you can also delete all affected devices.&lt;/p&gt;
&lt;!--kg-card-begin: markdown--&gt;
&lt;p&gt;&lt;mark&gt;&lt;em&gt;Please be careful when running the script because when removing a device from Azure AD the stored Bitlocker recovery keys are also removed. I can recommend &lt;a href="http://rzander.azurewebsites.net/bitlocker-management-with-azure-storage-table/"&gt;Roger Zander&amp;rsquo;s Azure table-based Bitlocker recovery key solution&lt;/a&gt;.&lt;/em&gt;&lt;/mark&gt;&lt;/p&gt;
&lt;!--kg-card-end: markdown--&gt;&lt;!--kg-card-begin: markdown--&gt;&lt;script src="https://gist.github.com/nicolonsky/231844d2c383396331a94024bffbd7ff.js"&gt; &lt;/script&gt;&lt;!--kg-card-end: markdown--&gt;</description></item><item><title>Set Office 365 UsageLocation property with Azure automation</title><link>https://tech.nicolonsky.ch/office-usage-location-azure-automation/</link><pubDate>Wed, 09 Jan 2019 15:23:00 +0000</pubDate><guid>https://tech.nicolonsky.ch/office-usage-location-azure-automation/</guid><description>&lt;p&gt;If you want to assign Microsoft licenses to your Azure AD users e.g. Microsoft 365 E3 licenses you can do this with group based licensing as described &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/active-directory-licensing-whatis-azure-portal" target="_blank" rel="noreferrer"&gt;here&lt;/a&gt;. &lt;del&gt;The problem is that even with group based licensing the UsageLocation property for each user must be set individually.&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;mark&gt;Update: 13.01.2019: Since group based licensing is GA the tenant location is used if no UsageLocation is set on a user object. Use this guide if you want to manually assign licenses or override the tenant settings if you need to configure different UsageLocations.&lt;/mark&gt;&lt;/p&gt;

&lt;h2 class="relative group"&gt;Possible bulk and automation solutions
 &lt;div id="possible-bulk-and-automation-solutions" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#possible-bulk-and-automation-solutions" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;You can achieve this with the following options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Manual&amp;rdquo; trough Azure or Office 365 portal&lt;/li&gt;
&lt;li&gt;PowerShell (must be triggered manually or through scheduled task)&lt;/li&gt;
&lt;li&gt;Azure AD Connect synchronisation (UsageLocation populated in on prem AD)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure automation with PowerShell runbook as in this post 🙂&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 class="relative group"&gt;Azure automation sounds expensive?
 &lt;div id="azure-automation-sounds-expensive" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#azure-automation-sounds-expensive" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;Fortunately Azure automation offers 500 minutes of script runtime for free. Find more details under Automation pricing. Just to give you an idea: If the executed script has an average runtime of 1 minute you could run it (500 minutes / (30 calendear days / 1 minute script runtime)) = 16x per day. Each month. For free.&lt;/p&gt;</description></item><item><title>PowerShell Script Test Open TCP Ports</title><link>https://tech.nicolonsky.ch/power-shell-script-test-open-tcp-ports/</link><pubDate>Wed, 18 Oct 2017 13:28:12 +0000</pubDate><guid>https://tech.nicolonsky.ch/power-shell-script-test-open-tcp-ports/</guid><description>&lt;p&gt;Recently I was troubleshooting ADFS connection issues when I discovered a nice little Cmdlet called &amp;ldquo;Test-NetConnection&amp;rdquo;. With this Cmdelet you can verify TCP connectivity, in my case from a client to the ADFS server.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The &lt;strong&gt;Test-NetConnection&lt;/strong&gt; cmdlet displays diagnostic information for a connection. It supports ping test, TCP test, route tracing, and route selection diagnostics. Depending on the input parameters, the output can include the DNS lookup results, a list of IP interfaces, IPsec rules, route/source address selection results, and/or confirmation of connection establishment.&lt;/p&gt;
&lt;/blockquote&gt;&lt;p&gt;Find a full documentation on the &lt;a href="https://docs.microsoft.com/en-us/powershell/module/nettcpip/test-netconnection?view=win10-ps" target="_blank" rel="noreferrer"&gt;Microsoft Docs Page&lt;/a&gt;.&lt;/p&gt;

&lt;h2 class="relative group"&gt;About the script
 &lt;div id="about-the-script" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#about-the-script" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;With this Script you are able to specify server names and port numbers to check in a CSV File. The Script generates an CSV output file as a report. You can use this script for troubleshooting or engineering purposes to verify if TCP ports are opened.&lt;/p&gt;
&lt;p&gt;Simply add the hostname and TCP port to the &amp;ldquo;CheckList.csv&amp;rdquo; and the script checks the specified servers and ports.&lt;/p&gt;
&lt;p&gt;The script will generate an output file for the same path containing the suffix &amp;ldquo;Report_&amp;rdquo; with the test results.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CheckList.csv: &lt;br&gt;
&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="CheckList.csv"
 src="https://tech.nicolonsky.ch/content/images//2017/10/2017-10-18_1242.png"
 &gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Report_CheckList.csv generated after script execution:
&lt;figure&gt;&lt;img
 class="my-0 rounded-md"
 loading="lazy"
 decoding="async"
 fetchpriority="low"
 alt="Report_CheckList.csv"
 src="https://tech.nicolonsky.ch/content/images//2017/10/2017-10-18_1252.png"
 &gt;&lt;/figure&gt;
&lt;/p&gt;</description></item><item><title>PowerShell Function Validate Object Properties Using ValidateScript</title><link>https://tech.nicolonsky.ch/power-shell-function-validate-object-properties/</link><pubDate>Thu, 12 Oct 2017 10:25:09 +0000</pubDate><guid>https://tech.nicolonsky.ch/power-shell-function-validate-object-properties/</guid><description>&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Recently I was working on a PowerShell script with many custom functions. When I started to use PowerShell custom objects I wanted to be able to pass them to a function. So I faced the challenge of validating my object for all required properties and came up with this solution, using the ValidateScript block to test the object:&lt;/p&gt;
&lt;script src="https://gist.github.com/nicolonsky/9c09096dd91ceb7a8fa43ab5cf4c3740.js"&gt;&lt;/script&gt;

&lt;h4 class="relative group"&gt;Customizing the ValidateScript
 &lt;div id="customizing-the-validatescript" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#customizing-the-validatescript" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;p&gt;As you can see I use a ValidateScript for the parameter validation to test the object for the required properties. The properties can be specified in an array:
&lt;code&gt;$requiredProperties=@(&amp;quot;Property1&amp;quot;,&amp;quot;Property2&amp;quot;,&amp;quot;Property3&amp;quot;, &amp;quot;Property4&amp;quot;)&lt;/code&gt;
When we call the Function with an appropriate object:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;PSCUSTOMOBJECT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property3&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property4&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We receive the following output:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;PS &lt;/span&gt;&lt;span class="n"&gt;H:&lt;/span&gt;&lt;span class="p"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;New-Example&lt;/span&gt; &lt;span class="n"&gt;-InputObject&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Succesfully&lt;/span&gt; &lt;span class="n"&gt;passed&lt;/span&gt; &lt;span class="nb"&gt;ValidateScript&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 class="relative group"&gt;Result
 &lt;div id="result" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#result" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;p&gt;If we remove one or more properties from our custom object, an error is thrown:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;PS &lt;/span&gt;&lt;span class="n"&gt;H:&lt;/span&gt;&lt;span class="p"&gt;\&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;PSCUSTOMOBJECT&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="vm"&gt;@&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property1&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property2&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;property3&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Value&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;New-Example&lt;/span&gt; &lt;span class="n"&gt;-InputObject&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;New-Example&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cannot&lt;/span&gt; &lt;span class="n"&gt;validate&lt;/span&gt; &lt;span class="n"&gt;argument&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="nb"&gt;parameter&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;InputObject&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Property&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Property4&amp;#39;&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="n"&gt;At&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;10&lt;/span&gt; &lt;span class="n"&gt;char&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;26&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;New-Example&lt;/span&gt; &lt;span class="n"&gt;-InputObject&lt;/span&gt; &lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;~~~~~~~&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;CategoryInfo&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InvalidData&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;New-Example&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ParameterBindingValidationException&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;FullyQualifiedErrorId&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ParameterArgumentValidationError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nb"&gt;New-Example&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If you want to go a step further you could extend the ValidateScript to&amp;hellip;&lt;/p&gt;

&lt;h3 class="relative group"&gt;Prevent passing properties with a NULL or empty value
 &lt;div id="prevent-passing-properties-with-a-null-or-empty-value" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#prevent-passing-properties-with-a-null-or-empty-value" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h3&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;PSObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Properties&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;ForEach-Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="no"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]::&lt;/span&gt;&lt;span class="n"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;))){&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;Throw&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;System.Management.Automation.ValidationMetadataException&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Property &amp;#39;&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; has either a NULL or empty value&amp;#34;&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;If we call our function again with the added IsNullOrEmpty validation NULL or emtpy values throw an exception:&lt;/p&gt;</description></item><item><title>Managing printers with PowerShell</title><link>https://tech.nicolonsky.ch/managing-printers-power-shell/</link><pubDate>Tue, 10 Oct 2017 17:54:54 +0000</pubDate><guid>https://tech.nicolonsky.ch/managing-printers-power-shell/</guid><description>&lt;p&gt;Managing printers with PowerShell instead of VBScript? Sometimes it&amp;rsquo;s necessary to add and remove specific printers to a computer. For example during a client deployment or when a user logs on. This post covers how to manage printers with PowerShell.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The following PowerShell commands are supported with PowerShell version 4 and newer.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 class="relative group"&gt;Installing a local network printer
 &lt;div id="installing-a-local-network-printer" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#installing-a-local-network-printer" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;Installing a local printer (without a printserver) consists of the following steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add the printer driver to your system&amp;rsquo;s driverstore&lt;/li&gt;
&lt;li&gt;Install the printer driver from the driverstore&lt;/li&gt;
&lt;li&gt;Add a printer port to communicate with the printer&lt;/li&gt;
&lt;li&gt;Last but not least add the printer&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 class="relative group"&gt;Add the printer driver to the driverstore
 &lt;div id="add-the-printer-driver-to-the-driverstore" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-the-printer-driver-to-the-driverstore" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;p&gt;Before you can install the printer driver you need to import the printer driver to your system&amp;rsquo;s driverstore.&lt;/p&gt;
&lt;p&gt;This can be achieved with the built in Windows &amp;ldquo;pnputil&amp;rdquo; utility.&lt;/p&gt;
&lt;p&gt;The following code adds all drivers from the specified path to the driverstore:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Get-ChildItem %PathToYourDriverFolder% -Filter *.inf -Recurse | % {pnputil.exe /a $_.FullName}&lt;/code&gt;&lt;/p&gt;

&lt;h4 class="relative group"&gt;Install the printer driver from the driverstore
 &lt;div id="install-the-printer-driver-from-the-driverstore" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#install-the-printer-driver-from-the-driverstore" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h4&gt;
&lt;p&gt;This step is quite simple, you just need to know the name of the printer driver you want to install. For example &amp;ldquo;HP Universal Printing PCL 6&amp;rdquo;.&lt;/p&gt;</description></item></channel></rss>