Using Microsoft Graph PowerShell SDK

In a previous post we went over connecting to Microsoft GraphApi using PowerShell and Graph Rest API.

In this post we will be going through using the PowerShell SDK as I am more familiar with PowerShell and this will make creating scripts and querying data easier.

First step to use the Graph SDK is to install the PowerShell Module.

Open an admin PowerShell Window and run the below command.

Install-Module Microsoft.Graph

Once the module is installed we can now connect to graph.

I only want to query user information so I will use User.Read.All as the scoped permission.

Below is a link to the Microsoft document on graph permissions.

Microsoft Graph permissions reference – Microsoft Graph | Microsoft Docs

Connect-MgGraph -Scopes "User.Read.All"

Go to the URL in the PowerShell response and enter the code.

Accept the permission request.

Once accepted, PowerShell should now be connected.

All command’s in the Graph Module use MG in the name which can be used to find commands needed.

To get all users we can use Get-mguser.

By default the Graph SDK will use API v1.0 to change to the beta version which has more information and is the default one that I use.

To set the profile run the below.

Select-MgProfile -Name "beta"

Now if we look at the user type it now returns guest or member value since the change to the beta version.

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

We can also use filters to only return required users, the below will only return guest users.

Using connect-msgraph with a user account requires manual steps to connect so it’s not great for automation.

We can use a certificate and app registration to automate the connection.

Use app-only authentication with the Microsoft Graph PowerShell SDK – Microsoft Graph | Microsoft Docs

We covered the app registration in a previous post so we wont go over creating the app registration again.

First step is to create the cert that will be used, we will be using self signed cert but you can use a cert issue from an internal CA also.

To create the self signed cert I used the below command

New-SelfSignedCertificate -DnsName TennatDomainName -CertStoreLocation "Cert:\CurrentUser\My" -FriendlyName "Graph_SDK"

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_SDK_Cert_Test.cer

Logon to Azure portal > Azure Active Directory > App registrations > graph app registration

Select the exported certificate

Once uploaded the cert detail will show under certificates.

Now that the cert is uploaded we can use either the thumbprint or the certificate name, ApplicationID and TenantID to automate connections to Microsoft Graph.

Connect-MgGraph -CertificateThumbprint Thumbprint -ClientID YOUR_APP_ID -TenantId YOUR_TENANT_ID

I used the below to automate checking for Guest users and there sign-in logs.

$Cert = "Thumbprint"
$AppID = "YOUR_APP_ID "
$TenantID = "YOUR_TENANT_ID"

Connect-MgGraph -CertificateThumbprint $Cert -ClientId $AppID -TenantId  $TenantID

$GuestUsers = Get-MgUser -Filter "usertype eq 'Guest'"

foreach ($Guest in $GuestUsers) {
Write-Warning "Checking Guest User $($Guest.DisplayName) SignIn Logs"
Get-MgAuditLogSignIn -Filter "UserId  eq '$($Guest.ID)'" | 
Select-Object UserDisplayName,UserPrincipalName,IPAddress,AppDisplayName,ResourceDisplayName,ClientAppUsed
}

Windows 10 20H2 Feature Update Error 0xC1900208 ConfigMgr

Recently we have been testing Windows 10 20H2 Feature update deployment using ConfigMgr. When the update was applying it was failing and the error in software center was 0xC1900208.

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

To check what the code means I usually use the below Microsoft Doc but unfortunately the below code was not showing.

Windows Update error code list by component – Windows Deployment | Microsoft Docs

The error is listed under below Windows 10 upgrade errors doc

Get help with Windows 10 upgrade and installation errors (microsoft.com)

Since the error was relating to incompatible app next I checked the feature update logs these are located under a hidden folder on the root of the C drive C:\$WINDOWS.~BT\Sources\Panther

I checked the Setupact.log and the compatdata XML.

In the compadata XML there was an incompatibility with the version of McAfee DLP installed on the device. This device was not getting the latest version of DLP.

