In this section

0.5 Your First Practical Task

Module 0

Introduction

The course's practice uses two tools side by side: the Entra admin center, where most people start, and Microsoft Graph PowerShell, which does the same things in a form that can be repeated, recorded and checked. This lesson sets up Graph PowerShell and uses it for a first complete task: reading one sign-in and one assignment in both tools and showing that they agree.

It covers installing the module, connecting with a read-only scope, finding the command and the smallest permission it needs, reading a sign-in two ways, tracing an assignment from the portal to the audit record, and what the session leaves behind in the tenant. The task follows the template every build in the course follows, from requirement to cleanup.

1

Installing Graph PowerShell

One module, on PowerShell 7

Microsoft Graph PowerShell is a set of modules that wrap the Microsoft Graph API, the same API the admin center uses behind its pages. Microsoft recommends PowerShell 7 or later on every platform, with no other prerequisites. Windows PowerShell 5.1 also works, with .NET Framework 4.7.2 or later installed.

Installing for your own account needs no administrator rights on the machine:

PS C:\> $PSVersionTable.PSVersion.Major
PS C:\> Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery -Force
PS C:\> Get-InstalledModule Microsoft.Graph | Format-Table Name, Repository

7

Name            Repository
----            ----------
Microsoft.Graph PSGallery

The Microsoft.Graph module installs a set of submodules, one per area of the API, such as Users, Groups, Applications and Reports. Commands load from whichever submodule they belong to when first used. Get-InstalledModule with no name lists every submodule and its version, which should match the latest on the PowerShell Gallery.

On Windows, the script execution policy must allow signed remote scripts. Get-ExecutionPolicy shows the current setting, and Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser changes it for your account alone.

Check the installation with one command before relying on it. Get-Command Get-MgUser should name the Users submodule; if it says the command is not found, the module installed for a different PowerShell, which happens on Windows machines with both Windows PowerShell and PowerShell 7. Each keeps its own modules, so install into the one you run.

Keep the module up to date during the course. Microsoft releases new versions often, commands gain parameters, and a lesson's command that fails on an old version usually works on the current one. Update-Module Microsoft.Graph brings every submodule up to date at once.

Installing the whole module takes a few minutes, because it brings every submodule. Installing only the submodules a task needs, such as Microsoft.Graph.Users and Microsoft.Graph.Reports, is faster but means installing more as the course reaches new areas. The course assumes the whole module.

There is also a beta module, Microsoft.Graph.Beta, which wraps the beta version of the API. This course uses the version 1.0 commands wherever they exist, because beta behavior can change without notice; where a lesson needs a beta command, it says so.

2

Connecting With a Read-Only Scope

Ask for what the task needs

Connect-MgGraph signs you in and asks for the permissions, called scopes, the session needs. The scope is a delegated permission, so the session can do only what both the scope and your own roles allow. For reading sign-ins, the scope is AuditLog.Read.All:

PS C:\> Connect-MgGraph -Scopes "AuditLog.Read.All" -NoWelcome
PS C:\> Get-MgContext | Format-List Account, AppName, AuthType, ContextScope
PS C:\> Get-MgContext | Select-Object -ExpandProperty Scopes

Account      : you@yourlab.onmicrosoft.com
AppName      : Microsoft Graph Command Line Tools
AuthType     : Delegated
ContextScope : CurrentUser

AuditLog.Read.All
openid
profile
User.Read
email

The application signing in is Microsoft Graph Command Line Tools, Microsoft's own application that Graph PowerShell uses unless you register your own. The scopes include the one asked for plus the basic ones every sign-in gets, openid, profile, email and User.Read, which let the tool know who you are.

The first time an account asks for a scope, Entra ID shows a consent prompt, because the tool has never been granted it:

Consent prompt

Read what the first connection asked for:
Sign-in window › Permissions requested
App Microsoft Graph Command Line Tools
Permission Read audit log data
Consent on behalf of your organization Not ticked
The prompt names the application, Microsoft Graph Command Line Tools, and each permission. An administrator also sees a checkbox to consent for the whole organization; leave it clear.

Consenting for yourself grants the tool the scope when you use it. An administrator who ticks the organization checkbox grants it for every user in the tenant, which is rarely what a personal session needs. Leaving it clear keeps the grant to your own account and makes it a deliberate decision, made once, if the organization ever wants it.

