
1
00:00:00,000 –> 00:00:03,880
If your script is still lean on MS Online or Azure AD, they’re already legacy.
2
00:00:03,880 –> 00:00:05,800
And if you think that doesn’t apply to you,
3
00:00:05,800 –> 00:00:08,080
oh boy, you’re exactly who I’m talking to.
4
00:00:08,080 –> 00:00:09,080
The cloud moved on.
5
00:00:09,080 –> 00:00:10,040
Your modules didn’t.
6
00:00:10,040 –> 00:00:12,640
Modules break on Linux runners, containers, CI/CD.
7
00:00:12,640 –> 00:00:13,440
Rest doesn’t.
8
00:00:13,440 –> 00:00:14,640
PowerShell isn’t going away.
9
00:00:14,640 –> 00:00:16,640
The modules are, we’re going API first.
10
00:00:16,640 –> 00:00:18,840
I’ll show you the raw rest pattern, three auth flows,
11
00:00:18,840 –> 00:00:21,000
and three enterprise demos that actually ship.
12
00:00:21,000 –> 00:00:24,480
There’s one gotcha that ruins most graph scripts will fix it later.
13
00:00:24,480 –> 00:00:26,880
If you’re still loading modules in 2025,
14
00:00:26,880 –> 00:00:29,680
you’re heating the office with old exchange servers.
15
00:00:29,680 –> 00:00:32,560
Why PowerShell without modules is the future?
16
00:00:32,560 –> 00:00:35,400
Everything you care about lives in Microsoft Graph now.
17
00:00:35,400 –> 00:00:39,480
Users, groups, devices, Intune Teams, SharePoint licenses, app registrations,
18
00:00:39,480 –> 00:00:41,800
the portal writes graph, your scripts should too.
19
00:00:41,800 –> 00:00:44,440
Rest beats modules because it cuts out the middle mess,
20
00:00:44,440 –> 00:00:47,040
no load times, no dependency, roulette, no version drama.
21
00:00:47,040 –> 00:00:50,040
You call the endpoint, you get the data, you move on with your day.
22
00:00:50,040 –> 00:00:51,920
Token speed credentials full stop.
23
00:00:51,920 –> 00:00:55,040
Oauth2 with search or managed identity gives you short-lived access,
24
00:00:55,040 –> 00:00:58,280
clean audit trails, and automation that doesn’t depend on a human
25
00:00:58,280 –> 00:01:01,040
remembering a password they already wrote on a sticky note.
26
00:01:01,040 –> 00:01:03,000
Managed identity means no secrets at all.
27
00:01:03,000 –> 00:01:04,000
That’s the point.
28
00:01:04,000 –> 00:01:06,880
Less to steal, less to rotate, less to screw up.
29
00:01:06,880 –> 00:01:09,080
CloudNative means it runs everywhere.
30
00:01:09,080 –> 00:01:15,080
Azure Automation, Functions, GitHub Actions, Containers, Linux, Local,
31
00:01:15,080 –> 00:01:18,040
PowerShell Core is cross-platform, but Graph is the constant.
32
00:01:18,040 –> 00:01:19,840
Curl works on anything with a pulse.
33
00:01:19,840 –> 00:01:22,920
Invogrest method does the job without dragging in the structural integrity
34
00:01:22,920 –> 00:01:24,000
of wet cardboard.
35
00:01:24,000 –> 00:01:27,360
Remember when installing a module meant praying to the new get-gods?
36
00:01:27,360 –> 00:01:28,840
Those days are over.
37
00:01:28,840 –> 00:01:31,200
Benchmarks aren’t glamorous, but they’re loud.
38
00:01:31,200 –> 00:01:34,560
Load time, modules lag, rest is fire and go.
39
00:01:34,560 –> 00:01:36,960
Cold start in a function or an actions runner.
40
00:01:36,960 –> 00:01:38,400
Rest starts immediately.
41
00:01:38,400 –> 00:01:40,200
Modules sit there thinking.
42
00:01:40,200 –> 00:01:44,680
Reliability, modules choke on throttling or stale tokens you never ask for.
43
00:01:44,680 –> 00:01:48,320
Rest is predictable if you set headers and handle retries.
44
00:01:48,320 –> 00:01:52,320
Portability, Linux and containers don’t care about your module drama.
45
00:01:52,320 –> 00:01:54,000
Rest just runs.
46
00:01:54,000 –> 00:01:56,840
Here’s the business side because someone will ask about value.
47
00:01:56,840 –> 00:02:00,000
Faster delivery because you don’t wait on module updates.
48
00:02:00,000 –> 00:02:03,000
Fewer outages because you control the token and the retry logic.
49
00:02:03,000 –> 00:02:06,280
Easier governance because permissions are explicit and scoped per job,
50
00:02:06,280 –> 00:02:08,400
not hidden inside someone’s global profile.
51
00:02:08,400 –> 00:02:11,920
Cost, your cold start, stop wasting minutes, your failures go down.
52
00:02:11,920 –> 00:02:13,720
No works on my laptop nonsense.
53
00:02:13,720 –> 00:02:16,400
The thing most people miss, Graph updates instantly,
54
00:02:16,400 –> 00:02:18,520
modules lag by weeks or never.
55
00:02:18,520 –> 00:02:20,280
New feature? It lands on Graph first.
56
00:02:20,280 –> 00:02:21,720
You can call it today.
57
00:02:21,720 –> 00:02:25,040
Waiting for a module means waiting for a maintainer who isn’t on your payroll.
58
00:02:25,040 –> 00:02:26,920
Meanwhile, your project deadline didn’t move.
59
00:02:26,920 –> 00:02:30,360
And yes, modules load slower than my ancient exchange server.
60
00:02:30,360 –> 00:02:32,240
Graph doesn’t care. It just responds.
61
00:02:32,240 –> 00:02:34,280
You can pin versions on V1.
62
00:02:34,280 –> 00:02:36,880
Test beta endpoints when needed and guarded with feature flags.
63
00:02:36,880 –> 00:02:39,200
You get to control change instead of being surprised by it.
64
00:02:39,200 –> 00:02:41,040
Security teams will actually like this.
65
00:02:41,040 –> 00:02:43,120
These privileged scopes per app registration.
66
00:02:43,120 –> 00:02:44,760
Admin consent reviewed on a schedule.
67
00:02:44,760 –> 00:02:46,800
Search based auth with short lifetimes.
68
00:02:46,800 –> 00:02:48,720
Managed identity where you can.
69
00:02:48,720 –> 00:02:50,560
Search where you must.
70
00:02:50,560 –> 00:02:51,760
Every call leaves a trail.
71
00:02:51,760 –> 00:02:54,840
Request IDs, correlation IDs, who consented to what?
72
00:02:54,840 –> 00:02:57,560
You don’t get that from a plain text password stuffed into a script
73
00:02:57,560 –> 00:02:59,560
like a loose wire in a breaker panel.
74
00:02:59,560 –> 00:03:02,360
So why now? Because the cross-platform reality is here.
75
00:03:02,360 –> 00:03:04,360
You’re running on Linux, runners, building containers,
76
00:03:04,360 –> 00:03:05,880
pushing jobs into functions.
77
00:03:05,880 –> 00:03:08,280
The module stack was built for a Windows First World
78
00:03:08,280 –> 00:03:09,640
and a simpler set of products.
79
00:03:09,640 –> 00:03:11,080
We don’t live there anymore.
80
00:03:11,080 –> 00:03:12,080
All right?
81
00:03:12,080 –> 00:03:13,120
Enough theory.
82
00:03:13,120 –> 00:03:14,600
Here’s the pattern you’ll use everywhere.
83
00:03:14,600 –> 00:03:17,240
Get a token, set headers, call rest, handle paging,
84
00:03:17,240 –> 00:03:18,800
honor retry after and move on.
85
00:03:18,800 –> 00:03:21,000
It’s boring, which is why it works.
86
00:03:21,000 –> 00:03:21,880
The core pattern.
87
00:03:21,880 –> 00:03:24,840
Native PowerShell plus rest plus graph API.
88
00:03:24,840 –> 00:03:26,040
Here’s the pattern I promised.
89
00:03:26,040 –> 00:03:27,560
Token, headers, rest, call.
90
00:03:27,560 –> 00:03:28,440
That’s the loop.
91
00:03:28,440 –> 00:03:30,400
You’ll reuse it for everything from listing users
92
00:03:30,400 –> 00:03:32,480
to smacking non-compliant devices.
93
00:03:32,480 –> 00:03:34,320
Scripts don’t fail because of PowerShell.
94
00:03:34,320 –> 00:03:35,560
They fail because of assumptions.
95
00:03:35,560 –> 00:03:36,880
So stop assuming magic.
96
00:03:36,880 –> 00:03:39,400
Build the three pieces every time and you’ll sleep at night.
97
00:03:39,400 –> 00:03:40,640
Start with the token.
98
00:03:40,640 –> 00:03:43,400
You’ve got three ways to get one and they map to real life.
99
00:03:43,400 –> 00:03:46,520
Device code for local testing when it’s just you at a console.
100
00:03:46,520 –> 00:03:48,760
Client credentials with a certificate for automation
101
00:03:48,760 –> 00:03:50,360
where no one’s clicking anything.
102
00:03:50,360 –> 00:03:52,240
Managed identity when you’re in Azure
103
00:03:52,240 –> 00:03:54,960
and you want secrets to disappear like they should have years ago.
104
00:03:54,960 –> 00:03:56,440
Same outcome, different doors.
105
00:03:56,440 –> 00:03:57,800
Device code is the friendly one.
106
00:03:57,800 –> 00:04:01,840
You request a token for HTTPS, graph, Microsoft.com.
107
00:04:01,840 –> 00:04:05,160
With the scopes you need, you get a code, you open a browser,
108
00:04:05,160 –> 00:04:08,080
you confirm it’s you and PowerShell gets a token back.
109
00:04:08,080 –> 00:04:10,640
Great for building the first version and poking endpoints.
110
00:04:10,640 –> 00:04:13,480
Bad for production because humans are squishy and forgetful.
111
00:04:13,480 –> 00:04:15,080
Client credentials is the adult path.
112
00:04:15,080 –> 00:04:16,400
You create an app registration.
113
00:04:16,400 –> 00:04:18,880
You granted only the graph application permissions it needs
114
00:04:18,880 –> 00:04:20,160
and you add a certificate.
115
00:04:20,160 –> 00:04:22,480
Your script signs a JWT with that cert
116
00:04:22,480 –> 00:04:25,800
and requests a token using the pass-sass default scope for graph.
117
00:04:25,800 –> 00:04:27,200
No user, no prompts.
118
00:04:27,200 –> 00:04:29,320
Clean audit trail, rotate the cert and move on.
119
00:04:29,320 –> 00:04:31,520
If I see a client secret pasted in plain text again,
120
00:04:31,520 –> 00:04:32,680
I’m revoking Wi-Fi.
121
00:04:32,680 –> 00:04:34,600
Managed identity is the quiet killer.
122
00:04:34,600 –> 00:04:37,840
You enable it on your automation account, function app or VM.
123
00:04:37,840 –> 00:04:40,880
Then you call the local identity endpoint, ask for a graph token
124
00:04:40,880 –> 00:04:43,120
and Azure hands you one tied to that identity.
125
00:04:43,120 –> 00:04:44,560
No vault lookups in your script.
126
00:04:44,560 –> 00:04:45,960
No secrets to rotate.
127
00:04:45,960 –> 00:04:47,640
You just need to grant that identity.
128
00:04:47,640 –> 00:04:52,160
The graph app rolls it requires at least privilege means fewer to a m calls.
129
00:04:52,160 –> 00:04:53,160
Now the headers.
130
00:04:53,160 –> 00:04:54,240
Don’t overthink it.
131
00:04:54,240 –> 00:04:55,960
Authorization, bearer your token.
132
00:04:55,960 –> 00:04:58,880
Content type, application, JSON for anything with a body.
133
00:04:58,880 –> 00:05:02,680
When you’re doing advanced queries or searches, add consistency.
134
00:05:02,680 –> 00:05:05,680
Level, eventual and the appropriate prefer headers
135
00:05:05,680 –> 00:05:07,480
if the endpoint supports them.
136
00:05:07,480 –> 00:05:10,120
The thing most people miss is they forget the consistency level
137
00:05:10,120 –> 00:05:13,000
and then wonder why their account or filter looks drunk.
138
00:05:13,000 –> 00:05:14,240
Then make the call.
139
00:05:14,240 –> 00:05:15,680
In VogueGress method is fine.
140
00:05:15,680 –> 00:05:17,280
Method, URI,
141
00:05:17,280 –> 00:05:19,280
headers, maybe a body.
142
00:05:19,280 –> 00:05:20,600
The mental model is simple.
143
00:05:20,600 –> 00:05:24,040
Token, headers, call, check, page, retry, continue.
144
00:05:24,040 –> 00:05:26,080
You’ll page through results using AdO data.
145
00:05:26,080 –> 00:05:27,440
Next link whenever it shows up.
146
00:05:27,440 –> 00:05:29,520
If you only got 100 items, that’s not a mystery.
147
00:05:29,520 –> 00:05:31,080
That’s the default page size.
148
00:05:31,080 –> 00:05:32,880
Follow next link until it stops.
149
00:05:32,880 –> 00:05:36,120
Put a guard on your loop so it can’t run forever if the API burps.
150
00:05:36,120 –> 00:05:37,560
Now here’s where most people mess up.
151
00:05:37,560 –> 00:05:39,240
You must respect throttling.
152
00:05:39,240 –> 00:05:43,480
Graph doesn’t care about your feelings, implement retries or enjoy failures.
153
00:05:43,480 –> 00:05:46,680
If you see 420503 look for retry after,
154
00:05:46,680 –> 00:05:48,680
sleep for that duration plus a little jitter
155
00:05:48,680 –> 00:05:51,640
so you don’t join a thundering herd, then try again.
156
00:05:51,640 –> 00:05:53,840
Exponential back off beats panic clicking.
157
00:05:53,840 –> 00:05:57,160
If your automation can’t survive transient errors, it’s not automation.
158
00:05:57,160 –> 00:05:58,360
It’s a suggestion.
159
00:05:58,360 –> 00:06:00,520
Common mistakes, so you don’t repeat them.
160
00:06:00,520 –> 00:06:03,600
One wrong audience, you ask entra for a token to management.
161
00:06:03,600 –> 00:06:06,440
Azure.com and then called graph, Microsoft.com.
162
00:06:06,440 –> 00:06:08,560
That’s a 401, not a conspiracy.
163
00:06:08,560 –> 00:06:11,440
Two pagination denial, why 100 rows only?
164
00:06:11,440 –> 00:06:13,000
Because you never read next link?
165
00:06:13,000 –> 00:06:15,040
Three tight loops without delay.
166
00:06:15,040 –> 00:06:17,760
You angered the throttle gods and now everything slower.
167
00:06:17,760 –> 00:06:20,120
Four over permissioned app with directory.
168
00:06:20,120 –> 00:06:21,360
Read right all just to test.
169
00:06:21,360 –> 00:06:23,280
You just failed and ordered you haven’t had yet.
170
00:06:23,280 –> 00:06:25,640
Let me show you the quick wins you can do today.
171
00:06:25,640 –> 00:06:29,480
Device code, grab a token, get me and confirm you can read your own profile.
172
00:06:29,480 –> 00:06:31,480
That proves your token and headers are wired.
173
00:06:31,480 –> 00:06:34,360
Client credentials use default call users,
174
00:06:34,360 –> 00:06:39,240
select id, display name, mail to keep payload small and process a page or two.
175
00:06:39,240 –> 00:06:40,360
Managed identity.
176
00:06:40,360 –> 00:06:45,840
In Azure call intune’s device endpoint via graph, set top, follow next link and dump only
177
00:06:45,840 –> 00:06:48,760
id device name and last check in date time.
178
00:06:48,760 –> 00:06:51,160
Good words, bad scripts because contrast helps.
179
00:06:51,160 –> 00:06:54,840
Bad module error, import, fail update, fail, copy, fail.
180
00:06:54,840 –> 00:06:57,080
Good rest error token call done.
181
00:06:57,080 –> 00:07:01,480
Sure, your rapid and functions are at logging, but the backbone is boring on purpose.
182
00:07:01,480 –> 00:07:06,000
One more pro move, build a tiny retry helper and a pagination helper once.
183
00:07:06,000 –> 00:07:09,920
Pass in the your eye and headers, get back the full data set with retries already handled.
184
00:07:09,920 –> 00:07:13,320
Suddenly every script is 20 lines shorter and 10 times calmer.
185
00:07:13,320 –> 00:07:17,680
The game changer nobody talks about is you can test these helpers locally, then drop them
186
00:07:17,680 –> 00:07:20,320
in a container or a function without changing a line.
187
00:07:20,320 –> 00:07:23,520
Done, enterprise demo one, intune device cleanup.
188
00:07:23,520 –> 00:07:27,520
Ten and SWAT devices stack up like abandoned cards in a grocery lot.
189
00:07:27,520 –> 00:07:31,600
Policies get noisy, compliance drifts and suddenly your reports look haunted.
190
00:07:31,600 –> 00:07:35,680
Let’s clean it with graph, no modules on a schedule, with logs you can show to security
191
00:07:35,680 –> 00:07:36,680
without blushing.
192
00:07:36,680 –> 00:07:41,040
Here’s the plan, we query intune devices from graph where last check and date time is older
193
00:07:41,040 –> 00:07:42,920
than a threshold you set.
194
00:07:42,920 –> 00:07:48,120
We decide action by agent tags, disable if stale, retire if older, delete if fossilized.
195
00:07:48,120 –> 00:07:51,440
And we check ownership first so you don’t nuke personal devices because someone missed
196
00:07:51,440 –> 00:07:54,840
a field, boring, predictable, safe.
197
00:07:54,840 –> 00:07:59,160
Start with the end point, you’re calling gethttps/graph.
198
00:07:59,160 –> 00:08:03,600
Microsoft.com/beta-divisemanagement-devices-with-select-trim-payload.
199
00:08:03,600 –> 00:08:09,760
ID, device name, operating system, last check and date time, manage device owner type,
200
00:08:09,760 –> 00:08:13,160
as your AD device eat and any tag you rely on.
201
00:08:13,160 –> 00:08:14,960
Use filter for server side cut.
202
00:08:14,960 –> 00:08:20,280
Last check and date time LT24-0101-TZ-UZC-OCE.
203
00:08:20,280 –> 00:08:23,760
If you can’t filter exactly how you want, pull with the conservative window and filter
204
00:08:23,760 –> 00:08:24,760
in PowerShell.
205
00:08:24,760 –> 00:08:25,760
You’ll get paging.
206
00:08:25,760 –> 00:08:27,880
Follow @odeta.nextlink until it stops.
207
00:08:27,880 –> 00:08:29,800
Guard the loop so it can’t spin forever.
208
00:08:29,800 –> 00:08:34,240
Then classification, corporate owned, evaluate action thresholds.
209
00:08:34,240 –> 00:08:42,080
For example, 30 to 60 days, mark for review, 60 to 120, retire, 120 plus delete.
210
00:08:42,080 –> 00:08:45,360
Personal owned, maybe you only notify or tag for review.
211
00:08:45,360 –> 00:08:50,240
The thing most people miss is time skew and inactive but just reprovision devices.
212
00:08:50,240 –> 00:08:54,560
Cross check as your AD device ID against Entra device last seen if you need more confidence.
213
00:08:54,560 –> 00:09:00,480
If there’s conflict, skip and lock now actions retire is opposed to manage devices, ID retire.
214
00:09:00,480 –> 00:09:04,520
Delete is delete, manage devices like ID.
215
00:09:04,520 –> 00:09:08,320
Disable often means flipping state where supported or writing a tag and letting policy handle
216
00:09:08,320 –> 00:09:11,720
it, batch where the endpoint supports it but don’t stamp it.
217
00:09:11,720 –> 00:09:15,720
Respect for 29503, owner, retry after with jitter.
218
00:09:15,720 –> 00:09:20,680
Write every action to lock analytics, device, ID, action, recent time stamp result, request
219
00:09:20,680 –> 00:09:24,800
ID, correlate with a runead so you can reconstruct the story later.
220
00:09:24,800 –> 00:09:25,800
Horror time.
221
00:09:25,800 –> 00:09:30,000
I watched someone delete 800 devices because they didn’t understand last check in timestamps.
222
00:09:30,000 –> 00:09:34,320
They filtered on a property that lagged for re-enrolled devices and skipped dry run.
223
00:09:34,320 –> 00:09:38,280
Graphed it exactly what they asked, it always does, in tune, never lies but boy does it stay
224
00:09:38,280 –> 00:09:41,120
quiet until it’s too late, don’t be that headline.
225
00:09:41,120 –> 00:09:46,040
Automation setup, use an automation account with a system assigned managed identity, granted
226
00:09:46,040 –> 00:09:50,360
least privilege, graph roles for device read and the specific device actions.
227
00:09:50,360 –> 00:09:55,080
For non-secret config invariables, thresholds, tag names, action map have a feature flag
228
00:09:55,080 –> 00:09:56,080
for dry run.
229
00:09:56,080 –> 00:09:58,040
Dry run writes what it would do not what it did.
230
00:09:58,040 –> 00:10:01,240
Run that first, then run it again, then maybe touch production.
231
00:10:01,240 –> 00:10:05,220
Mistakes to avoid, looking devices during a regional time skew, forgetting to limit by
232
00:10:05,220 –> 00:10:07,880
platform when your Mac fleet reports differently.
233
00:10:07,880 –> 00:10:11,600
Running with directory, read write, all just to test.
234
00:10:11,600 –> 00:10:16,120
No back off policy and hitting global throttle so the next team’s job also fails.
235
00:10:16,120 –> 00:10:18,240
Write locks, not feelings, punch line.
236
00:10:18,240 –> 00:10:22,840
If the portal shows it, graph can do it faster, quieter and on schedule.
237
00:10:22,840 –> 00:10:25,920
No module drama, just tokens headers calls.
238
00:10:25,920 –> 00:10:30,680
Enterprise demo 2, identity onboarding via graph only, 450 words.
239
00:10:30,680 –> 00:10:33,360
Onboarding should be boring, if it’s exciting something is wrong.
240
00:10:33,360 –> 00:10:38,560
We’re wiring HR to identity with graph, so accounts show up, licensed, grouped and ready,
241
00:10:38,560 –> 00:10:41,000
before the manager gets impatient and opens a ticket.
242
00:10:41,000 –> 00:10:44,440
Flow is simple, client credentials with a certificate, not a secret.
243
00:10:44,440 –> 00:10:47,320
Your app registration has only the graph app roles it needs.
244
00:10:47,320 –> 00:10:51,040
User, read write, all if you must create users group.
245
00:10:51,040 –> 00:10:55,400
Read write, all if you must assign membership, directory, read all for lookups and the license
246
00:10:55,400 –> 00:10:56,640
assignment roles.
247
00:10:56,640 –> 00:10:58,880
Admin consented once, reviewed quarterly.
248
00:10:58,880 –> 00:11:05,360
Your script signs the request, asks graph for a token using passgars default for http.graph.microsoft.com
249
00:11:05,360 –> 00:11:06,840
and starts the pipeline.
250
00:11:06,840 –> 00:11:08,800
Step one, create the user.
251
00:11:08,800 –> 00:11:11,080
Post 2, users with minimal attributes.
252
00:11:11,080 –> 00:11:16,240
Account enabled, true, display name, mail nickname, user principle name, usage location
253
00:11:16,240 –> 00:11:20,000
and a temporary password with force change password next sign.
254
00:11:20,000 –> 00:11:23,400
In true, if you’re not using SSPR start, keep it lean.
255
00:11:23,400 –> 00:11:25,840
If the user already exists, you patch, not freak out.
256
00:11:25,840 –> 00:11:29,800
It impotency means you can rerun safely after a failure and it won’t make a mess.
257
00:11:29,800 –> 00:11:31,840
Step 2, assign a baseline license.
258
00:11:31,840 –> 00:11:36,160
You’ll get subscribescuse once, cache the skew map and pick the right skew ID, then post
259
00:11:36,160 –> 00:11:41,320
2, users rush ID, assign license with ad licenses containing the skew ID and disable plans
260
00:11:41,320 –> 00:11:45,080
array if you do selective services, handle quota gracefully.
261
00:11:45,080 –> 00:11:48,880
If you’re out of licenses, you log a blocking event and notify the right channel, not
262
00:11:48,880 –> 00:11:51,440
explode the run and leave half created objects.
263
00:11:51,440 –> 00:11:53,200
Step 3, groups by role.
264
00:11:53,200 –> 00:11:57,520
You keep a configuration map from job code or department to static group IDs.
265
00:11:57,520 –> 00:11:58,760
Names drift IDs don’t.
266
00:11:58,760 –> 00:12:05,200
You put or post 2, groups slash, group id, members ref with the user’s directory object ID.
267
00:12:05,200 –> 00:12:07,200
If the user is already a member, skip.
268
00:12:07,200 –> 00:12:10,920
If the group doesn’t exist, that’s a configuration failure, not a runtime adventure.
269
00:12:10,920 –> 00:12:15,840
For script keeps moving for other memberships and logs they miss with correlation id.
270
00:12:15,840 –> 00:12:17,680
Step 4, app access.
271
00:12:17,680 –> 00:12:20,840
Many enterprise apps hang off group assignments or app roles.
272
00:12:20,840 –> 00:12:26,240
For app roles, you post to a service principles, a speed, app role assigned to with the user’s
273
00:12:26,240 –> 00:12:28,640
object id and the app role id.
274
00:12:28,640 –> 00:12:32,160
For group based SSO, adding the user to the right group is enough.
275
00:12:32,160 –> 00:12:33,880
Again, use IDs from config.
276
00:12:33,880 –> 00:12:38,040
No name, lookups and hotpots, guard rails, correlation id per onboarding.
277
00:12:38,040 –> 00:12:42,720
Every graph call logs request id, URI, method, status, duration, retries.
278
00:12:42,720 –> 00:12:49,160
Retry, back off on 429.5.6, feature flag for dry run, which creates a plan, but does no rights.
279
00:12:49,160 –> 00:12:50,600
Item potency everywhere.
280
00:12:50,600 –> 00:12:54,800
If user exists, patch, if license exists, skip.
281
00:12:54,800 –> 00:12:57,080
If group membership exists, skip.
282
00:12:57,080 –> 00:13:01,240
And for the last time, if I see a client secret hard coded again, I’m revoking Wi-Fi.
283
00:13:01,240 –> 00:13:02,720
User third or manage identity.
284
00:13:02,720 –> 00:13:05,440
Quick win, this runs on a Linux runner with PowerShell Core.
285
00:13:05,440 –> 00:13:08,760
No modular load, no waiting for someone to publish a fix.
286
00:13:08,760 –> 00:13:13,040
User shows up in seconds with baseline access and your help desk doesn’t touch a thing.
287
00:13:13,040 –> 00:13:17,240
Now your pipeline is the quiet boring part of onboarding, the way it should be.
288
00:13:17,240 –> 00:13:20,720
Enterprise demo three, compliance drift detection and remediation.
289
00:13:20,720 –> 00:13:23,440
Compliance sprawl is the slow leak that flattens your weak.
290
00:13:23,440 –> 00:13:27,120
Devices drift, users get risky, tickets pile up like snow.
291
00:13:27,120 –> 00:13:31,720
We’re going to scan, target, remediate and verify, all with graph, no modules, and without
292
00:13:31,720 –> 00:13:35,880
waking up set-ups at 2 a.m. start with a schedule and a managed identity.
293
00:13:35,880 –> 00:13:38,560
This job isn’t special, it just needs to be reliable.
294
00:13:38,560 –> 00:13:41,920
The identity gets only the graph rolls it needs.
295
00:13:41,920 –> 00:13:45,800
Device compliance read, device actions if you remediate identity protection, read for
296
00:13:45,800 –> 00:13:49,000
user risk and the session revoke permission.
297
00:13:49,000 –> 00:13:53,480
Don’t grab directory.
298
00:13:53,480 –> 00:13:54,480
Read right.
299
00:13:54,480 –> 00:13:56,960
All just to test.
300
00:13:56,960 –> 00:13:58,640
That’s how audits become folklore.
301
00:13:58,640 –> 00:14:05,920
First pass devices call get a tbsgrushishgraph.microsoft.com/v1, device management, device compliance policy,
302
00:14:05,920 –> 00:14:10,640
settings eight, summaries or the device compliance states and point your tenant users.
303
00:14:10,640 –> 00:14:12,560
Use select to keep payload small.
304
00:14:12,560 –> 00:14:17,240
ID, device name, user principle name, operating system compliance state.
305
00:14:17,240 –> 00:14:19,840
Filter where you can, compliance state EQ non-compliant.
306
00:14:19,840 –> 00:14:24,320
You’ll get pages, follow adodata.next link, put a guard on the loop, now classify.
307
00:14:24,320 –> 00:14:27,280
Non-compliant doesn’t mean execute order 66.
308
00:14:27,280 –> 00:14:31,520
You map severity to action for low severity as maybe a push notification or an email with
309
00:14:31,520 –> 00:14:36,200
a remediation guide, medium trigger a remediation script or force of policy sync.
310
00:14:36,200 –> 00:14:39,400
High, quarantine the device or block access to sensitive apps.
311
00:14:39,400 –> 00:14:44,040
Batch where the endpoint supports it, but keep the degree of parallelism low.
312
00:14:44,040 –> 00:14:46,320
Throttling friendly, not stumpy.
313
00:14:46,320 –> 00:14:48,160
When you act, log like an adult.
314
00:14:48,160 –> 00:14:52,520
For every device you touch, write run ID, device it action reason, request it, status and
315
00:14:52,520 –> 00:14:54,000
latency to log analytics.
316
00:14:54,000 –> 00:14:57,880
If a device flips to compliant during the run, skip and note the flip.
317
00:14:57,880 –> 00:14:59,960
After a remediation, reach agst status.
318
00:14:59,960 –> 00:15:04,680
If it’s still non-compliant, escalate once, not five times, alert thresholds, not spam,
319
00:15:04,680 –> 00:15:05,680
users next.
320
00:15:05,680 –> 00:15:08,000
Pull risky users from identity protection via get.
321
00:15:08,000 –> 00:15:15,480
HTTPS, xxgraph, Microsoft.com/v1, identity protection, risky users, filter, risk level
322
00:15:15,480 –> 00:15:20,760
EQ high and selected user principle name risk level risk state for each high risk user
323
00:15:20,760 –> 00:15:27,200
take targeted action, revoke sign in sessions via posts or users to ID, revoke sign in sessions.
324
00:15:27,200 –> 00:15:32,400
If your policy demands it temporarily block sign in, patch users ID with account enabled
325
00:15:32,400 –> 00:15:36,840
false time box and logged mini rant, don’t carpet bomb sign in, use severity.
326
00:15:36,840 –> 00:15:39,800
You’re not diffusing a movie bomb.
327
00:15:39,800 –> 00:15:42,160
Reality check compliance isn’t a state.
328
00:15:42,160 –> 00:15:44,040
It’s a drifting target you have to chase.
329
00:15:44,040 –> 00:15:46,160
That’s why we use delta where possible.
330
00:15:46,160 –> 00:15:51,240
For device compliance, if delta endpoints exist for your scenario, use them to avoid rescanning
331
00:15:51,240 –> 00:15:52,240
the world.
332
00:15:52,240 –> 00:15:58,680
For users, keep a cache of last process risk change timestamp query only what changed since.
333
00:15:58,680 –> 00:16:01,400
That’s how you keep runs under budget and under the throttle radar.
334
00:16:01,400 –> 00:16:05,400
Common mistakes, blanket blocks without a severity filter, congratulations, you just created
335
00:16:05,400 –> 00:16:06,840
a help desk fire drill.
336
00:16:06,840 –> 00:16:08,040
No audit lock.
337
00:16:08,040 –> 00:16:11,320
Now security wants names, times and reasons you can’t show.
338
00:16:11,320 –> 00:16:14,680
Hard coded IDs, someone renames a policy and your script face plans.
339
00:16:14,680 –> 00:16:16,600
Identity IDs in config, not code.
340
00:16:16,600 –> 00:16:19,640
If you can’t reproduce a run from logs, you’re guessing.
341
00:16:19,640 –> 00:16:26,560
Punch line, detect target, remediate, verify, log, quiet, repeatable and boring on purpose.
342
00:16:26,560 –> 00:16:31,120
Architecture breakdown, identity, automation, execution, observability, you’ve seen the
343
00:16:31,120 –> 00:16:32,120
pattern.
344
00:16:32,120 –> 00:16:33,120
Now why are the plumbing?
345
00:16:33,120 –> 00:16:35,760
So it doesn’t fall over when someone sneezes near Azure.
346
00:16:35,760 –> 00:16:37,120
Identity layer first.
347
00:16:37,120 –> 00:16:39,440
Managed identity wherever the workload lives.
348
00:16:39,440 –> 00:16:43,360
Automation account, function app container in ACI, VM, flip it on.
349
00:16:43,360 –> 00:16:47,520
And only the graph app rolls the job needs and stop thinking about secrets.
350
00:16:47,520 –> 00:16:51,520
If you can’t use managed identity, fine, and trap registration with a certificate, short
351
00:16:51,520 –> 00:16:54,280
lifetime stored in key vault rotated on a schedule.
352
00:16:54,280 –> 00:16:58,000
No secrets in scripts, not in dev, not just testing, not ever.
353
00:16:58,000 –> 00:16:59,000
Automation layer.
354
00:16:59,000 –> 00:17:02,680
Use Azure automation for simple schedules with runbooks that wake up, do one job and
355
00:17:02,680 –> 00:17:03,960
go back to sleep.
356
00:17:03,960 –> 00:17:06,440
Use functions for event driven flows.
357
00:17:06,440 –> 00:17:10,360
User created device status changed, license inventory dipped.
358
00:17:10,360 –> 00:17:14,280
Small, fast, cold start friendly when you’re not dragging modules.
359
00:17:14,280 –> 00:17:19,320
GitHub actions for CICD and cross OS runners stick the same scripts in pipelines that validate
360
00:17:19,320 –> 00:17:20,760
then deploy to prod.
361
00:17:20,760 –> 00:17:25,080
Local power shell for validation and reproducible test before you throw anything at production,
362
00:17:25,080 –> 00:17:28,520
execution layer, power shell core plus invoke rest method.
363
00:17:28,520 –> 00:17:30,840
Version pin your endpoints V1.
364
00:17:30,840 –> 00:17:34,080
For stable, beta only behind feature flags with clear blast radius.
365
00:17:34,080 –> 00:17:35,320
Build two helpers once.
366
00:17:35,320 –> 00:17:41,480
Retry handler that honors 429 503 with exponential back off and jitter and a pager that follows
367
00:17:41,480 –> 00:17:42,480
at or data.
368
00:17:42,480 –> 00:17:45,280
Next link with guards, drop those helpers into every script.
369
00:17:45,280 –> 00:17:49,320
Suddenly your code is small, predictable and not stitched together with three connectors
370
00:17:49,320 –> 00:17:50,720
and a prayer.
371
00:17:50,720 –> 00:17:51,720
Configuration and secrets.
372
00:17:51,720 –> 00:17:57,440
Store non-secret config like group IDs, sqmaps, thresholds, in json or environment variables.
373
00:17:57,440 –> 00:18:01,440
Keep one config per environment so you don’t hard code anything that will drift.
374
00:18:01,440 –> 00:18:03,160
Secrets and certs live in key vault.
375
00:18:03,160 –> 00:18:07,400
The code reads via managed identity, not a magic string in a ps1 file.
376
00:18:07,400 –> 00:18:11,400
Feature flags for dry run and confirm impact make rollout safe instead of theatrical.
377
00:18:11,400 –> 00:18:12,400
Observability.
378
00:18:12,400 –> 00:18:15,240
If you can’t see your automation, you can’t trust your automation.
379
00:18:15,240 –> 00:18:20,880
Send logs to log analytics, request id, correlation id, uri, method, status, duration retry
380
00:18:20,880 –> 00:18:21,880
count.
381
00:18:21,880 –> 00:18:25,040
Trace the whole flow with a run id so you can reconstruct what happened without calling
382
00:18:25,040 –> 00:18:26,280
six people.
383
00:18:26,280 –> 00:18:29,720
App insights for dependencies and live telemetry on functions.
384
00:18:29,720 –> 00:18:33,960
Change your monitor alerts on patterns that matter, failure rate spikes, throttle rate surges,
385
00:18:33,960 –> 00:18:36,160
SLA breaches, not every 404.
386
00:18:36,160 –> 00:18:37,160
Guard rails.
387
00:18:37,160 –> 00:18:40,480
Lease privilege map to jobs, not teams, pin versions.
388
00:18:40,480 –> 00:18:42,120
Review app consent squatterly.
389
00:18:42,120 –> 00:18:44,240
Dry run by default in new environments.
390
00:18:44,240 –> 00:18:48,360
And yes, Microsoft wants you here, not because it’s cute, because modules can’t keep up.
391
00:18:48,360 –> 00:18:51,760
This stack is faster to ship, simpler to govern and it doesn’t panic when you move it from
392
00:18:51,760 –> 00:18:53,720
your laptop to Linux to a container.
393
00:18:53,720 –> 00:18:54,720
That’s the point.
394
00:18:54,720 –> 00:18:56,680
Why Microsoft wants you on graph?
395
00:18:56,680 –> 00:19:00,840
Microsoft wants you on graph because it’s the one surface they can actually ship to at speed.
396
00:19:00,840 –> 00:19:04,600
Identity devices, apps, content, the portal rights graph, so your code should too.
397
00:19:04,600 –> 00:19:05,600
Features hit graph first.
398
00:19:05,600 –> 00:19:07,600
Modules get love when someone finds the time.
399
00:19:07,600 –> 00:19:08,600
Sure.
400
00:19:08,600 –> 00:19:09,600
So is winning the lottery.
401
00:19:09,600 –> 00:19:10,600
Governance gets cleaner.
402
00:19:10,600 –> 00:19:14,600
O-auth scopes and consent tell you exactly who can do what and every call leaves a trail
403
00:19:14,600 –> 00:19:18,240
you can audit without rummaging through someone’s profile script.
404
00:19:18,240 –> 00:19:19,240
Scale.
405
00:19:19,240 –> 00:19:23,320
The endpoints handle global traffic if you handle paging and back off like an adult.
406
00:19:23,320 –> 00:19:27,680
That from reality, Microsoft ships, PowerShell core, but graph is the constant.
407
00:19:27,680 –> 00:19:31,480
The thing most people miss is maintainers don’t control product release speed.
408
00:19:31,480 –> 00:19:34,360
Rest schema changes land your code can adopt them that day.
409
00:19:34,360 –> 00:19:37,400
Beta has risk, pin versions and wrap with feature flags.
410
00:19:37,400 –> 00:19:39,240
Permissions will sprawl if you’re lazy.
411
00:19:39,240 –> 00:19:40,440
Design roles per job.
412
00:19:40,440 –> 00:19:42,800
The portal is just a pretty face on top of graph.
413
00:19:42,800 –> 00:19:44,800
Don’t be the last person to realize it.
414
00:19:44,800 –> 00:19:48,440
A best practices security reliability speed.
415
00:19:48,440 –> 00:19:49,440
Security first.
416
00:19:49,440 –> 00:19:53,040
Use managed identity wherever it exists when it doesn’t set off speed secrets.
417
00:19:53,040 –> 00:19:55,800
Microsoft lifetimes rotate on schedule, store in key vault.
418
00:19:55,800 –> 00:19:58,520
Least privileged graph roles per job, not per team.
419
00:19:58,520 –> 00:20:01,560
Quarterly consent reviews or enjoy surprise outages.
420
00:20:01,560 –> 00:20:02,560
Reliability next.
421
00:20:02,560 –> 00:20:04,680
Handle pagination on every list endpoint.
422
00:20:04,680 –> 00:20:07,240
Detect 429503 on a retry after.
423
00:20:07,240 –> 00:20:09,360
Add exponential back off with jitter.
424
00:20:09,360 –> 00:20:10,760
E-dampotency everywhere.
425
00:20:10,760 –> 00:20:11,760
Check before change.
426
00:20:11,760 –> 00:20:12,760
Abset patterns.
427
00:20:12,760 –> 00:20:14,280
Use e-tags when available.
428
00:20:14,280 –> 00:20:16,240
Delta queries cut scan time and cost.
429
00:20:16,240 –> 00:20:17,240
Performance matters.
430
00:20:17,240 –> 00:20:19,120
Use select to trim payloads.
431
00:20:19,120 –> 00:20:20,600
Batch wear supported.
432
00:20:20,600 –> 00:20:23,000
Parallel with limits so you don’t stampede the API.
433
00:20:23,000 –> 00:20:27,080
Cashestatic lookups like group IDs and SKU maps with a TTL.
434
00:20:27,080 –> 00:20:28,520
Observability isn’t optional.
435
00:20:28,520 –> 00:20:33,240
Log request ID, correlation ID, URI methods, status, duration, retry count.
436
00:20:33,240 –> 00:20:34,920
Keep a run in for multi-step flows.
437
00:20:34,920 –> 00:20:38,880
Track success rate, P95 latency, throttle rate and delta efficiency.
438
00:20:38,880 –> 00:20:43,080
If your automation can’t survive a 429, it’s not automation, it’s a suggestion.
439
00:20:43,080 –> 00:20:45,840
Write logs not feelings code hygiene keeps you sane.
440
00:20:45,840 –> 00:20:48,000
Small functions over 500 line scripts.
441
00:20:48,000 –> 00:20:51,640
Config driven via JSON or environment variables, no hard coded IDs.
442
00:20:51,640 –> 00:20:53,640
Triflex for dry run and save rollout.
443
00:20:53,640 –> 00:20:54,960
And yes, test your back off.
444
00:20:54,960 –> 00:20:57,640
Graph doesn’t care about your feelings.
445
00:20:57,640 –> 00:20:59,400
The gotcha that ruins most graph scripts.
446
00:20:59,400 –> 00:21:00,880
Alright, the promised gotcha.
447
00:21:00,880 –> 00:21:04,360
This one ruins more graph scripts than anything else and it’s not even interesting.
448
00:21:04,360 –> 00:21:05,600
Wrong token audience.
449
00:21:05,600 –> 00:21:07,440
You ask for a token to management.
450
00:21:07,440 –> 00:21:09,320
As your dot com, then you call graph.
451
00:21:09,320 –> 00:21:10,320
Microsoft dot com.
452
00:21:10,320 –> 00:21:13,680
And you stand there wondering why you got a 401 like it’s a plot twist.
453
00:21:13,680 –> 00:21:14,680
It’s not.
454
00:21:14,680 –> 00:21:16,080
Fix is dull and absolute.
455
00:21:16,080 –> 00:21:17,280
Audience must match resource.
456
00:21:17,280 –> 00:21:22,440
If you’re using client credentials, you request for HTTPS, Graph, Microsoft dot com with
457
00:21:22,440 –> 00:21:25,320
the world default scope device code.
458
00:21:25,320 –> 00:21:26,320
Same story.
459
00:21:26,320 –> 00:21:30,280
Scopes for graph, not something you copied from an Azure arm tutorial in 2018.
460
00:21:30,280 –> 00:21:34,560
Managed identity, ask the local endpoint for graph, not whatever’s in the sample.
461
00:21:34,560 –> 00:21:38,200
If you mess up your token audience, don’t worry, you’ll know immediately.
462
00:21:38,200 –> 00:21:41,160
Graph will reject you faster than a bad Tinder opener.
463
00:21:41,160 –> 00:21:43,040
Bonus trap pagination plus filtering.
464
00:21:43,040 –> 00:21:45,040
Not every property is filterable server side.
465
00:21:45,040 –> 00:21:46,720
If the docs say it isn’t, believe them.
466
00:21:46,720 –> 00:21:50,760
Do what you can with filter on supported fields, then finish the cut in PowerShell.
467
00:21:50,760 –> 00:21:54,680
And when you mix search, count or advanced queries, remember the consistency level eventual
468
00:21:54,680 –> 00:21:59,320
header, or you’ll get results that feel like they were assembled by a drunk spider.
469
00:21:59,320 –> 00:22:01,240
Sanity checklist before you hit run.
470
00:22:01,240 –> 00:22:02,960
Token audience is graph.
471
00:22:02,960 –> 00:22:06,520
Required scopes granted and admin consented pagination handled with guards.
472
00:22:06,520 –> 00:22:10,400
Retry with back off wired and tested, logging on every call with request id and correlation
473
00:22:10,400 –> 00:22:11,720
id.
474
00:22:11,720 –> 00:22:14,720
Scope it right or enjoy 401s and a long walk through
475
00:22:14,720 –> 00:22:15,720
the path.
476
00:22:15,720 –> 00:22:16,720
You’ll see the future of the path.
477
00:22:16,720 –> 00:22:17,720
The future is pure graph.
478
00:22:17,720 –> 00:22:18,720
Here’s the take away.
479
00:22:18,720 –> 00:22:22,520
The future of PowerShell is tokens, rest and graph, not modules.
480
00:22:22,520 –> 00:22:24,040
Move one thing to graph this week.
481
00:22:24,040 –> 00:22:25,720
Start with your worst module script.
482
00:22:25,720 –> 00:22:27,480
Ship the token headers call pattern.
483
00:22:27,480 –> 00:22:28,480
Use retries.
484
00:22:28,480 –> 00:22:29,480
Kill secrets.
485
00:22:29,480 –> 00:22:32,560
Then come back for the advanced patterns and my throttle save retry function.
486
00:22:32,560 –> 00:22:34,760
Next episode has the reusable auth wrappers.
487
00:22:34,760 –> 00:22:36,280
Stop living like it’s 2016.
488
00:22:36,280 –> 00:22:37,280
Graph is the job now.
489
00:22:37,280 –> 00:22:38,280
Modules are nostalgic.
490
00:22:38,280 –> 00:22:39,280
Graph gets the job done.