To try fix the issue I updated the version to the latest release which support Windows 1020H2 and tried the update again but it continued to fail with a different error code 0x80240020.

To get the update to work correctly I had to remove the following folders

C:\Windows\ccmcache

C:\$WINDOWS.~BT\

C:\Windows\SoftwareDistribution (Need to stop the Windows update service before deleting this folder and start the service again to recreate the software distribution folder)

Run the below three action from the configuration manager agent in the control panel:

Machine policy retrieval and evaluation cycle, Software updates deployment evaluation cycle and software updates scan cycle.

Wait around 5 minutes for the scan’s to complete and try the update again and it installed without issue this time.

ConfigMgr query all devices software update install status

During a recent audit we need to get a list of all update and there install status on every device.

We use Configmgr for applying software update but I was not getting the information (well that I could find) using the out of the box reports.

I decided to write a SQL query to all devices, applicable updates and some device information.

To get the compliance status I used the Microsoft doc for the v_UpdateComplianceStatus SQL view.

SMS_UpdateComplianceStatus Class – Configuration Manager | Microsoft Docs

Below is the full SQL query

select rsys.name0 AS 'Computername',
 os.Caption0 AS 'Operating System',
 os.BuildNumber0 AS 'Build Number',
 ui.ArticleID AS 'Update ID',
 ui.Title,
 ui.Description,
 --Set Install Status
 Case
 when ucs.Status='0' then 'Detection state unknown'
 When ucs.Status='1' then 'Update is not required'
 when ucs.Status='2' then 'Update is required'
 When ucs.Status='3' then 'Update is installed'
 End As 'Install Status',
 --Set client Activity
 Case
 When ch.ClientActiveStatus='1' then 'Active'
 When ch.ClientActiveStatus='0' then 'Inactive'
 End As 'Client Active Status',
 ui.InfoURL,
 ui.DatePosted
 from v_updateinfo ui
 join v_UpdateComplianceStatus ucs on ucs.ci_id=ui.ci_id
 join v_CICategories_All catall on catall.CI_ID=UCS.CI_ID
 join v_CategoryInfo catinfo on catall.CategoryInstance_UniqueID = catinfo.CategoryInstance_UniqueID and catinfo.CategoryTypeName='UpdateClassification'
 join v_R_System rsys on rsys.resourceid=ucs.resourceid
 join v_GS_OPERATING_SYSTEM os on os.ResourceID = ucs.ResourceID
 join v_CH_ClientSummary ch on ch.resourceID = ucs.ResourceID
 order by rsys.name0

This is what the output should look like.

Checking Sign-in logs in Azure AD using Microsoft Graph API

In a previous post we went through configuring and connecting to Microsoft Graph API.

In this post we will going through querying sign-in logs.

Connecting to Microsoft GraphAPI Using PowerShell – TheSleepyAdmins

We have been trying to audit guest account activity and sign-in logs are the only way I have been able to find if these account’s have been active for the last 30 days. Instead of manually filtering sign-in logs from Azure AD I want to automate this using Graph.

To query sign-in logs the below API permission are required. since we are using client secret we only require Application permission.

Below is the link to the Microsoft doc I used for getting info on listing sign-ins.

List signIns – Microsoft Graph v1.0 | Microsoft Docs

Permission typePermissions (from least to most privileged)
Delegated (work or school account)AuditLog.Read.All and Directory.Read.All
Delegated (personal Microsoft account)Not supported
ApplicationAuditLog.Read.All and Directory.Read.All

Next step was to run the command to get to the access token for connecting to Microsoft Graph this is covered in the previous post so we won’t be going over that here.

To connect to the sign-in Graph use the below Url

https://graph.microsoft.com/v1.0/auditLogs/signIns

Below is the command to connect and view all sign-in logs data

$LoginUrl = "https://graph.microsoft.com/v1.0/auditLogs/signIns"
(Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)"} -Uri $LoginUrl -Method Get).value

This will return all the default values for each sign-in log.

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