The session persists. Microsoft Graph PowerShell caches the token and refreshes it, so you stay signed in across PowerShell windows until you run Disconnect-MgGraph. Adding -ContextScope Process limits the sign-in to the current window, which suits a shared machine or a short task with a sensitive scope.

Get-MgContext is the command to run whenever a result surprises you. It says which account is signed in, to which tenant, through which application and with which scopes, and a surprising result often turns out to be the right answer for the wrong tenant or the wrong account. Running it before any change is a cheap habit that prevents the most embarrassing kind of mistake.

Ask for the smallest scope each time. Connecting with a broad scope once, because it saves typing later, means every later command in that session runs with it, and the grant it leaves lasts beyond the session. The habit this course builds is a fresh connection with the scope the task needs, read-only unless the task changes something.

3

Finding the Command and Its Permission

And where the smallest permission is written

Graph PowerShell has thousands of commands, and most tasks start by finding the right one. Find-MgGraphCommand searches by API address or by command name. To find the command that reads sign-ins, search by the address the API uses for them:

PS C:\> Find-MgGraphCommand -Uri "/auditLogs/signIns" -Method GET |
    Format-Table Command, Module, Method, URI

Command              Module  Method URI
-------              ------  ------ ---
Get-MgAuditLogSignIn Reports GET    /auditLogs/signIns
Get-MgAuditLogSignIn Reports GET    /auditLogs/signIns/{signIn-id}

Get-MgAuditLogSignIn lists sign-ins, and with an ID reads one. The output also has a Permissions column, and here Microsoft's documentation is direct: the permissions it lists are the ones that would work, and they do not indicate privilege levels. Picking the first one listed often picks a broader permission than the task needs.

The smallest permission is written on the API reference page for the operation, under Permissions, in a column headed least privileged. For listing sign-ins, it says:

List sign-ins: what Graph needs

Look it up

Least privileged permission

AuditLog.Read.All

Higher privileged

None listed

Roles for the person

Reports Reader, Security Reader, Global Reader

To see policy results

A role that reads Conditional Access too

Where it is stated

The API reference page, Permissions

Not stated by

Find-MgGraphCommand

Find-MgGraphCommand lists the permissions that would work, not the smallest one. The API reference marks the least privileged.

So the workflow is two steps. Find-MgGraphCommand finds the command and the API operation behind it; the API reference page for that operation gives the least privileged permission and the roles the person needs. Every Graph command in this course was chosen that way, and every scope it connects with comes from that column.

The role requirement is worth reading as carefully as the scope. Listing sign-ins needs Reports Reader or a broader reading role, and seeing which Conditional Access policies applied needs a role that can read Conditional Access as well. Without the second, the sign-ins come back with the policy results left out, which looks like no policy applied when in fact the data was withheld.

Reading the reference page also shows whether an operation supports application permissions, which matters later when a script runs unattended, and whether it exists in version 1.0 or only in beta. Both are on the same page, a few lines from the permissions, and both decide whether a command will work in the way a task needs before any time is spent on it.

That last case is the kind of silent difference that misleads people. An empty field and a field you were not allowed to read look the same in the output, so knowing which roles a command needs is part of reading its result correctly.

4

Reading One Sign-In Two Ways

Portal and Graph, field by field

The task is to read one sign-in in both tools and show that they agree. In Northgate's tenant, the sign-in is Phil Greaves's administrator account connecting Graph PowerShell on the morning of 20 October:

Entra Admin Center

Find the sign-in in the portal:
Entra ID › Monitoring & health › Sign-in logs › User sign-ins (interactive)
User a-pgreaves@ne.com
Application Microsoft Graph Command Line Tools
Date 10/20/2026 10:47:52 AM
Filter on the user and the application, open the row, and read the Basic info, Authentication Details and Conditional Access tabs.

The same record through Graph, by its ID, which is the value the portal shows as the request ID:

PS C:\> Connect-MgGraph -Scopes "AuditLog.Read.All" -NoWelcome
PS C:\> $s = Get-MgAuditLogSignIn -SignInId "9e41c7a8-2d6b-4f05-8c31-e0b7a5d2f800"
PS C:\> $s | Format-List CreatedDateTime, UserPrincipalName, AppDisplayName,
    IPAddress, AuthenticationRequirement, ConditionalAccessStatus
