Jekyll2023-05-15T12:42:35+02:00https://www.cloudpilot.no/feed.xmlCloudPilot.no | Just another Cloud and PowerShell blogCloudPilot.no | Just another Cloud and PowerShell blogFreddie ChristiansenProduct review: FIDO Certified Security Key from FEITIAN2023-03-21T08:00:00+01:002023-03-21T08:00:00+01:00https://www.cloudpilot.no/blog/Product-review-FIDO-Certified-Security-Key-from-FEITIAN<p>In recent years, the need for secure and easy-to-use authentication methods has become increasingly important in the world of online security. Enter FIDO, or Fast Identity Online, an open standard that aims to provide strong authentication using public key cryptography. Among the various FIDO options available, one that stands out is the FIDO Key from <a href="https://www.ftsafe.com/">Feitian Technologies</a>.</p>
<p><img src="/assets/images/FIDO/01.jpg" /></p>
<p>The FIDO Key is a USB device that provides strong two-factor authentication, meaning it requires both a physical key and a PIN or biometric identifier to access an account. It is compatible with various operating systems, including Windows, macOS, and Linux, and supports a range of browsers, such as Chrome, Firefox, and Edge.</p>
<p>One of the key features of the FIDO Key is its simplicity. Users only need to plug it in and enter their PIN or biometric identifier to complete the authentication process, making it easy to use even for those who are not tech-savvy. Furthermore, the key is designed to resist phishing attacks and other types of fraud, thanks to its use of public key cryptography and FIDO protocols.</p>
<p>Watch the video below on how their security keys integrates perfectly with <code class="language-plaintext highlighter-rouge">Azure Active Directory</code> for a true passwordless experience.</p>
<p><a href="https://youtu.be/3ZfpAHQyl3Y"><img src="http://img.youtube.com/vi/3ZfpAHQyl3Y/0.jpg" alt="IMAGE ALT TEXT" /></a></p>
<p>The FIDO Key is also versatile and can be used for a variety of applications, including online banking, email, and social media. It can also be used for enterprise-level applications, providing an extra layer of security for corporate accounts and networks.</p>
<p><img src="/assets/images/FIDO/02.jpg" /></p>
<p>Overall, the FIDO Key from Feitian Technologies is a reliable and user-friendly option for strong authentication. Its ease of use, compatibility with multiple platforms and browsers, and resistance to fraud make it a standout choice for those looking to enhance their online security.</p>
<p>If you want to know more about the security keys available from FEITIAN Technologies, visit their <a href="https://www.ftsafe.com/">website</a> :blush:</p>Freddie ChristiansenIn recent years, the need for secure and easy-to-use authentication methods has become increasingly important in the world of online security. Enter FIDO, or Fast Identity Online, an open standard that aims to provide strong authentication using public key cryptography. Among the various FIDO options available, one that stands out is the FIDO Key from Feitian Technologies.Using Single Sign-On with Azure AD Connect Cloud Sync2023-01-16T21:19:00+01:002023-01-16T21:19:00+01:00https://www.cloudpilot.no/blog/Using-Single-Sign-On-with-Azure-AD-Connect-Cloud-Sync<h2 id="what-is-azure-active-directory-seamless-single-sign-on">What is Azure Active Directory Seamless Single Sign-On?</h2>
<p>Azure Active Directory Seamless Single Sign-On (Azure AD Seamless SSO) automatically signs users in when they are on their corporate devices connected to your corporate network. When enabled, users don’t need to type in their passwords to sign in to Azure AD, and usually, even type in their username. This feature provides your users easy access to your cloud-based applications without needing any additional on-premises components.</p>
<p>I recommend reading my previous blog post, <a href="https://www.cloudpilot.no/blog/Deep-dive-Hybrid-Identity-using-Azure-AD-Connect-Cloud-Sync/">Deep dive: Hybrid Identity using Azure AD Connect Cloud Sync</a>, before continuing with the steps below.</p>
<p><strong>The time to migrate from <code class="language-plaintext highlighter-rouge">Azure AD Connect</code> to <code class="language-plaintext highlighter-rouge">Azure AD Connect Cloud Sync</code> is now</strong>.</p>
<h2 id="setup">Setup</h2>
<h3 id="step-1-download-and-extract-azure-ad-connect-files">Step 1: Download and extract Azure AD Connect files</h3>
<ol>
<li>
<p>Start off by downloading the latest version of <a href="https://www.microsoft.com/download/details.aspx?id=47594">Azure AD Connect</a>↗️</p>
</li>
<li>
<p>Open a PowerShell window (as administrator) and navigate to the msi file you just downloaded.</p>
<p><em>In this guide I have placed the msi file in a new folder named</em> <code class="language-plaintext highlighter-rouge">AADConnect</code><em>, right below the <code class="language-plaintext highlighter-rouge">C:</code> drive.</em>
<img src="/assets/images/ADSSO/08.png" alt="img" /></p>
</li>
<li>
<p>Run the following: <code class="language-plaintext highlighter-rouge">msiexec /a C:\AADConnect\AzureADConnect.msi /qb TARGETDIR=C:\AADConnect\<Desired name of subfolder></code></p>
<p><em>Remember to change the filepath and name the subfolder to match your file path and desired name</em>.
This will extract the files under the new sub-folder named after your choice.</p>
<p>In the example below I extracted the files to a sub-folder named <code class="language-plaintext highlighter-rouge">Extract</code>.</p>
</li>
</ol>
<p><img src="/assets/images/ADSSO/09.png" alt="img" /></p>
<p><img src="/assets/images/ADSSO/10.png" alt="img" /></p>
<h3 id="step-2-import-the-seamless-sso-powershell-module">Step 2: Import the Seamless SSO PowerShell module</h3>
<ol>
<li>
<p>Download and install <a href="https://learn.microsoft.com/en-us/powershell/azure/active-directory/overview">Azure AD Powershell</a>↗️</p>
</li>
<li>
<p>In your PowerShell window, navigate to your <code class="language-plaintext highlighter-rouge">ExtractFolder\Microsoft Azure Active Directory Connect</code> folder.</p>
</li>
<li>
<p>Import the <code class="language-plaintext highlighter-rouge">Seamless SSO</code> PowerShell module by using this command: <code class="language-plaintext highlighter-rouge">Import-Module .\AzureADSSO.psd1</code>.</p>
</li>
</ol>
<p><img src="/assets/images/ADSSO/11.png" alt="img" /></p>
<h3 id="step-3-get-the-list-of-active-directory-forest-on-which-seamless-sso-has-already-been-enabled">Step 3: Get the list of Active Directory forest on which Seamless SSO has (already) been enabled</h3>
<ol>
<li>In your PowerShell window (<em>as you already have successfully imported the</em> <code class="language-plaintext highlighter-rouge">AzureADSSO</code> <em>module in</em>), call the <code class="language-plaintext highlighter-rouge">New-AzureADSSOAuthenticationContext</code> cmdlet. When prompted, enter your tenant’s <em>Global Administrator</em> credentials.</li>
</ol>
<p><img src="/assets/images/ADSSO/12.png" alt="img" /></p>
<ol>
<li>Next, call the <code class="language-plaintext highlighter-rouge">Get-AzureADSSOStatus</code> cmdlet. This command provides you with the list of Active Directory forests (look at the “Domains” list in the output) on which this feature has (already) been enabled.</li>
</ol>
<p><img src="/assets/images/ADSSO/13.png" alt="img" /></p>
<h3 id="step-4-enable-seamless-sso-for-each-active-directory-forest">Step 4: Enable Seamless SSO for each Active Directory forest</h3>
<ol>
<li>Call the <code class="language-plaintext highlighter-rouge">Enable-AzureADSSOForest</code> cmdlet. When prompted, enter the <em>domain administrator</em> credentials for the intended Active Directory forest.</li>
</ol>
<blockquote>
<p><strong>ℹ️ Note</strong>
The domain administrator credentials username must the entered in the SAM account name format (example: <code class="language-plaintext highlighter-rouge">mydomain\johndoe</code> or <code class="language-plaintext highlighter-rouge">mydomain.com\johndoe</code>).</p>
</blockquote>
<ol>
<li>Repeat the preceding step for each Active Directory forest where you want to setup the feature.</li>
</ol>
<h3 id="step-5-enable-the-feature-on-your-tenant">Step 5: Enable the feature on your tenant</h3>
<p>To turn on the feature on your tenant, call <code class="language-plaintext highlighter-rouge">Enable-AzureADSSO -Enable $true</code>.</p>
<p><img src="/assets/images/ADSSO/14.png" alt="img" /></p>
<h2 id="verify">Verify</h2>
<p>Follow this steps in the Azure portal to verify that you have enabled Seamless SSO correctly:</p>
<ol>
<li>
<p>Sign in to the <a href="https://aad.portal.azure.com/">Azure Active Directory administrative center</a>↗️ with a <em>Global Administrator</em> or <em>Hybrid Identity Administrator</em> credentials of your tenant.</p>
</li>
<li>
<p>Select <strong>Azure Active Directory</strong> in the left pane.</p>
</li>
<li>
<p>Select <strong>Azure AD Connect</strong>.</p>
</li>
<li>
<p>Select <strong>Connect Sync</strong>.</p>
</li>
<li>
<p>Verify that the <strong>Seamless single sign-on</strong> feature appears as <strong>Enabled</strong>.</p>
</li>
</ol>
<p><img src="/assets/images/ADSSO/01_02.png" alt="img" /></p>
<h2 id="roll-out-the-feature">Roll out the feature</h2>
<p>After you have verified that Seamless SSO has been successfully enabled, it’s time to roll out the feature to your end-users.</p>
<p>You start by adding the following Azure AD URL to all or selected user’s <code class="language-plaintext highlighter-rouge">Intranet zone</code> settings by using Group Policy in Active Directory:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://autologon.microsoftazuread-sso.com
</code></pre></div></div>
<p>In addition, you need to enable an Intranet zone policy setting called <strong>Allow updates to status bar via script</strong> through Group Policy.</p>
<h3 id="detailed-steps">Detailed steps</h3>
<ol>
<li>
<p>Open the Group Policy Management Editor tool.</p>
</li>
<li>
<p>Edit the group policy that’s applied to some or all your users. This example uses <strong>Default Domain Policy</strong>.</p>
</li>
<li>
<p>Browse to <strong>User Configuration</strong> > <strong>Policies</strong> > <strong>Administrative Templates</strong> > <strong>Windows Components</strong> > <strong>Internet Explorer</strong> > <strong>Internet Control Panel</strong> > <strong>Security Page</strong>. Then select <strong>Site to Zone Assignment List</strong>.</p>
</li>
<li>
<p>Enable the policy, and then enter the following values in the dialog box:</p>
</li>
</ol>
<ul>
<li>
<p><strong>Value name</strong>: <code class="language-plaintext highlighter-rouge">https://autologon.microsoftazuread-sso.com</code>.
The Azure AD URL where the Kerberos tickets are forwarded.</p>
</li>
<li>
<p><strong>Value</strong> (Data): <code class="language-plaintext highlighter-rouge">1</code>.
This indicates the <em>Intranet Zone</em>.</p>
</li>
</ul>
<p><img src="/assets/images/ADSSO/03.png" alt="img" /></p>
<ol>
<li>
<p>Select <strong>OK</strong> and then select <strong>OK</strong> again.</p>
</li>
<li>
<p>Browse to <strong>User Configuration</strong> > <strong>Policies</strong> > <strong>Administrative Templates</strong> > <strong>Windows Components</strong> > <strong>Internet Explorer</strong> > <strong>Internet Control Panel</strong> > <strong>Security Page</strong> > <strong>Intranet Zone</strong>. Then scroll down until you find <strong>Allow updates to status bar via script</strong>.</p>
</li>
</ol>
<p><img src="/assets/images/ADSSO/05.png" alt="img" /></p>
<ol>
<li>Enable the policy setting, and then select <strong>OK</strong>.</li>
</ol>
<p><img src="/assets/images/ADSSO/06.png" alt="img" /></p>
<h2 id="test-the-feature">Test the feature</h2>
<p>To test the feature for a specific user, ensure that all the following conditions are in place:</p>
<ul>
<li>The user signs in on a corporate device</li>
<li>The device is joined to your Active Directory domain. The devices <em>doesn’t</em> need to be <em>Azure AD Joined</em>.</li>
<li>The device has a direct connection to your Domain Controller (DC), either on the corporate wired or wireless network or via a remote access connection, such as a VPN connection.</li>
<li>You have rolled out the feature to this user throught Group Policy.</li>
</ul>
<p>To test the scenario where the user enters only the username, but not the password:</p>
<ul>
<li>Sign in to <code class="language-plaintext highlighter-rouge">https://myapps.microsoft.com</code>.</li>
</ul>
<p>To test the scenario where the user doesn’t have to enter the username or the password, use one of these steps:</p>
<ul>
<li>
<p>Sign in to <code class="language-plaintext highlighter-rouge">https://myapps.microsoft.com/Tenantname.onmicrosoft.com</code>
Replace <em>Tenantname</em> with the name of your tenant.</p>
</li>
<li>
<p>Sign in to <code class="language-plaintext highlighter-rouge">https://myapps.microsoft.com/domain.com</code>
Replace <em>domain.com</em> with one of your tenants verified domains.</p>
</li>
</ul>
<p>Below is an example of the sign-in experience after Seamless SSO has been enabled:</p>
<p><img src="/assets/images/ADSSO/SSO.gif" alt="img" /></p>
<h2 id="disable-single-sign-on">Disable Single Sign-On</h2>
<p>Perform the follwing steps on the on-premises server where you have the <code class="language-plaintext highlighter-rouge">Azure AD Connect Cloud Sync agent</code> installed:</p>
<h3 id="step-1-disable-the-feature-on-your-tenant">Step 1. Disable the feature on your tenant:</h3>
<ol>
<li>
<p>You need to have the Azure AD PowerShell module installed. You should already have this in place, as this one is a prerequisite for enabling Single Sign-On in the first place. If you need to re-install it, see guidance found <a href="https://learn.microsoft.com/en-us/powershell/azure/active-directory/install-adv2?view=azureadps-2.0">here</a>↗️</p>
</li>
<li>
<p>Open a new PowerShell window (as administrator) and navigate to the path where you extracted the files from Azure AD Connect under the <strong>Setup section</strong> in this blog post > <strong>Step 3</strong>.</p>
</li>
<li>
<p>Import the <code class="language-plaintext highlighter-rouge">Seamless SSO</code> PowerShell module: <code class="language-plaintext highlighter-rouge">Import-Module .\AzureADSSO.psd1</code>.</p>
</li>
<li>
<p>Call the <code class="language-plaintext highlighter-rouge">New-AzureADSSOAuthenticationContext</code> cmdlet. A popup windows should open. Log in using either a <code class="language-plaintext highlighter-rouge">Global Administrator</code> or a <code class="language-plaintext highlighter-rouge">Hybrid Identity Administrator</code> account.</p>
</li>
<li>
<p>Call <code class="language-plaintext highlighter-rouge">Enable-AzureADSSO -Enable $false</code> to disable SSO.</p>
</li>
</ol>
<h3 id="step-2-get-a-list-of-ad-forests-where-seamless-sso-has-been-enabled">Step 2. Get a list of AD forests where Seamless SSO has been enabled:</h3>
<p>Call <code class="language-plaintext highlighter-rouge">Get-AzureADSSOStatus | Convert-FromJson</code>. This command provides you with a list of AD forests (look at the “Domains” list) on which this feature has been enabled.</p>
<h3 id="step-3-manually-delete-the-azureadssoacc-computer-account-from-each-ad-forests-that-you-see-listed">Step 3. Manually delete the <code class="language-plaintext highlighter-rouge">AZUREADSSOACC</code> computer account from each AD forests that you see listed:</h3>
<p><img src="/assets/images/ADSSO/15.png" alt="img" /></p>
<h2 id="useful-resources">Useful resources</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-sso">Using Single Sign-On with cloud sync</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-sso-faq">Azure Active Directory Seamless Single Sign-On: Frequently asked questions</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-sso-how-it-works">Azure Active Directory Seamless Single Sign-On: Technical deep dive</a></li>
</ul>Freddie ChristiansenWhat is Azure Active Directory Seamless Single Sign-On?Deep dive: Hybrid Identity using Azure AD Connect Cloud Sync2023-01-08T08:31:00+01:002023-01-08T08:31:00+01:00https://www.cloudpilot.no/blog/Deep-dive-Hybrid-Identity-using-Azure-AD-Connect-Cloud-Sync<h2 id="deep-dive-hybrid-identity-using-azure-ad-connect-cloud-sync">Deep dive: Hybrid Identity using Azure AD Connect Cloud Sync</h2>
<p><img src="/assets/images/AADCloudSync/AAD.png" alt="img" /></p>
<p>Today, businesses are often becoming a mixture of on-premises and cloud applications. Users require access to applications both on-premises and in the cloud. This design requires the need to have a single identity across these various on-premises and cloud applications.</p>
<p>Organizations with an on-premises Active Directory Domain Services (AD DS) domain or forest can synchronize their AD DS user accounts, groups, and contacts with the Azure AD tenant of their Microsoft 365 subscription. This design is known as hybrid identity for Microsoft 365.</p>
<p>When an organization with an on-premises Active Directory plans to implement Microsoft 365, it must bring its on-premises accounts into Azure Active Directory. Doing so enables its users to use Microsoft 365’s cloud services, such as Exchange Online, SharePoint Online, Teams, and so on. To support Microsoft 365, most organizations want to avoid creating new user accounts in Azure AD, and instead use their existing on-premises accounts. While providing greater efficiency, this design also saves them from having to manage different passwords for each account.</p>
<p>Integrating on-premises directories with Azure AD makes users more productive by providing a common identity for accessing both cloud and on-premises resources. Users and organizations can take advantage of the following features:</p>
<ul>
<li>Users can use a single identity to access on-premises applications and cloud services such as Microsoft 365.</li>
<li>Organizations can use a single tool to provide an easy deployment experience for synchronization and sign in.
There are two options to choose from to implement user account synchronization between on-premises AD and Azure AD - <em>Azure AD Connect sync</em> and <em>Azure AD Connect Cloud Sync</em>.</li>
</ul>
<p>First of all, let’s take a trip down memory lane with a little overview of the currently most used synchronization tool for hybrid identity in Microsoft 365, Azure AD Connect.</p>
<h2 id="azure-ad-connect">Azure AD Connect</h2>
<p>Azure AD Connect sync is an on-premises Microsoft application that’s designed to meet and accomplish your hybrid identity goals. It’s typically installed on an on-premises domain-joined server, although it can be installed on a domain controller. Its only requirement is an outbound HTTPS connection to Microsoft 365 servers.</p>
<p>Azure AD Connect sync (formerly known as Dirsync and AD sync) was the first solution built for provisioning from on-premises AD to Azure AD. It currently has support for the most Azure AD hybrid scenarios, and it can support organizations with large directories. While Azure AD Connect sync is robust in its capabilities, it can also:</p>
<ul>
<li>Require a heavy investment in infrastructure resources.</li>
<li>Be complicated to configure.</li>
<li>Result in higher maintenance costs.</li>
</ul>
<p>Azure AD Connect comes with several features you can optionally turn on or are enabled by default. Some features may require more configuration in certain scenarios and topologies.</p>
<h2 id="what-is-azure-ad-connect-cloud-sync">What is Azure AD Connect Cloud Sync?</h2>
<p>Azure AD Connect cloud sync is a new offering from Microsoft designed to meet and accomplish your hybrid identity goals for synchronization of users, groups, and contacts to Azure AD. It accomplishes this by using the Azure AD cloud provisioning agent instead of the Azure AD Connect application.</p>
<p><img src="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/media/what-is-cloud-sync/architecture-1.png" alt="" /></p>
<h2 id="azure-ad-connect-cloud-sync">Azure AD Connect Cloud Sync</h2>
<p>Azure AD Connect Cloud Sync is also designed to meet and accomplish an organization’s hybrid identity goals for synchronization of on-premises users, groups, and contacts to Azure AD. Since Azure AD Connect Cloud Sync and Azure AD Connect sync both synchronize identities, what makes them different? While the next section outlines the feature differences between these two directory synchronization tools, suffice it to say that Azure AD Connect sync, which is based on older synchronization technology, requires a greater investment to deploy and support. By comparison, Azure AD Connect Cloud Sync uses a lightweight agent design that requires a minimal on-premises footprint. It also enables organizations to manage all their provisioning configuration in the cloud.</p>
<p>With Azure AD Connect Cloud Sync, provisioning from on-premises Active Directory to Azure AD is orchestrated in Microsoft Online Services. An organization only needs to deploy, in their on-premises or IaaS-hosted environment, a light-weight agent that acts as a bridge between on-premises Active Directory and Azure AD. The provisioning configuration is stored in Azure AD and managed as part of the service.</p>
<p>Azure AD Connect Cloud Sync supports most of the common Azure AD hybrid scenarios, with one major exception - Exchange hybrid deployments.</p>
<blockquote>
<p><strong>⚠️ Warning</strong>
An Exchange hybrid deployment allows for the co-existence of Exchange mailboxes both on-premises and in Microsoft 365. In an Exchange hybrid deployment, Azure AD Connect synchronizes a specific set of attributes from Azure AD back into the organization’s on-premises directory. However, the cloud provisioning agent for Azure AD Connect Cloud Sync doesn’t currently synchronize these attributes back into the on-premises directory. <strong>Therefore, organizations that plan to implement an Exchange hybrid deployment must use Azure AD Connect sync.</strong></p>
</blockquote>
<p>For organizations that don’t have, or that don’t plan to have an Exchange hybrid deployment, Azure AD Cloud Sync offers several advantages over Azure AD Connect sync. One of the major reasons to consider choosing Azure AD Cloud Sync is cost savings. Because Azure AD Cloud Sync uses a lightweight agent, organizations don’t have to deploy a robust server in their data centers to run the service. And while Azure AD Connect sync requires SQL Server for larger deployments, that’s not the case with Azure AD Cloud Sync. This design can potentially save and organization money on licensing costs. Along with the infrastructure savings, organizations will also spend less on support and maintenance throughout the life of the service due to its simplified architecture.</p>
<p>Due to its smaller on-premises footprint and multi-agent support, Azure AD Connect Cloud Sync is easier to set up. It also provides resiliency that isn’t available in Azure AD Connect sync. This design enables organizations to get Azure AD Connect Cloud Sync up and running in their deployments in a fraction of the time spent deploying Azure AD Connect sync. The simple setup is intuitive and streamlined, which enables end users to start collaborating quickly and seamlessly with minimal effort.</p>
<p>An organization can also deploy multiple agents to provide high availability and automatic failover. This design prevents service outages due to a server or network failure. As a result, end user frustration is eliminated. Support calls are also reduced for things like unprovisioned users and outdated group memberships.</p>
<blockquote>
<p><strong>ℹ️ Note</strong>
The cloud provisioning agent does not load balance if you have multiple agents installed. Only one agent is ever active.</p>
</blockquote>
<p>Azure AD Cloud Sync is also the ideal solution if you find yourself needing to provision users from multiple Active Directory forests that have no network connectivity between them. This scenario is often the case in complex business arrangements such as mergers and acquisitions. Azure AD Cloud Sync enables an organization to deploy agents into each of the isolated networks that can communicate independently between the forest and the respective network and Azure AD. <strong>And if you already have Azure AD Connect sync deployed in your environment, that doesn’t exclude you from deploying Azure AD Cloud Sync to that environment as well. Azure AD Cloud Sync can be used side by side with Azure AD Connect sync</strong>.</p>
<p>And lastly, Azure AD Cloud Sync can keep Azure AD up-to-date with greater frequency than Azure AD Connect sync. As such, organizations no longer have to wait 30 minutes for on-premises changes to be seen in Azure AD, as is the case when using Azure AD Connect sync.</p>
<h2 id="azure-ad-connect-vs-azure-ad-connect-cloud-sync">Azure AD Connect vs. Azure AD Connect Cloud Sync</h2>
<p>One of the primary differences between the two tools is where the provisioning configuration is stored and where provisioning occurs:</p>
<ul>
<li><strong>Azure AD Connect sync</strong>. The provisioning configuration is stored on the on-premises sync server. Provisioning also runs on the on-premises sync server.</li>
<li><strong>Azure AD Connect Cloud Sync</strong>. The provisioning configuration is stored in the cloud. Provisioning also runs in the cloud as part of the Azure AD provisioning service.</li>
</ul>
<p>The following table provides a comparison of the features in Azure AD Connect and Azure AD Connect Cloud Sync (as per january 2023):</p>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Azure AD Connect</th>
<th>Azure AD Connect Cloud Sync</th>
</tr>
</thead>
<tbody>
<tr>
<td>Connect to single on-premises AD forest</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Connect to multiple on-premises AD forests</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Connect to multiple disconnected on-premises AD forests</td>
<td> </td>
<td>✔</td>
</tr>
<tr>
<td>Lightweight agent installation model</td>
<td> </td>
<td>✔</td>
</tr>
<tr>
<td>Multiple active agents for high availability</td>
<td> </td>
<td>✔</td>
</tr>
<tr>
<td>Connect to LDAP directories</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for user objects</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for group objects</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for contact objects</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for device objects</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Allow basic customization for attribute flows</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Synchronize Exchange online attributes</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Synchronize extension attributes 1-15</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Synchronize customer defined AD attributes (directory extensions)</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for Password Hash Sync</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for Pass-Through Authentication</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for federation</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Seamless Single Sign-on</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Supports installation on a Domain Controller</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for Windows Server 2016</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Filter on Domains/OUs/groups</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Filter on objects’ attribute values</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Allow minimal set of attributes to be synchronized (MinSync)</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Allow removing attributes from flowing from AD to Azure AD</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Allow advanced customization for attribute flows</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for password writeback</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for device writeback</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for group writeback</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for merging user attributes from multiple domains</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Azure AD Domain Services support</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Exchange Hybrid writeback</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Unlimited number of objects per AD domain</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Support for up to 150,000 objects per AD domain</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Groups with up to 50,000 members</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Large groups with up to 250,000 members</td>
<td>✔</td>
<td> </td>
</tr>
<tr>
<td>Cross domain references</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>On-demand provisioning</td>
<td>✔</td>
<td>✔</td>
</tr>
<tr>
<td>Support for US Government</td>
<td>✔</td>
<td>✔</td>
</tr>
</tbody>
</table>
<h2 id="why-move-from-azure-ad-connect-to-azure-ad-connect-cloud-sync">Why move from Azure AD Connect to Azure AD Connect Cloud Sync?</h2>
<table>
<thead>
<tr>
<th> </th>
<th>Azure AD Connect</th>
<th>Azure AD Connect Cloud Sync</th>
</tr>
</thead>
<tbody>
<tr>
<td>Infrastructure</td>
<td>Heavy on-prem footprint</td>
<td>Lightweight agent</td>
</tr>
<tr>
<td>Deployment</td>
<td>Complex and time consuming</td>
<td>Quick and easy to setup w/configuration managed from Azure AD</td>
</tr>
<tr>
<td>Cost</td>
<td>Expensive</td>
<td>Low costs</td>
</tr>
<tr>
<td>Resilliency</td>
<td>No high availability</td>
<td>High availability</td>
</tr>
<tr>
<td>Performance</td>
<td>Sequential sync when connecting multiple source</td>
<td>Parallel sync accross multiple sources</td>
</tr>
<tr>
<td>AD Consolidation (Disconneced forests)</td>
<td>Requires AD consolidation</td>
<td>No AD consolidation needed</td>
</tr>
</tbody>
</table>
<h2 id="plan-for-directory-synchronization-using-azure-ad-connect-cloud-sync">Plan for directory synchronization using Azure AD Connect Cloud Sync</h2>
<p>The following list describes the various on-premises and Azure Active Directory (Azure AD) topologies that support Azure AD Connect Cloud Sync:</p>
<ul>
<li>
<p><strong>Single forest, single Azure AD tenant</strong>. The simplest topology is a single on-premises forest, with one or multiple domains, and a single Azure AD tenant. For an example of this scenario, see Microsoft Tutorial: <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/tutorial-single-forest">A single forest with a single Azure AD tenant</a> ↗️</p>
</li>
<li>
<p><strong>Multi-forest, single Azure AD tenant</strong>. A common topology includes multiple AD forests, with one or multiple domains, and a single Azure AD tenant.</p>
</li>
<li>
<p><strong>Existing forest with Azure AD Connect, new forest with cloud provisioning</strong>. This scenario is similar to the multi-forest scenario; however. this one involves an existing Azure AD Connect environment and then bringing on a new forest using Azure AD Connect Cloud Sync. For an example of this scenario, see Microsoft Tutorial: <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/tutorial-existing-forest">An existing forest with a new single Azure AD tenant </a> ↗️</p>
</li>
<li>
<p><strong>Piloting Azure AD Connect Cloud Sync in an existing hybrid AD forest</strong>. The piloting scenario involves the existence of both Azure AD Connect and Azure AD Connect Cloud Sync in the same forest. In this scenario, an object should be in scope in only one of the tools. For an example of this scenario: see Microsoft Tutorial: <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/tutorial-pilot-aadc-aadccp">Pilot Azure AD Connect Cloud Sync in an existing synced AD forest</a> ↗️</p>
</li>
</ul>
<p>Organizations should keep the following information in mind when considering these topologies:</p>
<ul>
<li>Users and groups must be uniquely identified across all forests.</li>
<li>Matching across forests doesn’t occur with Azure AD Connect Cloud Sync.</li>
<li>A user or group must be represented only once across all forests.</li>
<li>The source anchor for objects is chosen automatically. It uses <em>ms-DS-ConsistencyGuid</em> if present; otherwise, <em>ObjectGUID</em> is used.</li>
<li>You can’t change the attribute that’s used for the source anchor.</li>
</ul>
<h2 id="prerequisites-for-azure-ad-connect-cloud-sync">Prerequisites for Azure AD Connect Cloud Sync</h2>
<p>Organizations need the following to use Azure AD Connect Cloud Sync:</p>
<ul>
<li>A group Managed Service Account (gMSA). Azure AD Connect Cloud Sync supports and uses a gMSA for running the lightweight Cloud Sync agent. A gMSA is a managed domain account that:
<ul>
<li>provides automatic password management.</li>
<li>provides simplified service principal name (SPN) management.</li>
<li>delegates the management to other administrators.</li>
<li>extends this functionality over multiple servers.</li>
</ul>
</li>
<li>
<p>Domain Administrator or Enterprise Administrator credentials to create the Azure AD Connect Cloud Sync gMSA (group Managed Service Account) to run the agent service.</p>
</li>
<li>
<p>A hybrid identity administrator account for your Azure AD tenant that’s not a guest user.</p>
</li>
<li>
<p>An on-premises server for the Cloud Sync agent with Windows 2016 or later. This server should be a tier 0 server based on the <a href="https://learn.microsoft.com/en-us/windows-server/identity/securing-privileged-access/securing-privileged-access-reference-material">Active Directory administrative tier model</a>. Installing the Cloud Sync agent on a domain controller is supported.</p>
</li>
<li>
<p>High availability refers to the Azure AD Connect Cloud Sync’s ability to operate continuously without failure for a long time. By having multiple active Cloud Sync agents installed and running, Azure AD Connect Cloud Sync can continue to function even if one agent should fail. It’s recommended that organizations have three active agents installed for high availability.</p>
</li>
<li>On-premises firewall configurations.</li>
</ul>
<h2 id="azure-ad-connect-cloud-sync-video">Azure AD Connect Cloud Sync video</h2>
<p>Microsoft has provided the community with a video that shows an excellent overview of Azure AD Connect Cloud Sync:
<a href="https://www.youtube.com/watch?v=9T6lKEloq0Q"><img src="https://img.youtube.com/vi/9T6lKEloq0Q/0.jpg" alt="IMAGE_ALT" /></a></p>
<h2 id="migrate-from-azure-ad-connect-to-azure-ad-connect-cloud-sync">Migrate from Azure AD Connect to Azure AD Connect Cloud Sync</h2>
<p>In the following sections, I will descripe a <em>possible</em> workflow for transition from <code class="language-plaintext highlighter-rouge">Azure AD Connect</code> to <code class="language-plaintext highlighter-rouge">Azure AD Connect Cloud Sync</code>.
Note: This may not be best-practice.</p>
<p>Scenario: A reasonably traditional Azure AD Connect configuration. In my demo environment, I have a have a <strong>single forest, single domain with a single domain controller with Azure AD Connect</strong> installed. I prefer to use <em>Organization based</em> synchronisation. By using this approach I am able to select which OUs (containing users and/or groups) that will be synchronized to Azure AD.</p>
<h3 id="step-1-configuring-staging-mode-for-azure-ad-connect">Step 1: Configuring <em>Staging mode</em> for Azure AD Connect</h3>
<p>Start off by setting Azure AD Connect into <em>Staging mode</em>. This is so we can ensure that any changes made by deploying the new Azure <em>AD Connect Cloud Sync</em> are not being impacted by our existing <em>Azure AD Connect</em> implementation.</p>
<ol>
<li>
<p>Start by opening <strong>Azure AD Connect</strong> on your server.</p>
</li>
<li>
<p>Select <strong>Configure</strong>.
<img src="/assets/images/AADCloudSync/01.png" alt="img" /></p>
</li>
<li>
<p>Select <strong>Configure staging mode</strong> and click <strong>Next</strong>.
<img src="/assets/images/AADCloudSync/02.png" alt="img" /></p>
</li>
<li>
<p>Log in with either your <strong>Azure AD Global Administrator</strong> or <strong>Hybrid Identity Administrator</strong> credentials and click <strong>Next</strong>.
<img src="/assets/images/AADCloudSync/03.png" alt="enter image description here" /></p>
</li>
<li>
<p>Check <strong>Enable stagning mode</strong> and click <strong>Next</strong>.
<img src="/assets/images/AADCloudSync/04.png" alt="img" /></p>
</li>
<li>
<p>Verify that the <em>Start the synchronization process when configuration completes</em> <strong>checkbox</strong> is ticked off and then press <strong>Configure</strong>.
<img src="/assets/images/AADCloudSync/05.png" alt="img" /></p>
</li>
<li>
<p>The configuration completes with a confirmation that the <strong>staging mode</strong> is successfully enabled. Press <strong>Exit</strong> to close the wizard.
<img src="/assets/images/AADCloudSync/06.png" alt="img" /></p>
</li>
</ol>
<p>You can also verify this by using PowerShell:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-ADSyncScheduler</span><span class="w">
</span><span class="n">AllowedSyncCycleInterval</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">00:30:00</span><span class="w">
</span><span class="n">CurrentlyEffectiveSyncCycleInterval</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">00:30:00</span><span class="w">
</span><span class="n">CustomizedSyncCycleInterval</span><span class="w"> </span><span class="p">:</span><span class="w">
</span><span class="n">NextSyncCyclePolicyType</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Delta</span><span class="w">
</span><span class="n">NextSyncCycleStartTimeInUTC</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">05.01.2023</span><span class="w"> </span><span class="nx">20:28:37</span><span class="w">
</span><span class="n">PurgeRunHistoryInterval</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">7.00:00:00</span><span class="w">
</span><span class="n">SyncCycleEnabled</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span><span class="n">MaintenanceEnabled</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span><span class="n">StagingModeEnabled</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span><span class="n">SchedulerSuspended</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">False</span><span class="w">
</span><span class="n">SyncCycleInProgress</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">False</span><span class="w">
</span></code></pre></div></div>
<p><em>(See that the returned property named <code class="language-plaintext highlighter-rouge">StagingModeEnabled</code> equals <code class="language-plaintext highlighter-rouge">True</code> in the code snippet above)</em>.</p>
<p>This completes step 1. In the next step we will download and install the <code class="language-plaintext highlighter-rouge">Azure AD Connect Cloud Sync agent</code>.</p>
<h3 id="step-2-download-and-install-the-new-azure-ad-connect-cloud-sync-agent">Step 2: Download and install the new Azure AD Connect Cloud Sync agent</h3>
<p>Now that we have successfully put out existing Azure AD Connect instance in <strong>staging mode</strong>, we are ready to continue our migration. The next step is to download and install the lightweight <strong>Azure AD Connect Cloud Sync agent</strong> on the same server.</p>
<ol>
<li>
<p>Start by logging into <code class="language-plaintext highlighter-rouge">Azure Active Directory admin center</code>: <a href="https://aad.portal.azure.com">https://aad.portal.azure.com</a></p>
</li>
<li>
<p>Select <strong>Azure Active Directory,</strong> then click <strong>Azure AD Connect</strong>.
<img src="/assets/images/AADCloudSync/07.png" alt="img" /></p>
</li>
<li>
<p>Select <strong>Manage Azure AD cloud sync</strong>, this will take you to the cloud sync configuration blade.
<img src="/assets/images/AADCloudSync/08.png" alt="img" /></p>
</li>
<li>
<p>Press <strong>Download agent</strong> on the top menu and start the download the pressing the <strong>Accept terms & download</strong> button.
<img src="/assets/images/AADCloudSync/09.png" alt="img" />
<img src="/assets/images/AADCloudSync/10.png" alt="img" /></p>
</li>
<li>
<p>When the download is complete, run the installer. When prompted, agree to the terms and conditions and click <strong>Install</strong>.</p>
</li>
</ol>
<p><img src="/assets/images/AADCloudSync/11.png" alt="img" /></p>
<ol>
<li>
<p>When the configuration wizard launches, click <strong>Next</strong> to continue.
<img src="/assets/images/AADCloudSync/12.png" alt="img" /></p>
</li>
<li>
<p>On the <em>Connect to Azure AD</em> screen, the authentication box will immediately open and prompts you to sign in with your <strong>Azure AD Global Administrator</strong> or <strong>Hybrid Identity Administrator</strong> credentials. Complete the sign in process to continue.</p>
</li>
<li>
<p>The <em>Configure Service Account</em> screen opens. Select the <strong>Create gMSA</strong> options, then enter your Active Directory domain admin credentials to setup the <strong>gMSA account</strong> and click <strong>Next</strong> to continue.
<img src="/assets/images/AADCloudSync/13.png" alt="img" /></p>
</li>
</ol>
<blockquote>
<p>ℹ️ <em>Tip</em>
If you want to read more about gMSA - <strong>Group Managed Service Accounts</strong>, see official Microsoft documentation found <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-prerequisites?tabs=public-cloud#group-managed-service-accounts">here</a>↗️</p>
</blockquote>
<ol>
<li>
<p>On the <em>Connect Active Directory</em> screen, your domain should already be connected. If not, click <strong>Add directory</strong> to add your domain and click <strong>Next</strong>.
<img src="/assets/images/AADCloudSync/14.png" alt="img" /></p>
</li>
<li>
<p>On the final page, you will see the name of the <em>group managed service account</em> that will be created. Press <strong>Confirm</strong> to complete the setup. This will take approx. 2 minutes.
<img src="/assets/images/AADCloudSync/15.png" alt="img" /></p>
</li>
<li>
<p>Then the agent installation and configuration is completed, close the wizard by clicking <strong>Exit</strong>.
<img src="/assets/images/AADCloudSync/16.png" alt="enter image description here" /></p>
</li>
</ol>
<p>This completes step 2. In the next step we will configure and test synchronization to the cloud with our new <strong>Azure AD Connect Cloud Sync</strong> instance.</p>
<h3 id="step-3-configure-azure-ad-connect-cloud-sync">Step 3: Configure Azure AD Connect Cloud Sync</h3>
<p>When the sync agent is successfully installed on our server, the rest of the configuration is done through <strong>Azure Active Directory admin center</strong>.</p>
<ol>
<li>
<p>Start off by returning to the <strong>Azure AD Connect cloud sync</strong> configuration blade: <a href="https://aad.portal.azure.com/#view/Microsoft_AAD_Connect_Provisioning/ProvisioningManagementBlade">https://aad.portal.azure.com/#view/Microsoft_AAD_Connect_Provisioning/ProvisioningManagementBlade</a></p>
</li>
<li>
<p>The <strong>New configuration</strong> options on the top menu is no longer greyed out. Select it.
<img src="/assets/images/AADCloudSync/17.png" alt="img" /></p>
</li>
<li>
<p>Under <strong>Which Active Directory domain would you like to sync</strong>, you should see only one (<em>the domain you configured under step 2 in this tutorial</em>). Re-enable <em>password hash sync</em>, and click the <strong>Create</strong> button at the bottom of the page.
<img src="/assets/images/AADCloudSync/18.png" alt="img" /></p>
</li>
<li>
<p>The cloud sync configuration will take 20-30 seconds to complete and then you will be redirected to the <strong>Edit cloud sync configuration</strong> blade.
<img src="/assets/images/AADCloudSync/19.png" alt="img" /></p>
</li>
<li>
<p>Start on the top and work your way down the numbered settings. Start at the <strong>Scope</strong> settings. Click <strong>Click to edit scoping filters</strong>.
<img src="/assets/images/AADCloudSync/20.png" alt="enter image description here" /></p>
</li>
<li>
<p>A flyout will appear on the right. Here you will have to option to filter by <em>All users</em>, <em>Selected security groups</em> or <em>Selected organizational units</em>. As mentioned before, I prefer to filter by organizational units. Select the <strong>radio button</strong> next to <strong>Selected organizational units</strong>. Now we need the <em>distinguished name</em> of the OU object we want to synchronize to Azure AD.
<img src="/assets/images/AADCloudSync/21.png" alt="img" /></p>
</li>
<li>
<p>Head over to your server where you have access to <strong>Active Directory Users And Computers</strong>. Verify what you have enabled <em>Advanced Features</em> under <em>View</em>.
<img src="/assets/images/AADCloudSync/22.png" alt="img" /></p>
</li>
<li>
<p>Right-click your target OU, and select <strong>Properties</strong>. Select the <strong>Attribute Editor</strong> tab and double-click on the <strong>distinguisedName</strong> value.</p>
</li>
<li>
<p>Copy the <strong>Value</strong> (<em>right-click > Copy</em> or <em>Ctrl + C</em>)
<img src="/assets/images/AADCloudSync/22_01.png" alt="img" /></p>
</li>
<li>
<p>Paste the value into the textbox into your <strong>Scope users</strong> configuration and click the <strong>Add button</strong>. Repeat this process if you have additional OUs (or users/security groups ) that you wish to synchronize. Click <strong>Done</strong> button at the buttom of the blade then finished.
<img src="/assets/images/AADCloudSync/23.png" alt="enter image description here" /></p>
</li>
</ol>
<blockquote>
<p>⚠️ <strong>Important!</strong>
When migrating to Azure AD Connect Cloud Sync from an existing Azure AD Connect implementation, it is crusial that you include <strong>all previously synced AD objects</strong>. If not, you may find yourself in need of restoring deleted objects.</p>
</blockquote>
<ol>
<li>
<p>You can leave the <strong>Manage attributtes</strong> options as <strong>default</strong> unless your organization have some custom mapping of attributes. Ensure <strong>Sync password hashes</strong> are enabled.
<img src="/assets/images/AADCloudSync/24.png" alt="img" /></p>
</li>
<li>
<p>Under the <strong>Validate</strong> step, it is recommended that the sync is working as expected. Select the <strong>Provision a user</strong> button. Repeat the steps previously described in this tutorial to collect the <strong>distinguishedName</strong> value of your test user account in one of your already set up OUs. Paste the value on the <strong>Provision on-demand</strong> blade and click <strong>Provision</strong>.
<img src="/assets/images/AADCloudSync/25.png" alt="img" /></p>
</li>
</ol>
<p><img src="/assets/images/AADCloudSync/26.png" alt="enter image description here" /></p>
<ol>
<li>
<p>Wait 5-10 seconds for the provisioning to complete. Click on the <strong>View details</strong> button under each step to get more insights and to verify that the settings display as expected. Click <strong>Finish</strong> button at the buttom of the page to continue.
<img src="/assets/images/AADCloudSync/27.png" alt="enter image description here" /></p>
</li>
<li>
<p>Under the <strong>Settings</strong> step, enter an email address to receive notifications when an error has occured. Also note that under this step, you’ll have the option to adjust the setting <em>Prevent accidential deletion</em>. I highly recommend that you leave this option <strong>on</strong> and adjust the threshold under <em>Accidential delete threshold</em> from the default value of 500 to somewhat more appropriate and manageable level.
<img src="/assets/images/AADCloudSync/28.png" alt="img" /></p>
</li>
<li>
<p>In the final <strong>Deploy step</strong>, when ready, click <strong>Enable</strong> and then click <strong>Save</strong> at the top of the page.
<img src="/assets/images/AADCloudSync/29.png" alt="img" /></p>
</li>
</ol>
<p>This completes step 3. In our final step in this tutorial, we will complete the migration by removing the old instance of <code class="language-plaintext highlighter-rouge">Azure AD Connect</code>.</p>
<h3 id="step-4--removal-of-azure-ad-connect-agent">Step 4: Removal of Azure AD Connect agent</h3>
<p>Now that Azure AD Connect Cloud Sync is installed successfully and running, we ar ready to uninstall the old Azure AD Connect instance.</p>
<ol>
<li>
<p>On your server, open the <strong>Control panel</strong>, under <em>Programs</em>, select <strong>Uninstall a program</strong>.
<img src="/assets/images/AADCloudSync/31.png" alt="img" /></p>
</li>
<li>
<p>Select <strong>Microsoft Azure AD Connect</strong> and click <strong>Uninstall</strong>.
<img src="/assets/images/AADCloudSync/32.png" alt="img" /></p>
</li>
<li>
<p>Leave the defaults and click <strong>Remove</strong>.
<img src="/assets/images/AADCloudSync/33.png" alt="img" /></p>
</li>
<li>
<p>Wait for the unistall to complete. When finished, click <strong>Exit</strong>.
<img src="/assets/images/AADCloudSync/34.png" alt="img" /></p>
</li>
</ol>
<h2 id="enable-password-writeback-in-azure-ad-connect-cloud-sync">Enable password writeback in Azure AD Connect Cloud Sync</h2>
<p>You can enable password writeback for your synced users from on-premises directories directly in the Azure portal or through PowerShell.</p>
<ul>
<li>Azure portal:
<ol>
<li>Sign in to the Azure portal using a <code class="language-plaintext highlighter-rouge">Global Administrator account</code>.</li>
<li>Search for and select <strong>Azure Active Directory</strong>, select <strong>Password reset</strong>, then choose <strong>On-premises integration</strong>.</li>
<li>Check the option for <strong>Write back passwords to your on-premises directory</strong>.</li>
<li>If Azure AD Connect provisioning agents are detected, you can additionally check the option for <strong>Write back passwords with Azure AD Connect cloud sync</strong>.
<img src="/assets/images/AADCloudSync/38.png" alt="enter image description here" /></li>
</ol>
</li>
<li>Powershell:
With PowerShell you can enable Azure AD Connect cloud sync by using the <code class="language-plaintext highlighter-rouge">Set-AADCloudSyncPasswordWritebackConfiguration</code> cmdlet on the servers with the provisioning agents. You will need global administrator credentials:
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Import-Module</span><span class="w"> </span><span class="s2">"C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Microsoft.CloudSync.Powershell.dll"</span><span class="w">
</span><span class="n">Set-AADCloudSyncPasswordWritebackConfiguration</span><span class="w"> </span><span class="nt">-Enable</span><span class="w"> </span><span class="bp">$true</span><span class="w"> </span><span class="nt">-Credential</span><span class="w"> </span><span class="p">(</span><span class="n">Get-Credential</span><span class="p">)</span><span class="w">
</span></code></pre></div> </div>
</li>
</ul>
<h2 id="monitoring">Monitoring</h2>
<h3 id="view-sync-status-info">View sync status info</h3>
<p>Under the <strong>Azure AD Connect cloud sync</strong> blade, under <strong>Configuration</strong> you’ll see your installed agents. Click the <strong>Status state</strong> to see the status of your agent configuration.
<img src="/assets/images/AADCloudSync/35.png" alt="img" /></p>
<p><img src="/assets/images/AADCloudSync/36.png" alt="img" /></p>
<h3 id="directory-sync-status">Directory sync status</h3>
<p>After Azure AD Connect Cloud Sync is successfully installed, you’ll notice that there are created a new <code class="language-plaintext highlighter-rouge">service principal</code> for the directory sync. The account details and more information can be seen in the <a href="https://admin.microsoft.com">Microsoft 365 admin center</a>, under <strong>Health</strong>, <strong>Directory sync status</strong>.
<img src="/assets/images/AADCloudSync/39.png" alt="img" /></p>
<h3 id="logging">Logging</h3>
<ul>
<li>
<p>On the server where Azure AD Connect Cloud Sync is installed:
By default, the agent emits minimal error messages and stack trace information. You can find these trace logs in the following folder: <code class="language-plaintext highlighter-rouge">C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent\Trace</code>.</p>
</li>
<li>
<p>In Azure:
Under <strong>Manage Azure AD Cloud sync</strong> under <strong>Azure AD Connect</strong> in the <strong>Azure portal</strong>, on the top of the page, you’ll find the <strong>Logs</strong> button. Click on it to view the <em>Provisioning Logs</em>. This is a great tool for monitoring and get insights into the objects being synchronized between your on-premises Active Directory environment and Azure AD. There is also an <em>Download</em> button for exporting the log into <em>JSON or CSV format</em>.</p>
</li>
</ul>
<p><img src="/assets/images/AADCloudSync/30.png" alt="img" /></p>
<h2 id="manage-azure-ad-connect-cloud-sync-using-powershell">Manage Azure AD Connect Cloud Sync using PowerShell</h2>
<p>No blog post without some PowerShell :smile:</p>
<p>Microsoft has provided us with a PowerShell module named <code class="language-plaintext highlighter-rouge">AADCloudSyncTools</code>.
The <code class="language-plaintext highlighter-rouge">AADCloudSyncTools</code> module provides a set of useful tools that can help you manage your deployments of Azure Active Directory Connect (Azure AD Connect) cloud sync.</p>
<h3 id="install-the-aadcloudsynctools-powershell-module">Install the <code class="language-plaintext highlighter-rouge">AADCloudSyncTools</code> PowerShell module</h3>
<ol>
<li>
<p>Open Windows PowerShell with administrative privileges.</p>
</li>
<li>
<p>Run <code class="language-plaintext highlighter-rouge">Import-module -Name "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools"</code>.</p>
</li>
<li>
<p>To verify that the module was imported, run <code class="language-plaintext highlighter-rouge">Get-module AADCloudSyncTools</code>.</p>
<p>You should now see information about the module.</p>
</li>
<li>
<p>To install the AADCloudSyncTools module prerequisites, run <code class="language-plaintext highlighter-rouge">Install-AADCloudSyncToolsPrerequisites</code>.</p>
</li>
<li>
<p>On the first run, the PowerShellGet module will be installed if it’s not present. To load the new PowerShellGet module, close the PowerShell window and open a new PowerShell session with administrative privileges.</p>
</li>
<li>
<p>Import the module again by running <code class="language-plaintext highlighter-rouge">Import-module -Name "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools"</code>.</p>
</li>
<li>
<p>Run <code class="language-plaintext highlighter-rouge">Install-AADCloudSyncToolsPrerequisites</code> again to install the MSAL and Azure AD modules.
All <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/reference-powershell#prerequisites">prerequisites</a> should now be installed.
<img src="/assets/images/AADCloudSync/37.png" alt="img" /></p>
</li>
<li>
<p>Every time you want to use the <code class="language-plaintext highlighter-rouge">AADCloudSyncTools</code> module in a new PowerShell session, run the following command:</p>
</li>
</ol>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Import-Module</span><span class="w"> </span><span class="s2">"C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools"</span><span class="w">
</span></code></pre></div></div>
<h3 id="aadcloudsynctools-cmdlet-overview">AADCloudSyncTools cmdlet overview</h3>
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Connect-AADCloudSyncTools</td>
<td>This cmdlet uses the <code class="language-plaintext highlighter-rouge">MSAL.PS</code> module to request a token for the Azure AD administrator to access Microsoft Graph.</td>
</tr>
<tr>
<td>Export-AADCloudSyncToolsLogs</td>
<td>This cmdlet exports and packages all the troubleshooting data in a compressed file. See details found <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/reference-powershell#export-aadcloudsynctoolslogs">here</a>.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsInfo</td>
<td>This cmdlet shows Azure AD tenant details and the state of internal variables.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJob</td>
<td>This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s information. You can also call it by using the specific sync job ID as a parameter.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJobSchedule</td>
<td>This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s schedule. You can also call it by using the specific sync job ID as a parameter.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJobSchema</td>
<td>This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s schema.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJobScope</td>
<td>This cmdlet uses Microsoft Graph to get the sync job’s schema for the provided sync job ID and outputs all filter groups’ scopes.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJobSettings</td>
<td>This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s settings. You can also call it by using the specific sync job ID as a parameter.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsJobStatus</td>
<td>This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s status. You can also call it by using the specific sync job ID as a parameter.</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsServiceAccount</td>
<td>Returns the Azure AD Cloud Sync Service Account</td>
</tr>
<tr>
<td>Get-AADCloudSyncToolsServicePrincipal</td>
<td>This cmdlet uses Microsoft Graph to get the service principals for Azure AD and/or Azure Service Fabric. Without parameters, it will return only Azure AD service principals.</td>
</tr>
<tr>
<td>Install-AADCloudSyncToolsPrerequisites</td>
<td>This cmdlet checks for the presence of PowerShellGet v2.2.4.1 or later, the Azure AD module, and the <code class="language-plaintext highlighter-rouge">MSAL.PS</code> module. It installs these items if they’re missing.</td>
</tr>
<tr>
<td>Invoke-AADCloudSyncToolsGraphQuery</td>
<td>This cmdlet invokes a web request for the URI, method, and body specified as parameters.</td>
</tr>
<tr>
<td>Remove-AADCloudSyncToolsGroupMembers</td>
<td>Searches members of Azure AD group in AD and prompts the members not found in AD for removing from Azure AD</td>
</tr>
<tr>
<td>Repair-AADCloudSyncToolsAccount</td>
<td>This cmdlet uses Azure AD PowerShell to delete the current account (if present). It then resets the sync account authentication with a new sync account in Azure AD.</td>
</tr>
<tr>
<td>Restart-AADCloudSyncToolsJob</td>
<td>This cmdlet restarts a full synchronization.</td>
</tr>
<tr>
<td>Resume-AADCloudSyncToolsJob</td>
<td>This cmdlet continues synchronization from the previous watermark.</td>
</tr>
<tr>
<td>Set-AADCloudSyncToolsJobSchema</td>
<td>Uses Graph to get AD2AAD Service Principals and returns the Synchronization Job’s Schema.</td>
</tr>
<tr>
<td>Set-AADCloudSyncToolsTenantId</td>
<td>This can be used when the Cloud Sync Agent is not currently installed or it is installed but you want to target a different Azure AD tenant than the one Cloud Sync Agent is using. The TenantId set with this function will only persist during the PowerShell session and you’ll need to call this function before using <code class="language-plaintext highlighter-rouge">Connect-AADCloudSyncTools</code>.</td>
</tr>
<tr>
<td>Start-AADCloudSyncToolsVerboseLogs</td>
<td>This cmdlet modifies <em>AADConnectProvisioningAgent.exe.config</em> to enable verbose tracing and restarts the AADConnectProvisioningAgent service. You can use <code class="language-plaintext highlighter-rouge">-SkipServiceRestart</code> to prevent service restart, but any configuration changes will not take effect. You can find these trace logs in the folder <em>C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent\Trace</em>.</td>
</tr>
<tr>
<td>Stop-AADCloudSyncToolsVerboseLogs</td>
<td>This cmdlet modifies <em>AADConnectProvisioningAgent.exe.config</em> to disable verbose tracing and restarts the AADConnectProvisioningAgent service. You can use <code class="language-plaintext highlighter-rouge">-SkipServiceRestart</code> to prevent service restart, but any configuration changes will not take effect.</td>
</tr>
<tr>
<td>Suspend-AADCloudSyncToolsJob</td>
<td>This cmdlet pauses synchronization.</td>
</tr>
</tbody>
</table>
<h3 id="additional-useful-resources">Additional useful resources</h3>
<ul>
<li>
<p>Frequently asked questions: <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/reference-cloud-sync-faq">Aure Active Directory Connect cloud sync FAQ</a></p>
</li>
<li>
<p>Read the official <a href="https://aka.ms/cloudprovisioningdeploymentguide">configuration guide</a> from Microsoft for further details into creating a hybrid identity environment using Azure Active Directory (Azure AD) Connect cloud sync.</p>
</li>
<li>
<p>For troubleshooting tips, see this article: <a href="https://learn.microsoft.com/en-us/azure/active-directory/cloud-sync/how-to-troubleshoot">Cloud sync troubleshooting</a>.</p>
</li>
</ul>Freddie ChristiansenDeep dive: Hybrid Identity using Azure AD Connect Cloud Sync Today, businesses are often becoming a mixture of on-premises and cloud applications. Users require access to applications both on-premises and in the cloud. This design requires the need to have a single identity across these various on-premises and cloud applications. Organizations with an on-premises Active Directory Domain Services (AD DS) domain or forest can synchronize their AD DS user accounts, groups, and contacts with the Azure AD tenant of their Microsoft 365 subscription. This design is known as hybrid identity for Microsoft 365. When an organization with an on-premises Active Directory plans to implement Microsoft 365, it must bring its on-premises accounts into Azure Active Directory. Doing so enables its users to use Microsoft 365’s cloud services, such as Exchange Online, SharePoint Online, Teams, and so on. To support Microsoft 365, most organizations want to avoid creating new user accounts in Azure AD, and instead use their existing on-premises accounts. While providing greater efficiency, this design also saves them from having to manage different passwords for each account. Integrating on-premises directories with Azure AD makes users more productive by providing a common identity for accessing both cloud and on-premises resources. Users and organizations can take advantage of the following features: Users can use a single identity to access on-premises applications and cloud services such as Microsoft 365. Organizations can use a single tool to provide an easy deployment experience for synchronization and sign in. There are two options to choose from to implement user account synchronization between on-premises AD and Azure AD - Azure AD Connect sync and Azure AD Connect Cloud Sync. First of all, let’s take a trip down memory lane with a little overview of the currently most used synchronization tool for hybrid identity in Microsoft 365, Azure AD Connect. Azure AD Connect Azure AD Connect sync is an on-premises Microsoft application that’s designed to meet and accomplish your hybrid identity goals. It’s typically installed on an on-premises domain-joined server, although it can be installed on a domain controller. Its only requirement is an outbound HTTPS connection to Microsoft 365 servers. Azure AD Connect sync (formerly known as Dirsync and AD sync) was the first solution built for provisioning from on-premises AD to Azure AD. It currently has support for the most Azure AD hybrid scenarios, and it can support organizations with large directories. While Azure AD Connect sync is robust in its capabilities, it can also: Require a heavy investment in infrastructure resources. Be complicated to configure. Result in higher maintenance costs. Azure AD Connect comes with several features you can optionally turn on or are enabled by default. Some features may require more configuration in certain scenarios and topologies. What is Azure AD Connect Cloud Sync? Azure AD Connect cloud sync is a new offering from Microsoft designed to meet and accomplish your hybrid identity goals for synchronization of users, groups, and contacts to Azure AD. It accomplishes this by using the Azure AD cloud provisioning agent instead of the Azure AD Connect application. Azure AD Connect Cloud Sync Azure AD Connect Cloud Sync is also designed to meet and accomplish an organization’s hybrid identity goals for synchronization of on-premises users, groups, and contacts to Azure AD. Since Azure AD Connect Cloud Sync and Azure AD Connect sync both synchronize identities, what makes them different? While the next section outlines the feature differences between these two directory synchronization tools, suffice it to say that Azure AD Connect sync, which is based on older synchronization technology, requires a greater investment to deploy and support. By comparison, Azure AD Connect Cloud Sync uses a lightweight agent design that requires a minimal on-premises footprint. It also enables organizations to manage all their provisioning configuration in the cloud. With Azure AD Connect Cloud Sync, provisioning from on-premises Active Directory to Azure AD is orchestrated in Microsoft Online Services. An organization only needs to deploy, in their on-premises or IaaS-hosted environment, a light-weight agent that acts as a bridge between on-premises Active Directory and Azure AD. The provisioning configuration is stored in Azure AD and managed as part of the service. Azure AD Connect Cloud Sync supports most of the common Azure AD hybrid scenarios, with one major exception - Exchange hybrid deployments. ⚠️ Warning An Exchange hybrid deployment allows for the co-existence of Exchange mailboxes both on-premises and in Microsoft 365. In an Exchange hybrid deployment, Azure AD Connect synchronizes a specific set of attributes from Azure AD back into the organization’s on-premises directory. However, the cloud provisioning agent for Azure AD Connect Cloud Sync doesn’t currently synchronize these attributes back into the on-premises directory. Therefore, organizations that plan to implement an Exchange hybrid deployment must use Azure AD Connect sync. For organizations that don’t have, or that don’t plan to have an Exchange hybrid deployment, Azure AD Cloud Sync offers several advantages over Azure AD Connect sync. One of the major reasons to consider choosing Azure AD Cloud Sync is cost savings. Because Azure AD Cloud Sync uses a lightweight agent, organizations don’t have to deploy a robust server in their data centers to run the service. And while Azure AD Connect sync requires SQL Server for larger deployments, that’s not the case with Azure AD Cloud Sync. This design can potentially save and organization money on licensing costs. Along with the infrastructure savings, organizations will also spend less on support and maintenance throughout the life of the service due to its simplified architecture. Due to its smaller on-premises footprint and multi-agent support, Azure AD Connect Cloud Sync is easier to set up. It also provides resiliency that isn’t available in Azure AD Connect sync. This design enables organizations to get Azure AD Connect Cloud Sync up and running in their deployments in a fraction of the time spent deploying Azure AD Connect sync. The simple setup is intuitive and streamlined, which enables end users to start collaborating quickly and seamlessly with minimal effort. An organization can also deploy multiple agents to provide high availability and automatic failover. This design prevents service outages due to a server or network failure. As a result, end user frustration is eliminated. Support calls are also reduced for things like unprovisioned users and outdated group memberships. ℹ️ Note The cloud provisioning agent does not load balance if you have multiple agents installed. Only one agent is ever active. Azure AD Cloud Sync is also the ideal solution if you find yourself needing to provision users from multiple Active Directory forests that have no network connectivity between them. This scenario is often the case in complex business arrangements such as mergers and acquisitions. Azure AD Cloud Sync enables an organization to deploy agents into each of the isolated networks that can communicate independently between the forest and the respective network and Azure AD. And if you already have Azure AD Connect sync deployed in your environment, that doesn’t exclude you from deploying Azure AD Cloud Sync to that environment as well. Azure AD Cloud Sync can be used side by side with Azure AD Connect sync. And lastly, Azure AD Cloud Sync can keep Azure AD up-to-date with greater frequency than Azure AD Connect sync. As such, organizations no longer have to wait 30 minutes for on-premises changes to be seen in Azure AD, as is the case when using Azure AD Connect sync. Azure AD Connect vs. Azure AD Connect Cloud Sync One of the primary differences between the two tools is where the provisioning configuration is stored and where provisioning occurs: Azure AD Connect sync. The provisioning configuration is stored on the on-premises sync server. Provisioning also runs on the on-premises sync server. Azure AD Connect Cloud Sync. The provisioning configuration is stored in the cloud. Provisioning also runs in the cloud as part of the Azure AD provisioning service. The following table provides a comparison of the features in Azure AD Connect and Azure AD Connect Cloud Sync (as per january 2023): Feature Azure AD Connect Azure AD Connect Cloud Sync Connect to single on-premises AD forest ✔ ✔ Connect to multiple on-premises AD forests ✔ ✔ Connect to multiple disconnected on-premises AD forests ✔ Lightweight agent installation model ✔ Multiple active agents for high availability ✔ Connect to LDAP directories ✔ Support for user objects ✔ ✔ Support for group objects ✔ ✔ Support for contact objects ✔ ✔ Support for device objects ✔ Allow basic customization for attribute flows ✔ ✔ Synchronize Exchange online attributes ✔ ✔ Synchronize extension attributes 1-15 ✔ ✔ Synchronize customer defined AD attributes (directory extensions) ✔ Support for Password Hash Sync ✔ ✔ Support for Pass-Through Authentication ✔ Support for federation ✔ ✔ Seamless Single Sign-on ✔ ✔ Supports installation on a Domain Controller ✔ ✔ Support for Windows Server 2016 ✔ ✔ Filter on Domains/OUs/groups ✔ ✔ Filter on objects’ attribute values ✔ Allow minimal set of attributes to be synchronized (MinSync) ✔ ✔ Allow removing attributes from flowing from AD to Azure AD ✔ ✔ Allow advanced customization for attribute flows ✔ Support for password writeback ✔ ✔ Support for device writeback ✔ Support for group writeback ✔ Support for merging user attributes from multiple domains ✔ Azure AD Domain Services support ✔ Exchange Hybrid writeback ✔ Unlimited number of objects per AD domain ✔ Support for up to 150,000 objects per AD domain ✔ ✔ Groups with up to 50,000 members ✔ ✔ Large groups with up to 250,000 members ✔ Cross domain references ✔ ✔ On-demand provisioning ✔ ✔ Support for US Government ✔ ✔ Why move from Azure AD Connect to Azure AD Connect Cloud Sync? Azure AD Connect Azure AD Connect Cloud Sync Infrastructure Heavy on-prem footprint Lightweight agent Deployment Complex and time consuming Quick and easy to setup w/configuration managed from Azure AD Cost Expensive Low costs Resilliency No high availability High availability Performance Sequential sync when connecting multiple source Parallel sync accross multiple sources AD Consolidation (Disconneced forests) Requires AD consolidation No AD consolidation needed Plan for directory synchronization using Azure AD Connect Cloud Sync The following list describes the various on-premises and Azure Active Directory (Azure AD) topologies that support Azure AD Connect Cloud Sync: Single forest, single Azure AD tenant. The simplest topology is a single on-premises forest, with one or multiple domains, and a single Azure AD tenant. For an example of this scenario, see Microsoft Tutorial: A single forest with a single Azure AD tenant ↗️ Multi-forest, single Azure AD tenant. A common topology includes multiple AD forests, with one or multiple domains, and a single Azure AD tenant. Existing forest with Azure AD Connect, new forest with cloud provisioning. This scenario is similar to the multi-forest scenario; however. this one involves an existing Azure AD Connect environment and then bringing on a new forest using Azure AD Connect Cloud Sync. For an example of this scenario, see Microsoft Tutorial: An existing forest with a new single Azure AD tenant ↗️ Piloting Azure AD Connect Cloud Sync in an existing hybrid AD forest. The piloting scenario involves the existence of both Azure AD Connect and Azure AD Connect Cloud Sync in the same forest. In this scenario, an object should be in scope in only one of the tools. For an example of this scenario: see Microsoft Tutorial: Pilot Azure AD Connect Cloud Sync in an existing synced AD forest ↗️ Organizations should keep the following information in mind when considering these topologies: Users and groups must be uniquely identified across all forests. Matching across forests doesn’t occur with Azure AD Connect Cloud Sync. A user or group must be represented only once across all forests. The source anchor for objects is chosen automatically. It uses ms-DS-ConsistencyGuid if present; otherwise, ObjectGUID is used. You can’t change the attribute that’s used for the source anchor. Prerequisites for Azure AD Connect Cloud Sync Organizations need the following to use Azure AD Connect Cloud Sync: A group Managed Service Account (gMSA). Azure AD Connect Cloud Sync supports and uses a gMSA for running the lightweight Cloud Sync agent. A gMSA is a managed domain account that: provides automatic password management. provides simplified service principal name (SPN) management. delegates the management to other administrators. extends this functionality over multiple servers. Domain Administrator or Enterprise Administrator credentials to create the Azure AD Connect Cloud Sync gMSA (group Managed Service Account) to run the agent service. A hybrid identity administrator account for your Azure AD tenant that’s not a guest user. An on-premises server for the Cloud Sync agent with Windows 2016 or later. This server should be a tier 0 server based on the Active Directory administrative tier model. Installing the Cloud Sync agent on a domain controller is supported. High availability refers to the Azure AD Connect Cloud Sync’s ability to operate continuously without failure for a long time. By having multiple active Cloud Sync agents installed and running, Azure AD Connect Cloud Sync can continue to function even if one agent should fail. It’s recommended that organizations have three active agents installed for high availability. On-premises firewall configurations. Azure AD Connect Cloud Sync video Microsoft has provided the community with a video that shows an excellent overview of Azure AD Connect Cloud Sync: Migrate from Azure AD Connect to Azure AD Connect Cloud Sync In the following sections, I will descripe a possible workflow for transition from Azure AD Connect to Azure AD Connect Cloud Sync. Note: This may not be best-practice. Scenario: A reasonably traditional Azure AD Connect configuration. In my demo environment, I have a have a single forest, single domain with a single domain controller with Azure AD Connect installed. I prefer to use Organization based synchronisation. By using this approach I am able to select which OUs (containing users and/or groups) that will be synchronized to Azure AD. Step 1: Configuring Staging mode for Azure AD Connect Start off by setting Azure AD Connect into Staging mode. This is so we can ensure that any changes made by deploying the new Azure AD Connect Cloud Sync are not being impacted by our existing Azure AD Connect implementation. Start by opening Azure AD Connect on your server. Select Configure. Select Configure staging mode and click Next. Log in with either your Azure AD Global Administrator or Hybrid Identity Administrator credentials and click Next. Check Enable stagning mode and click Next. Verify that the Start the synchronization process when configuration completes checkbox is ticked off and then press Configure. The configuration completes with a confirmation that the staging mode is successfully enabled. Press Exit to close the wizard. You can also verify this by using PowerShell: Get-ADSyncScheduler AllowedSyncCycleInterval : 00:30:00 CurrentlyEffectiveSyncCycleInterval : 00:30:00 CustomizedSyncCycleInterval : NextSyncCyclePolicyType : Delta NextSyncCycleStartTimeInUTC : 05.01.2023 20:28:37 PurgeRunHistoryInterval : 7.00:00:00 SyncCycleEnabled : True MaintenanceEnabled : True StagingModeEnabled : True SchedulerSuspended : False SyncCycleInProgress : False (See that the returned property named StagingModeEnabled equals True in the code snippet above). This completes step 1. In the next step we will download and install the Azure AD Connect Cloud Sync agent. Step 2: Download and install the new Azure AD Connect Cloud Sync agent Now that we have successfully put out existing Azure AD Connect instance in staging mode, we are ready to continue our migration. The next step is to download and install the lightweight Azure AD Connect Cloud Sync agent on the same server. Start by logging into Azure Active Directory admin center: https://aad.portal.azure.com Select Azure Active Directory, then click Azure AD Connect. Select Manage Azure AD cloud sync, this will take you to the cloud sync configuration blade. Press Download agent on the top menu and start the download the pressing the Accept terms & download button. When the download is complete, run the installer. When prompted, agree to the terms and conditions and click Install. When the configuration wizard launches, click Next to continue. On the Connect to Azure AD screen, the authentication box will immediately open and prompts you to sign in with your Azure AD Global Administrator or Hybrid Identity Administrator credentials. Complete the sign in process to continue. The Configure Service Account screen opens. Select the Create gMSA options, then enter your Active Directory domain admin credentials to setup the gMSA account and click Next to continue. ℹ️ Tip If you want to read more about gMSA - Group Managed Service Accounts, see official Microsoft documentation found here↗️ On the Connect Active Directory screen, your domain should already be connected. If not, click Add directory to add your domain and click Next. On the final page, you will see the name of the group managed service account that will be created. Press Confirm to complete the setup. This will take approx. 2 minutes. Then the agent installation and configuration is completed, close the wizard by clicking Exit. This completes step 2. In the next step we will configure and test synchronization to the cloud with our new Azure AD Connect Cloud Sync instance. Step 3: Configure Azure AD Connect Cloud Sync When the sync agent is successfully installed on our server, the rest of the configuration is done through Azure Active Directory admin center. Start off by returning to the Azure AD Connect cloud sync configuration blade: https://aad.portal.azure.com/#view/Microsoft_AAD_Connect_Provisioning/ProvisioningManagementBlade The New configuration options on the top menu is no longer greyed out. Select it. Under Which Active Directory domain would you like to sync, you should see only one (the domain you configured under step 2 in this tutorial). Re-enable password hash sync, and click the Create button at the bottom of the page. The cloud sync configuration will take 20-30 seconds to complete and then you will be redirected to the Edit cloud sync configuration blade. Start on the top and work your way down the numbered settings. Start at the Scope settings. Click Click to edit scoping filters. A flyout will appear on the right. Here you will have to option to filter by All users, Selected security groups or Selected organizational units. As mentioned before, I prefer to filter by organizational units. Select the radio button next to Selected organizational units. Now we need the distinguished name of the OU object we want to synchronize to Azure AD. Head over to your server where you have access to Active Directory Users And Computers. Verify what you have enabled Advanced Features under View. Right-click your target OU, and select Properties. Select the Attribute Editor tab and double-click on the distinguisedName value. Copy the Value (right-click > Copy or Ctrl + C) Paste the value into the textbox into your Scope users configuration and click the Add button. Repeat this process if you have additional OUs (or users/security groups ) that you wish to synchronize. Click Done button at the buttom of the blade then finished. ⚠️ Important! When migrating to Azure AD Connect Cloud Sync from an existing Azure AD Connect implementation, it is crusial that you include all previously synced AD objects. If not, you may find yourself in need of restoring deleted objects. You can leave the Manage attributtes options as default unless your organization have some custom mapping of attributes. Ensure Sync password hashes are enabled. Under the Validate step, it is recommended that the sync is working as expected. Select the Provision a user button. Repeat the steps previously described in this tutorial to collect the distinguishedName value of your test user account in one of your already set up OUs. Paste the value on the Provision on-demand blade and click Provision. Wait 5-10 seconds for the provisioning to complete. Click on the View details button under each step to get more insights and to verify that the settings display as expected. Click Finish button at the buttom of the page to continue. Under the Settings step, enter an email address to receive notifications when an error has occured. Also note that under this step, you’ll have the option to adjust the setting Prevent accidential deletion. I highly recommend that you leave this option on and adjust the threshold under Accidential delete threshold from the default value of 500 to somewhat more appropriate and manageable level. In the final Deploy step, when ready, click Enable and then click Save at the top of the page. This completes step 3. In our final step in this tutorial, we will complete the migration by removing the old instance of Azure AD Connect. Step 4: Removal of Azure AD Connect agent Now that Azure AD Connect Cloud Sync is installed successfully and running, we ar ready to uninstall the old Azure AD Connect instance. On your server, open the Control panel, under Programs, select Uninstall a program. Select Microsoft Azure AD Connect and click Uninstall. Leave the defaults and click Remove. Wait for the unistall to complete. When finished, click Exit. Enable password writeback in Azure AD Connect Cloud Sync You can enable password writeback for your synced users from on-premises directories directly in the Azure portal or through PowerShell. Azure portal: Sign in to the Azure portal using a Global Administrator account. Search for and select Azure Active Directory, select Password reset, then choose On-premises integration. Check the option for Write back passwords to your on-premises directory. If Azure AD Connect provisioning agents are detected, you can additionally check the option for Write back passwords with Azure AD Connect cloud sync. Powershell: With PowerShell you can enable Azure AD Connect cloud sync by using the Set-AADCloudSyncPasswordWritebackConfiguration cmdlet on the servers with the provisioning agents. You will need global administrator credentials: Import-Module "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Microsoft.CloudSync.Powershell.dll" Set-AADCloudSyncPasswordWritebackConfiguration -Enable $true -Credential (Get-Credential) Monitoring View sync status info Under the Azure AD Connect cloud sync blade, under Configuration you’ll see your installed agents. Click the Status state to see the status of your agent configuration. Directory sync status After Azure AD Connect Cloud Sync is successfully installed, you’ll notice that there are created a new service principal for the directory sync. The account details and more information can be seen in the Microsoft 365 admin center, under Health, Directory sync status. Logging On the server where Azure AD Connect Cloud Sync is installed: By default, the agent emits minimal error messages and stack trace information. You can find these trace logs in the following folder: C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent\Trace. In Azure: Under Manage Azure AD Cloud sync under Azure AD Connect in the Azure portal, on the top of the page, you’ll find the Logs button. Click on it to view the Provisioning Logs. This is a great tool for monitoring and get insights into the objects being synchronized between your on-premises Active Directory environment and Azure AD. There is also an Download button for exporting the log into JSON or CSV format. Manage Azure AD Connect Cloud Sync using PowerShell No blog post without some PowerShell :smile: Microsoft has provided us with a PowerShell module named AADCloudSyncTools. The AADCloudSyncTools module provides a set of useful tools that can help you manage your deployments of Azure Active Directory Connect (Azure AD Connect) cloud sync. Install the AADCloudSyncTools PowerShell module Open Windows PowerShell with administrative privileges. Run Import-module -Name "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools". To verify that the module was imported, run Get-module AADCloudSyncTools. You should now see information about the module. To install the AADCloudSyncTools module prerequisites, run Install-AADCloudSyncToolsPrerequisites. On the first run, the PowerShellGet module will be installed if it’s not present. To load the new PowerShellGet module, close the PowerShell window and open a new PowerShell session with administrative privileges. Import the module again by running Import-module -Name "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools". Run Install-AADCloudSyncToolsPrerequisites again to install the MSAL and Azure AD modules. All prerequisites should now be installed. Every time you want to use the AADCloudSyncTools module in a new PowerShell session, run the following command: Import-Module "C:\Program Files\Microsoft Azure AD Connect Provisioning Agent\Utility\AADCloudSyncTools" AADCloudSyncTools cmdlet overview Name Description Connect-AADCloudSyncTools This cmdlet uses the MSAL.PS module to request a token for the Azure AD administrator to access Microsoft Graph. Export-AADCloudSyncToolsLogs This cmdlet exports and packages all the troubleshooting data in a compressed file. See details found here. Get-AADCloudSyncToolsInfo This cmdlet shows Azure AD tenant details and the state of internal variables. Get-AADCloudSyncToolsJob This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s information. You can also call it by using the specific sync job ID as a parameter. Get-AADCloudSyncToolsJobSchedule This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s schedule. You can also call it by using the specific sync job ID as a parameter. Get-AADCloudSyncToolsJobSchema This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s schema. Get-AADCloudSyncToolsJobScope This cmdlet uses Microsoft Graph to get the sync job’s schema for the provided sync job ID and outputs all filter groups’ scopes. Get-AADCloudSyncToolsJobSettings This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s settings. You can also call it by using the specific sync job ID as a parameter. Get-AADCloudSyncToolsJobStatus This cmdlet uses Microsoft Graph to get Azure AD service principals and returns the sync job’s status. You can also call it by using the specific sync job ID as a parameter. Get-AADCloudSyncToolsServiceAccount Returns the Azure AD Cloud Sync Service Account Get-AADCloudSyncToolsServicePrincipal This cmdlet uses Microsoft Graph to get the service principals for Azure AD and/or Azure Service Fabric. Without parameters, it will return only Azure AD service principals. Install-AADCloudSyncToolsPrerequisites This cmdlet checks for the presence of PowerShellGet v2.2.4.1 or later, the Azure AD module, and the MSAL.PS module. It installs these items if they’re missing. Invoke-AADCloudSyncToolsGraphQuery This cmdlet invokes a web request for the URI, method, and body specified as parameters. Remove-AADCloudSyncToolsGroupMembers Searches members of Azure AD group in AD and prompts the members not found in AD for removing from Azure AD Repair-AADCloudSyncToolsAccount This cmdlet uses Azure AD PowerShell to delete the current account (if present). It then resets the sync account authentication with a new sync account in Azure AD. Restart-AADCloudSyncToolsJob This cmdlet restarts a full synchronization. Resume-AADCloudSyncToolsJob This cmdlet continues synchronization from the previous watermark. Set-AADCloudSyncToolsJobSchema Uses Graph to get AD2AAD Service Principals and returns the Synchronization Job’s Schema. Set-AADCloudSyncToolsTenantId This can be used when the Cloud Sync Agent is not currently installed or it is installed but you want to target a different Azure AD tenant than the one Cloud Sync Agent is using. The TenantId set with this function will only persist during the PowerShell session and you’ll need to call this function before using Connect-AADCloudSyncTools. Start-AADCloudSyncToolsVerboseLogs This cmdlet modifies AADConnectProvisioningAgent.exe.config to enable verbose tracing and restarts the AADConnectProvisioningAgent service. You can use -SkipServiceRestart to prevent service restart, but any configuration changes will not take effect. You can find these trace logs in the folder C:\ProgramData\Microsoft\Azure AD Connect Provisioning Agent\Trace. Stop-AADCloudSyncToolsVerboseLogs This cmdlet modifies AADConnectProvisioningAgent.exe.config to disable verbose tracing and restarts the AADConnectProvisioningAgent service. You can use -SkipServiceRestart to prevent service restart, but any configuration changes will not take effect. Suspend-AADCloudSyncToolsJob This cmdlet pauses synchronization. Additional useful resources Frequently asked questions: Aure Active Directory Connect cloud sync FAQ Read the official configuration guide from Microsoft for further details into creating a hybrid identity environment using Azure Active Directory (Azure AD) Connect cloud sync. For troubleshooting tips, see this article: Cloud sync troubleshooting.Enable and setup passwordless sign-in with Microsoft Authenticator app2022-01-26T19:00:00+01:002022-01-26T19:00:00+01:00https://www.cloudpilot.no/blog/Enable-and-setup-passwordless-sign-in-with-Microsoft-Authenticator-app<h2 id="enable-and-setup-passwordless-sign-in-with-microsoft-authenticator-app">Enable and setup passwordless sign-in with Microsoft Authenticator app</h2>
<p>No need to remember your account password! The Microsoft Authenticator app can be used to sign in to any Azure AD account without using a password.</p>
<h4 id="prerequisites">Prerequisites:</h4>
<p>To use passwordless phone sign-in with the Microsoft Authenticator app, the following prerequisites must be met:</p>
<ul>
<li>Recommended: Azure AD Multi-Factor Authentication, with push notifications allowed as a verification method. Push notifications to your smartphone or tablet help the Authenticator app to prevent unauthorized access to accounts and stop fraudulent transactions. The Authenticator app automatically generates codes when set up to do push notifications so a user has a backup sign-in method even if their device doesn’t have connectivity.</li>
<li>Latest version of Microsoft Authenticator installed on devices running iOS 8.0 or greater, or Android 6.0 or greater.</li>
<li>The device on which the Microsoft Authenticator app is installed must be registered within the Azure AD tenant to an individual user.</li>
</ul>
<h2 id="enable-passwordless-authentication-methods">Enable passwordless authentication methods</h2>
<p>To use passwordless authentication in Azure AD, first enable the combined registration experience, then enable users for the passwordless method.</p>
<p>To enable the authentication method for passwordless phone sign-in, complete the following steps:</p>
<ol>
<li>Sign in to the <a href="https://portal.azure.com">Azure portal</a>.</li>
<li>Navigate to <strong>Azure Active Directory</strong>, then browse to <strong>Security</strong> > <strong>Authentication methods</strong> > <strong>Policies</strong>.</li>
</ol>
<p><img src="/assets/images/PASSWORDLESS/01.png" alt="" /></p>
<ol>
<li>Under <strong>Microsoft Authenticator</strong>, select the following options:
a. <strong>Enable</strong> - Yes
b. <strong>Target</strong> - All users or Selected users. In my example, I’ll added a single user, <code class="language-plaintext highlighter-rouge">John Doe</code>.</li>
</ol>
<p><img src="/assets/images/PASSWORDLESS/02.png" alt="" /></p>
<ol>
<li>Each added group or user is enabled by default to use Microsoft Authenticator in both passwordless and push notification modes (“Any” mode). To change this, for each row:
a. Browse to <strong>…</strong> (3) > <strong>Configure</strong>.</li>
</ol>
<p><img src="/assets/images/PASSWORDLESS/03.png" alt="" /></p>
<p>b. For <strong>Authentication mode</strong> - choose <strong>Any</strong> (4), or <strong>Passwordless</strong>. Choosing <strong>Push</strong> prevents the use of the passwordless phone sign-in credential.</p>
<p>c. To apply the new policy, click <strong>Save</strong> (6).</p>
<p><img src="/assets/images/PASSWORDLESS/04.png" alt="" /></p>
<h2 id="end-user-registration-and-management-of-microsoft-authenticator">End-user registration and management of Microsoft Authenticator</h2>
<p>Users register themself for the passwordless authentication method of Azure AD by using the following steps:</p>
<ol>
<li>Browse to <a href="https://aka.ms/mysecurityinfo">https://aka.ms/mysecurityinfo</a>.</li>
<li>Sign in, then click <strong>Add method</strong> > <strong>Authenticator app</strong> > <strong>Add</strong> to add the <code class="language-plaintext highlighter-rouge">Authenticator app</code>.</li>
<li>Follow the instructions to install and configure the <strong>Microsoft Authenticator</strong> app on their device.</li>
<li>Select <strong>Done</strong> to complete the Authenticator configuration.</li>
<li>On their device, in <strong>Microsoft Authenticator</strong>, choose <strong>Setup phone sign-in</strong> from the drop-down menu for the account registered.</li>
</ol>
<ul>
<li>Select <code class="language-plaintext highlighter-rouge">***</code> on the top right corner.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/05.jpg" alt="" /></p>
<ul>
<li>Select <strong>Set up phone sign-in</strong>.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/06.jpg" alt="" /></p>
<ul>
<li>Select <strong>Continue</strong>.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/07.jpg" alt="" /></p>
<ul>
<li>Enter your account password and click <strong>Sign-in</strong>.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/08.jpg" alt="" /></p>
<ul>
<li>Confirm MFA <strong>Sign-in request</strong> on your device.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/09.jpg" alt="" /></p>
<ul>
<li>Press <strong>Register</strong> to register your device in Azure Active Directory.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/10.jpg" alt="" /></p>
<h4 id="device-registration">Device registration</h4>
<p>Before you can create this new strong credential, there are prerequisites. One prerequisite is that the device on which the Microsoft Authenticator app is installed must be registered within the Azure AD tenant to an individual user.</p>
<p>Currently, a device can only be enabled for passwordless sign-in in a <strong>single tenant</strong>. This limit means that only one work or school account in the Microsoft Authenticator app can be enabled for phone sign-in.</p>
<blockquote>
<p><strong>Note</strong> Device registration is not the same as device management or mobile device management (MDM). Device registration only associates a device ID and a user ID together, in the Azure AD directory.</p>
</blockquote>
<ul>
<li>Press <strong>Finish</strong> to complete the passwordless sign-in setup.</li>
</ul>
<p><img src="/assets/images/PASSWORDLESS/11.jpg" alt="" /></p>
<h2 id="sign-in-using-passwordless-credentials">Sign in using passwordless credentials</h2>
<ol>
<li>User enters her name the sign-in page.</li>
<li>Selects <strong>Next</strong>.</li>
<li>If necassary, selects <strong>Others ways to sign in</strong>.</li>
<li>Select <strong>Approve a request on my Microsoft Authenticator app</strong>.</li>
</ol>
<p>The user is then presented with a number on their screen:</p>
<p><img src="/assets/images/PASSWORDLESS/12.png" alt="" /></p>
<p>The <strong>Authenticator app</strong> prompts the user to autenticate by typing in the correct number, instead of by entering their account password:</p>
<p><img src="/assets/images/PASSWORDLESS/13.jpg" alt="" /></p>
<p>After the user has successfully entered the same number in the <strong>Authenticator app</strong>, pressed <strong>Yes</strong>, the user are securely logged in.</p>
<p>If the user would like to disable phone sign-in, they can select <strong>Disable phone sign-in</strong> in the <strong>Authenticator app</strong> and follow the steps to disable this service.</p>
<p><img src="/assets/images/PASSWORDLESS/15.jpg" alt="" /></p>Freddie ChristiansenEnable and setup passwordless sign-in with Microsoft Authenticator app No need to remember your account password! The Microsoft Authenticator app can be used to sign in to any Azure AD account without using a password. Prerequisites: To use passwordless phone sign-in with the Microsoft Authenticator app, the following prerequisites must be met: Recommended: Azure AD Multi-Factor Authentication, with push notifications allowed as a verification method. Push notifications to your smartphone or tablet help the Authenticator app to prevent unauthorized access to accounts and stop fraudulent transactions. The Authenticator app automatically generates codes when set up to do push notifications so a user has a backup sign-in method even if their device doesn’t have connectivity. Latest version of Microsoft Authenticator installed on devices running iOS 8.0 or greater, or Android 6.0 or greater. The device on which the Microsoft Authenticator app is installed must be registered within the Azure AD tenant to an individual user. Enable passwordless authentication methods To use passwordless authentication in Azure AD, first enable the combined registration experience, then enable users for the passwordless method. To enable the authentication method for passwordless phone sign-in, complete the following steps: Sign in to the Azure portal. Navigate to Azure Active Directory, then browse to Security > Authentication methods > Policies. Under Microsoft Authenticator, select the following options: a. Enable - Yes b. Target - All users or Selected users. In my example, I’ll added a single user, John Doe. Each added group or user is enabled by default to use Microsoft Authenticator in both passwordless and push notification modes (“Any” mode). To change this, for each row: a. Browse to … (3) > Configure. b. For Authentication mode - choose Any (4), or Passwordless. Choosing Push prevents the use of the passwordless phone sign-in credential. c. To apply the new policy, click Save (6). End-user registration and management of Microsoft Authenticator Users register themself for the passwordless authentication method of Azure AD by using the following steps: Browse to https://aka.ms/mysecurityinfo. Sign in, then click Add method > Authenticator app > Add to add the Authenticator app. Follow the instructions to install and configure the Microsoft Authenticator app on their device. Select Done to complete the Authenticator configuration. On their device, in Microsoft Authenticator, choose Setup phone sign-in from the drop-down menu for the account registered. Select *** on the top right corner. Select Set up phone sign-in. Select Continue. Enter your account password and click Sign-in. Confirm MFA Sign-in request on your device. Press Register to register your device in Azure Active Directory. Device registration Before you can create this new strong credential, there are prerequisites. One prerequisite is that the device on which the Microsoft Authenticator app is installed must be registered within the Azure AD tenant to an individual user. Currently, a device can only be enabled for passwordless sign-in in a single tenant. This limit means that only one work or school account in the Microsoft Authenticator app can be enabled for phone sign-in. Note Device registration is not the same as device management or mobile device management (MDM). Device registration only associates a device ID and a user ID together, in the Azure AD directory. Press Finish to complete the passwordless sign-in setup. Sign in using passwordless credentials User enters her name the sign-in page. Selects Next. If necassary, selects Others ways to sign in. Select Approve a request on my Microsoft Authenticator app. The user is then presented with a number on their screen: The Authenticator app prompts the user to autenticate by typing in the correct number, instead of by entering their account password: After the user has successfully entered the same number in the Authenticator app, pressed Yes, the user are securely logged in. If the user would like to disable phone sign-in, they can select Disable phone sign-in in the Authenticator app and follow the steps to disable this service.Microsoft Graph PowerShell SDK2022-01-11T16:30:00+01:002022-01-11T16:30:00+01:00https://www.cloudpilot.no/blog/Microsoft-Graph-PowerShell-SDK<h2 id="microsoft-graph-powershell-sdk">Microsoft Graph PowerShell SDK</h2>
<p>I have spend a lot of time interacting with the Microsoft Graph API over the years.
Normally I have written a bunch of code with the help of <code class="language-plaintext highlighter-rouge">Invoke-RestMethod</code> to get the job done.
Sometimes I’ve also used <code class="language-plaintext highlighter-rouge">Microsoft Graph Explorer</code> when I need to test something out, or when I need to do a simple <code class="language-plaintext highlighter-rouge">GET request</code> to get my hands of some data.</p>
<p>In this blog post, I thought I could dive a little deeper into the available SDK for PowerShell against Microsoft Graph.</p>
<p>Let’s start with a little introduction.</p>
<h3 id="what-is-microsoft-graph">What is Microsoft Graph?</h3>
<p>I go for the short answer:
<em>A RESTful, single API endpoint (https://graph.microsoft.com) for interacting with your data in your Microsoft 365 subscription.</em></p>
<p><img src="https://docs.microsoft.com/en-us/graph/images/microsoft-graph.png" alt="Overview of Microsoft Graph" /></p>
<h3 id="microsoft-graph-documentation">Microsoft Graph documentation</h3>
<ul>
<li>Found here: <a href="https://aka.ms/graphapiref">https://aka.ms/graphapiref</a></li>
<li>API docs will show the correct endpoint, HTTP request, required permissions (<em>least to most privileged</em>), request headers, request body, response messages and a lot of examples for you to play around with.</li>
<li>You can copy/paste examples found in docs and re-use them in <a href="https://aka.ms/ge">Microsoft Graph Explorer</a> with a sample tenant.</li>
</ul>
<h3 id="what-is-microsoft-graph-explorer">What is Microsoft Graph Explorer?</h3>
<blockquote>
<p>“…<a href="https://developer.microsoft.com/graph/graph-explorer/">Graph Explorer</a> is a developer tool that lets you conveniently make Microsoft Graph REST API requests and view corresponding responses. Use Graph Explorer to try APIs on the default sample tenant to explore capabilities, or sign in to your own tenant and use it as a prototyping tool to fulfill your app scenarios….”</p>
</blockquote>
<p>Use Graph Explorer to:</p>
<ul>
<li>Make Microsoft Graph API requests (GET, POST, PUT, PATCH, and DELETE) and see responses including response code and any headers and bodies.</li>
<li>Consent to permissions. Graph Explorer supports only <a href="https://docs.microsoft.com/en-us/graph/auth/auth-concepts#delegated-and-application-permissions">delegated permissions</a>.</li>
<li>Add a request body and request header to your query.</li>
<li>View and copy the access token.</li>
<li>View sample queries for different services in Microsoft Graph.</li>
<li>View, download, or delete the queries you ran in the last 30 days.</li>
<li>View and copy code snippets of each query you run in C#, Java, JavaScript, and Objective C.</li>
<li>Access Microsoft Graph Toolkit components and adaptive cards for some sample queries.</li>
<li>Share queries, including the request body and request headers.</li>
</ul>
<h4 id="get-started">Get Started</h4>
<ul>
<li>Graph Explorer is a web application found <a href="https://aka.ms/ge">here</a>.</li>
<li>If you are new to this great tool provided by Microsoft, I recommend starting out by reading <a href="https://docs.microsoft.com/en-us/graph/graph-explorer/graph-explorer-overview#get-started">this</a> article found on Microsoft Docs.</li>
</ul>
<h3 id="microsoft-graph-powershell-sdk-1">Microsoft Graph PowerShell SDK</h3>
<ul>
<li>Provides a well known PowerShell-style way of making calls to the Microsoft Graph API</li>
<li>Cmdlets have a prefix of <em>MG</em> , a safety mechanism for preventing the cmdlets to crash against other PowerShell cmdlets in different modules (example: <em>Connect-MgGraph</em>, <em>Get-MgUser</em>, <em>Add-MgTeamMember</em>)</li>
<li>Cmdlets follows the PowerShell convention of <em>Verb-Noun</em> format</li>
</ul>
<h3 id="installing-the-microsoft-graph-powershell-sdk">Installing the Microsoft Graph PowerShell SDK</h3>
<blockquote>
<p><strong><em>Note</em></strong>
Installing the main module of the SDK will install all 38 sub modules. You might consider only installing the necessary modules, including <code class="language-plaintext highlighter-rouge">Microsoft.Graph.Authentication</code>.</p>
</blockquote>
<p>The Microsoft Graph PowerShell SDK is published on the <a href="https://www.powershellgallery.com/packages/Microsoft.Graph">PowerShell Gallery</a>. You can install the SDK in PowerShell Core or Windows PowerShell using the following command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Install-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="w"> </span><span class="nt">-Scope</span><span class="w"> </span><span class="nx">CurrentUser</span><span class="w">
</span></code></pre></div></div>
<p>Optionally, you can change the scope of the installation using the <code class="language-plaintext highlighter-rouge">-Scope</code> parameter. This requires admin permissions.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Install-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="w"> </span><span class="nt">-Scope</span><span class="w"> </span><span class="nx">AllUsers</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p><strong><em>Important</em></strong>
Installing the SDK in one version of PowerShell does not install it for the other. Be sure to run the installation command inside the version of PowerShell (Core or Windows) you intend to use it in.</p>
</blockquote>
<h4 id="verify-installation">Verify installation</h4>
<p>After the installation completes, you can verify the installed version with the following command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-InstalledModule</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="w">
</span></code></pre></div></div>
<h4 id="updating-the-sdk">Updating the SDK</h4>
<p>You can update the SDK and all of its dependencies using the following command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Update-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p><strong><em>Current version</em></strong>
At the time of this writing, the current version of the Microsoft Graph PowerShell SDK is <strong>1.9.1</strong>. The version history can be found <a href="https://www.powershellgallery.com/packages/Microsoft.Graph/1.9.1#">here</a>.</p>
</blockquote>
<h4 id="uninstalling-the-sdk">Uninstalling the SDK</h4>
<p>First, use the following command to uninstall the main module:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Uninstall-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="w">
</span></code></pre></div></div>
<p>Then, remove all of the dependency modules by running the following commands:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-InstalledModule</span><span class="w"> </span><span class="nx">Microsoft.Graph.</span><span class="o">*</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">ForEach-Object</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kr">if</span><span class="p">(</span><span class="bp">$_</span><span class="o">.</span><span class="nf">Name</span><span class="w"> </span><span class="o">-ne</span><span class="w"> </span><span class="s2">"Microsoft.Graph.Authentication"</span><span class="p">){</span><span class="w"> </span><span class="n">Uninstall-Module</span><span class="w"> </span><span class="bp">$_</span><span class="o">.</span><span class="nf">Name</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="n">Uninstall-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph.Authentication</span><span class="w">
</span></code></pre></div></div>
<h3 id="examples-of-use">Examples of use</h3>
<p>As you can see below, at the time of this writing, there are thousands of available cmdlets available in this module.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Get Command count from module</span><span class="w">
</span><span class="nv">$Commands</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-Command</span><span class="w"> </span><span class="nt">-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="o">*</span><span class="w">
</span><span class="nv">$Commands</span><span class="o">.</span><span class="nf">Count</span><span class="w">
</span><span class="mi">6978</span><span class="w">
</span></code></pre></div></div>
<h3 id="finding-available-commands">Finding available commands</h3>
<p>Due to the amount of available commands and the complexity of this module, there can be hard to find the right command to use. In this case, you can use the <code class="language-plaintext highlighter-rouge">Get-Command</code> to search for available commands in the SDK. For example, if you are looking for commands related to <code class="language-plaintext highlighter-rouge">Microsoft Teams</code>, you can run the following command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-Command</span><span class="w"> </span><span class="nt">-Module</span><span class="w"> </span><span class="nx">Microsoft.Graph</span><span class="o">*</span><span class="w"> </span><span class="o">*</span><span class="nx">team</span><span class="o">*</span><span class="w">
</span></code></pre></div></div>
<h4 id="api-version">API version</h4>
<p>By default, the SDK uses the <a href="https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0&preserve-view=true">Microsoft Graph REST API v1.0</a>. You can change this by using the <code class="language-plaintext highlighter-rouge">Select-MgProfile</code> command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Change to the Beta API</span><span class="w">
</span><span class="n">Select-MgProfile</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"beta"</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p><strong><em>Microsoft Graph Beta API</em></strong>
Please note that this API is subject for change, and should <strong>not</strong> be used in production.</p>
</blockquote>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Change back to the version 1.0</span><span class="w">
</span><span class="n">Select-MgProfile</span><span class="w"> </span><span class="nt">-Name</span><span class="w"> </span><span class="s2">"v1.0"</span><span class="w">
</span></code></pre></div></div>
<h4 id="authentication">Authentication</h4>
<p>The PowerShell SDK supports two types of authentication: delegated access, and app-only access. In this guide, you will use delegated access to login as a user, grant consent to the SDK to act on your behalf, and call the Microsoft Graph.</p>
<h4 id="determine-required-permission-scopes">Determine required permission scopes</h4>
<p>Each API in the Microsoft Graph is protected by one or more permission scopes. The user logging in must consent to one of the required scopes for the APIs you plan to use. In this example, we’ll use the following APIs.</p>
<ul>
<li><a href="https://docs.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&preserve-view=true">List users</a> to find the user ID of the logged-in user</li>
<li><a href="https://docs.microsoft.com/en-us/graph/api/user-list-joinedteams?view=graph-rest-1.0&preserve-view=true">List joinedTeams</a> to get the Teams the user is a member of.</li>
<li><a href="https://docs.microsoft.com/en-us/graph/api/channel-list?view=graph-rest-1.0&preserve-view=true">List channels</a> to get the channels in a Team.</li>
<li><a href="https://docs.microsoft.com/en-us/graph/api/channel-post-messages?view=graph-rest-1.0&preserve-view=true">Send message</a> to send a message to a Team channel.</li>
</ul>
<p>The <code class="language-plaintext highlighter-rouge">User.Read.All</code> permission scope will enable the first two calls, and the <code class="language-plaintext highlighter-rouge">Group.ReadWrite.All</code> scope will enable the rest. These permissions require an admin account.</p>
<h4 id="sign-in">Sign in</h4>
<p>Use the <code class="language-plaintext highlighter-rouge">Connect-MgGraph</code> command to sign in with the required scopes. You’ll need to sign in with an admin account to consent to the required scopes.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-MgGraph</span><span class="w"> </span><span class="nt">-Scopes</span><span class="w"> </span><span class="s2">"User.Read.All"</span><span class="p">,</span><span class="s2">"Group.ReadWrite.All"</span><span class="w">
</span></code></pre></div></div>
<p>The command prompts you to go to a web page to sign in using a device code. Once you’ve done that, the command indicates success with a <code class="language-plaintext highlighter-rouge">Welcome To Microsoft Graph!</code> message. You only need to do this once per session.</p>
<blockquote>
<p><strong><em>Note</em></strong>
You can add additional permissions by repeating the <code class="language-plaintext highlighter-rouge">Connect-MgGraph</code> command with the new permission scopes.</p>
</blockquote>
<p>Example: If you are going to create a new team, head over to the Microsoft Graph documentation on <a href="https://docs.microsoft.com/en-us/graph/api/team-post?view=graph-rest-1.0&tabs=http">create team</a>, and look up the required permissons table:</p>
<p><img src="/assets/images/MSGraphSDK/CreateTeamPermissions.png" alt="Create new team" /></p>
<p>Like shown here, you need to change (or add) the permissions to include (from least to most privileged):</p>
<ul>
<li><em>Team.Create</em></li>
<li>*Group.ReadWrite.All** (deprecated and should not be used)</li>
<li><em>Directory.ReadWrite.All</em> (deprecated and should not be used)</li>
</ul>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-MgGraph</span><span class="w"> </span><span class="nt">-Scopes</span><span class="w"> </span><span class="s2">"User.Read.All"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Group.ReadWrite.All"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Team.Create"</span><span class="w">
</span></code></pre></div></div>
<p>After you have executed the cmdlet shown above, a web page are opened and requires you to accept the newly added permissions (see that <code class="language-plaintext highlighter-rouge">Create teams</code> are added to the list) :</p>
<p><img src="/assets/images/MSGraphSDK/NewPermissions.png" alt="Accept permissions" /></p>
<p>Accept the required permissions to complete the connection to Microsoft Graph.</p>
<h4 id="call-microsoft-graph">Call Microsoft Graph</h4>
<p>Now that you’re signed in, you can start making calls to Microsoft Graph.</p>
<h4 id="get-the-signed-in-user">Get the signed-in user</h4>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Get-MgUser</span><span class="w">
</span></code></pre></div></div>
<p>This outputs a listing of users in your Microsoft 365 subscription:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Id</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w"> </span><span class="nx">Mail</span><span class="w"> </span><span class="nx">UserPrincipalName</span><span class="w"> </span><span class="nx">UserType</span><span class="w">
</span><span class="o">--</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">----</span><span class="w"> </span><span class="o">-----------------</span><span class="w"> </span><span class="o">--------</span><span class="w">
</span><span class="mi">73</span><span class="n">b8a426-22b1-49d1-a181-ae931495f6f4</span><span class="w"> </span><span class="nx">Adele</span><span class="w"> </span><span class="nx">Vance</span><span class="w"> </span><span class="nx">adelev</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w"> </span><span class="nx">adelev</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w">
</span><span class="mi">42</span><span class="n">d7a7a0-e6d0-46f1-b570-fae50ae12899</span><span class="w"> </span><span class="nx">Admin</span><span class="w"> </span><span class="nx">admin</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w"> </span><span class="nx">admin</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w">
</span><span class="mi">2</span><span class="n">a7887cd-c2aa-4b81-9a6b-e8fa42f1cddf</span><span class="w"> </span><span class="nx">Alex</span><span class="w"> </span><span class="nx">Wilber</span><span class="w"> </span><span class="nx">AlexW</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w"> </span><span class="nx">AlexW</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w">
</span><span class="mi">3</span><span class="n">e3ef4b2-20fa-41fa-b764-3dac0bbdcc42</span><span class="w"> </span><span class="nx">Berit</span><span class="w"> </span><span class="nx">Nordmann</span><span class="w"> </span><span class="nx">berit</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w"> </span><span class="nx">berit</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w">
</span><span class="mi">2996</span><span class="n">d76a-a2db-426b-a1ec-93c6e6c92725</span><span class="w"> </span><span class="nx">Bodil</span><span class="w"> </span><span class="nx">Hansen</span><span class="w"> </span><span class="nx">bodil.hansen</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w"> </span><span class="nx">bodil.hansen</span><span class="err">@</span><span class="nx">cloudpilotlabs.xyz</span><span class="w">
</span></code></pre></div></div>
<h3 id="filtering">Filtering</h3>
<p>The PowerShell SDK supports <a href="https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter">OData</a> filtering:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Get user by filtering by DisplayName</span><span class="w">
</span><span class="nv">$User</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-MgUser</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'Alex Wilber'"</span><span class="w">
</span></code></pre></div></div>
<p>Verify that worked by entering the following:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Return DisplayName</span><span class="w">
</span><span class="nv">$User</span><span class="o">.</span><span class="nf">DisplayName</span><span class="w">
</span></code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">Alex Wilber</code> should be returned in the console.</p>
<h3 id="using-query-parameter">Using Query Parameter</h3>
<p>You can use the <code class="language-plaintext highlighter-rouge">-Property</code> parameter to only return some properties from your query:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Get user by Id and return DisplayName and Mail </span><span class="w">
</span><span class="n">Get-MgUser</span><span class="w"> </span><span class="nt">-UserId</span><span class="w"> </span><span class="nv">$User</span><span class="o">.</span><span class="nf">Id</span><span class="w"> </span><span class="nt">-Property</span><span class="w"> </span><span class="nx">DisplayName</span><span class="p">,</span><span class="w"> </span><span class="nx">Mail</span><span class="w">
</span></code></pre></div></div>
<p>The following is then returned:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Id DisplayName Mail UserPrincipalName UserType
-- ----------- ---- ----------------- --------
Alex Wilber AlexW@cloudpilotlabs.xyz
</code></pre></div></div>
<h3 id="list-the-users-joined-teams">List the user’s joined teams</h3>
<p>Use the user’s ID as a parameter to the <code class="language-plaintext highlighter-rouge">Get-MgUserJoinedTeam</code> command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#List all Joined Team for spesific user by Id</span><span class="w">
</span><span class="n">Get-MgUserJoinedTeam</span><span class="w"> </span><span class="nt">-UserId</span><span class="w"> </span><span class="nv">$User</span><span class="o">.</span><span class="nf">Id</span><span class="w">
</span></code></pre></div></div>
<p>A list of joined teams is returned (see example below:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Id</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w"> </span><span class="nx">Description</span><span class="w"> </span><span class="nx">IsArchived</span><span class="w">
</span><span class="o">--</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">----------</span><span class="w">
</span><span class="n">c0b599d5-480d-43a4-9f7a-f366ae39850a</span><span class="w"> </span><span class="nx">cloudpilotdev</span><span class="w"> </span><span class="nx">False</span><span class="w">
</span><span class="n">c550e12e-9647-49a8-9e1a-37e3f1cc690e</span><span class="w"> </span><span class="nx">External</span><span class="w"> </span><span class="nx">Team</span><span class="w"> </span><span class="nx">A</span><span class="w"> </span><span class="nx">team</span><span class="w"> </span><span class="nx">used</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="nx">allowing</span><span class="w"> </span><span class="nx">and</span><span class="w"> </span><span class="nx">testing</span><span class="w"> </span><span class="nx">external</span><span class="w"> </span><span class="nx">access</span><span class="w"> </span><span class="nx">False</span><span class="w">
</span></code></pre></div></div>
<h3 id="list-team-channels">List team channels</h3>
<p>Let’s use the team’s Id as a parameter to the <code class="language-plaintext highlighter-rouge">Get-MgTeamChannel</code> command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#1. Save the "External Team's Id into a variable for future use</span><span class="w">
</span><span class="nv">$externalTeam</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"c550e12e-9647-49a8-9e1a-37e3f1cc690e"</span><span class="w">
</span><span class="c">#2. Save the 'General' channel Id into a variable for future use</span><span class="w">
</span><span class="nv">$general</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-MgTeamChannel</span><span class="w"> </span><span class="nt">-TeamId</span><span class="w"> </span><span class="nv">$externalTeam</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'General'"</span><span class="w">
</span></code></pre></div></div>
<h3 id="send-a-message-to-teams-channel">Send a message to teams channel</h3>
<p>Now let’s use our <code class="language-plaintext highlighter-rouge">$externalTeam</code> variable and our <code class="language-plaintext highlighter-rouge">$general</code> variable to post a message to our <code class="language-plaintext highlighter-rouge">External Team</code> <em>general</em> channel using the following command:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#New Team Channel Massage using Microsoft Graph SDK</span><span class="w">
</span><span class="n">New-MgTeamChannelMessage</span><span class="w"> </span><span class="nt">-TeamId</span><span class="w"> </span><span class="nv">$externalTeam</span><span class="w"> </span><span class="nt">-ChannelId</span><span class="w"> </span><span class="nv">$general</span><span class="o">.</span><span class="nf">Id</span><span class="w"> </span><span class="nt">-Body</span><span class="w"> </span><span class="p">@{</span><span class="w"> </span><span class="nx">Content</span><span class="o">=</span><span class="s2">"Hello World"</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p><img src="/assets/images/MSGraphSDK/TeamMessage.png" alt="New teams message" /></p>
<p>If you want to send an <code class="language-plaintext highlighter-rouge">urgent</code> message, add the <code class="language-plaintext highlighter-rouge">-Importance</code> parameter as shown below:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#New Urgent Team Channel Message using Microsoft Graph SDK</span><span class="w">
</span><span class="n">New-MgTeamChannelMessage</span><span class="w"> </span><span class="nt">-TeamId</span><span class="w"> </span><span class="nv">$externalTeam</span><span class="w"> </span><span class="nt">-ChannelId</span><span class="w"> </span><span class="nv">$general</span><span class="o">.</span><span class="nf">Id</span><span class="w"> </span><span class="nt">-Body</span><span class="w"> </span><span class="p">@{</span><span class="w"> </span><span class="nx">Content</span><span class="o">=</span><span class="s2">"Please bring me a coffee!"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="nt">-Importance</span><span class="w"> </span><span class="s2">"urgent"</span><span class="w">
</span></code></pre></div></div>
<p><img src="/assets/images/MSGraphSDK/TeamUrgentMessage.png" alt="New urgent teams message" /></p>
<h3 id="create-a-new-user">Create a new user</h3>
<p>Example 1:
Uses the traditional format:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Create a new user account using Microsoft Graph SDK for PowerShell</span><span class="w">
</span><span class="nv">$PasswordProfile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'P@ssW0rd123'</span><span class="p">;</span><span class="w"> </span><span class="nx">ForceChangePasswordNextSignIn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w"> </span><span class="p">}</span><span class="w">
</span><span class="n">New-MgUser</span><span class="w"> </span><span class="nt">-DisplayName</span><span class="w"> </span><span class="s2">"Ola Nordmann"</span><span class="w"> </span><span class="nt">-PasswordProfile</span><span class="w"> </span><span class="nv">$PasswordProfile</span><span class="w"> </span><span class="nt">-AccountEnabled</span><span class="w"> </span><span class="nt">-MailNickName</span><span class="w"> </span><span class="s2">"OlaNor"</span><span class="w"> </span><span class="nt">-UserPrincipalName</span><span class="w"> </span><span class="s2">"Ola.Nordmann@cloudpilotlabs.xyz"</span><span class="w">
</span></code></pre></div></div>
<p>Example 2:
Use <code class="language-plaintext highlighter-rouge">splatting</code> and <code class="language-plaintext highlighter-rouge">hash tables</code> for easier readability:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$Parameters</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="w">
</span><span class="nx">PasswordProfile</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="w">
</span><span class="nx">Password</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">'P@ssW0rd123'</span><span class="w">
</span><span class="nx">ForceChangePasswordNextSignIn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="nx">DisplayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Ola Nordmann"</span><span class="w">
</span><span class="nx">AccountEnabled</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">$true</span><span class="w">
</span><span class="nx">MailNickName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"OlaNor"</span><span class="w">
</span><span class="nx">UserPrincipalName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Ola.Nordmann@cloudpilotlabs.xyz"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">New-MgUser</span><span class="w"> </span><span class="err">@</span><span class="nx">Parameters</span><span class="w">
</span></code></pre></div></div>
<h3 id="remove-a-user">Remove a user</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Remove a user account using Microsoft Graph SDK for PowerShell</span><span class="w">
</span><span class="c">#Assign the User's Id into a variable named $DeleteUser</span><span class="w">
</span><span class="nv">$DeleteUser</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-MgUser</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'Ola Nordmann'"</span><span class="w">
</span><span class="c">#Confirm the Id</span><span class="w">
</span><span class="nv">$DeleteUser</span><span class="o">.</span><span class="nf">Id</span><span class="w">
</span><span class="mi">97</span><span class="n">b1eb01-a366-4023-b737-8d82b2877a82</span><span class="w">
</span><span class="c">#Delete the user</span><span class="w">
</span><span class="n">Remove-MgUser</span><span class="w"> </span><span class="nt">-UserId</span><span class="w"> </span><span class="nv">$DeleteUser</span><span class="o">.</span><span class="nf">Id</span><span class="w">
</span></code></pre></div></div>
<h3 id="create-new-team">Create new team</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Create a new team using splatting</span><span class="w">
</span><span class="nv">$Splat</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="w">
</span><span class="nx">DisplayName</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"My PowerShell SDK Team"</span><span class="w">
</span><span class="nx">Description</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"My awesome PowerShell SDK made Team!"</span><span class="w">
</span><span class="nx">AdditionalProperties</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@{</span><span class="s2">"template@odata.bind"</span><span class="o">=</span><span class="s2">"https://graph.microsoft.com/v1.0/teamsTemplates('standard')"</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">New-MgTeam</span><span class="w"> </span><span class="err">@</span><span class="nx">Splat</span><span class="w">
</span></code></pre></div></div>
<h3 id="lookup-team">Lookup team</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Get new Team by searching by displayName</span><span class="w">
</span><span class="nv">$Team</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-MgGroup</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'My PowerShell SDK Team'"</span><span class="w">
</span><span class="nv">$Team</span><span class="w">
</span><span class="n">Id</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w"> </span><span class="nx">Description</span><span class="w"> </span><span class="nx">GroupTypes</span><span class="w"> </span><span class="nx">AccessType</span><span class="w">
</span><span class="o">--</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">----------</span><span class="w"> </span><span class="o">----------</span><span class="w">
</span><span class="mi">01</span><span class="n">f5ff67-6174-4c9b-b142-a423ee94dbe8</span><span class="w"> </span><span class="nx">My</span><span class="w"> </span><span class="nx">PowerShell</span><span class="w"> </span><span class="nx">SDK</span><span class="w"> </span><span class="nx">Team</span><span class="w"> </span><span class="nx">My</span><span class="w"> </span><span class="nx">awesome</span><span class="w"> </span><span class="nx">PowerShell</span><span class="w"> </span><span class="nx">SDK</span><span class="w"> </span><span class="nx">made</span><span class="w"> </span><span class="nx">Team</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="n">Unified</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="add-me-as-team-owner">Add me as team owner</h3>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#1. Put user account with displayName of 'Admin' into a variable</span><span class="w">
</span><span class="nv">$Me</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-Mguser</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'Admin'"</span><span class="w">
</span><span class="c">#2. Put newly created team into a $Team variable</span><span class="w">
</span><span class="nv">$Team</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Get-MgGroup</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"displayName eq 'My PowerShell SDK Team'"</span><span class="w">
</span><span class="c">#3. Add 'Admin' as a 'Owner' of the newly created team using the 'Id' of both user and team</span><span class="w">
</span><span class="n">New-MgTeamMember</span><span class="w"> </span><span class="nt">-Id</span><span class="w"> </span><span class="nv">$Me</span><span class="o">.</span><span class="nf">Id</span><span class="w"> </span><span class="nt">-TeamId</span><span class="w"> </span><span class="nv">$Team</span><span class="o">.</span><span class="nf">Id</span><span class="w"> </span><span class="nt">-Roles</span><span class="w"> </span><span class="s2">"Owner"</span><span class="w"> </span><span class="nt">-AdditionalProperties</span><span class="w"> </span><span class="p">@{</span><span class="w"> </span><span class="s2">"@odata.type"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"#microsoft.graph.aadUserConversationMember"</span><span class="p">;</span><span class="w"> </span><span class="s2">"user@odata.bind"</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"https://graph.microsoft.com/v1.0/users/"</span><span class="w"> </span><span class="err">+</span><span class="w"> </span><span class="nv">$Me</span><span class="err">.</span><span class="nx">Id</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<h3 id="need-help-with-a-cmdlet-or-required-syntax">Need help with a cmdlet or required syntax?</h3>
<p>Did you know that PowerShell has a great build-in help system? Write <code class="language-plaintext highlighter-rouge">help</code> (<em>help</em> is an alias for <code class="language-plaintext highlighter-rouge">Get-Help</code>) in front of any cmdlet to access this system:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">help</span><span class="w"> </span><span class="nx">New-MgUser</span><span class="w">
</span></code></pre></div></div>
<p>Not a fan of reading documentation in a terminal window? No worries! :blush:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Navigates to Microsoft Docs in browser </span><span class="w">
</span><span class="n">help</span><span class="w"> </span><span class="nx">New-MgUser</span><span class="w"> </span><span class="nt">-Online</span><span class="w">
</span><span class="c">#Show help in pop-up window (also provides a handy search bar)</span><span class="w">
</span><span class="n">help</span><span class="w"> </span><span class="nx">New-MgUser</span><span class="w"> </span><span class="nt">-ShowWindow</span><span class="w">
</span></code></pre></div></div>
<p>To keep the help documention up to date on your system, write:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#Updates the help system</span><span class="w">
</span><span class="n">Update-Help</span><span class="w">
</span></code></pre></div></div>
<h3 id="sign-out-of-microsoft-graph">Sign out of Microsoft Graph</h3>
<p>It is a good practice to sign out when you are finished</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Disconnect-MgGraph</span><span class="w">
</span></code></pre></div></div>
<p>I hope this blog post has inspired you to having a go against Microsoft Graph, and I have managed to show how powerful this module is when you want to manage data in Microsoft 365 using PowerShell :blush:</p>
<p>-Freddie</p>Freddie ChristiansenMicrosoft Graph PowerShell SDK I have spend a lot of time interacting with the Microsoft Graph API over the years. Normally I have written a bunch of code with the help of Invoke-RestMethod to get the job done. Sometimes I’ve also used Microsoft Graph Explorer when I need to test something out, or when I need to do a simple GET request to get my hands of some data. In this blog post, I thought I could dive a little deeper into the available SDK for PowerShell against Microsoft Graph. Let’s start with a little introduction. What is Microsoft Graph? I go for the short answer: A RESTful, single API endpoint (https://graph.microsoft.com) for interacting with your data in your Microsoft 365 subscription. Microsoft Graph documentation Found here: https://aka.ms/graphapiref API docs will show the correct endpoint, HTTP request, required permissions (least to most privileged), request headers, request body, response messages and a lot of examples for you to play around with. You can copy/paste examples found in docs and re-use them in Microsoft Graph Explorer with a sample tenant. What is Microsoft Graph Explorer? “…Graph Explorer is a developer tool that lets you conveniently make Microsoft Graph REST API requests and view corresponding responses. Use Graph Explorer to try APIs on the default sample tenant to explore capabilities, or sign in to your own tenant and use it as a prototyping tool to fulfill your app scenarios….” Use Graph Explorer to: Make Microsoft Graph API requests (GET, POST, PUT, PATCH, and DELETE) and see responses including response code and any headers and bodies. Consent to permissions. Graph Explorer supports only delegated permissions. Add a request body and request header to your query. View and copy the access token. View sample queries for different services in Microsoft Graph. View, download, or delete the queries you ran in the last 30 days. View and copy code snippets of each query you run in C#, Java, JavaScript, and Objective C. Access Microsoft Graph Toolkit components and adaptive cards for some sample queries. Share queries, including the request body and request headers. Get Started Graph Explorer is a web application found here. If you are new to this great tool provided by Microsoft, I recommend starting out by reading this article found on Microsoft Docs. Microsoft Graph PowerShell SDK Provides a well known PowerShell-style way of making calls to the Microsoft Graph API Cmdlets have a prefix of MG , a safety mechanism for preventing the cmdlets to crash against other PowerShell cmdlets in different modules (example: Connect-MgGraph, Get-MgUser, Add-MgTeamMember) Cmdlets follows the PowerShell convention of Verb-Noun format Installing the Microsoft Graph PowerShell SDK Note Installing the main module of the SDK will install all 38 sub modules. You might consider only installing the necessary modules, including Microsoft.Graph.Authentication. The Microsoft Graph PowerShell SDK is published on the PowerShell Gallery. You can install the SDK in PowerShell Core or Windows PowerShell using the following command: Install-Module Microsoft.Graph -Scope CurrentUser Optionally, you can change the scope of the installation using the -Scope parameter. This requires admin permissions. Install-Module Microsoft.Graph -Scope AllUsers Important Installing the SDK in one version of PowerShell does not install it for the other. Be sure to run the installation command inside the version of PowerShell (Core or Windows) you intend to use it in. Verify installation After the installation completes, you can verify the installed version with the following command: Get-InstalledModule Microsoft.Graph Updating the SDK You can update the SDK and all of its dependencies using the following command: Update-Module Microsoft.Graph Current version At the time of this writing, the current version of the Microsoft Graph PowerShell SDK is 1.9.1. The version history can be found here. Uninstalling the SDK First, use the following command to uninstall the main module: Uninstall-Module Microsoft.Graph Then, remove all of the dependency modules by running the following commands: Get-InstalledModule Microsoft.Graph.* | ForEach-Object { if($_.Name -ne "Microsoft.Graph.Authentication"){ Uninstall-Module $_.Name } } Uninstall-Module Microsoft.Graph.Authentication Examples of use As you can see below, at the time of this writing, there are thousands of available cmdlets available in this module. #Get Command count from module $Commands = Get-Command -Module Microsoft.Graph* $Commands.Count 6978 Finding available commands Due to the amount of available commands and the complexity of this module, there can be hard to find the right command to use. In this case, you can use the Get-Command to search for available commands in the SDK. For example, if you are looking for commands related to Microsoft Teams, you can run the following command: Get-Command -Module Microsoft.Graph* *team* API version By default, the SDK uses the Microsoft Graph REST API v1.0. You can change this by using the Select-MgProfile command: #Change to the Beta API Select-MgProfile -Name "beta" Microsoft Graph Beta API Please note that this API is subject for change, and should not be used in production. #Change back to the version 1.0 Select-MgProfile -Name "v1.0" Authentication The PowerShell SDK supports two types of authentication: delegated access, and app-only access. In this guide, you will use delegated access to login as a user, grant consent to the SDK to act on your behalf, and call the Microsoft Graph. Determine required permission scopes Each API in the Microsoft Graph is protected by one or more permission scopes. The user logging in must consent to one of the required scopes for the APIs you plan to use. In this example, we’ll use the following APIs. List users to find the user ID of the logged-in user List joinedTeams to get the Teams the user is a member of. List channels to get the channels in a Team. Send message to send a message to a Team channel. The User.Read.All permission scope will enable the first two calls, and the Group.ReadWrite.All scope will enable the rest. These permissions require an admin account. Sign in Use the Connect-MgGraph command to sign in with the required scopes. You’ll need to sign in with an admin account to consent to the required scopes. Connect-MgGraph -Scopes "User.Read.All","Group.ReadWrite.All" The command prompts you to go to a web page to sign in using a device code. Once you’ve done that, the command indicates success with a Welcome To Microsoft Graph! message. You only need to do this once per session. Note You can add additional permissions by repeating the Connect-MgGraph command with the new permission scopes. Example: If you are going to create a new team, head over to the Microsoft Graph documentation on create team, and look up the required permissons table: Like shown here, you need to change (or add) the permissions to include (from least to most privileged): Team.Create *Group.ReadWrite.All** (deprecated and should not be used) Directory.ReadWrite.All (deprecated and should not be used) Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All", "Team.Create" After you have executed the cmdlet shown above, a web page are opened and requires you to accept the newly added permissions (see that Create teams are added to the list) : Accept the required permissions to complete the connection to Microsoft Graph. Call Microsoft Graph Now that you’re signed in, you can start making calls to Microsoft Graph. Get the signed-in user Get-MgUser This outputs a listing of users in your Microsoft 365 subscription: Id DisplayName Mail UserPrincipalName UserType -- ----------- ---- ----------------- -------- 73b8a426-22b1-49d1-a181-ae931495f6f4 Adele Vance adelev@cloudpilotlabs.xyz adelev@cloudpilotlabs.xyz 42d7a7a0-e6d0-46f1-b570-fae50ae12899 Admin admin@cloudpilotlabs.xyz admin@cloudpilotlabs.xyz 2a7887cd-c2aa-4b81-9a6b-e8fa42f1cddf Alex Wilber AlexW@cloudpilotdev.onmicrosoft.com AlexW@cloudpilotdev.onmicrosoft.com 3e3ef4b2-20fa-41fa-b764-3dac0bbdcc42 Berit Nordmann berit@cloudpilotdev.onmicrosoft.com berit@cloudpilotdev.onmicrosoft.com 2996d76a-a2db-426b-a1ec-93c6e6c92725 Bodil Hansen bodil.hansen@cloudpilotlabs.xyz bodil.hansen@cloudpilotlabs.xyz Filtering The PowerShell SDK supports OData filtering: #Get user by filtering by DisplayName $User = Get-MgUser -Filter "displayName eq 'Alex Wilber'" Verify that worked by entering the following: #Return DisplayName $User.DisplayName Alex Wilber should be returned in the console. Using Query Parameter You can use the -Property parameter to only return some properties from your query: #Get user by Id and return DisplayName and Mail Get-MgUser -UserId $User.Id -Property DisplayName, Mail The following is then returned: Id DisplayName Mail UserPrincipalName UserType -- ----------- ---- ----------------- -------- Alex Wilber AlexW@cloudpilotlabs.xyz List the user’s joined teams Use the user’s ID as a parameter to the Get-MgUserJoinedTeam command: #List all Joined Team for spesific user by Id Get-MgUserJoinedTeam -UserId $User.Id A list of joined teams is returned (see example below: Id DisplayName Description IsArchived -- ----------- ----------- ---------- c0b599d5-480d-43a4-9f7a-f366ae39850a cloudpilotdev False c550e12e-9647-49a8-9e1a-37e3f1cc690e External Team A team used for allowing and testing external access False List team channels Let’s use the team’s Id as a parameter to the Get-MgTeamChannel command: #1. Save the "External Team's Id into a variable for future use $externalTeam = "c550e12e-9647-49a8-9e1a-37e3f1cc690e" #2. Save the 'General' channel Id into a variable for future use $general = Get-MgTeamChannel -TeamId $externalTeam -Filter "displayName eq 'General'" Send a message to teams channel Now let’s use our $externalTeam variable and our $general variable to post a message to our External Team general channel using the following command: #New Team Channel Massage using Microsoft Graph SDK New-MgTeamChannelMessage -TeamId $externalTeam -ChannelId $general.Id -Body @{ Content="Hello World" } If you want to send an urgent message, add the -Importance parameter as shown below: #New Urgent Team Channel Message using Microsoft Graph SDK New-MgTeamChannelMessage -TeamId $externalTeam -ChannelId $general.Id -Body @{ Content="Please bring me a coffee!" } -Importance "urgent" Create a new user Example 1: Uses the traditional format: #Create a new user account using Microsoft Graph SDK for PowerShell $PasswordProfile = @{ Password = 'P@ssW0rd123'; ForceChangePasswordNextSignIn = $true } New-MgUser -DisplayName "Ola Nordmann" -PasswordProfile $PasswordProfile -AccountEnabled -MailNickName "OlaNor" -UserPrincipalName "Ola.Nordmann@cloudpilotlabs.xyz" Example 2: Use splatting and hash tables for easier readability: $Parameters = @{ PasswordProfile = @{ Password = 'P@ssW0rd123' ForceChangePasswordNextSignIn = $true } DisplayName = "Ola Nordmann" AccountEnabled = $true MailNickName = "OlaNor" UserPrincipalName = "Ola.Nordmann@cloudpilotlabs.xyz" } New-MgUser @Parameters Remove a user #Remove a user account using Microsoft Graph SDK for PowerShell #Assign the User's Id into a variable named $DeleteUser $DeleteUser = Get-MgUser -Filter "displayName eq 'Ola Nordmann'" #Confirm the Id $DeleteUser.Id 97b1eb01-a366-4023-b737-8d82b2877a82 #Delete the user Remove-MgUser -UserId $DeleteUser.Id Create new team #Create a new team using splatting $Splat = @{ DisplayName = "My PowerShell SDK Team" Description = "My awesome PowerShell SDK made Team!" AdditionalProperties = @{"template@odata.bind"="https://graph.microsoft.com/v1.0/teamsTemplates('standard')"} } New-MgTeam @Splat Lookup team #Get new Team by searching by displayName $Team = Get-MgGroup -Filter "displayName eq 'My PowerShell SDK Team'" $Team Id DisplayName Description GroupTypes AccessType -- ----------- ----------- ---------- ---------- 01f5ff67-6174-4c9b-b142-a423ee94dbe8 My PowerShell SDK Team My awesome PowerShell SDK made Team! {Unified} Add me as team owner #1. Put user account with displayName of 'Admin' into a variable $Me = Get-Mguser -Filter "displayName eq 'Admin'" #2. Put newly created team into a $Team variable $Team = Get-MgGroup -Filter "displayName eq 'My PowerShell SDK Team'" #3. Add 'Admin' as a 'Owner' of the newly created team using the 'Id' of both user and team New-MgTeamMember -Id $Me.Id -TeamId $Team.Id -Roles "Owner" -AdditionalProperties @{ "@odata.type" = "#microsoft.graph.aadUserConversationMember"; "user@odata.bind" = "https://graph.microsoft.com/v1.0/users/" + $Me.Id } Need help with a cmdlet or required syntax? Did you know that PowerShell has a great build-in help system? Write help (help is an alias for Get-Help) in front of any cmdlet to access this system: help New-MgUser Not a fan of reading documentation in a terminal window? No worries! :blush: #Navigates to Microsoft Docs in browser help New-MgUser -Online #Show help in pop-up window (also provides a handy search bar) help New-MgUser -ShowWindow To keep the help documention up to date on your system, write: #Updates the help system Update-Help Sign out of Microsoft Graph It is a good practice to sign out when you are finished Disconnect-MgGraph I hope this blog post has inspired you to having a go against Microsoft Graph, and I have managed to show how powerful this module is when you want to manage data in Microsoft 365 using PowerShell :blush: -FreddieScoping Azure AD Application permissions to specific Exchange Online mailboxes2021-05-19T23:00:00+02:002021-05-19T23:00:00+02:00https://www.cloudpilot.no/blog/Scoping-Azure-AD-Application-permissions-to-specific-Exchange%20Online-mailboxes<h3 id="scoping-azure-ad-application-permissions-to-specific-exchange-online-mailboxes">Scoping Azure AD Application permissions to specific Exchange Online mailboxes</h3>
<p>When working with application permissions via Microsoft Graph, some IT administrators may want to limit the app access to a specific set of mailboxes. For example, the <em>Mail.Read</em> application permission allows apps to read mail in all mailboxes without a signed-in user. This can be unfortunate in some contexts. Using this approach will allow granular access control to your organization’s user data.</p>
<blockquote>
<p>Please note - at the time of this writing, only specific permissions against the <em>Outlook REST APIs</em> are supported. See the list of supported permissions copied from <a href="https://docs.microsoft.com/en-us/graph/auth-limit-mailbox-access">Microsoft Docs</a> below:</p>
</blockquote>
<ul>
<li><em>Mail.Read</em></li>
<li><em>Mail.ReadBasic</em></li>
<li><em>Mail.ReadBasic.All</em></li>
<li><em>Mail.ReadWrite</em></li>
<li><em>Mail.Send</em></li>
<li><em>MailboxSettings.Read</em></li>
<li><em>MailboxSettings.ReadWrite</em></li>
<li><em>Calendars.Read</em></li>
<li><em>Calendars.ReadWrite</em></li>
<li><em>Contacts.Read</em></li>
<li><em>Contacts.ReadWrite</em></li>
</ul>
<h2 id="gather-prerequisites">Gather prerequisites</h2>
<p>Connect to Azure AD using PowerShell:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Connect-AzureAD</span><span class="w">
</span></code></pre></div></div>
<p>Get details of your Azure AD application. Make a note of the returned <em>AppId</em> for future use.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Get-AzureADApplication</span><span class="w"> </span><span class="nt">-Filter</span><span class="w"> </span><span class="s2">"DisplayName eq 'MySampleApplication'"</span><span class="w">
</span><span class="n">ObjectId</span><span class="w"> </span><span class="nx">AppId</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w">
</span><span class="o">--------</span><span class="w"> </span><span class="o">-----</span><span class="w"> </span><span class="o">-----------</span><span class="w">
</span><span class="n">abb699b3-537f-4516-81bf-2fc4edb22990</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w"> </span><span class="nx">MySampleApplication</span><span class="w">
</span></code></pre></div></div>
<p>Unfortunately, it is not possible to create a new mail-enabled security group using PowerShell, so we will need to create it via the EAC (Exchange Admin Center):</p>
<ol>
<li>
<p>In the <a href="https://admin.exchange.microsoft.com/">EAC</a>, navigate to <strong>Recipients</strong> > <strong>Groups</strong> > <strong>Mail-enabled security</strong>.</p>
</li>
<li>
<p>Click <strong>Add a group</strong> and follow the instructions in the details pane.</p>
<ul>
<li>
<p>Under <strong>Choose a group type</strong> section, select <strong>Mail-enabled security</strong> and click <strong>Next</strong>.</p>
</li>
<li>
<p>Under <strong>Set up the basics</strong> section, enter the details and click <strong>Next</strong>.</p>
</li>
</ul>
</li>
<li>
<p>In <strong>Assign owners</strong> section, click <strong>+ Assign owners</strong>, select the group owner from the list, and click <strong>Next</strong>.</p>
</li>
<li>
<p>Under <strong>Add members</strong>, click <strong>+ Add members</strong>, select the group members from the list, and click <strong>Next</strong>.</p>
</li>
<li>
<p>In <strong>Edit settings</strong> section, enter the group email address (this you will use when creating a new application policy) and then click <strong>Next</strong>:</p>
</li>
<li>
<p>In <strong>Review and finish adding group</strong> section, verify all the details, click <strong>Create group</strong>, and then click <strong>Close</strong>.</p>
</li>
</ol>
<h2 id="configure-a-new-application-access-policy--2-examples-">Configure a new Application Access Policy ( 2 examples )</h2>
<h3 id="example-1---create-a-new-deny-access-application-policy">Example 1 - Create a new <em>Deny Access</em> Application Policy</h3>
<p>Creating this app policy will <strong>deny access</strong> to <strong>all</strong> Microsoft Graph APIs for Outlook resources mentioned above to <strong>all</strong> members of the selected security group.</p>
<p>Connect to Exchange Online using PowerShell:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-ExchangeOnline</span><span class="w">
</span></code></pre></div></div>
<p>Create a new <em>deny</em> access policy:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">New-ApplicationAccessPolicy</span><span class="w"> </span><span class="nt">-AccessRight</span><span class="w"> </span><span class="nx">DenyAccess</span><span class="w"> </span><span class="nt">-AppId</span><span class="w"> </span><span class="s2">"<Your Azure AD Application Id>"</span><span class="w"> </span><span class="nt">-PolicyScopeGroupID</span><span class="w"> </span><span class="s2">"<Display Name of your mail-enabled security group>"</span><span class="w"> </span><span class="nt">-Description</span><span class="w"> </span><span class="s2">"<An appropriate description>"</span><span class="w">
</span></code></pre></div></div>
<p>Real world example:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">New-ApplicationAccessPolicy</span><span class="w"> </span><span class="nt">-AccessRight</span><span class="w"> </span><span class="nx">RestrictAccess</span><span class="w"> </span><span class="nt">-AppId</span><span class="w"> </span><span class="s2">"720062ad-3b7f-4e0a-85b3-2b2c1fce5a4c"</span><span class="w"> </span><span class="nt">-PolicyScopeGroupId</span><span class="w"> </span><span class="s2">"MyRestrictedSecGroup"</span><span class="w"> </span><span class="nt">-Description</span><span class="w"> </span><span class="s2">"Restrict this app to members of the security group MyRestrictedUsersGroup"</span><span class="w">
</span></code></pre></div></div>
<p>To view details of your newly created app policy (or all others) , you could use the following cmdlet:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Return a list of application access policies</span><span class="w">
</span><span class="n">Get-ApplicationAccessPolicy</span><span class="w">
</span></code></pre></div></div>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Get-ApplicationAccessPolicy</span><span class="w">
</span><span class="n">RunspaceId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">21903e33-af08-41c2-b7bc-3b45bf99fb96</span><span class="w">
</span><span class="n">ScopeName</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">MyRestrictedSecGroup</span><span class="w">
</span><span class="n">ScopeIdentity</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">MyRestrictedSecGroup20210509121159</span><span class="w">
</span><span class="n">Identity</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">0218a6c0-f384-4e87-8313-2f1b0da4ce33\5050e2d9-1797-4207-a012-27bf174ab2a7:S-1-5-21-3169798578-2053692951-2481737396-4642945</span><span class="p">;</span><span class="mi">3</span><span class="n">c976d04-866b-40ce-8825-5fb08d71d9bd</span><span class="w">
</span><span class="nx">AppId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w">
</span><span class="n">ScopeIdentityRaw</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">S-1-5-21-3169798578-2053692951-2481737396-4642945</span><span class="p">;</span><span class="mi">3</span><span class="n">c976d04-866b-40ce-8825-5fb08d71d9bd</span><span class="w">
</span><span class="nx">Description</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Restrict</span><span class="w"> </span><span class="nx">this</span><span class="w"> </span><span class="nx">app</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">members</span><span class="w"> </span><span class="nx">of</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">security</span><span class="w"> </span><span class="nx">group</span><span class="w"> </span><span class="nx">MyRestrictedUsersGroup</span><span class="w">
</span><span class="n">AccessRight</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">RestrictAccess</span><span class="w">
</span><span class="n">ShardType</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">All</span><span class="w">
</span><span class="n">IsValid</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">True</span><span class="w">
</span><span class="n">ObjectState</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Unchanged</span><span class="w">
</span></code></pre></div></div>
<h3 id="example-2---create-a-new-restrict-access-application-policy">Example 2 - Create a new <em>Restrict Access</em> Application Policy</h3>
<p>Creating this app policy will <strong>restrict access</strong> to <strong>all</strong> Microsoft Graph APIs for Outlook resources mentioned above to <strong>only</strong> the members of the selected security group.</p>
<p>Connect to Exchange Online if not already connected:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-ExchangeOnline</span><span class="w">
</span></code></pre></div></div>
<p>Create a new <em>restrict</em> access policy:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">New-ApplicationAccessPolicy</span><span class="w"> </span><span class="nt">-AccessRight</span><span class="w"> </span><span class="nx">RestrictAccess</span><span class="w"> </span><span class="nt">-AppId</span><span class="w"> </span><span class="s2">"<Your Azure AD Application Id>"</span><span class="w"> </span><span class="nt">-PolicyScopeGroupID</span><span class="w"> </span><span class="s2">"<Display Name of your mail-enabled security group>"</span><span class="w"> </span><span class="nt">-Description</span><span class="w"> </span><span class="s2">"<An appropriate description>"</span><span class="w">
</span></code></pre></div></div>
<h3 id="optional-testing-and-verification">(Optional) Testing and verification</h3>
<p>The following is targeted against a policy of scope <em>RestrictAccess</em>, but the workflow will also be similar agaist testing a policy of scope <em>DenyAccess</em>.</p>
<p>Get a list of all members of out selected security group:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># List members of our 'MyRestrictedSecGroup'</span><span class="w">
</span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="p">(</span><span class="n">Get-AzureADGroup</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Where-Object</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="o">.</span><span class="nf">DisplayName</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="s1">'MyRestrictedSecGroup'</span><span class="p">})</span><span class="o">.</span><span class="nf">ObjectId</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Get-AzureADGroupMember</span><span class="w">
</span><span class="n">ObjectId</span><span class="w"> </span><span class="nx">DisplayName</span><span class="w"> </span><span class="nx">UserPrincipalName</span><span class="w"> </span><span class="nx">UserType</span><span class="w">
</span><span class="o">--------</span><span class="w"> </span><span class="o">-----------</span><span class="w"> </span><span class="o">-----------------</span><span class="w"> </span><span class="o">--------</span><span class="w">
</span><span class="mi">73</span><span class="n">b8a426-22b1-49d1-a181-ae931495f6f4</span><span class="w"> </span><span class="nx">Adele</span><span class="w"> </span><span class="nx">Vance</span><span class="w"> </span><span class="nx">AdeleV</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w"> </span><span class="nx">Member</span><span class="w">
</span><span class="mi">2</span><span class="n">a7887cd-c2aa-4b81-9a6b-e8fa42f1cddf</span><span class="w"> </span><span class="nx">Alex</span><span class="w"> </span><span class="nx">Wilber</span><span class="w"> </span><span class="nx">AlexW</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w"> </span><span class="nx">Member</span><span class="w">
</span></code></pre></div></div>
<p>Now you have some user accounts to test out new newly created app policy.</p>
<p>When sending a GET request to Microsoft Graph API Endpoint to list calendars to a users who is member of the selected security group, you’ll get a normal and expected response in return:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET https://graph.microsoft.com/v1.0/users/AdeleV@cloudpilotdev.onmicrosoft.com/calendar
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"@odata.context"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://graph.microsoft.com/v1.0/$metadata#users('AdeleV%40cloudpilotdev.onmicrosoft.com')/calendar/$entity"</span><span class="p">,</span><span class="w">
</span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AQMkADg0MThmNGVkLTdmMjQtNGY5OC1iZGUwLWQzZjk5ADFhNTAxYmEARgAAA4gfPgDAnWtGvewW3CeyKRcHAOxt54xXh55Lgicv6QBaDbIAAAIBBgAAAOxt54xXh55Lgicv6QBaDbIAAAJyUgAAAA=="</span><span class="p">,</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Calendar"</span><span class="p">,</span><span class="w">
</span><span class="nl">"color"</span><span class="p">:</span><span class="w"> </span><span class="s2">"auto"</span><span class="p">,</span><span class="w">
</span><span class="nl">"hexColor"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"isDefaultCalendar"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"changeKey"</span><span class="p">:</span><span class="w"> </span><span class="s2">"7G3njFeHnkuCJy/pAFoNsgAAAAAGQQ=="</span><span class="p">,</span><span class="w">
</span><span class="nl">"canShare"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"canViewPrivateItems"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"canEdit"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"allowedOnlineMeetingProviders"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"teamsForBusiness"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"defaultOnlineMeetingProvider"</span><span class="p">:</span><span class="w"> </span><span class="s2">"teamsForBusiness"</span><span class="p">,</span><span class="w">
</span><span class="nl">"isTallyingResponses"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"isRemovable"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"owner"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Adele Vance"</span><span class="p">,</span><span class="w">
</span><span class="nl">"address"</span><span class="p">:</span><span class="w"> </span><span class="s2">"AdeleV@cloudpilotdev.onmicrosoft.com"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>But when trying to do the same thing against a user who is <strong>not</strong> a part of the selected security group, you’ll recieve an <em>access denied</em> response back. It works as expected.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GET https://graph.microsoft.com/v1.0/users/HenriettaM@cloudpilotdev.onmicrosoft.com/calendar
</code></pre></div></div>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"error"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ErrorAccessDenied"</span><span class="p">,</span><span class="w">
</span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Access to OData is disabled."</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p><strong>ProTip!</strong></p>
<p>If you prefer, it is also possible to test access via PowerShell :blush:
You could use the <em>Test-ApplicationAccessPolicy</em> cmdlet to test access rights of an application to a specific user/mailbox.</p>
<p>Testing a user account who <strong>is</strong> a part of the selected security group:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Test-ApplicationAccessPolicy</span><span class="w"> </span><span class="nt">-AppId</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w"> </span><span class="nt">-Identity</span><span class="w"> </span><span class="nx">AdeleV</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w">
</span><span class="n">RunspaceId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">ec48c12d-4d40-4ae3-ad0d-9d8d347a0c2a</span><span class="w">
</span><span class="n">AppId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w">
</span><span class="n">Mailbox</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">AdeleV</span><span class="w">
</span><span class="n">MailboxId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">73b8a426-22b1-49d1-a181-ae931495f6f4</span><span class="w">
</span><span class="n">MailboxSid</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">S-1-5-21-3169798578-2053692951-2481737396-4020940</span><span class="w">
</span><span class="n">AccessCheckResult</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Granted</span><span class="w">
</span></code></pre></div></div>
<p>As you can see, <em>AccessCheckResult</em> returns <em>Granted</em>.</p>
<p>Testing a user account who is <strong>not</strong> a part of the selected security group:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Test-ApplicationAccessPolicy</span><span class="w"> </span><span class="nt">-AppId</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w"> </span><span class="nt">-Identity</span><span class="w"> </span><span class="nx">LeeG</span><span class="err">@</span><span class="nx">cloudpilotdev.onmicrosoft.com</span><span class="w">
</span><span class="n">RunspaceId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">ec48c12d-4d40-4ae3-ad0d-9d8d347a0c2a</span><span class="w">
</span><span class="n">AppId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">5050e2d9-1797-4207-a012-27bf174ab2a7</span><span class="w">
</span><span class="n">Mailbox</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">LeeG</span><span class="w">
</span><span class="n">MailboxId</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">b9806fa0-42c9-4175-a052-8cd70bceb1f2</span><span class="w">
</span><span class="n">MailboxSid</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">S-1-5-21-3169798578-2053692951-2481737396-4020925</span><span class="w">
</span><span class="n">AccessCheckResult</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Denied</span><span class="w">
</span></code></pre></div></div>
<p>In this example, <em>AccessCheckResult</em> returns <em>Denied</em>. It works :smile:</p>
<p><strong>Additional resources found at Microsoft Docs:</strong></p>
<ul>
<li>
<p><a href="https://docs.microsoft.com/en-us/powershell/module/exchange/new-applicationaccesspolicy?view=exchange-ps">New-ApplicationAccessPolicy</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/powershell/module/exchange/get-applicationaccesspolicy?view=exchange-ps">Get-ApplicationAccessPolicy</a></p>
</li>
<li>
<p><a href="https://docs.microsoft.com/en-us/powershell/module/exchange/test-applicationaccesspolicy?view=exchange-ps">Test-ApplicationPolicy</a></p>
</li>
</ul>Freddie ChristiansenScoping Azure AD Application permissions to specific Exchange Online mailboxes When working with application permissions via Microsoft Graph, some IT administrators may want to limit the app access to a specific set of mailboxes. For example, the Mail.Read application permission allows apps to read mail in all mailboxes without a signed-in user. This can be unfortunate in some contexts. Using this approach will allow granular access control to your organization’s user data. Please note - at the time of this writing, only specific permissions against the Outlook REST APIs are supported. See the list of supported permissions copied from Microsoft Docs below: Mail.Read Mail.ReadBasic Mail.ReadBasic.All Mail.ReadWrite Mail.Send MailboxSettings.Read MailboxSettings.ReadWrite Calendars.Read Calendars.ReadWrite Contacts.Read Contacts.ReadWrite Gather prerequisites Connect to Azure AD using PowerShell: C:\WINDOWS\system32> Connect-AzureAD Get details of your Azure AD application. Make a note of the returned AppId for future use. C:\WINDOWS\system32> Get-AzureADApplication -Filter "DisplayName eq 'MySampleApplication'" ObjectId AppId DisplayName -------- ----- ----------- abb699b3-537f-4516-81bf-2fc4edb22990 5050e2d9-1797-4207-a012-27bf174ab2a7 MySampleApplication Unfortunately, it is not possible to create a new mail-enabled security group using PowerShell, so we will need to create it via the EAC (Exchange Admin Center): In the EAC, navigate to Recipients > Groups > Mail-enabled security. Click Add a group and follow the instructions in the details pane. Under Choose a group type section, select Mail-enabled security and click Next. Under Set up the basics section, enter the details and click Next. In Assign owners section, click + Assign owners, select the group owner from the list, and click Next. Under Add members, click + Add members, select the group members from the list, and click Next. In Edit settings section, enter the group email address (this you will use when creating a new application policy) and then click Next: In Review and finish adding group section, verify all the details, click Create group, and then click Close. Configure a new Application Access Policy ( 2 examples ) Example 1 - Create a new Deny Access Application Policy Creating this app policy will deny access to all Microsoft Graph APIs for Outlook resources mentioned above to all members of the selected security group. Connect to Exchange Online using PowerShell: Connect-ExchangeOnline Create a new deny access policy: New-ApplicationAccessPolicy -AccessRight DenyAccess -AppId "<Your Azure AD Application Id>" -PolicyScopeGroupID "<Display Name of your mail-enabled security group>" -Description "<An appropriate description>" Real world example: New-ApplicationAccessPolicy -AccessRight RestrictAccess -AppId "720062ad-3b7f-4e0a-85b3-2b2c1fce5a4c" -PolicyScopeGroupId "MyRestrictedSecGroup" -Description "Restrict this app to members of the security group MyRestrictedUsersGroup" To view details of your newly created app policy (or all others) , you could use the following cmdlet: # Return a list of application access policies Get-ApplicationAccessPolicy PS C:\WINDOWS\system32> Get-ApplicationAccessPolicy RunspaceId : 21903e33-af08-41c2-b7bc-3b45bf99fb96 ScopeName : MyRestrictedSecGroup ScopeIdentity : MyRestrictedSecGroup20210509121159 Identity : 0218a6c0-f384-4e87-8313-2f1b0da4ce33\5050e2d9-1797-4207-a012-27bf174ab2a7:S-1-5-21-3169798578-2053692951-2481737396-4642945;3c976d04-866b-40ce-8825-5fb08d71d9bd AppId : 5050e2d9-1797-4207-a012-27bf174ab2a7 ScopeIdentityRaw : S-1-5-21-3169798578-2053692951-2481737396-4642945;3c976d04-866b-40ce-8825-5fb08d71d9bd Description : Restrict this app to members of the security group MyRestrictedUsersGroup AccessRight : RestrictAccess ShardType : All IsValid : True ObjectState : Unchanged Example 2 - Create a new Restrict Access Application Policy Creating this app policy will restrict access to all Microsoft Graph APIs for Outlook resources mentioned above to only the members of the selected security group. Connect to Exchange Online if not already connected: Connect-ExchangeOnline Create a new restrict access policy: New-ApplicationAccessPolicy -AccessRight RestrictAccess -AppId "<Your Azure AD Application Id>" -PolicyScopeGroupID "<Display Name of your mail-enabled security group>" -Description "<An appropriate description>" (Optional) Testing and verification The following is targeted against a policy of scope RestrictAccess, but the workflow will also be similar agaist testing a policy of scope DenyAccess. Get a list of all members of out selected security group: # List members of our 'MyRestrictedSecGroup' PS C:\WINDOWS\system32> (Get-AzureADGroup | Where-Object {$_.DisplayName -eq 'MyRestrictedSecGroup'}).ObjectId | Get-AzureADGroupMember ObjectId DisplayName UserPrincipalName UserType -------- ----------- ----------------- -------- 73b8a426-22b1-49d1-a181-ae931495f6f4 Adele Vance AdeleV@cloudpilotdev.onmicrosoft.com Member 2a7887cd-c2aa-4b81-9a6b-e8fa42f1cddf Alex Wilber AlexW@cloudpilotdev.onmicrosoft.com Member Now you have some user accounts to test out new newly created app policy. When sending a GET request to Microsoft Graph API Endpoint to list calendars to a users who is member of the selected security group, you’ll get a normal and expected response in return: GET https://graph.microsoft.com/v1.0/users/AdeleV@cloudpilotdev.onmicrosoft.com/calendar { "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('AdeleV%40cloudpilotdev.onmicrosoft.com')/calendar/$entity", "id": "AQMkADg0MThmNGVkLTdmMjQtNGY5OC1iZGUwLWQzZjk5ADFhNTAxYmEARgAAA4gfPgDAnWtGvewW3CeyKRcHAOxt54xXh55Lgicv6QBaDbIAAAIBBgAAAOxt54xXh55Lgicv6QBaDbIAAAJyUgAAAA==", "name": "Calendar", "color": "auto", "hexColor": "", "isDefaultCalendar": true, "changeKey": "7G3njFeHnkuCJy/pAFoNsgAAAAAGQQ==", "canShare": true, "canViewPrivateItems": true, "canEdit": true, "allowedOnlineMeetingProviders": [ "teamsForBusiness" ], "defaultOnlineMeetingProvider": "teamsForBusiness", "isTallyingResponses": true, "isRemovable": false, "owner": { "name": "Adele Vance", "address": "AdeleV@cloudpilotdev.onmicrosoft.com" } } But when trying to do the same thing against a user who is not a part of the selected security group, you’ll recieve an access denied response back. It works as expected. GET https://graph.microsoft.com/v1.0/users/HenriettaM@cloudpilotdev.onmicrosoft.com/calendar { "error": { "code": "ErrorAccessDenied", "message": "Access to OData is disabled." } } ProTip! If you prefer, it is also possible to test access via PowerShell :blush: You could use the Test-ApplicationAccessPolicy cmdlet to test access rights of an application to a specific user/mailbox. Testing a user account who is a part of the selected security group: PS C:\WINDOWS\system32> Test-ApplicationAccessPolicy -AppId 5050e2d9-1797-4207-a012-27bf174ab2a7 -Identity AdeleV@cloudpilotdev.onmicrosoft.com RunspaceId : ec48c12d-4d40-4ae3-ad0d-9d8d347a0c2a AppId : 5050e2d9-1797-4207-a012-27bf174ab2a7 Mailbox : AdeleV MailboxId : 73b8a426-22b1-49d1-a181-ae931495f6f4 MailboxSid : S-1-5-21-3169798578-2053692951-2481737396-4020940 AccessCheckResult : Granted As you can see, AccessCheckResult returns Granted. Testing a user account who is not a part of the selected security group: PS C:\WINDOWS\system32> Test-ApplicationAccessPolicy -AppId 5050e2d9-1797-4207-a012-27bf174ab2a7 -Identity LeeG@cloudpilotdev.onmicrosoft.com RunspaceId : ec48c12d-4d40-4ae3-ad0d-9d8d347a0c2a AppId : 5050e2d9-1797-4207-a012-27bf174ab2a7 Mailbox : LeeG MailboxId : b9806fa0-42c9-4175-a052-8cd70bceb1f2 MailboxSid : S-1-5-21-3169798578-2053692951-2481737396-4020925 AccessCheckResult : Denied In this example, AccessCheckResult returns Denied. It works :smile: Additional resources found at Microsoft Docs: New-ApplicationAccessPolicy Get-ApplicationAccessPolicy Test-ApplicationPolicyTransition from per-user MFA to Conditional Access MFA2021-05-12T23:00:00+02:002021-05-12T23:00:00+02:00https://www.cloudpilot.no/blog/Transition-from-per-user%20MFA-to-Conditional-Access-MFA<h3 id="transition-from-per-user-mfa-to-conditional-access-mfa">Transition from per-user MFA to Conditional Access MFA</h3>
<p>In the blog post, I thought to show how to easily transition from per-user based MFA to a Conditional Access based MFA.</p>
<blockquote>
<p><strong>What is Conditional Access?</strong>
“..The modern security perimeter now extends beyond an organization’s network to include user and device identity. Organizations can utilize these identity signals as part of their access control decisions… Conditional Access is the tool used by Azure Active Directory to bring signals together, to make decisions, and enforce organizational policies. Conditional Access is at the heart of the new identity driven control plane.”</p>
</blockquote>
<h3 id="check-azure-ad-premium-license">Check Azure AD Premium license</h3>
<p>Check first if your organization has either Azure AD Premium Plan 1 og 2. Log into <a href="https://portal.azure.com">Microsoft Azure</a>. In the portal, navigate to <em>Azure Active Directory > Overview</em>. In my example below, Azure AD Premium P2 is shown.</p>
<p><img src="/assets/images/MFA_GA/01.png" alt="PATH" /></p>
<h3 id="check-per-user-mfa-status">Check per-user MFA status</h3>
<p>Log into <a href="https://admin.microsoft.com">Microsoft 365 admin center</a>. Navigate to <em>Users > Active Users > Multi-factor authentication</em>.</p>
<p><img src="/assets/images/MFA_GA/02.png" alt="PATH" /></p>
<p>A new page will open, showing all the users, and their <em>multi-factor autentication status</em>. In the example below (in norwegian this one..), we have a couple of users who have MFA enabled, some enforced and some others in disabled state.</p>
<p><img src="/assets/images/MFA_GA/03.png" alt="PATH" /></p>
<h3 id="connect-to-azure-ad-using-powershell">Connect to Azure AD using PowerShell</h3>
<p>To start our transition from per-user MFA to Conditional Access, you’ll need to start Windows PowerShell as an administrator and connect to Azure AD using the following in cmdlet:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Connect-MsolService</span><span class="w">
</span></code></pre></div></div>
<p>This cmdlet is part of the <em>MSOnline module</em>. If you haven’t got this module installed in your system, you can install it by typing the following in your elevated PowerShell console:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Install-Module</span><span class="w"> </span><span class="nx">MSOnline</span><span class="w">
</span></code></pre></div></div>
<h3 id="convert-per-user-mfa-to-conditional-access-based-mfa-with-powershell">Convert per-user MFA to Conditional Access based MFA with PowerShell</h3>
<p>When we are connected to Azure AD with PowerShell, Microsoft has luckily provided the community with a great script that we could use for helping us with the convertion.</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Sets the MFA requirement state</span><span class="w">
</span><span class="kr">function</span><span class="w"> </span><span class="nf">Set-MfaState</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="p">[</span><span class="n">CmdletBinding</span><span class="p">()]</span><span class="w">
</span><span class="kr">param</span><span class="p">(</span><span class="w">
</span><span class="p">[</span><span class="n">Parameter</span><span class="p">(</span><span class="n">ValueFromPipelineByPropertyName</span><span class="o">=</span><span class="nv">$True</span><span class="p">)]</span><span class="w">
</span><span class="nv">$ObjectId</span><span class="p">,</span><span class="w">
</span><span class="p">[</span><span class="n">Parameter</span><span class="p">(</span><span class="n">ValueFromPipelineByPropertyName</span><span class="o">=</span><span class="nv">$True</span><span class="p">)]</span><span class="w">
</span><span class="nv">$UserPrincipalName</span><span class="p">,</span><span class="w">
</span><span class="p">[</span><span class="n">ValidateSet</span><span class="p">(</span><span class="s2">"Disabled"</span><span class="p">,</span><span class="s2">"Enabled"</span><span class="p">,</span><span class="s2">"Enforced"</span><span class="p">)]</span><span class="w">
</span><span class="nv">$State</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="kr">Process</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="n">Write-Verbose</span><span class="w"> </span><span class="p">(</span><span class="s2">"Setting MFA state for user '{0}' to '{1}'."</span><span class="w"> </span><span class="nt">-f</span><span class="w"> </span><span class="nv">$ObjectId</span><span class="p">,</span><span class="w"> </span><span class="nv">$State</span><span class="p">)</span><span class="w">
</span><span class="nv">$Requirements</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">@()</span><span class="w">
</span><span class="kr">if</span><span class="w"> </span><span class="p">(</span><span class="nv">$State</span><span class="w"> </span><span class="o">-ne</span><span class="w"> </span><span class="s2">"Disabled"</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nv">$Requirement</span><span class="w"> </span><span class="o">=</span><span class="w">
</span><span class="p">[</span><span class="n">Microsoft.Online.Administration.StrongAuthenticationRequirement</span><span class="p">]::</span><span class="n">new</span><span class="p">()</span><span class="w">
</span><span class="nv">$Requirement</span><span class="o">.</span><span class="nf">RelyingParty</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"*"</span><span class="w">
</span><span class="nv">$Requirement</span><span class="o">.</span><span class="nf">State</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">$State</span><span class="w">
</span><span class="nv">$Requirements</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nv">$Requirement</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="n">Set-MsolUser</span><span class="w"> </span><span class="nt">-ObjectId</span><span class="w"> </span><span class="nv">$ObjectId</span><span class="w"> </span><span class="nt">-UserPrincipalName</span><span class="w"> </span><span class="nv">$UserPrincipalName</span><span class="w"> </span><span class="se">`
</span><span class="w"> </span><span class="nt">-StrongAuthenticationRequirements</span><span class="w"> </span><span class="nv">$Requirements</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="c"># Disable MFA for all users</span><span class="w">
</span><span class="n">Get-MsolUser</span><span class="w"> </span><span class="nt">-All</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Set-MfaState</span><span class="w"> </span><span class="nt">-State</span><span class="w"> </span><span class="nx">Disabled</span><span class="w">
</span></code></pre></div></div>
<p>Run the script to disable MFA for all users. You’ll find both the script and some great documentation by visiting <a href="https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-mfa-userstates#convert-users-from-per-user-mfa-to-conditional-access-based-mfa">this site</a> on Microsoft Docs.</p>
<h3 id="create-mfa-conditional-access-policy">Create MFA Conditional Access policy</h3>
<p>In the previous step, you’ll disabled MFA for all users when running the PowerShell script. If some user logs in with their credentials at this time, they will not be asked for MFA. It’s time to create our new <em>Conditional Access MFA Policy</em>.</p>
<p>Log in to the <a href="https://portal.azure.com">Azure portal</a> . Open the menu and browse to <em>Azure Active Directory > Security > Conditional Access</em>. Click on <em>New policy</em>.</p>
<p><img src="/assets/images/MFA_GA/04.png" alt="PATH" /></p>
<p>Name your policy. In my example, I went for <em>MFA All Users</em>. Select <em>All Users</em> and <em>All Cloud Apps</em>.
Under <em>Access control > Grant</em>, select <em>Grant access</em>, and enable <em>Require multi-factor autentication</em>.
Enable the policy and click <em>Save</em>.</p>
<p><img src="/assets/images/MFA_GA/05.png" alt="PATH" /></p>
<p>In a few minutes our new Conditional policy will take affect.
At this time, you’ll successfully moved from per-user MFA to Conditional Access based MFA.
The last step is to verify the changes and confirm that it’s in working order.</p>
<h3 id="verify-the-transition">Verify the transition</h3>
<p>All users show the MFA status <em>disabled</em> on the Microsoft 365 Multi-Factor Autentication page.</p>
<p><img src="/assets/images/MFA_GA/06.png" alt="PATH" /></p>
<p>When logging into, for example <a href="https://portal.office.com">portal.office.com</a>, with an account that already had MFA configured, it will work again without need of completing the MFA setup again.</p>
<p><img src="/assets/images/MFA_GA/07.png" alt="PATH" /></p>
<p>Any user that did not have MFA enabled, or any new users created in the future will be asked to go through the MFA setup before they will be able to log in again, for example into <a href="https://portal.office.com">portal.office.com</a> as mentioned earlier.</p>
<p><img src="/assets/images/MFA_GA/08.png" alt="PATH" /></p>Freddie ChristiansenTransition from per-user MFA to Conditional Access MFA In the blog post, I thought to show how to easily transition from per-user based MFA to a Conditional Access based MFA. What is Conditional Access? “..The modern security perimeter now extends beyond an organization’s network to include user and device identity. Organizations can utilize these identity signals as part of their access control decisions… Conditional Access is the tool used by Azure Active Directory to bring signals together, to make decisions, and enforce organizational policies. Conditional Access is at the heart of the new identity driven control plane.” Check Azure AD Premium license Check first if your organization has either Azure AD Premium Plan 1 og 2. Log into Microsoft Azure. In the portal, navigate to Azure Active Directory > Overview. In my example below, Azure AD Premium P2 is shown. Check per-user MFA status Log into Microsoft 365 admin center. Navigate to Users > Active Users > Multi-factor authentication. A new page will open, showing all the users, and their multi-factor autentication status. In the example below (in norwegian this one..), we have a couple of users who have MFA enabled, some enforced and some others in disabled state. Connect to Azure AD using PowerShell To start our transition from per-user MFA to Conditional Access, you’ll need to start Windows PowerShell as an administrator and connect to Azure AD using the following in cmdlet: Connect-MsolService This cmdlet is part of the MSOnline module. If you haven’t got this module installed in your system, you can install it by typing the following in your elevated PowerShell console: Install-Module MSOnline Convert per-user MFA to Conditional Access based MFA with PowerShell When we are connected to Azure AD with PowerShell, Microsoft has luckily provided the community with a great script that we could use for helping us with the convertion. # Sets the MFA requirement state function Set-MfaState { [CmdletBinding()] param( [Parameter(ValueFromPipelineByPropertyName=$True)] $ObjectId, [Parameter(ValueFromPipelineByPropertyName=$True)] $UserPrincipalName, [ValidateSet("Disabled","Enabled","Enforced")] $State ) Process { Write-Verbose ("Setting MFA state for user '{0}' to '{1}'." -f $ObjectId, $State) $Requirements = @() if ($State -ne "Disabled") { $Requirement = [Microsoft.Online.Administration.StrongAuthenticationRequirement]::new() $Requirement.RelyingParty = "*" $Requirement.State = $State $Requirements += $Requirement } Set-MsolUser -ObjectId $ObjectId -UserPrincipalName $UserPrincipalName ` -StrongAuthenticationRequirements $Requirements } } # Disable MFA for all users Get-MsolUser -All | Set-MfaState -State Disabled Run the script to disable MFA for all users. You’ll find both the script and some great documentation by visiting this site on Microsoft Docs. Create MFA Conditional Access policy In the previous step, you’ll disabled MFA for all users when running the PowerShell script. If some user logs in with their credentials at this time, they will not be asked for MFA. It’s time to create our new Conditional Access MFA Policy. Log in to the Azure portal . Open the menu and browse to Azure Active Directory > Security > Conditional Access. Click on New policy. Name your policy. In my example, I went for MFA All Users. Select All Users and All Cloud Apps. Under Access control > Grant, select Grant access, and enable Require multi-factor autentication. Enable the policy and click Save. In a few minutes our new Conditional policy will take affect. At this time, you’ll successfully moved from per-user MFA to Conditional Access based MFA. The last step is to verify the changes and confirm that it’s in working order. Verify the transition All users show the MFA status disabled on the Microsoft 365 Multi-Factor Autentication page. When logging into, for example portal.office.com, with an account that already had MFA configured, it will work again without need of completing the MFA setup again. Any user that did not have MFA enabled, or any new users created in the future will be asked to go through the MFA setup before they will be able to log in again, for example into portal.office.com as mentioned earlier.List all your syncronized libraries in OneDrive for Business using PowerShell2021-03-10T21:05:00+01:002021-03-10T21:05:00+01:00https://www.cloudpilot.no/blog/List-all-your-syncronized-libraries-in-OneDrive-for-Business-using-PowerShell<h3 id="list-all-your-syncronized-libraries-in-onedrive-for-business-using-powershell">List all your syncronized libraries in OneDrive for Business using PowerShell</h3>
<p>Sometimes it can be useful to be able to quickly get an overview of which libraries you have set up synchronization via OneDrive for Business for.</p>
<p>To my knowledge, Microsoft 365 does not yet have the ability to store this information somewere in the cloud, which would have made for example a replacement of your computer very easy.</p>
<p>I found a UserVoice on the subject <a href="https://onedrive.uservoice.com/forums/913522-onedrive-on-windows/suggestions/35131279-export-import-sharepoint-document-libraries-list-f">here</a>, so feel free to leave a vote if you find a solution like this helpful for you and your organization, so maybe it will come in the future.</p>
<p>Yes, there are the <a href="https://docs.microsoft.com/en-us/onedrive/use-group-policy#configure-team-site-libraries-to-sync-automatically">AutoMountTeamSites</a>, which allows you as a IT-administrator to configure specific SharePoint Team Site Libraries your end-users to sync automatically the next time they log-in.</p>
<p>Back to my function. Information about synced libraries in OneDrive for Business is stored in the registry, under the following path:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive
</code></pre></div></div>
<p>Here you’ll see (based on number of syncronized libraries), some keys/folders with some wierd looking names. All of these contains information about each of your current syncronized library. The function will simply get all registry entries contained in the OneDrive registry subkey, and store each of them in a <code class="language-plaintext highlighter-rouge">PSCustomObject</code>.</p>
<p>To use the function, import or dot-source the script into your PowerShell session, and then type <code class="language-plaintext highlighter-rouge">Get-ODfBSyncLibrary</code> .
<em>Result:</em></p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Get-ODfBSyncLibrary</span><span class="w">
</span><span class="n">Url</span><span class="w">
</span><span class="o">---</span><span class="w">
</span><span class="n">https://contoso-my.sharepoint.com/personal/john_doe_contoso_com/Documents/</span><span class="w">
</span><span class="nx">https://fabrikam.sharepoint.com/sites/Projects/Shared</span><span class="w"> </span><span class="nx">documents/</span><span class="w">
</span></code></pre></div></div>
<blockquote>
<p><strong>Hint:</strong> If you apply the <code class="language-plaintext highlighter-rouge">-Detailed</code> switch, the function will return more detailed information about each entry.</p>
</blockquote>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\WINDOWS\system32</span><span class="err">></span><span class="w"> </span><span class="nx">Get-ODfBSyncLibrary</span><span class="w"> </span><span class="nt">-Detailed</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">Format-List</span><span class="w">
</span><span class="n">Url</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">https://contoso-my.sharepoint.com/personal/john_doe_contoso_com/Documents/</span><span class="w">
</span><span class="n">MountPoint</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">C:\Users\John</span><span class="w"> </span><span class="nx">Doe\OneDrive</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">Contoso</span><span class="w"> </span><span class="nx">Ltd</span><span class="w">
</span><span class="n">LibraryType</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Mysite</span><span class="w">
</span><span class="n">LastModifiedTime</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">10.03.2021</span><span class="w"> </span><span class="nx">17:45:45</span><span class="w">
</span><span class="n">Url</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">https://fabrikam.sharepoint.com/sites/Projects/Shared</span><span class="w"> </span><span class="nx">documents/</span><span class="w">
</span><span class="n">MountPoint</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">C:\Users\John</span><span class="w"> </span><span class="nx">Doe\OneDrive</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nx">Fabrikam</span><span class="w"> </span><span class="nx">Inc</span><span class="w">
</span><span class="n">LibraryType</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">Teamsite</span><span class="w">
</span><span class="n">LastModifiedTime</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="nx">10.03.2021</span><span class="w"> </span><span class="nx">17:45:48</span><span class="w">
</span></code></pre></div></div>
<p>As you can see in the example above, the function will return the Url, MountPoint, LibraryType, and the LastModifiedTime from each of your syncronized library.
Look at the different <code class="language-plaintext highlighter-rouge">MountPoint</code>. Mysite equals OneDrive for Business and Teamsite equals SharePoint Document Libraries.</p>
<p>From this point on, the possibilities (as with PowerShell in general:blush: ) of further use is endless. You can easily export this information into a text file, send it out via an email or maybe use PSexec to run this function on multiple remote computers to collect this information from different users throughout your organization?</p>
<p>The function is found on my GitHub repo:<br />
<a href="https://github.com/freddiecode/Get-ODfBSyncLibrary">https://github.com/freddiecode/Get-ODfBSyncLibrary</a></p>
<p><em>My function does not return any information about the sync status of the different libraries, so if this is functionality you are looking for, you could check <a href="https://techcommunity.microsoft.com/t5/onedrive-for-business/is-there-any-way-to-get-the-sync-status-using-powershell-script/m-p/699065">this</a> article out on the Microsoft Tech Community.</em></p>
<p>Freddie</p>Freddie ChristiansenList all your syncronized libraries in OneDrive for Business using PowerShell Sometimes it can be useful to be able to quickly get an overview of which libraries you have set up synchronization via OneDrive for Business for. To my knowledge, Microsoft 365 does not yet have the ability to store this information somewere in the cloud, which would have made for example a replacement of your computer very easy. I found a UserVoice on the subject here, so feel free to leave a vote if you find a solution like this helpful for you and your organization, so maybe it will come in the future. Yes, there are the AutoMountTeamSites, which allows you as a IT-administrator to configure specific SharePoint Team Site Libraries your end-users to sync automatically the next time they log-in. Back to my function. Information about synced libraries in OneDrive for Business is stored in the registry, under the following path: HKEY_CURRENT_USER\SOFTWARE\SyncEngines\Providers\OneDrive Here you’ll see (based on number of syncronized libraries), some keys/folders with some wierd looking names. All of these contains information about each of your current syncronized library. The function will simply get all registry entries contained in the OneDrive registry subkey, and store each of them in a PSCustomObject. To use the function, import or dot-source the script into your PowerShell session, and then type Get-ODfBSyncLibrary . Result: PS C:\WINDOWS\system32> Get-ODfBSyncLibrary Url --- https://contoso-my.sharepoint.com/personal/john_doe_contoso_com/Documents/ https://fabrikam.sharepoint.com/sites/Projects/Shared documents/ Hint: If you apply the -Detailed switch, the function will return more detailed information about each entry. PS C:\WINDOWS\system32> Get-ODfBSyncLibrary -Detailed | Format-List Url : https://contoso-my.sharepoint.com/personal/john_doe_contoso_com/Documents/ MountPoint : C:\Users\John Doe\OneDrive - Contoso Ltd LibraryType : Mysite LastModifiedTime : 10.03.2021 17:45:45 Url : https://fabrikam.sharepoint.com/sites/Projects/Shared documents/ MountPoint : C:\Users\John Doe\OneDrive - Fabrikam Inc LibraryType : Teamsite LastModifiedTime : 10.03.2021 17:45:48 As you can see in the example above, the function will return the Url, MountPoint, LibraryType, and the LastModifiedTime from each of your syncronized library. Look at the different MountPoint. Mysite equals OneDrive for Business and Teamsite equals SharePoint Document Libraries. From this point on, the possibilities (as with PowerShell in general:blush: ) of further use is endless. You can easily export this information into a text file, send it out via an email or maybe use PSexec to run this function on multiple remote computers to collect this information from different users throughout your organization? The function is found on my GitHub repo: https://github.com/freddiecode/Get-ODfBSyncLibrary My function does not return any information about the sync status of the different libraries, so if this is functionality you are looking for, you could check this article out on the Microsoft Tech Community. FreddieFizzBuzz - The PowerShell Way2020-11-16T19:05:00+01:002020-11-16T19:05:00+01:00https://www.cloudpilot.no/blog/Solving-the-FizzBuzz-challenge-using-PowerShell<h3 id="solving-the-fizzbuzz-challenge-using-powershell">Solving the FizzBuzz challenge using PowerShell</h3>
<p>The “FizzBuzz” challenge is a typical interview question designed to help determine job candidates solving skills to filter out job candidates.</p>
<p><strong>The challenge is the following</strong>:</p>
<p>Print the numbers from <strong>1</strong> to <strong>100</strong> inclusive, each on their own line.</p>
<p>If, however, the number is a multiple of <strong>three</strong> then print <strong>Fizz</strong> instead, and if the number is a multiple of <strong>five</strong> then print <strong>Buzz</strong>.</p>
<p>For numbers which are multiples of <strong>both three and five</strong> then print <strong>FizzBuzz</strong>.</p>
<p>My approach:</p>
<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="mi">1</span><span class="o">..</span><span class="mi">100</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kr">foreach</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="kr">if</span><span class="p">(</span><span class="bp">$_</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="o">-and</span><span class="w"> </span><span class="bp">$_</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="n">Write-Output</span><span class="w"> </span><span class="s2">"FizzBuzz"</span><span class="p">}</span><span class="w">
</span><span class="kr">elseif</span><span class="p">(</span><span class="bp">$_</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">3</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="n">Write-Output</span><span class="w"> </span><span class="s2">"Fizz"</span><span class="p">}</span><span class="w">
</span><span class="kr">elseif</span><span class="p">(</span><span class="bp">$_</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">5</span><span class="w"> </span><span class="o">-eq</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="n">Write-Output</span><span class="w"> </span><span class="s2">"Buzz"</span><span class="p">}</span><span class="w">
</span><span class="kr">else</span><span class="w"> </span><span class="p">{</span><span class="bp">$_</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Write your own script, visit <a href="https://code.golf/fizz-buzz#powershell">https://code.golf/fizz-buzz#powershell</a> and give it a shot :blush:</p>Freddie ChristiansenSolving the FizzBuzz challenge using PowerShell The “FizzBuzz” challenge is a typical interview question designed to help determine job candidates solving skills to filter out job candidates.Automating and executing your resources in Azure with the help of Siri2020-07-06T16:30:00+02:002020-07-06T16:30:00+02:00https://www.cloudpilot.no/blog/Automating-and-executing-your-resources-in%20Azure-with-the-help-of-Siri<h3 id="automating-executing-and-controlling-your-resources-in-azure-with-the-help-of-siri">Automating, executing and controlling your resources in Azure with the help of Siri</h3>
<p>So you wrote an awesome PowerShell script and need a place to run it? :blush:</p>
<p>When I started working in IT, I has immediately facinated by Microsoft Azure.
The possibilities there are endless and only your own imagination set’s the limits of what you can with the help of this platform.</p>
<p>A while back, I wrote a blog post describing my first publicly available PowerShell module, allowing you to control your Mitsubishi heat pump with the help of PowerShell.
If you have missed it, it can be found <a href="https://www.cloudpilot.no/blog/Control-your-Mitsubishi-heat-pump-using-PowerShell/">here</a>.</p>
<p>A super cool feature in Azure is <code class="language-plaintext highlighter-rouge">Azure Automation</code>. It allows you to automate and orchestrate tasks within Azure and accross external systems.</p>
<p>This got me thinking - could I combine these two elements and get some cool functionality out of it? Maybe also include some voice-controlling from my iPhone? :blush:</p>
<p><strong>Let’s get going!</strong></p>
<h3 id="create-and-configure-an-azure-automation-account">Create and configure an Azure Automation Account</h3>
<p>The first thing to do when you start using Azure Automation is create an Automation account. Automation accounts are like Azure Storage accounts in that they serve as a container.</p>
<p>The first thing you need is a valid Azure Subscription. If you haven’t got this already, sign-up for a free trial here: https://azure.microsoft.com/en-gb/offers/ms-azr-0044p/</p>
<p>After successfully signed into the Azure portal, search for <code class="language-plaintext highlighter-rouge">Automation</code> in the top search bar:</p>
<p><img src="/assets/images/AZUREAUTO/AUTOMATION.PNG" alt="PATH" /></p>
<p>Select <code class="language-plaintext highlighter-rouge">Automation Account</code> from the available Services-list.</p>
<p><img src="/assets/images/AZUREAUTO/ADDAUTACC.PNG" alt="PATH" /></p>
<p>Next, click the <code class="language-plaintext highlighter-rouge">Add</code> button to create a new Automation Account.</p>
<p>In the next window you’ll need to fill in some information.</p>
<p><img src="/assets/images/AZUREAUTO/ACCWIZARD.PNG" alt="PATH" /></p>
<p>Enter a suitable name in the <code class="language-plaintext highlighter-rouge">Name</code> box,
Select your <code class="language-plaintext highlighter-rouge">Subscription</code>,
Select or create a <code class="language-plaintext highlighter-rouge">Resource group</code></p>
<blockquote>
<p><strong>What is a resource group you say?</strong> I will describe it as a logical container that can hold all of your shared resources.
They also provide a way to monitor, control access, provision and manage billing for collections of assets that are required to run an application, or used by a client or company department.</p>
</blockquote>
<p>Finish off the creation by clicking the <code class="language-plaintext highlighter-rouge">Create</code> button on the bottom of the page.</p>
<p>Now that your <code class="language-plaintext highlighter-rouge">Automation Account</code> is in place, you’ll need to create a <code class="language-plaintext highlighter-rouge">Runbook</code> inside of it.
There are multiple types to choose from.</p>
<h5 id="oveview-of-different-runbook-types"><strong>Oveview of different Runbook types:</strong></h5>
<table>
<thead>
<tr>
<th>Type</th>
<th style="text-align: center">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>PowerShell</td>
<td style="text-align: center">Text runbook based on Windows PowerShell script</td>
</tr>
<tr>
<td>PowerShell Workflow</td>
<td style="text-align: center">Text runbook based on Windows PowerShell Workflow.</td>
</tr>
<tr>
<td>Python</td>
<td style="text-align: center">Text runbook based on Python.</td>
</tr>
<tr>
<td>Graphical PowerShell Workflow</td>
<td style="text-align: center">Based on Windows PowerShell Workflow and created and edited completely in the graphical editor in Azure portal.</td>
</tr>
<tr>
<td>Graphical</td>
<td style="text-align: center">Based on Windows PowerShell and created and edited completely in graphical editor in Azure portal.</td>
</tr>
</tbody>
</table>
<p><em>You’ll find more information regarding the different runbook types by heading over to <a href="https://docs.microsoft.com/en-us/azure/automation/automation-runbook-types">Microsoft Docs</a>.</em></p>
<p>Under <em>Process Automation</em> in your newly made Automation Account, select <em>Runbooks</em>.</p>
<p><img src="/assets/images/AZUREAUTO/NEWRUNBOOK.png" alt="PATH" /></p>
<p><img src="/assets/images/AZUREAUTO/CREATE01.png" alt="PATH" /></p>
<p>This opens the <em>Create a runbook blade</em>:</p>
<ul>
<li>Enter a suitable name for your Runbook.</li>
<li>In this blog post we will focus on the <em>PowerShell Runbook</em> so select this one under <em>Runbook type</em>.</li>
<li>Fill in a <em>Description</em> text if you like.</li>
<li>Finish of the creation by hitting the blue <em>Create-button</em> on the bottom of the page.</li>
</ul>
<p><img src="/assets/images/AZUREAUTO/CREATE02.png" alt="PATH" /></p>
<p>When you click the name of your new Runbook, you’ll see someting like this:</p>
<p><img src="/assets/images/AZUREAUTO/ENTERRUNBOOK.PNG" alt="PATH" /></p>
<p>On the main bar, select <em>Edit</em>.</p>
<p><img src="/assets/images/AZUREAUTO/EDITRUNBOOK.PNG" alt="PATH" /></p>
<p>You are now able to start authoring your runbook using standard PowerShell Workflow syntax.
Here you could potentially write scripts that targets your own resources deployed in Azure (maybe for Start / Stop your VMs during off-hours?) among all other things you normally use PowerShell for.</p>
<p>When you are happy with your newly created masterpiece, you’ll have the ability to <em>test</em> your script.
This could be done by clicking <em>Test pane</em> in the main bar, and then choosing <em>Start</em>.</p>
<p><img src="/assets/images/AZUREAUTO/TEST01.PNG" alt="PATH" /></p>
<p><img src="/assets/images/AZUREAUTO/TEST02.PNG" alt="PATH" /></p>
<h3 id="starting-an-azure-automation-runbook-with-a-webhook">Starting an Azure Automation runbook with a webhook</h3>
<p>A webook allows an external service to start a particular runbook in Azure Automation through a single HTTP request.
Any external application able to send a <code class="language-plaintext highlighter-rouge">HTTP Post</code> could potentially be used to start your Azure runbook using the <em>Azure Automation API</em>.</p>
<p>Inside your Runbook, select <em>Webhooks</em> under <em>Resources</em>.</p>
<p><img src="/assets/images/AZUREAUTO/WEBHOOK01.PNG" alt="PATH" /></p>
<p>Select <em>Add Webhook</em> on the main bar, and then click <em>Create new webook</em>.</p>
<p><img src="/assets/images/AZUREAUTO/WEBHOOK02.PNG" alt="PATH" /></p>
<p>Give it a suitable name, verify that it is enabled, set disired expiration date and don’t forget to copy the URL at the bottom. Press <em>OK</em> at the buttom to close the blade.</p>
<p><img src="/assets/images/AZUREAUTO/WEBHOOK03.PNG" alt="PATH" /></p>
<p>Now that we have both a working PowerShell script and a webhook ready to be used, how could we utilize them in a simple way?</p>
<h3 id="hey-siri">Hey Siri</h3>
<p>Ok, let’s look into how you can use Apple’s very own Siri to execute and control your script’s running in Azure.</p>
<p>First of all, open the <em>Shortcuts</em> app on your phone.</p>
<p><img src="/assets/images/AZUREAUTO/SIRI01.PNG" alt="PATH" /></p>
<p>Select the <em>Plus</em> icon on the top, right side to create a new Shutcut.</p>
<p><img src="/assets/images/AZUREAUTO/SIRI02.PNG" alt="PATH" /></p>
<p>Then, select the <em>Add Action</em> button on the middle of the screen.</p>
<p><img src="/assets/images/AZUREAUTO/SIRI03.PNG" alt="PATH" /></p>
<p>Add a new <em>URL</em> action and don’t forget to enter your webhook URL that you got when you created your webhook in Azure.</p>
<p>Next, add another action by selecting the <em>plus</em> icon and search for <em>Get Content of URL</em> into the top search-bar. Click on the right action to add it into your <em>Shortcut</em>.</p>
<p>Now your <em>Shortcut</em> should look something like this:</p>
<p><img src="/assets/images/AZUREAUTO/SIRI04.PNG" alt="PATH" /></p>
<p>Select the <em>Show more</em> option and change the <em>Method</em> from <em>GET</em> into <em>POST</em>. Confirm that the <em>Request Body</em> is set to <em>JSON</em>.</p>
<p><img src="/assets/images/AZUREAUTO/SIRI05.PNG" alt="PATH" /></p>
<p>New done, select <em>Next</em> on the top right.</p>
<p>Now it’s time to name your new Shortcut. As you can see from my example below, I’ll named my new Shortcut for <em>Run Azure Script</em>.</p>
<p><img src="/assets/images/AZUREAUTO/SIRI06.PNG" alt="PATH" /></p>
<p>When I want to execute a <em>POST request</em> against my webook in Azure and fire off my PowerShell script, I simply have to wake up Siri, and say:</p>
<p><strong>Hey Siri, run Azure script</strong>.</p>
<p>You have now combined the superpowers of Azure with the voice-controlled functionality of Siri.</p>
<p>Isn’t this the coolest thing? :blush:</p>
<p>Hope someone found this guide helpful.</p>
<p>Freddie</p>Freddie ChristiansenAutomating, executing and controlling your resources in Azure with the help of Siri So you wrote an awesome PowerShell script and need a place to run it? :blush: When I started working in IT, I has immediately facinated by Microsoft Azure. The possibilities there are endless and only your own imagination set’s the limits of what you can with the help of this platform. A while back, I wrote a blog post describing my first publicly available PowerShell module, allowing you to control your Mitsubishi heat pump with the help of PowerShell. If you have missed it, it can be found here. A super cool feature in Azure is Azure Automation. It allows you to automate and orchestrate tasks within Azure and accross external systems. This got me thinking - could I combine these two elements and get some cool functionality out of it? Maybe also include some voice-controlling from my iPhone? :blush: Let’s get going! Create and configure an Azure Automation Account The first thing to do when you start using Azure Automation is create an Automation account. Automation accounts are like Azure Storage accounts in that they serve as a container. The first thing you need is a valid Azure Subscription. If you haven’t got this already, sign-up for a free trial here: https://azure.microsoft.com/en-gb/offers/ms-azr-0044p/ After successfully signed into the Azure portal, search for Automation in the top search bar: Select Automation Account from the available Services-list. Next, click the Add button to create a new Automation Account. In the next window you’ll need to fill in some information. Enter a suitable name in the Name box, Select your Subscription, Select or create a Resource group What is a resource group you say? I will describe it as a logical container that can hold all of your shared resources. They also provide a way to monitor, control access, provision and manage billing for collections of assets that are required to run an application, or used by a client or company department. Finish off the creation by clicking the Create button on the bottom of the page. Now that your Automation Account is in place, you’ll need to create a Runbook inside of it. There are multiple types to choose from. Oveview of different Runbook types: Type Description PowerShell Text runbook based on Windows PowerShell script PowerShell Workflow Text runbook based on Windows PowerShell Workflow. Python Text runbook based on Python. Graphical PowerShell Workflow Based on Windows PowerShell Workflow and created and edited completely in the graphical editor in Azure portal. Graphical Based on Windows PowerShell and created and edited completely in graphical editor in Azure portal. You’ll find more information regarding the different runbook types by heading over to Microsoft Docs. Under Process Automation in your newly made Automation Account, select Runbooks. This opens the Create a runbook blade: Enter a suitable name for your Runbook. In this blog post we will focus on the PowerShell Runbook so select this one under Runbook type. Fill in a Description text if you like. Finish of the creation by hitting the blue Create-button on the bottom of the page. When you click the name of your new Runbook, you’ll see someting like this: On the main bar, select Edit. You are now able to start authoring your runbook using standard PowerShell Workflow syntax. Here you could potentially write scripts that targets your own resources deployed in Azure (maybe for Start / Stop your VMs during off-hours?) among all other things you normally use PowerShell for. When you are happy with your newly created masterpiece, you’ll have the ability to test your script. This could be done by clicking Test pane in the main bar, and then choosing Start. Starting an Azure Automation runbook with a webhook A webook allows an external service to start a particular runbook in Azure Automation through a single HTTP request. Any external application able to send a HTTP Post could potentially be used to start your Azure runbook using the Azure Automation API. Inside your Runbook, select Webhooks under Resources. Select Add Webhook on the main bar, and then click Create new webook. Give it a suitable name, verify that it is enabled, set disired expiration date and don’t forget to copy the URL at the bottom. Press OK at the buttom to close the blade. Now that we have both a working PowerShell script and a webhook ready to be used, how could we utilize them in a simple way? Hey Siri Ok, let’s look into how you can use Apple’s very own Siri to execute and control your script’s running in Azure. First of all, open the Shortcuts app on your phone. Select the Plus icon on the top, right side to create a new Shutcut. Then, select the Add Action button on the middle of the screen. Add a new URL action and don’t forget to enter your webhook URL that you got when you created your webhook in Azure. Next, add another action by selecting the plus icon and search for Get Content of URL into the top search-bar. Click on the right action to add it into your Shortcut. Now your Shortcut should look something like this: Select the Show more option and change the Method from GET into POST. Confirm that the Request Body is set to JSON. New done, select Next on the top right. Now it’s time to name your new Shortcut. As you can see from my example below, I’ll named my new Shortcut for Run Azure Script. When I want to execute a POST request against my webook in Azure and fire off my PowerShell script, I simply have to wake up Siri, and say: Hey Siri, run Azure script. You have now combined the superpowers of Azure with the voice-controlled functionality of Siri. Isn’t this the coolest thing? :blush: Hope someone found this guide helpful. Freddie