We only wanted to have Displayname,UPN,IP,App used and date the log was created. We also wanted to only have Logs that where created in the last 30 days.

$LoginUrl = "https://graph.microsoft.com/v1.0/auditLogs/signIns"(Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)"} -Uri $LoginUrl -Method Get).value | Select-Object userDisplayName,userPrincipalName,ipAddress,clientAppUsed,createdDateTime | Where-Object {$_.userPrincipalName -notlike "*DomainName.com" -and $_.createdDateTime -gt "2020-09-29"}

Below is the results from the above query.

Now that we have the query, we can either run the query manually or in my case I will be setting up an script to run on a scheduled to export this data.

Connecting to Microsoft GraphAPI Using PowerShell

Recently I have been looking to use Microsoft Graph to query specific information for Microsoft 365 services.

Microsoft Graph is an development tool that connects to multiple Microsoft 365 services to allow querying data and automate tasks.

There are a few steps required to start using Graph which involves creating a app registration on Azure to issue authentication tokens and API permission to view data.

Use the Microsoft Graph API – Microsoft Graph | Microsoft Docs

I also used a blog post by adamtheautomator as this was very good at explaining the process and goes more in depth.

Using the Microsoft Graph API with PowerShell (adamtheautomator.com)

In this post we will be going through configuring the app registration and query some data from Azure AD.

First step is to logon to the Azure portal > Azure AD > App registration and click on New registration.

Give the app a name and specify the support account type in this case we only want account from our tenant.

Once completed, we should now see the app has been created.

Next step we need to configure the API permissions, depending on the type of access required we will use either delegated or application permission as some data can only be access by either permission types.

below is a extract from the Microsft Docs on permission types

Microsoft identity platform developer glossary | Microsoft Docs

permissions

client application gains access to a resource server by declaring permission requests. Two types are available:

  • “Delegated” permissions, which specify scope-based access using delegated authorization from the signed-in resource owner, are presented to the resource at run-time as “scp” claims in the client’s access token.
  • “Application” permissions, which specify role-based access using the client application’s credentials/identity, are presented to the resource at run-time as “roles” claims in the client’s access token.

To assign permission go to the app registration we created earlier and go to API permissions > Add a permission and select Microsoft Graph.

To check which permissions are required I used the below Microsoft Docs .

Microsoft Graph permissions reference – Microsoft Graph | Microsoft Docs

Select the permission type and the required permission in this case I want to be able to read groups, users and directory so.

Once the required permissions are added if they required admin permission those will need to be granted using the grant admin consent option below.

There are many different way’s to connect to Microsoft Graph but in this post we will be using client secret.

We will need the application ID

We will create a client secret

Give the client secret a name and set the expire in this case we will use 1 year.

There should now be client secret and the value is used to authenticate. (Take note of the value and save in secure location like a password vault or Azure Key vault as once you leave the app blade the value will be hidden and if you lose will have to be recreated.)

Once we have the above configured we can connect to GraphApi to generate a token. We will used Invoke-RestMethod.

The secret can be hardcoded but I decided to use read-host so that I could add the secret manually, as it’s not recommend to have any password/secret hardcoded in script.

Below is the command I used to get the token.

$ApplicationID = ""
$TenatDomainName = ""
$AccessSecret = Read-Host "Enter Secret"