PS C:\> $s.AppliedConditionalAccessPolicies | Format-Table DisplayName, Result

CreatedDateTime           : 10/20/2026 9:47:52 AM
UserPrincipalName         : a-pgreaves@ne.com
AppDisplayName            : Microsoft Graph Command Line Tools
IPAddress                 : 203.0.113.42
AuthenticationRequirement : singleFactorAuthentication
ConditionalAccessStatus   : notApplied

DisplayName                                       Result
-----------                                       ------
Block legacy authentication                       notApplied
Require MFA for all users                         notApplied
Require MFA for guest access                      notApplied
Require compliant device for Exchange             notApplied
Require phishing-resistant MFA for administrators reportOnlyFailure

Times are the first thing to compare, and the first to trip people up. The portal shows times in the browser's local time zone, while Graph returns them in UTC and PowerShell then formats them for the machine running it.

Northgate's office runs on UK time, which in October is an hour ahead of UTC, so the portal above shows this sign-in at 10:47 while Graph returns 9:47. Both are right, and the hour between them is the time zone, not a second sign-in.

The two agree, field for field, and together they show something worth noticing. The account holds Global Administrator, and it signed in to a tool that can change the whole tenant with a password alone: the authentication requirement is single-factor, and Conditional Access was not applied.

The reason is in the policy results. The policy requiring multifactor authentication for all users did not apply, because this account is in an exclusion group; the policy that would require phishing-resistant authentication for administrators is in report-only, and reports that it would have failed.

That is a finding, and this lesson does not fix it. The point of the task is the method: a question answered from the record, in two tools, with the same answer in both. Fixing the finding means changing an exclusion and enforcing a policy, both tenant-wide changes, and the course does that once it has taught both.

The device fields add context without changing the finding. The sign-in came from NE-MGMT01, a compliant, managed management workstation, from Northgate's own office address. That is the right place for an administrator to work from, and it is why a finding like this is an exposure rather than an incident: nothing here says the account was misused, only that its sign-in would not stop someone who had its password.

Comparing the two tools also shows what each is better at. The portal is faster to read once, with the tabs laid out. Graph returns the same fields in a form that can be filtered across thousands of sign-ins, saved, compared next month and attached to a ticket. The course uses the portal to look and Graph to prove.

5

Tracing an Assignment to Its Record

From the application to the audit log

The second half of the task traces an assignment. Start from the application, where most people would look:

Entra Admin Center

Start from the application:
Entra ID › Enterprise apps › NGE Site Reporting › Manage › Users and groups
Row 1 SG-App-SiteReporting-Engineers, Group, Site Engineer
Row 2 Gareth Brown, User, Site Manager
Each row is an assignment: a user or group and the role it holds. The date column is when it was made.

The same two rows come from Graph with Get-MgServicePrincipalAppRoleAssignedTo, with the assignment ID, the principal and the role. Then the audit log says who made the assignment and when. The engineers' group was assigned in February 2025, long past the thirty days Entra ID keeps audit records, so the answer comes from the workspace that Northgate exports its audit log to:

AuditLogs
| where OperationName == "Add app role assignment to group"
| where TargetResources has "SG-App-SiteReporting-Engineers"
| project TimeGenerated, OperationName,
    Actor = tostring(InitiatedBy.user.userPrincipalName)

The query filters on the activity name first, which is cheap, then on the group's name anywhere in the targets, and keeps only the time, the activity and the account that made it:

TimeGenerated         OperationName                      Actor
2/11/2025 9:42:18 AM  Add app role assignment to group   a-pgreaves@ne.com

The record is one this lesson adds to Northgate's evidence, at the time the assignment itself carries. It names the administrator who made the change, Phil Greaves's administrator account, at the time the assignment was created.

Without the export, that question would have no answer at all: the assignment would still be visible, but nothing would say who made it or why. That is the practical meaning of the retention limit, and the reason the lab exports its logs from the first day.

