Microsoft Graph Filtering Results PowerShell

During a recent audit we have been trying to automate a report on what Guest account’s are active in the last 30 days and to find what groups these guest account are members of so they can be removed if they are no longer required.

We could use AzureAD PowerShell for getting guest account and group membership but it was not that easy to automate this.

We decided to use Microsoft Graph as we have a few task already automated using Microsoft Graph and certificates so there is no need to hard code any password or application secret in my script.

I have done a previous post on the getting the access token and using certificate based authentication so if you need to know how to setup you can check that post.

Connect to Microsoft Graph using MSAL and Certificate based Authentication – TheSleepyAdmins

In this query we will be using three different Microsoft Graph url’s. First is to get a list of guest users, second will be to get sign-in logs and the last will be to get the guest users group membership.

To get guest users we will filtering using userType attribute

user resource type – Microsoft Graph v1.0 | Microsoft Docs

below are the permission that will be required

ApplicationUser.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All

To get sign-in’s we will be filtering on two different attributes (userPrincipalName & createdDateTime)

List signIns – Microsoft Graph v1.0 | Microsoft Docs

below are the permission that will be required

ApplicationAuditLog.Read.All and Directory.Read.All

To get Group membership we will be using the memberof

user: getMemberGroups – Microsoft Graph v1.0 | Microsoft Docs

below are the permission that will be required for getting the group membership.

ApplicationUser.Read.All, Directory.Read.All, Directory.ReadWrite.All

First step as with any Graph query is to create the token request that will be used to access the different Graph Url’s. We will be using MSAL module and a certificate to generate.

To filer results using Graph we can put in ?filter= and then use a supported filter attribute. In this case we will be using usertype.

We will also create a blank array variable that will be used to output to later and a date variable to filer the sign-in logs.

This first filter will get all guest users. Currently usertype is only available when using the beta Graph version so we will be using that.

" eq 'Guest'"

Once we have the Guest users we will loop through each one, use a second filter that uses the mail address to filter the guest user sign-in logs and use a date variable to show only sign-in over the last 30 days.

" eq '$($User.mail)' and createdDateTime ge $Date"

The next filter is going to use the user ID to get the memberof for the user and we will use the group-object to put all groups display names in to one object so it easier to call to the console windows or export to a CSV.


Once we have all the information returned from the Graph queries we need to format the results. For this we will use a hash table and an if statement to output user with or without sign-in logs to the correct hash table and add the values to the results variable.

This script is only going to return the latest sign-in log as I only really want to have a report that the users has been active in the last 30 day that’s why I am using [0] to select the latest object in the $logins variable.

The last part of the script just outputs the results and use select-object to format the order.

Now that we have the full script the below is what the result should look like.

To make the script more reusable there are three variable for TenantId , ClientId and CertThumprint.

To export the result to a csv instead of the console windows there is an -Exportpath parameter that just needs to have the path specified.

The full script can be download from my Github repository