$Body = @{    
Grant_Type    = "client_credentials"
Scope         = "https://graph.microsoft.com/.default"
client_Id     = $ApplicationID
Client_Secret = $AccessSecret


$ConnectGraph = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$TenatDomainName/oauth2/v2.0/token" `
-Method POST -Body $Body

$token = $ConnectGraph.access_token

To verify we have a token run the variable $ConnectGraph to view.

Now that we have a token we can run a queries against GraphAPI.

Below we will be running a query for Azure AD groups and selecting display name.

$GrapGroupUrl = 'https://graph.microsoft.com/v1.0/Groups/'
(Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)"} -Uri $GrapGroupUrl -Method Get).value.displayName

To view some examples we can use Graph Explorer.

Graph Explorer – Microsoft Graph

In a future post we will be going through more query’s and automating tasks using GraphAPI.

Create Azure conditional access policy with named location

In this post we will be going through creating an Azure conditional access policy to restrict logging on to Azure / Office 365 from specific locations.

Conditional access policies are used to set requirements for accessing Azure or Office 365 resource, when using Named locations we can then set based on IP range, Trusted locations or Countries and regions.

Below diagram is from Microsoft on how conditionals access works.

What is Conditional Access in Azure Active Directory? | Microsoft Docs

Conceptual Conditional Access process flow

First step is to logon to Azure and go to Azure AD conditional access

Create a named location that will be used to restrict access.

Once in named location we can either create a location based on IP range or countries / regions. In this case we will be using a country.

Next we will create the conditional policy

Give the policy a name, we will be using a group to apply the policy but this could be change to all users or by directory role. (We can use select users or groups to apply the policy to a specific group as if applied to all users there could be a risk of locking yourself out of Azure portal)

Next we can apply the policy to specific apps or all apps. (If applying to all apps there will be a warning to not lock yourself out)

Next we set the conditions in this case we will be using location, I will be applying the policy to any location.

We can then set the named location as excluded in the policy so that it wont be applied if the access is coming from a country that is on the allowed named location.

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

Next we can set the access control settings, in this case we will block access but this could be changed to require MFA or other settings below.

Last step is to set session options to use conditional access app control, force sign-in frequency, Persistent browser session. We wont be setting any of the session settings as this policy is to block access.

There are three options report-only, on or off. To test the policy and check what will be blocked without running the risk of blocking real users set to report-only (This would be recommend for testing policy before rolling out to all users.)

Click create and the policy should now show and the state should be report-only.

To test if the policy is going to be applied we can check a users sign-ins, click on a signing log and go to Report-only, click on the three dots and show details.

This will show what part of the policy has been satisfied and not satisfied. Since the location has not been satisfied (as we are connecting from a excluded location) and the policy is not applied.

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

When the users tries to access from a country that is not in the allowed location, the result will be a failure and the users would be blocked from accessing.

Once testing has been completed and there are no unexpected blocks we can then go back to the conditional access policy and change the state to on, to apply the policy to users.

Now when a restricted user try’s to access an Azure / Office 365 resource from country not in the named location they will receive a message like the below.

Deploy MSI Application using Intune / MEM

In this post we will go over how to deploy an MSI application using Intune / MEM to a Windows 10 managed endpoint.

The below KB from Microsoft goes through deploying a line of business application

Add a Windows line-of-business app to Microsoft Intune | Microsoft Docs

To logon to the Microsoft Endpoint Manager admin center go to

https://endpoint.microsoft.com

Once in the MEM console go to Apps

Click on Add

Select app type: other > Line-of-business app

Once the wizard starts select app package and go to the MSI that is going to be deployed. We will be using Firefox in this post.

Next we can fill in the application details. The mandatory fields are Name, description and publisher.

Next we set the application deployment type either an install (required or available) or uninstall and deploy this to a certain group or all users / devices. We will be settings this deployment as available.

Last step is to review and create the app deployment

The application should now show in the Windows app blade.

Once the managed Windows device syncs with MEM the application should then show as available to install in the company portal.

If the application doesn’t show we can run a sync in MEM admin center or directly on the Windows 10 device.

To start the install click on the application.

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

Once installed we can check the application in MEM admin center to view compliance for the application deployment.

Export Remote Shares and Folder permissions using PowerShell

We have recently been looking to audit some Windows servers shares and permissions. I have previously used a script to export folder permissions, so some of this script will be from that previous script. The main difference is that we will be using WMI query to get the list of shares and a looping through specified servers.

To get the list of shares we will use the Win32_Share WMI class and filtered out the default shares.

Get-WmiObject -ComputerName $Server -Class win32_share -Filter "Description != 'Remote Admin' and Description != 'Default share' and Description != 'Remote IPC' and Description != 'Printer Drivers'" | Select-Object Name -ExpandProperty Name

The full script that will be used is located on my Github repository, see link below.

Scripts/Get-SharesAndPermissions.ps1 at master · TheSleepyAdmin/Scripts (github.com)

To run the script use the below and update the exportpath and servers. To add multiple server just a comma between server names.

.\Get-SharesAndPermissions.ps1 -ExportPath D:\Scripts\Folder_Permissions\Export -Servers Server1, Server2

Once the script has completed the result will be export to a csv in the exported folder path.

MECM Baseline: Check for GRUB vulnerability Windows 10

We needed to check for the GRUB vulnerability on our Windows 10 devices.

See advisory below:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV200011

We have a few thousand devices to check, so checking manually was going to be a issue, we decided to use MECM baseline to run a script to check for devices that had the issue.

Microsoft give the below command in the advisory to check if the issue exist

[System.Text.Encoding]::ASCII.GetString((Get-SecureBootUEFI db).bytes) -match ‘Microsoft Corporation UEFI CA 2011’

If this command returns a true value the device is vulnerable.

To use the command in a baseline we used the try / catch in PowerShell to get a compliance response as the above command is a terminating error and wont return a result other wise.

try{
$GRUBCheck = [System.Text.Encoding]::ASCII.GetString((Get-SecureBootUEFI db).bytes) -match ‘Microsoft Corporation UEFI CA 2011’
if ($GRUBCheck -eq $true){
$complinant = ‘False’
}
else
{
$complinant = ‘True’
}
}
catch {
}

$complinant

This slideshow requires JavaScript.

Once we had the script we needed to create the configuration item and baseline.

To create the configuration item open MECM console > Assets and Compliance > Overview > Compliance settings > Configuration item

Create Configuration ItemGRUB3Select Windows Desktops and Servers (custom)GRUB4Select Windows 10 as the version of Windows that will be assessed. GRUB5Add a new settings item. GRUB6Give the item a name

Setting type to script and data type to string. Click Add script. GRUB7Put in the script above.grub8Next add in a compliance rule.GRUB10

Give the compliance rule a name, select the settings item we create earlier, set the value to True so that any devices that doesn’t have the vulnerability will return as compliant. GRUB11Once all the settings and compliance rules are configure following the wizard to complete GRUB12Next we need to create the configuration baseline

Assets and Compliance > Overview > Compliance settings > Configuration item GRUB13Give the compliance baseline a name

Click add and select configuration item.GRUB14Add the configuration item created earlier. GRUB15Click ok to complete the baseline GRUB16Once the baseline is configured, deploy the baseline to the required device collection GRUB17You can either wait for the next time the client does a machine policy retrieval or run the action manually from the client.  Once the client get the updated policy the baseline should show under configurations.GRUB18

Once evaluated we can check the deployment in MECM to find device that are compliant or non-compliant.GRUB19

VMware 6.7 PSC Decommission: Failed to get the PSC thumbprint

As part of our recent VMware 6.7 upgrade we where migrating from external PSC to embedded PSC.

All went fine until we tried to decommission the old PSC’s, when trying to view the thumbprint we got the below error:

Failed to get the PSC thumbprint. Ensure PSC port is correct.

PSCWith out getting the thumbprint you can continue.

We tested accessing the PSC using web browser,

using openssl from the vCenter appliance

openssl s_client -connect PSC01.domain.local:443

We also tested access using telnet to confirm if port 443 was open

curl -v telnet://PSC01.domain.local:443

All test came back fine and there was no ports being blocked.

In the end the issue was cause by using the short name for the vCenter servers https://VCSA/UI

When we changed to the full FQDN, https://VCSA.domain.local/UI this fixed the issue and we where able to complete the decommission. The issue looks to be related to the SAN name on the cert only having the FQDN.

PSC2