An audit query is also only as good as its activity name. Each kind of change has its own name, and assigning a user writes a different activity from assigning a group, so a query for one silently misses the other. Before trusting a query that returns nothing, make a change of the kind it looks for in the lab and confirm the query finds it.

A trace like this is the basic unit of an access review. For any access a person or group holds, the questions are what it is, when it was granted and by whom. The configuration answers the first; only the audit record answers the other two.

Graph can run the same audit query, with Get-MgAuditLogDirectoryAudit and a filter on the activity name, but only within Entra ID's own retention. For anything older, the workspace is the only source, which is why the query here is KQL rather than PowerShell. The course uses whichever tool still holds the record, and says which.

The same trace works in the other direction. Starting from an audit entry, such as an assignment made late on a Friday by an account that rarely changes anything, the target in the record leads back to the application and the group, and from there to everyone who gained access by it.

6

What the Session Left Behind

A grant that outlasts the connection

Connecting Graph PowerShell changes the tenant in two ways, even when every command reads. The first connection by anyone in a tenant creates a service principal for Microsoft Graph Command Line Tools, if none exists yet. And each consent creates or extends a delegated permission grant to it:

PS C:\> Connect-MgGraph -Scopes "Directory.Read.All" -NoWelcome
PS C:\> $f = "appId eq '14d82eec-204b-4c2f-b7e8-296a70dab67e'"
PS C:\> $cli = Get-MgServicePrincipal -Filter $f
PS C:\> $cli.DisplayName
PS C:\> Get-MgOauth2PermissionGrant -Filter "clientId eq '$($cli.Id)'" |
    Format-List ConsentType, Scope
PS C:\> Disconnect-MgGraph | Out-Null

Microsoft Graph Command Line Tools

ConsentType : Principal
Scope       : AuditLog.Read.All openid profile User.Read email Directory.Read.All

The same grant is visible on the tool's own page, where a review of the tenant's applications would find it:

Entra Admin Center

See the grant in the admin center:
Entra ID › Enterprise apps › Microsoft Graph Command Line Tools › Security › Permissions
User consent One row per person and scope
Admin consent Empty, if nobody ticked the box
The User consent tab lists each person who consented and the scopes they granted. Admin consent lists any grant made for the whole organization.

Disconnect-MgGraph clears the cached token and ends the session, but it does not remove the grant. The next connection with the same scope is not prompted, because the grant is still there. Over months of use, one person's grant to the tool accumulates every scope they have ever asked for.

That accumulation matters because the grant, not the session, is what persists. A scope consented once for a single task stays available to every later connection by that person, without a prompt, and an organization-wide grant made by ticking the checkbox once stays available to everyone. Reading the tool's grants from time to time, and removing scopes nobody needs any longer, keeps them honest.

Microsoft's documentation offers a stricter option: registering your own application for Graph PowerShell and connecting with its client ID, so the consent belongs to an application the organization controls rather than to Microsoft's shared one. For a lab, the shared application is fine. For an organization, a dedicated application with assignment required lets the identity team decide exactly who may use Graph PowerShell at all.

Removing a scope means updating or deleting the grant itself. Remove-MgOauth2PermissionGrant deletes it, and the next connection prompts for consent again, which is a reasonable way to start each module with a clean grant. Nothing else in the tenant depends on a personal grant to the command-line tool, so removing it breaks nothing but the convenience of not being asked.

The grant is the last record the task leaves, and the build card records it as a change. Every build in this course lists what it changes, even when the change is as small as this, because an unrecorded change is the one nobody can explain later.

7

Writing the Task Down

The template every build follows

A practical task is only finished when someone else could check it.

Each build card in this course is laid out for that: a requirement that says what must be true at the end, the prerequisites with the role, license and cost, the steps in the portal and in Graph, success criteria that can be tested, the evidence to keep, the rollback, and the route for a learner without a tenant.

The task in this lesson, written down

D3 template

Requirement

One sign-in and one assignment read two ways

Prerequisites

Reports Reader, Global Reader; Free

Steps

Portal and Graph for each

Success

The two agree, field by field

Evidence

Request ID, assignment ID, audit time

Rollback

Remove the test assignment and grant

The same eight parts, in the same order, on every build card in the course.