Scripts/MSGraph/GuestUserAuditReport at master · TheSleepyAdmin/Scripts (

To filter on other attributes we can use the same process as above just change the Graph url and check the document for that url to view what is a valid filter attribute. Filtering results in the the Graph url speeds up the script and mean we don’t do addtional filtering after returning all data.

Connect to Microsoft Graph using MSAL and Certificate based Authentication

I had a recently gotten some question’s on how to connect to Microsoft GraphApi using certificate instead of an app secret. I have set this up before using the Microsoft Graph PowerShell SDK but I wanted to test this using Invoke-RestMethod.

There is a PowerShell module that has been created that allow for much easier creation of authentication tokens using Microsoft Authentication Library.

Learn about MSAL – Microsoft identity platform | Microsoft Docs

The Microsoft Authentication Library (MSAL) enables developers to acquire tokens from the Microsoft identity platform in order to authenticate users and access secured web APIs. It can be used to provide secure access to Microsoft Graph, other Microsoft APIs, third-party web APIs, or your own web API. MSAL supports many different application architectures and platforms including .NET, JavaScript, Java, Python, Android, and iOS.

MSAL gives you many ways to get tokens, with a consistent API for a number of platforms. Using MSAL provides the following benefits:

  • No need to directly use the OAuth libraries or code against the protocol in your application.
  • Acquires tokens on behalf of a user or on behalf of an application (when applicable to the platform).
  • Maintains a token cache and refreshes tokens for you when they are close to expire. You don’t need to handle token expiration on your own.
  • Helps you specify which audience you want your application to sign in (your org, several orgs, work, and school and Microsoft personal accounts, social identities with Azure AD B2C, users in sovereign, and national clouds).
  • Helps you set up your application from configuration files.
  • Helps you troubleshoot your app by exposing actionable exceptions, logging, and telemetry.

The PowerShell module that can be used to create tokens is called MSAL.PS and the latest version as of today is

PowerShell Gallery | MSAL.PS

In this post we will be going through installing and using this module to generate an authentication token using a self signed certificate and using that token to connect to Microsoft Gaph.

The first step is to install the MSAL.PS module

Once the module is installed we can generate the certificate that will be used to connect to Microsoft Graph

To create the self signed cert I used the below command. There was a error when trying to generated the token when I didn’t use -KeySpec when generating the cert. The fix to use -KeySpec was on the GitHub issue page.

Get-MsalToken with Client Certificate fails on Windows PowerShell 5.1 · Issue #15 · AzureAD/MSAL.PS (

New-SelfSignedCertificate -DnsName TennatDomainName -CertStoreLocation "Cert:\CurrentUser\My" -FriendlyName "MSAL_Cert" -KeySpec Signature

Once the cert has been created, export the cert so that it can be uploaded to the App registration.

Get-ChildItem Cert:\CurrentUser\my\CertThumbprint | Export-Certificate -FilePath C:\temp\Graph_MSAL_Cert.cer

Once the cert is exported we can upload to the app registration that we created before. See previous post if app registration hasn’t been created in Azure yet for Microsoft Graph.

Connecting to Microsoft GraphAPI Using PowerShell – TheSleepyAdmins

To add the cert open the Azure portal > Azure Active Directory >  App registrations and select the Graph app and go to certificates & secrets

Select upload certificate

Click add to apply the cert

After the cert is upload we should now be able to connect. We will need the TenantId, clientId and certificate

The tenantID and ClientId can be gotten from the app registration overview page and the cert can be gotten using Get-Item and the cert location / thumbprint.

Import-Module MSAL.PS
$TenantId = "TenantId"
$ClientId = "ClientId" 
$ClientCert = Get-ChildItem "Cert:\CurrentUser\my\CertThumbprint"
$MSToken = Get-MsalToken -ClientId $ClientId -TenantId $TenantId -ClientCertificate $ClientCert

If you want to use the same cert for any users on the device we can export and import the cert under localmachine cert store with the private key and update the $clientCert to use localmachine instead of currentuser.

Get-Item "Cert:\localmachine\my\CertThumbprint"

Once we have the token, it’s now the same process as we have used before to connect using invoke-restmethod but using the $MSToken.AccessToken we generated using the Get-MsalToken and our certificate.

$GraphGroupUrl = ''
(Invoke-RestMethod -Headers @{Authorization = "Bearer $($MSToken.AccessToken)"} -Uri $GraphGroupUrl -Method Get).value.displayName

Using MSAL.PS module provides a much quicker and easier way to generate access tokens for Microsoft Graph with the added security of using a certificate over having to put in any application secret.

Install and Configure vRealize Operations Manager 8.2 Part 3 AD Authentication

In the previous post in this series we went through installing vROps virtual appliance and connecting to vCenter. In this post we will go through adding an AD authentication source and configuring access groups.

Part 1: Install and Configure vRealize Operations Manager 8.2 Part 1 – TheSleepyAdmins

Part 2: Install and Configure vRealize Operations Manager 8.2 Part 2 Connect to vCenter – TheSleepyAdmins

There are 5 different authentication sources that can be added to vROps.

  • SSO SAML: An XML-based standard for a web browser single sign-on that enables users to perform single sign-on to multiple applications.
  • VMware Identity Manager: A platform where you can manage users and groups, manage resources and user authentication, and access policies and entitle users to resources.
  • Open LDAP: A platform-independent protocol that provides access to an LDAP database on another machine to import user accounts.
  • Active Directory: Specifies the use on Active directory to be used to import users accounts or groups.
  • Other: Specifies any other LDAP-based directory services, such as Novel or OpenDJ, used to import user accounts from an LDAP database on a Linux Mac machine.

First we need to logon to the vROps web client > Administration > Authentication Sources

Click Add and select the source type required. We use Microsoft AD so we will be using Active Directory.

Give the identity source a display name I usually use the domain name as this make it simpler when view settings. Use basic as this auto-discovers the DC and DN (Distinguished Name).

Add the user account that will be used to for the LDAP connections to the domain. This account should only need to have domain users rights.

I also always create a specific service account to be uses for each application AD integration. I would also recommend using SSL/TLS where possible as this will encrypt the LDAP requests between the appliance and the domain controller.

Click on details to view the auto discovered host and

Click test verify all settings are correct, if set to use SSL there will be a prompt to accept the certificate.

Once the test is successful we can complete adding the authentication source.

This image has an empty alt attribute; its file name is image-54.png

Once completed the AD source should show.

This image has an empty alt attribute; its file name is image-55.png

Next we will configure the groups in AD that will be used to assign access roles in vROps.

To add the groups they need to be imported from AD and then assign the required role.

Go to Administrator > Access Control > import

Use the search string to check for the groups.

Select the role that will be assigned to the group

Assign the other required roles and select the object that are required for the group.

To test we can open a new session and select the AD authentication source instead of local user.

We can check the domain controller security event logs to confirm the authentication.

Based on the roles assinged the user will only have limited access.

In the next post we will go through configure alerting and create some capacity planning reports that can be used to plan for future compute requirements.

Install and Configure vRealize Operations Manager 8.2 Part 2 Connect to vCenter

In part one of the blog series on installing and configuring vROps we deployed the virtual appliance. In this post we will be adding our vCenter server to vRops.

Part 1: Install and Configure vRealize Operations Manager 8.2 Part 1 – TheSleepyAdmins

There are a few different types of accounts that can be added.

  • vCenter
  • VMC
  • AWS
  • Microsoft Azure

To add vCenter we need to logon to the vROps web client and go to Administration > Cloud Accoutns > Add Account

Select the account type for vCenter

Give the cloud Account a name, description and the vCenter DNS address and a logon credentials.

Click the validate connect to confirm the details are correct. If the certificate is not trusted you will be asked to review and confirm the certificate.

Once successfully completed we can then added vCenter.

The connection will now be setup and once completed will show under cloud accounts.

To view if information on the vCenter server is being collected we can go to Environment > vSphere Hosts and Clusters > vSphere World.

vROps can take a little time before metrics and alert start to show.

In the next post we will go through configuring AD Authentication and configuring group based access control.

Install and Configure vRealize Operations Manager 8.2 Part 1

In the next set of post’s we will be going through installing and configure vRealize Operations manager (vROps). I haven’t had to install or configure vROps in a few years so want to go back over it before we replaced our existing deployment.

vROps is a application from VMware that can be used to monitor, optimize and manage VMware management tools like vCentre, ESXi..

There are 3 different editions of vROps.

Standard: Allows management of vSphere only.

Advanced: Adding VMware cloud (AWS / Azure), Operating system monitoring and dashboards.

Enterprise: Give all the advanced features but also allows for application / database monitoring and third party management packs.

vROps Editions: Series Overview – VMware Cloud Management

We will be using Enterpirse edition.

vRops can be used for performance monitoring, over or under provisioned VM’s, capacity planning and trend analysis.

In this post we will be going through the initial virtual appliance deployment.

First step is to check what size appliance is going to be required.

We can use the sizing guidelines to select the right appliance size for the environment.

vRealize Operations 8.2 Sizing Guidelines (80893) (

Or use the VMware sizing tool

vRealize Sizing Tool (

Select the version you are installing and then add in the number of vCenter, host, datastores and VM that will have data collected and this will then give you the recommended sizing for your vROps deployment.

In my case it was extra small deployment.

What I always do before deploying any VMware appliance is create a static DNS record. This makes it easier to connect to the appliance after it’s deployed and for some appliance (like vCenter server its a requirement or the deployment will fail.)

This image has an empty alt attribute; its file name is image-24.png

To download the required vROps appliance go to my VMware and select the required version.

Download VMware vSphere – My VMware

To deploy the OVA create a new VM in VMware and select deploy VM from OVF or OVA file.

Give the appliance a Name and either drag and drop the OVA file or browse to the location and select.

Select a datastore

Agree to the end user license agreement.

Select a network, deployment type size, disk provisioning (thin or thick) and if VMware will be powered on automatically. Since this is only a single vCenter setup we will be using a small deployment type.

Set the timezone and network IP, gateway, netmask and domain name

Review the settings and complete.

The VM will start to deploy.

Once the deployment is completed, connect to either the IP or FQDN of the appliance to start the setup.

Select either express or new installation. We will be using the express installation as we only have one vCenter.

Set the admin password.

Complete the install

When the deployment completed the vROps logon page should show.

Logon and completed the installation.

Accept the End user agreement.

Enter your product key or use the evaluation.

You can join customer experience or untick to not take part.

Click finish to complete.

vROps is now installed.

In the next post we will go through connecting to vCenter Server, configure Active directory integration and build out some dashboards.

Checking LAPS Password Cross-Forest Using PowerShell

We have been using LAPS for the last year or so and for the most part it is a great tool for managing local admins password.

In our environment we have multiple forest with trusts relationships and the one main issue that we have is that the LAPS UI client can’t check the local admin password of devices in other domains, unless we logon as a user in that domain and run the LAPS UI as that user.

We can use PowerShell but this requires either the commands to be run from a devices in the domain with the LAPS PowerShell module or can be done using remote ps session but this is not something everyone on the service desk would know how to do or will have rights to do.

Enter-PSSession dc.domain.local
Get-AdmPwdPassword -ComputerName "computer" | Select-Object Password,ExpirationTimestamp

We could also get the AD attributes by using get-adcomputer comandlet but this requires the AD PowerShell module.

Get-ADComputer -Identity "computer" -Server "DC" -properties ms-mcs-admpwd,ms-mcs-admpwdexpirationtime | select-object ms-mcs-admpwd,ms-mcs-admpwdexpirationtime

I wanted to try create a script that would allow them to check the other domains without the needing to know how to do PS remoting or having modules installed.

To get around using the AD module we will be using ADSI search instead as this a default part of PowerShell. I used the below blog to get started.

Use the PowerShell [adsiSearcher] Type Accelerator to Search Active Directory | Scripting Blog (

Hey, Scripting Guy! How Can I Search Active Directory from Within Windows PowerShell? | Scripting Blog (

To query the remote doamin using adsisearch we first need to have a way of converting the domain name to the format that adsisearch requires.

This can be done using DirectoryContect class



DirectoryContext Class (System.DirectoryServices.ActiveDirectory) | Microsoft Docs

The first part of the script gets the domain details and returns the objects back to the search variable.

Next part we will filter the results based on the client variable to return only the required computer details.

Once the script is working, I just needed to create some mandatory parameters and create a function called Check-LAPS so that it’s easier for people to use without having to hardcode anything in the script and has some help and examples.

Below is the link to the full script.

Scripts/ActiveDirectory/LAPS at master · TheSleepyAdmin/Scripts (

Below shows how the script is run and the results.

I used the above script with WPF to create a GUI that is now complied in to an exe. I though this would be a lot easier to pass on the service desk as all they need to do is run the exe.

This was compiled using ps2exe PowerShell module.

MScholtes/PS2EXE: Module to compile powershell scripts to executables (

All they need to do is put in the domain name and computer name.

This should then return the LAPS password.

I will do a different post on creating the GUI itself in as this was my first GUI and I would like to create a few other.

Exchange Online PowerShell Basic Authentication Connection Issue

Today I ran in to an issue where I couldn’t connect to Exchange Online PowerShell. I could connect to other service without issue (Office 365, Azure AD….).

When trying to connect to Exchange online I was getting an warning about basic auth.

At first I thought this was to do with Microsoft deprecating basic authentication

Exchange Online deprecating Basic Authentication – Microsoft Lifecycle | Microsoft Docs

but after reading the document it says that this doesn’t affect Exchange ActiveSync (EAS), IMAP, POP, and Remote PowerShell.

I then read the Exchange Online v2 module prerequisites

In this there is a part that winrm needs to be enabled for basic authentication even though the module uses modern authentication.

This had been working for a long time without issue so I didn’t know what exactly had changed so I decided to check the winrm config

To check the winrm configuration run the below command

winrm get winrm/config/client/auth

In my case it was being set by a GPO. We had recently applied the Windows 10 2004 baseline policy to all our Windows 10 devices so I decided to check the setting in that GPO.

Security baseline (FINAL): Windows 10 and Windows Server, version 2004 – Microsoft Tech Community

There is a spreadsheet in the document folder that has all the policy settings. In the computer policy we can see that Allow basic authentication is set to disabled.

Since this is a security risk (there is no encryption with basic authentication) and only a few device really need to connect, We decided to create a new policy that enabled this for only admins that will need to use Exchange Online PowerShell and use security filtering to only apply to a security group with the required devices.

Once basic authentication was re-enabled I was able to connect without issue.

If you have this issue and apply either the CIS or the Windows 10 baseline policy this could be the cause.