The requirement comes first because it decides what counts as done. "Read a sign-in" is not testable; "read one sign-in in both tools and show the fields agree" is. Writing the requirement that way before starting stops a task from drifting into whatever was interesting along the way.

The evidence is what makes the task checkable afterwards. A request ID identifies the exact sign-in; an assignment ID identifies the exact assignment; an audit time ties the change to its record. With those three values, anyone can repeat the reading and reach the same answer, which is the standard a change record in an organization should meet.

Success criteria are written as checks, each one true or false. "Portal and Graph agree" can be checked by putting the two outputs side by side; "it works" cannot be checked by anyone but the person who said it. A criterion that cannot fail is not a criterion, and reading each one back with that test is a quick way to improve a build card before starting it.

The rollback is written before the change, not after. For a read-only task it is small, but the habit matters for the builds that follow, where the rollback is the difference between a mistake that costs a minute and one that costs an afternoon. A build card without a rollback is a change nobody has thought through.

Do

Practice

Read one sign-in and one assignment, two ways
your tenant

The build is the complete task in your own tenant, in the template every later build follows: requirement, prerequisites, steps in the portal and in Graph, the comparison, success criteria, evidence, rollback, and the route without a tenant.

BuildRead one sign-in and one assignment, two waysYour tenant
Requirement

Connect Graph PowerShell with a read-only scope, find one sign-in and one application assignment in both the admin center and Graph, show that the two agree, find the audit entry that made the assignment, and leave the tenant as you found it apart from a recorded consent grant.

RoleReports Reader and Global Reader, on your daily account
LicenseEntra ID Free; P1 or P2 for 30 days of sign-ins
CostNone
ChangesOne delegated consent grant to Graph Command Line Tools
  1. Graph PowerShell

    Install and connect

    PowerShell 7, the Microsoft.Graph module, one read scope.

    PS C:\> Install-Module Microsoft.Graph -Scope CurrentUser -Repository PSGallery -Force
    PS C:\> Connect-MgGraph -Scopes "AuditLog.Read.All","Application.Read.All" -NoWelcome
  2. Admin center

    Read one sign-in

    Your own sign-in to Graph Command Line Tools: Basic info, Authentication Details, Conditional Access.

  3. Graph PowerShell

    Read the same sign-in

    By its request ID, which is the sign-in's id in Graph.

    PS C:\> Get-MgAuditLogSignIn -SignInId "<id>" | Format-List
  4. Admin center

    Assign a test group to an application

    Users and groups on any enterprise app; note the time.

  5. Graph PowerShell

    Read the assignment and its audit entry

    App role assignments, then the audit log.

    PS C:\> Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $sp.Id
    PS C:\> $f = "activityDisplayName eq 'Add app role assignment to group'"
    PS C:\> Get-MgAuditLogDirectoryAudit -Filter $f -Top 1 | Format-List ActivityDateTime
  6. Compare

    Compare portal and Graph

    Same time, same user, same method, same policies, same assignment.

Success criteria
  • Portal and Graph agree on every field compared.
  • The audit entry's time matches the assignment.
  • The consent grant left behind is recorded.
Evidence to keep
  • The sign-in's request ID.
  • The assignment ID and its audit entry.
  • The Get-MgContext scopes.
Rollback

Remove the test assignment, then remove the delegated grant if you do not want to keep it, and disconnect.

PS C:\> Remove-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $sp.Id `
    -AppRoleAssignmentId $a.Id
PS C:\> Disconnect-MgGraph
Without a tenant

From the outputs in this lesson, write what the Graph PowerShell sign-in shows about how Phil Greaves's administrator account signs in, which policy would change it, and what you would check before that policy is enforced.

Keep the outputs. They are the first evidence in your lab record, and the later lessons ask you to read your own sign-ins the way this one read Northgate's.

Graph PowerShell is installed once and connected per task, with the smallest scope the API reference names. Find-MgGraphCommand finds the command; the API reference gives the least privileged permission and the roles. A sign-in reads the same in the portal and in Graph, and an assignment is traced to the audit record that says who made it.

Disconnecting clears the session but not the consent grant, which is recorded like any other change. If you do one thing in your own tenant, read your own last sign-in to Graph Command Line Tools and say which policies applied.

Resources

Microsoft revises these pages; where a page and this lesson disagree, the page is newer.