Most organizations misunderstand Power Platform. They treat it like a productivity toy. Drag boxes. Automate an email. Call it transformation. It works at ten runs per day. It collapses at ten thousand. Not because the platform failed. Because...

Apple Podcasts podcast player iconSpotify podcast player iconYoutube Music podcast player iconSpreaker podcast player iconPodchaser podcast player iconAmazon Music podcast player icon

Most organizations misunderstand Power Platform. They treat it like a productivity toy.
Drag boxes. Automate an email. Call it transformation. It works at ten runs per day.
It collapses at ten thousand. Not because the platform failed.
Because complexity was never priced. So here’s the mandate:

  • Power Platform = Orchestration tier
  • Python = Execution tier
  • Azure = Governance tier
Separate coordination from computation.
Wrap it in identity, network containment, logging, and policy. If you don’t enforce boundaries, entropy does.
And entropy always scales faster than success. Body 1 — The Foundational Misunderstanding Power Platform Is a Control Plane (~700 words) The first mistake is calling Power Platform “a tool.” Excel is a tool.
Word is a tool. Power Platform is not. It is a control plane. It coordinates identity, connectors, environments, approvals, and data movement across your tenant. It doesn’t just automate work — it defines how work is allowed to happen. That distinction changes everything. When you treat a control plane like a toy, you stop designing it.
And when you stop designing it, the system designs itself. And it designs itself around exceptions. “Just one connector.”
“Just one bypass.”
“Just store the credential for now.”
“Just add a condition.” None of these feel large.
All of them accumulate. Eventually you’re not operating a deterministic architecture.
You’re operating a probabilistic one. The flow works — until:
  • The owner leaves
  • A token expires
  • A connector changes its payload
  • Licensing shifts
  • Throttling kicks in
  • A maker copies a flow and creates a parallel universe
It still “runs.” But it’s no longer governable. Then Python enters the conversation. The naive question is:
“Can Power Automate call Python?” Of course it can. The real question is:
Where does compute belong? Because Python is not “just code.”
It’s a runtime. Dependencies. Network paths. Secret handling. Patching. If you bolt that onto the control plane without boundaries, you don’t get hybrid architecture. You get shadow runtime. That’s how governance disappears — not through malice, but through convenience. So reframing is required: Power Platform orchestrates.
Python executes.
Azure governs. Treat Power Platform like a control plane, and you start asking architectural questions:
  • Which principal is calling what?
  • Where do secrets live?
  • What is the network path?
  • Where are logs correlated?
  • What happens at 10× scale?
Most teams don’t ask those because the first flow works. Then you have 500 flows. Then audit shows up. That’s the governance ceiling. Body 2 — The Low-Code Ceiling When Flows Become Pipelines (~700 words) The pattern always looks the same. Flow #1: notify someone.
Flow #2: move data.
Flow #3: transform a spreadsheet. Then trust increases. And trust becomes load. Suddenly your “workflow” is:
  • Parsing CSV
  • Normalizing columns
  • Deduplicating data
  • Joining sources
  • Handling bulk retries
  • Building error reports
Inside a designer built to coordinate steps — not compute. Symptoms appear:
  • Nested loops inside nested loops
  • Scopes inside scopes
  • Try/catch glued together
  • Run histories with 600 actions
  • Retry storms
  • Throttling workarounds
It works.
But you’ve turned orchestration into accidental ETL. This is where people say:
“Maybe we should call Python.” The instinct is right. But the boundary matters. If Python is:
  • A file watcher
  • A laptop script
  • A shared service account
  • A public HTTP trigger
  • A hidden token in a variable
You haven’t added architecture. You’ve added entropy. The right split is simple:
  • Flow decides that work happens
  • Python performs deterministic computation
  • Azure enforces identity and containment
When orchestration stays orchestration, flows become readable again. When execution moves out, retries become intentional. When governance is explicit, scale stops being luck. The low-code ceiling isn’t about capability. It’s about responsibility. Body 3 — Define the Three-Tier Model (~700 words) This isn’t a diagram. It’s an ownership contract. Tier 1 — Orchestration (Power Platform) Responsible for:
  • Triggers
  • Approvals
  • Routing
  • Status tracking
  • Notifications
  • Human interaction
Not responsible for:
  • Heavy transforms
  • Bulk compute
  • Dependency management
  • Runtime patching
Tier 2 — Execution (Python) Responsible for:
  • Deterministic compute
  • Validation
  • Deduplication
  • Inference
  • Bulk updates
  • Schema enforcement
  • Idempotency
Behaves like a service, not a script. That means:
  • Versioned contracts
  • Structured responses
  • Bounded payloads
  • Explicit failures
Tier 3 — Governance (Azure) Responsible for:
  • Workload identity (Entra ID)
  • Managed identities
  • Secrets
  • Network containment
  • Private endpoints
  • API policies
  • Logging and correlation
Without Tier 3, Tier 1 and 2 collapse under entropy. Body 4 — The Anti-Pattern Python Sidecar in the Shadows (~700 words) Every tenant has this:
  • File watcher polling a folder
  • Python script on a jump box
  • Shared automation account
  • Public function “temporarily exposed”
  • Secret pasted in environment variable
It works. Until it doesn’t. Failure modes:
  • OS patch breaks dependency
  • Credential expires
  • Laptop shuts down
  • Firewall changes
  • Package update changes behavior
  • Nobody knows who owns it
That’s not hybrid. That’s a haunted extension cord. The hybrid replacement is explicit:
  • Authenticated workload identity
  • Private endpoint
  • APIM enforcement
  • Structured contract
  • Correlated logging
If Python is going to

Become a supporter of this podcast: https://www.spreaker.com/podcast/m365-fm-modern-work-security-and-productivity-with-microsoft-365--6704921/support.

If this clashes with how you’ve seen it play out, I’m always curious. I use LinkedIn for the back-and-forth.
Transcript
1
00:00:00,000 --> 00:00:01,920
Most organizations treat the power platform

2
00:00:01,920 --> 00:00:03,920
like a harmless productivity toy.

3
00:00:03,920 --> 00:00:06,120
Drag some boxes, automate a few emails,

4
00:00:06,120 --> 00:00:08,080
call it digital transformation.

5
00:00:08,080 --> 00:00:11,440
Then it scales, and the same logic that felt clean at 10 runs

6
00:00:11,440 --> 00:00:13,800
a day becomes a pile of exceptions at 10,000.

7
00:00:13,800 --> 00:00:15,040
That's not success.

8
00:00:15,040 --> 00:00:16,600
That's unpriced complexity.

9
00:00:16,600 --> 00:00:19,760
The hybrid mandate is simple.

10
00:00:19,760 --> 00:00:21,680
Power platform is the orchestration tier.

11
00:00:21,680 --> 00:00:23,080
Python is the execution tier.

12
00:00:23,080 --> 00:00:24,320
Azure is the governance tier,

13
00:00:24,320 --> 00:00:26,200
separate coordination from computation,

14
00:00:26,200 --> 00:00:28,800
and rapid all in identity, network boundaries, logging,

15
00:00:28,800 --> 00:00:32,160
and policy so it stays governable after the hype dies.

16
00:00:32,160 --> 00:00:33,880
The foundational misunderstanding.

17
00:00:33,880 --> 00:00:36,520
Power platform as a tool, not a control plane.

18
00:00:36,520 --> 00:00:39,000
The foundational misunderstanding is that power platform

19
00:00:39,000 --> 00:00:41,480
is a tool in the same way Excel is a tool.

20
00:00:41,480 --> 00:00:43,920
A thing people use, a thing that sits at the edge.

21
00:00:43,920 --> 00:00:44,880
Something optional.

22
00:00:44,880 --> 00:00:46,480
That is not what you're deploying.

23
00:00:46,480 --> 00:00:50,200
In architectural terms, power platform is a control plane.

24
00:00:50,200 --> 00:00:51,960
A distributed decision engine

25
00:00:51,960 --> 00:00:55,680
that coordinates data movement, actions, approvals, identities,

26
00:00:55,680 --> 00:00:57,720
and connector calls across your tenant.

27
00:00:57,720 --> 00:00:59,120
It doesn't just automate work.

28
00:00:59,120 --> 00:01:01,360
It defines how work is allowed to happen

29
00:01:01,360 --> 00:01:02,920
and who is allowed to trigger it

30
00:01:02,920 --> 00:01:05,080
and which data is allowed to cross which boundary.

31
00:01:05,080 --> 00:01:07,400
That distinction matters because the moment you treat

32
00:01:07,400 --> 00:01:09,880
a control plane like a toy, you stop designing it.

33
00:01:09,880 --> 00:01:12,240
And when you stop designing it, the system designs itself.

34
00:01:12,240 --> 00:01:13,240
It always does.

35
00:01:13,240 --> 00:01:15,720
When people say, "Power Automate is just workflows."

36
00:01:15,720 --> 00:01:17,160
What they're really saying is,

37
00:01:17,160 --> 00:01:20,520
"I don't want to own the consequences of a workflow runtime

38
00:01:20,520 --> 00:01:22,960
that can call a thousand external systems

39
00:01:22,960 --> 00:01:26,400
with the privileges of whoever clicked create connection."

40
00:01:27,440 --> 00:01:29,920
The platform is not fragile, your assumptions are.

41
00:01:29,920 --> 00:01:31,720
Here's where decisions actually happen.

42
00:01:31,720 --> 00:01:33,720
Identity determines what a flow can do,

43
00:01:33,720 --> 00:01:35,640
connectors determine what a flow can reach,

44
00:01:35,640 --> 00:01:37,520
environments determine where it can be built

45
00:01:37,520 --> 00:01:38,640
and who can see it.

46
00:01:38,640 --> 00:01:41,000
DLP determines which connectors can be paired,

47
00:01:41,000 --> 00:01:43,040
solutions and ALM determine whether the thing

48
00:01:43,040 --> 00:01:45,280
running in production is an artifact with intent

49
00:01:45,280 --> 00:01:47,600
or just a screenshot of last month's prototype.

50
00:01:47,600 --> 00:01:50,000
Most organizations focus on the visible part,

51
00:01:50,000 --> 00:01:52,480
the canvas, the steps, the triggers.

52
00:01:52,480 --> 00:01:53,640
That's the UI.

53
00:01:53,640 --> 00:01:55,760
The real architecture is the graph behind it.

54
00:01:55,760 --> 00:01:57,600
Entry Identity's delegated permissions,

55
00:01:57,600 --> 00:01:59,600
connection references, environment roles,

56
00:01:59,600 --> 00:02:01,640
dataverse security and policy objects

57
00:02:01,640 --> 00:02:04,240
that live in admin portals nobody wants to open.

58
00:02:04,240 --> 00:02:05,240
So what goes wrong?

59
00:02:05,240 --> 00:02:06,120
Entropy.

60
00:02:06,120 --> 00:02:09,040
Because low-code teams under pressure don't build systems.

61
00:02:09,040 --> 00:02:09,920
They build exceptions.

62
00:02:09,920 --> 00:02:12,840
Just this one connector, just this one bypassed it,

63
00:02:12,840 --> 00:02:14,800
just put the credential in a variable.

64
00:02:14,800 --> 00:02:16,200
Just add a condition.

65
00:02:16,200 --> 00:02:17,360
Each one feels small.

66
00:02:17,360 --> 00:02:18,600
None of them is revisited.

67
00:02:18,600 --> 00:02:21,160
Over time, the exceptions become the architecture

68
00:02:21,160 --> 00:02:23,120
and once exceptions become the architecture,

69
00:02:23,120 --> 00:02:26,040
you're no longer operating a deterministic security model.

70
00:02:26,040 --> 00:02:27,640
You're operating a probabilistic one.

71
00:02:27,640 --> 00:02:29,640
The system still runs, but you can't predict

72
00:02:29,640 --> 00:02:31,160
its behavior underdrift.

73
00:02:31,160 --> 00:02:33,040
When people leave, when tokens expire,

74
00:02:33,040 --> 00:02:35,760
when licensing changes, when a connector update breaks

75
00:02:35,760 --> 00:02:37,760
a payload, when throttles kick in,

76
00:02:37,760 --> 00:02:40,400
when a maker copies a flow to fix it quickly

77
00:02:40,400 --> 00:02:43,080
and accidentally creates a parallel universe.

78
00:02:43,080 --> 00:02:46,200
This is why it works is not the same as it is governable.

79
00:02:46,200 --> 00:02:48,240
A flow can work while being unreviewable.

80
00:02:48,240 --> 00:02:50,560
It can work while having no owner you can contact.

81
00:02:50,560 --> 00:02:51,960
It can work while being impossible

82
00:02:51,960 --> 00:02:53,440
to reproduce in another environment.

83
00:02:53,440 --> 00:02:55,920
It can work while quietly moving data from dataverse

84
00:02:55,920 --> 00:02:57,280
to a consumer connector.

85
00:02:57,280 --> 00:03:00,400
Because somebody built it in the default environment at 2am.

86
00:03:00,400 --> 00:03:02,840
It can work while your incident response team

87
00:03:02,840 --> 00:03:05,800
has no correlated logs and no idea whether the last run

88
00:03:05,800 --> 00:03:08,040
failed because of data, permissions, throttling

89
00:03:08,040 --> 00:03:10,000
or a transient back end issue.

90
00:03:10,000 --> 00:03:10,960
That's the comfort trap.

91
00:03:10,960 --> 00:03:12,280
Success hides failure modes.

92
00:03:12,280 --> 00:03:13,760
Now add Python to the story.

93
00:03:13,760 --> 00:03:17,400
The naive question is, can power automate call Python?

94
00:03:17,400 --> 00:03:18,080
Sure.

95
00:03:18,080 --> 00:03:19,840
People already do it with file watches,

96
00:03:19,840 --> 00:03:22,800
one drive polling, local scripts, random service accounts

97
00:03:22,800 --> 00:03:24,960
and whatever laptop happens to be online.

98
00:03:24,960 --> 00:03:27,040
But the real question is, where does Python belong

99
00:03:27,040 --> 00:03:28,640
and what does it do to your control plane?

100
00:03:28,640 --> 00:03:30,520
Because Python isn't just code.

101
00:03:30,520 --> 00:03:33,480
It's an execution environment, packages, dependencies,

102
00:03:33,480 --> 00:03:36,480
runtime patching, outbound calls, secret handling

103
00:03:36,480 --> 00:03:37,920
and operational ownership.

104
00:03:37,920 --> 00:03:40,200
If you bolt it onto power platform without boundaries,

105
00:03:40,200 --> 00:03:41,800
you don't get a hybrid architecture.

106
00:03:41,800 --> 00:03:44,800
You get a shadow runtime with undefined responsibility.

107
00:03:44,800 --> 00:03:46,240
That is how governance disappears,

108
00:03:46,240 --> 00:03:48,320
not with malice but with convenience.

109
00:03:48,320 --> 00:03:51,600
So the right framing is this, power platform orchestrates.

110
00:03:51,600 --> 00:03:54,240
It roots, approves, schedules, notifies

111
00:03:54,240 --> 00:03:55,920
and handles human touch points.

112
00:03:55,920 --> 00:03:59,920
Python executes, it transforms, validates, deduplicates, scores

113
00:03:59,920 --> 00:04:01,640
and computes in a deterministic way

114
00:04:01,640 --> 00:04:03,680
that doesn't belong inside a flow designer.

115
00:04:03,680 --> 00:04:06,360
As your governs, it provides the enforcement layer.

116
00:04:06,360 --> 00:04:09,440
Identity primitives, network containment, secrets,

117
00:04:09,440 --> 00:04:13,080
observability, policy and life cycle control.

118
00:04:13,080 --> 00:04:15,240
Treat power platform as a control plane

119
00:04:15,240 --> 00:04:17,120
and you start asking better questions.

120
00:04:17,120 --> 00:04:18,280
Who is the principal?

121
00:04:18,280 --> 00:04:20,440
Which identity is calling, which service?

122
00:04:20,440 --> 00:04:21,560
What is the network path?

123
00:04:21,560 --> 00:04:22,520
Where do secrets live?

124
00:04:22,520 --> 00:04:24,280
What does internal mean?

125
00:04:24,280 --> 00:04:25,040
Where are the logs?

126
00:04:25,040 --> 00:04:27,360
And can they be correlated across tiers?

127
00:04:27,360 --> 00:04:29,960
What is the contract between orchestration and execution?

128
00:04:29,960 --> 00:04:31,920
What happens when this scales by 10 times?

129
00:04:31,920 --> 00:04:33,480
Most people never ask those questions

130
00:04:33,480 --> 00:04:34,720
because the first flow works.

131
00:04:34,720 --> 00:04:38,000
Then the second one works, then you have 50, then you have 500,

132
00:04:38,000 --> 00:04:40,760
then the audit shows up and everyone suddenly discovers

133
00:04:40,760 --> 00:04:43,280
they've been running a distributed integration platform

134
00:04:43,280 --> 00:04:44,600
with no architecture.

135
00:04:44,600 --> 00:04:46,640
And this is the moment the low-code team

136
00:04:46,640 --> 00:04:48,760
hits the ceiling, not a technical ceiling,

137
00:04:48,760 --> 00:04:51,200
a governance ceiling, the low-code ceiling

138
00:04:51,200 --> 00:04:53,680
when flows become data pipelines by accident.

139
00:04:53,680 --> 00:04:55,440
This is where the story always turns.

140
00:04:55,440 --> 00:04:57,880
The first few flows are fine, send a team's message,

141
00:04:57,880 --> 00:05:00,360
create a task, root an approval, write a row,

142
00:05:00,360 --> 00:05:02,480
then the business starts trusting the automation

143
00:05:02,480 --> 00:05:05,480
and trust turns into load and load turns into ambition.

144
00:05:05,480 --> 00:05:07,600
Suddenly the flow isn't automation anymore.

145
00:05:07,600 --> 00:05:10,320
It's an accidental data pipeline, it's passing CSVs,

146
00:05:10,320 --> 00:05:12,480
it's normalizing Excel tabs, it's doing lookups

147
00:05:12,480 --> 00:05:14,960
across multiple systems, it's joining data,

148
00:05:14,960 --> 00:05:16,320
it's deduplicating,

149
00:05:16,320 --> 00:05:19,520
it's trying to behave like an ETL tool, a rules engine

150
00:05:19,520 --> 00:05:21,840
and a batch processor, inside a designer

151
00:05:21,840 --> 00:05:24,840
that was built to coordinate steps, not compute.

152
00:05:24,840 --> 00:05:26,800
You can tell you've hit the ceiling by the symptoms

153
00:05:26,800 --> 00:05:29,000
and they're consistent across tenants.

154
00:05:29,000 --> 00:05:30,120
There's the spreadsheet phase,

155
00:05:30,120 --> 00:05:32,520
someone drops a file into one drive or share point,

156
00:05:32,520 --> 00:05:34,720
the flow reads rows and everything is fine

157
00:05:34,720 --> 00:05:36,040
until the file isn't fine.

158
00:05:36,040 --> 00:05:38,960
Extra columns embedded commas, we are date formats.

159
00:05:38,960 --> 00:05:40,760
Empty rows that aren't actually empty,

160
00:05:40,760 --> 00:05:42,800
a total line somebody forgot to delete.

161
00:05:42,800 --> 00:05:44,840
Now the flow has 10 string replacements

162
00:05:44,840 --> 00:05:47,280
and a set of conditions that look like a legal disclaimer.

163
00:05:47,280 --> 00:05:49,080
Then come the loops, nested loops,

164
00:05:49,080 --> 00:05:51,120
apply to each inside, apply to each,

165
00:05:51,120 --> 00:05:53,880
a scope inside a scope because somebody needed a try,

166
00:05:53,880 --> 00:05:55,960
catch, another scope because someone needed

167
00:05:55,960 --> 00:05:57,880
to continue on error.

168
00:05:57,880 --> 00:06:00,480
And now the flow run history is basically a crime scene.

169
00:06:00,480 --> 00:06:03,640
600 actions, three retries, four parallel branches

170
00:06:03,640 --> 00:06:06,560
and a final terminate with a message that tells you nothing.

171
00:06:06,560 --> 00:06:08,720
This might seem like a maker problem, it isn't.

172
00:06:08,720 --> 00:06:10,280
It's a platform economics problem.

173
00:06:10,280 --> 00:06:12,400
In power automate, every additional action

174
00:06:12,400 --> 00:06:14,280
is an unpriced maintenance contract.

175
00:06:14,280 --> 00:06:15,880
You don't pay the cost when you build it.

176
00:06:15,880 --> 00:06:18,760
You pay it when you debug it, version it, secure it

177
00:06:18,760 --> 00:06:20,760
and explain it to someone who didn't design it.

178
00:06:20,760 --> 00:06:22,240
And the platform encourages this

179
00:06:22,240 --> 00:06:25,240
because the UI makes every step feel equally cheap.

180
00:06:25,240 --> 00:06:27,440
One more action, one more condition, one more compose,

181
00:06:27,440 --> 00:06:28,560
it's fine, it isn't.

182
00:06:28,560 --> 00:06:30,360
Because flows don't give you determinism.

183
00:06:30,360 --> 00:06:31,760
They give you last run.

184
00:06:31,760 --> 00:06:34,120
When something breaks, you get a screenshot of a failed action

185
00:06:34,120 --> 00:06:36,200
and a payload that's half context, half noise

186
00:06:36,200 --> 00:06:38,720
and you're expected to reverse engineer intent

187
00:06:38,720 --> 00:06:39,880
from the remains.

188
00:06:39,880 --> 00:06:42,640
And changes don't carry version intent by default.

189
00:06:42,640 --> 00:06:45,800
A maker tweaks in expression, the flow runs, the incident stops

190
00:06:45,800 --> 00:06:48,000
and nobody can tell you what changed, why it changed

191
00:06:48,000 --> 00:06:49,920
or whether it created a new failure mode

192
00:06:49,920 --> 00:06:51,560
that just hasn't been triggered yet.

193
00:06:51,560 --> 00:06:53,560
The system becomes a set of invisible forks.

194
00:06:53,560 --> 00:06:55,000
This is also where platform limits

195
00:06:55,000 --> 00:06:58,160
quietly convert logic into workaround engineering.

196
00:06:58,160 --> 00:07:01,040
Connector throttles don't care about your business priority.

197
00:07:01,040 --> 00:07:03,080
Run limits don't care about your deadline.

198
00:07:03,080 --> 00:07:06,120
Large payload limits don't care that it's just a spreadsheet.

199
00:07:06,120 --> 00:07:08,680
So people start engineering around the constraints,

200
00:07:08,680 --> 00:07:11,080
chunking arrays, splitting files, delaying steps,

201
00:07:11,080 --> 00:07:13,240
adding weights, switching to concurrency,

202
00:07:13,240 --> 00:07:16,080
then turning concurrency off because it causes duplicates,

203
00:07:16,080 --> 00:07:18,760
then adding a do not run twice flag in dataverse

204
00:07:18,760 --> 00:07:20,560
that fails under race conditions.

205
00:07:20,560 --> 00:07:23,920
And the weird part is, all of these workarounds look like progress.

206
00:07:23,920 --> 00:07:25,800
They're not progress, they're symptoms of using

207
00:07:25,800 --> 00:07:28,040
an orchestration engine as a compute engine.

208
00:07:28,040 --> 00:07:31,320
So when someone asks, can power automate kick off Python?

209
00:07:31,320 --> 00:07:34,040
They're usually asking the wrong question with the right instinct.

210
00:07:34,040 --> 00:07:36,520
The instinct is that computation needs a real runtime.

211
00:07:36,520 --> 00:07:39,040
The wrong part is thinking the integration is the hard problem.

212
00:07:39,040 --> 00:07:40,200
The integration is easy.

213
00:07:40,200 --> 00:07:41,280
The boundary is the problem.

214
00:07:41,280 --> 00:07:43,960
Because the moment you bolt Python onto a flow without deciding

215
00:07:43,960 --> 00:07:46,480
where it lives, you've created an unknown execution tier.

216
00:07:46,480 --> 00:07:48,200
If it's a script on someone's laptop,

217
00:07:48,200 --> 00:07:50,760
you just made availability dependent on a human.

218
00:07:50,760 --> 00:07:54,240
If it's a file watcher, you just made your control plane depend on polling.

219
00:07:54,240 --> 00:07:56,440
If it's a service account, you just planted a credential

220
00:07:56,440 --> 00:07:58,200
that will outlive the person who created it.

221
00:07:58,200 --> 00:08:00,880
If it writes directly back to dataverse using ad hoc tokens,

222
00:08:00,880 --> 00:08:03,640
you just bypass the governance posture you thought you had.

223
00:08:03,640 --> 00:08:07,320
This is why the low-code ceiling is not the moment flows can't do the work.

224
00:08:07,320 --> 00:08:09,480
It's the moment the only way to keep doing the work

225
00:08:09,480 --> 00:08:12,960
is to add more actions, more exceptions, and more hidden dependencies.

226
00:08:12,960 --> 00:08:14,920
That is architectural erosion in real time.

227
00:08:14,920 --> 00:08:17,040
So the real question isn't can it call Python?

228
00:08:17,040 --> 00:08:19,560
The real question is, what stays in power platform

229
00:08:19,560 --> 00:08:22,560
because it's orchestration and what leaves because it's execution?

230
00:08:22,560 --> 00:08:26,400
If you don't answer that, your flows will keep mutating into pipelines by accident

231
00:08:26,400 --> 00:08:31,240
and you'll keep paying for it in the only currency that matters in production and baguity.

232
00:08:31,240 --> 00:08:34,720
And from here, the model has to become explicit or the entropy wins.

233
00:08:34,720 --> 00:08:36,360
Define the three tier model.

234
00:08:36,360 --> 00:08:39,680
So define the model explicitly before you integrate anything.

235
00:08:39,680 --> 00:08:43,360
Because if you don't define tiers, you don't get a hybrid architecture.

236
00:08:43,360 --> 00:08:44,880
You get a hybrid blame game.

237
00:08:44,880 --> 00:08:46,880
Power platform is the orchestration tier.

238
00:08:46,880 --> 00:08:48,200
Python is the execution tier.

239
00:08:48,200 --> 00:08:49,440
Azure is the governance tier.

240
00:08:49,440 --> 00:08:51,440
And yes, these words sound like a diagram,

241
00:08:51,440 --> 00:08:54,320
but treat them as ownership boundaries, not shapes on a slide.

242
00:08:54,320 --> 00:08:56,880
Each tier exists because it is good at one thing

243
00:08:56,880 --> 00:08:59,040
and dangerously mediocre at the other two.

244
00:08:59,040 --> 00:09:01,240
Start with orchestration.

245
00:09:01,240 --> 00:09:03,360
Power platform excels at coordination.

246
00:09:03,360 --> 00:09:08,000
Triggers, rooting, approvals, human in the loop steps, notifications,

247
00:09:08,000 --> 00:09:10,320
exception handling that a business can understand

248
00:09:10,320 --> 00:09:13,280
and state tracking that lives close to the process owners.

249
00:09:13,280 --> 00:09:17,360
It's also where connector policy and environment scoping can actually enforce behavior at scale.

250
00:09:17,360 --> 00:09:19,680
The orchestration tier should do three jobs.

251
00:09:19,680 --> 00:09:21,640
Decide that work needs to happen.

252
00:09:21,640 --> 00:09:23,200
Decide who is allowed to approve it

253
00:09:23,200 --> 00:09:25,240
and decide whether results should be recorded.

254
00:09:25,240 --> 00:09:25,760
That's it.

255
00:09:25,760 --> 00:09:30,800
The orchestration tier is not where you do heavy passing, complex validation, bulk data shaping

256
00:09:30,800 --> 00:09:32,920
or probabilistic, let's see what happens loops.

257
00:09:32,920 --> 00:09:36,720
Because once you do that, the flow stops being coordination and becomes computation

258
00:09:36,720 --> 00:09:39,840
and you lose determinism, version intent and debuggability.

259
00:09:39,840 --> 00:09:42,000
Now the execution tier.

260
00:09:42,000 --> 00:09:45,160
Python exists here because execution needs determinism.

261
00:09:45,160 --> 00:09:49,240
It needs a real runtime, real libraries, real testing, real error handling,

262
00:09:49,240 --> 00:09:52,480
real dependency management and real performance characteristics.

263
00:09:52,480 --> 00:09:55,800
This tier is where you do transforms, normalization, deduplication,

264
00:09:55,800 --> 00:09:58,760
model inference, scoring, enrichment and batch strategy.

265
00:09:58,760 --> 00:10:01,280
It's where you can implement idempotency

266
00:10:01,280 --> 00:10:04,920
as code, not as a pile of flags inside a low code designer.

267
00:10:04,920 --> 00:10:08,520
It's where you can unit test the rules that decide whether a row is valid,

268
00:10:08,520 --> 00:10:11,280
a record should be merged or a payload should be rejected.

269
00:10:11,280 --> 00:10:14,200
And the critical point, the execution tier should behave like a service,

270
00:10:14,200 --> 00:10:15,560
not a script, not a sidecar.

271
00:10:15,560 --> 00:10:19,600
A service has a contract, a service has versions, a service has clear inputs and outputs.

272
00:10:19,600 --> 00:10:25,200
A service can be patched without asking makers to open 50 flows and edit expressions they don't understand.

273
00:10:25,200 --> 00:10:28,800
A service can be monitored, a service can be throttled intentionally

274
00:10:28,800 --> 00:10:33,680
and a service can be owned by a team that is accountable for its behavior in production.

275
00:10:33,680 --> 00:10:35,640
That's what Python is for in this model,

276
00:10:35,640 --> 00:10:39,920
deterministic compute that stays stable while the orchestration changes around it.

277
00:10:39,920 --> 00:10:41,840
Now the governance tier.

278
00:10:41,840 --> 00:10:48,080
As you exist here because somebody has to enforce reality, identity, network boundaries, secrets,

279
00:10:48,080 --> 00:10:52,080
observability, policy, cost controls, life cycle.

280
00:10:52,080 --> 00:10:57,000
The governance tier is the place where you stop trusting its internal as a security strategy

281
00:10:57,000 --> 00:11:00,200
and start making it true with control plane primitives.

282
00:11:00,200 --> 00:11:02,520
This is where enter ID becomes non-negotiable.

283
00:11:02,520 --> 00:11:06,040
Non-interactive workloads should authenticate as principles, not as people.

284
00:11:06,040 --> 00:11:11,000
This is where key vault, managed identities and secret rotation stop being nice to have

285
00:11:11,000 --> 00:11:13,000
and become basic operational hygiene.

286
00:11:13,000 --> 00:11:17,640
This is where network containment turns reachable into a governed property, not a convenience.

287
00:11:17,640 --> 00:11:21,800
And this is where logs and metrics cross tier, so incident response becomes a query,

288
00:11:21,800 --> 00:11:22,880
not a group chat.

289
00:11:22,880 --> 00:11:25,760
A lot of teams try to skip this tier because it doesn't ship features.

290
00:11:25,760 --> 00:11:26,560
That's the mistake.

291
00:11:26,560 --> 00:11:28,320
The governance tier doesn't ship features.

292
00:11:28,320 --> 00:11:29,800
It prevents entropy.

293
00:11:29,800 --> 00:11:33,160
An entropy is what kills your features after they succeed.

294
00:11:33,160 --> 00:11:38,200
Now here's what changes when you adopt the three tier model, not capability, ownership.

295
00:11:38,200 --> 00:11:42,320
In a low-code purity world, the maker owns everything by default, the trigger, the logic,

296
00:11:42,320 --> 00:11:46,200
the data shaping, the credentials, the error handling, the notifications, and the downstream

297
00:11:46,200 --> 00:11:47,200
rights.

298
00:11:47,200 --> 00:11:49,480
That feels empowering right up until it becomes unreviewable.

299
00:11:49,480 --> 00:11:54,120
In the three tier model, the maker owns orchestration, engineers own execution, platform

300
00:11:54,120 --> 00:11:56,440
and security teams own governance.

301
00:11:56,440 --> 00:12:02,000
And the seams between them are formal, authentication, authorization, network paths, contracts and

302
00:12:02,000 --> 00:12:03,000
correlated logs.

303
00:12:03,000 --> 00:12:05,680
That distinction matters because it creates survivability.

304
00:12:05,680 --> 00:12:07,840
When someone leaves, the principle still exists.

305
00:12:07,840 --> 00:12:10,480
When a package needs patching, the service is updated once.

306
00:12:10,480 --> 00:12:14,680
When an audit asks how data moves, you can answer with boundaries, not with hope.

307
00:12:14,680 --> 00:12:18,720
When scale arrives, throttling and queues exist where they belong, not embedded as delay

308
00:12:18,720 --> 00:12:20,840
30 seconds inside a flow.

309
00:12:20,840 --> 00:12:24,160
And the punchline is simple, power platform doesn't lose value in this model.

310
00:12:24,160 --> 00:12:25,160
It gains value.

311
00:12:25,160 --> 00:12:29,440
Because now it can stay what it actually is, a control plane for business process.

312
00:12:29,440 --> 00:12:31,760
While Python does what it's always done, compute.

313
00:12:31,760 --> 00:12:36,000
And Azure does what enterprises always forget they need until after the incident.

314
00:12:36,000 --> 00:12:37,200
In force.

315
00:12:37,200 --> 00:12:40,080
The anti-pattern Python is a sidecar in the shadows.

316
00:12:40,080 --> 00:12:43,200
The anti-pattern looks innocent because it usually starts as a demo.

317
00:12:43,200 --> 00:12:47,440
Someone proves power automate can trigger Python by dropping a file in one drive, then a

318
00:12:47,440 --> 00:12:50,160
local script notices the file and does the work.

319
00:12:50,160 --> 00:12:53,320
Or Python triggers a flow by calling an HTTP endpoint.

320
00:12:53,320 --> 00:12:56,960
It works, everyone claps, the organization calls it innovation, what they actually build

321
00:12:56,960 --> 00:12:58,400
is a sidecar in the shadows.

322
00:12:58,400 --> 00:12:59,680
It's not an execution tier.

323
00:12:59,680 --> 00:13:01,000
It's a haunted extension cord.

324
00:13:01,000 --> 00:13:02,960
The first variant is the file watcher.

325
00:13:02,960 --> 00:13:04,600
Power automate writes a file to a folder.

326
00:13:04,600 --> 00:13:06,080
Python pulls the folder.

327
00:13:06,080 --> 00:13:07,320
When a file appears it runs.

328
00:13:07,320 --> 00:13:10,240
The weird part is how quickly this becomes production behavior.

329
00:13:10,240 --> 00:13:12,120
Not because it's good, because it's easy.

330
00:13:12,120 --> 00:13:15,800
And once a business process depends on it, nobody wants to touch it.

331
00:13:15,800 --> 00:13:18,840
But a polling script is a governance failure disguised as automation.

332
00:13:18,840 --> 00:13:19,840
It has no contract.

333
00:13:19,840 --> 00:13:21,280
It has no authenticated caller.

334
00:13:21,280 --> 00:13:23,160
It has no explicit authorization model.

335
00:13:23,160 --> 00:13:25,440
It has no deterministic retry behavior.

336
00:13:25,440 --> 00:13:29,520
It has wild true and asleep timer, which is basically the architectural equivalent of leaving

337
00:13:29,520 --> 00:13:32,800
a car idling because you don't trust it to start again.

338
00:13:32,800 --> 00:13:34,240
Then you get the next variant.

339
00:13:34,240 --> 00:13:36,320
Local scripts on laptops or jump boxes.

340
00:13:36,320 --> 00:13:38,880
The flow writes a file, a scheduled task runs Python.

341
00:13:38,880 --> 00:13:40,880
A team shares the script in TeamsChat.

342
00:13:40,880 --> 00:13:42,960
Somebody pins a message with, don't delete this.

343
00:13:42,960 --> 00:13:47,440
And now the availability of your automation depends on whether a workstation reboots during

344
00:13:47,440 --> 00:13:48,440
patch Tuesday.

345
00:13:48,440 --> 00:13:50,200
This is what incident reviews sound like.

346
00:13:50,200 --> 00:13:51,200
It worked yesterday.

347
00:13:51,200 --> 00:13:53,640
No, a person's workstation worked yesterday.

348
00:13:53,640 --> 00:13:57,840
The third variant is the service account because eventually somebody gets tired of MFA

349
00:13:57,840 --> 00:13:59,600
prompts and broken connections.

350
00:13:59,600 --> 00:14:04,160
So they create automation at company, com, granted broad access and store the password

351
00:14:04,160 --> 00:14:06,480
somewhere that feels private until it isn't.

352
00:14:06,480 --> 00:14:08,960
That account becomes the keystone of the whole arrangement.

353
00:14:08,960 --> 00:14:10,160
It authenticates the script.

354
00:14:10,160 --> 00:14:11,560
It authenticates the connectors.

355
00:14:11,560 --> 00:14:14,920
It authenticates the downstream rights and nobody can tell you who's responsible for

356
00:14:14,920 --> 00:14:15,920
it.

357
00:14:15,920 --> 00:14:18,240
In architectural terms, you didn't solve authentication.

358
00:14:18,240 --> 00:14:22,680
You manufactured an entropy generator and yes, the sidecar can be in the cloud and still

359
00:14:22,680 --> 00:14:24,520
be the same anti-pattern.

360
00:14:24,520 --> 00:14:29,480
A container running Python with a public endpoint, a function app with no network containment,

361
00:14:29,480 --> 00:14:32,760
a web hook that accepts anonymous requests because it's just internal.

362
00:14:32,760 --> 00:14:36,440
It is a key in a config file because we'll move it to Key Vault later.

363
00:14:36,440 --> 00:14:37,440
Later never arrives.

364
00:14:37,440 --> 00:14:40,080
Later is where security debt goes to retire comfortably.

365
00:14:40,080 --> 00:14:42,520
This is why sidecar Python fails in incident reviews.

366
00:14:42,520 --> 00:14:44,480
Ownership and boundaries are undefined.

367
00:14:44,480 --> 00:14:45,640
Who owns the runtime?

368
00:14:45,640 --> 00:14:48,120
Who patches the dependencies when a CVE drops?

369
00:14:48,120 --> 00:14:49,120
Who rotates the secret?

370
00:14:49,120 --> 00:14:50,120
Who reviews the code?

371
00:14:50,120 --> 00:14:52,080
Who decides what outbound calls are allowed?

372
00:14:52,080 --> 00:14:54,240
Who has the logs when it fails at 3am?

373
00:14:54,240 --> 00:14:55,600
And the most important question?

374
00:14:55,600 --> 00:14:59,680
Who is accountable when the script quietly exfiltrates data to somewhere that was never

375
00:14:59,680 --> 00:15:01,000
modeled?

376
00:15:01,000 --> 00:15:04,640
Because somebody imported a library that phones home for updates.

377
00:15:04,640 --> 00:15:06,600
The operational rod shows up first.

378
00:15:06,600 --> 00:15:08,800
The script works until a package version changes.

379
00:15:08,800 --> 00:15:10,360
It works until the OS updates.

380
00:15:10,360 --> 00:15:12,240
It works until the directory path changes.

381
00:15:12,240 --> 00:15:15,280
It works until the one drive sync client decides its offline.

382
00:15:15,280 --> 00:15:18,080
It works until the maker renames a file and the parser breaks.

383
00:15:18,080 --> 00:15:20,800
It works until concurrency shows up in two runs collide.

384
00:15:20,800 --> 00:15:24,920
And when it fails, it fails in a place the power platform team can't see.

385
00:15:24,920 --> 00:15:28,880
Outside the run history, outside the connector logs, outside the environment telemetry.

386
00:15:28,880 --> 00:15:33,000
Your control plane loses observability exactly where you introduced complexity.

387
00:15:33,000 --> 00:15:34,320
Then the security rod shows up.

388
00:15:34,320 --> 00:15:36,640
The data path becomes outbound by accident.

389
00:15:36,640 --> 00:15:40,040
Dataverse data gets written to files because that was the easiest handoff.

390
00:15:40,040 --> 00:15:43,760
Those files land in places with consumer sync clients, retention policies you didn't

391
00:15:43,760 --> 00:15:46,040
design and access you didn't review.

392
00:15:46,040 --> 00:15:50,280
For the Python script calls back into power automate through an HTTP trigger that has no

393
00:15:50,280 --> 00:15:53,680
meaningful authorization beyond, you know the URL.

394
00:15:53,680 --> 00:15:56,760
That's not integration, that's a back door you forgot you created.

395
00:15:56,760 --> 00:16:01,560
And the irony is that the bidirectional arrow, the thing people love to show on slides, can

396
00:16:01,560 --> 00:16:02,840
be real and safe.

397
00:16:02,840 --> 00:16:05,360
But only if you treat it like a tier boundary with enforcement.

398
00:16:05,360 --> 00:16:08,280
A side car is what you get when you treat Python like a hack.

399
00:16:08,280 --> 00:16:11,720
A hybrid architecture is what you get when you treat Python like a service and a

400
00:16:11,720 --> 00:16:15,400
zure like the enforcement layer and power platform like the coordinator that never stops

401
00:16:15,400 --> 00:16:16,960
being a control plane.

402
00:16:16,960 --> 00:16:18,720
That's the replacement pattern.

403
00:16:18,720 --> 00:16:22,080
And to do it, you start with the thing everyone tries to postpone.

404
00:16:22,080 --> 00:16:23,360
Identity.

405
00:16:23,360 --> 00:16:26,960
Integration paths, two directions, one governance standard.

406
00:16:26,960 --> 00:16:31,040
There are only two integration directions that matter and both are legitimate.

407
00:16:31,040 --> 00:16:33,680
Power automate to Python and Python to power automate.

408
00:16:33,680 --> 00:16:37,320
The problem is that most tenants implement both as if they're different species with different

409
00:16:37,320 --> 00:16:38,440
standards.

410
00:16:38,440 --> 00:16:41,080
One is treated as a flow calling an API.

411
00:16:41,080 --> 00:16:44,120
The other is treated as a script calling a web hook.

412
00:16:44,120 --> 00:16:47,200
And governance evaporates because it's just automation.

413
00:16:47,200 --> 00:16:48,200
It's not.

414
00:16:48,200 --> 00:16:52,080
It's crossed here execution and it needs one governance standard, no matter which direction

415
00:16:52,080 --> 00:16:54,920
the arrow points start with power automate to Python.

416
00:16:54,920 --> 00:16:59,520
This is the cleaner mental model for most organizations because it preserves what power

417
00:16:59,520 --> 00:17:01,000
platform is good at.

418
00:17:01,000 --> 00:17:02,320
It stays the coordinator.

419
00:17:02,320 --> 00:17:03,640
A trigger happens.

420
00:17:03,640 --> 00:17:07,160
A maker owned process decides that computation is required.

421
00:17:07,160 --> 00:17:10,880
Then the flow calls a deterministic execution service and waits for a result.

422
00:17:10,880 --> 00:17:14,960
The flow remains the system of record for the process state approvals notifications and

423
00:17:14,960 --> 00:17:15,960
escalation.

424
00:17:15,960 --> 00:17:18,880
Python does the heavy lifting and returns structured output.

425
00:17:18,880 --> 00:17:23,080
That output has to look like a contract, a schema, a status, a list of errors, a data and

426
00:17:23,080 --> 00:17:24,080
a correlation ID.

427
00:17:24,080 --> 00:17:26,400
Not here's a string, not check the logs.

428
00:17:26,400 --> 00:17:28,000
Not it worked on my machine.

429
00:17:28,000 --> 00:17:32,640
If the execution tier fails, the orchestration tier should be able to route that failure.

430
00:17:32,640 --> 00:17:36,720
Retry intentionally, raise an incident or park the record for human review.

431
00:17:36,720 --> 00:17:40,760
That means the flow needs stable responses and stable failure modes.

432
00:17:40,760 --> 00:17:42,760
The execution tier's job is not to be clever.

433
00:17:42,760 --> 00:17:43,920
It's to be predictable.

434
00:17:43,920 --> 00:17:44,920
Now flip it.

435
00:17:44,920 --> 00:17:46,400
Python to power automate.

436
00:17:46,400 --> 00:17:48,320
This direction exists for a reason.

437
00:17:48,320 --> 00:17:50,400
Sometimes the event source isn't power platform.

438
00:17:50,400 --> 00:17:51,400
It's a data science job.

439
00:17:51,400 --> 00:17:52,400
It's a batch ingestion.

440
00:17:52,400 --> 00:17:53,520
It's an external system.

441
00:17:53,520 --> 00:17:57,160
It's a scheduled compute process that decides something important happened and the business

442
00:17:57,160 --> 00:18:02,680
process needs to run, notify a team, create an approval, update a dataverse record through

443
00:18:02,680 --> 00:18:05,960
a governed path or kickoff downstream coordination.

444
00:18:05,960 --> 00:18:09,120
So Python calls a flow, usually through an HTTP trigger.

445
00:18:09,120 --> 00:18:13,000
And this is where people get sloppy because HTTP triggers look like convenience.

446
00:18:13,000 --> 00:18:14,000
They're not.

447
00:18:14,000 --> 00:18:15,400
They are an orchestration entry point.

448
00:18:15,400 --> 00:18:17,920
They are literally the front door to your control plane.

449
00:18:17,920 --> 00:18:21,520
Treating them like a back door is how you end up with anonymous URLs floating around

450
00:18:21,520 --> 00:18:26,240
in scripts, paste it into wikis, shared in email threads, and embedded in code that gets

451
00:18:26,240 --> 00:18:28,240
copied into the next quick fix.

452
00:18:28,240 --> 00:18:29,320
You can't rotate culture.

453
00:18:29,320 --> 00:18:30,520
You can rotate credentials.

454
00:18:30,520 --> 00:18:36,000
So if Python triggers power automate, it must do it as an authenticated, authorized principle

455
00:18:36,000 --> 00:18:40,120
with explicit permissions and the flow must enforce those assumptions.

456
00:18:40,120 --> 00:18:43,240
The caller identity shouldn't be anyone who knows the URL.

457
00:18:43,240 --> 00:18:46,360
It should be a workload identity you can disable scope and audit.

458
00:18:46,360 --> 00:18:50,520
By directionality matters because it decouples business coordination from compute.

459
00:18:50,520 --> 00:18:52,680
That's the entire point of the three tier model.

460
00:18:52,680 --> 00:18:57,240
You want the workflow to remain stable even as the execution logic evolves and you want

461
00:18:57,240 --> 00:19:01,200
execution to scale and change without rewriting the business process layer.

462
00:19:01,200 --> 00:19:03,240
Both directions support that decoupling.

463
00:19:03,240 --> 00:19:05,600
But only if they share one governance standard.

464
00:19:05,600 --> 00:19:07,840
There is that standard and it's non-negotiable.

465
00:19:07,840 --> 00:19:11,720
Every cross tier call must be authenticated, authorized, and logged.

466
00:19:11,720 --> 00:19:15,640
Authenticated means the caller proves who it is using tokens not shared secrets stuffed

467
00:19:15,640 --> 00:19:17,160
into a flow action.

468
00:19:17,160 --> 00:19:21,160
Authorized means the caller can only do the specific thing it needs to do, not contributor

469
00:19:21,160 --> 00:19:24,360
on the whole resource group because it was faster.

470
00:19:24,360 --> 00:19:28,440
Logged means the call creates evidence who called what was requested, what was returned,

471
00:19:28,440 --> 00:19:30,280
how long it took, and why it failed.

472
00:19:30,280 --> 00:19:33,200
And yes, the log requirement is part of the interface.

473
00:19:33,200 --> 00:19:37,640
If you can't correlate the flow run to the function invocation to the downstream data

474
00:19:37,640 --> 00:19:40,160
verse operation, you do not have an architecture.

475
00:19:40,160 --> 00:19:41,720
You have a distributed guessing game.

476
00:19:41,720 --> 00:19:45,920
This is also where people start arguing about tools, functions versus containers.

477
00:19:45,920 --> 00:19:48,440
APIM versus direct calls.

478
00:19:48,440 --> 00:19:50,800
Private endpoints versus public with IP restrictions.

479
00:19:50,800 --> 00:19:52,280
Those details matter later.

480
00:19:52,280 --> 00:19:53,960
First you decide the behavioral rules.

481
00:19:53,960 --> 00:19:57,120
The hybrid mandate doesn't care whether you like Python or dislike it.

482
00:19:57,120 --> 00:20:00,480
It cares whether the execution tier behaves like a govern service.

483
00:20:00,480 --> 00:20:03,960
It cares whether the orchestration tier remains readable and survivable.

484
00:20:03,960 --> 00:20:06,920
It cares whether Azure enforces the boundaries you claim exist.

485
00:20:06,920 --> 00:20:09,480
So pick your direction based on the event source.

486
00:20:09,480 --> 00:20:13,800
But enforce the same governance standard either way and start where every boundary decision

487
00:20:13,800 --> 00:20:16,560
starts, whether you admitted or not, identity.

488
00:20:16,560 --> 00:20:19,880
Identity boundary, flow authenticated via intra ID.

489
00:20:19,880 --> 00:20:24,000
Identity is where hybrid architectures either become governable or become folklore.

490
00:20:24,000 --> 00:20:27,640
Because the first thing most people do when they connect, power automate to some Python

491
00:20:27,640 --> 00:20:28,720
is borrow a human.

492
00:20:28,720 --> 00:20:32,040
They create a connection with their own account, test it, publish it and move on.

493
00:20:32,040 --> 00:20:33,440
It runs, the business is happy.

494
00:20:33,440 --> 00:20:38,160
And now production depends on one person's token refresh behavior, licensing status, MFA

495
00:20:38,160 --> 00:20:40,800
prompts, password resets and eventual offboarding.

496
00:20:40,800 --> 00:20:42,160
That isn't automation.

497
00:20:42,160 --> 00:20:44,240
That's employee impersonation at scale.

498
00:20:44,240 --> 00:20:48,640
In the hybrid mandate, every cross tier call is a workload call, not a human call.

499
00:20:48,640 --> 00:20:53,720
That means the principle has to be explicit, named, owned, auditable.

500
00:20:53,720 --> 00:20:57,880
And designed to survive leadership changes, reogs and attrition.

501
00:20:57,880 --> 00:21:00,520
Because those are the only constants in enterprise IT.

502
00:21:00,520 --> 00:21:02,000
So start with the hard rule.

503
00:21:02,000 --> 00:21:06,480
Stop using some users credentials for anything that runs without a user present.

504
00:21:06,480 --> 00:21:10,320
If a flow needs to call the Python execution tier, typically an Azure function, container

505
00:21:10,320 --> 00:21:15,120
endpoint or a service behind API management, then the flow must authenticate using intra

506
00:21:15,120 --> 00:21:17,280
ID as a workload identity.

507
00:21:17,280 --> 00:21:21,960
In practice, that usually means one of two things, a service principle or a managed identity.

508
00:21:21,960 --> 00:21:24,160
A service principle is the classic pattern.

509
00:21:24,160 --> 00:21:29,040
An app registration in intra ID with defined permissions, a credential and a clear life cycle.

510
00:21:29,040 --> 00:21:30,040
It's fine.

511
00:21:30,040 --> 00:21:34,000
It's also where entropy enters because people treat client secrets like passwords and

512
00:21:34,000 --> 00:21:36,080
then paste them into places they don't belong.

513
00:21:36,080 --> 00:21:40,920
A secret in a flow action, a secret in an environment variable that's shared across environments,

514
00:21:40,920 --> 00:21:44,000
a secret in a wiki page because the SOE needs it.

515
00:21:44,000 --> 00:21:48,080
Then six months later, nobody knows where it's used, so nobody rotates it.

516
00:21:48,080 --> 00:21:50,720
Now the secret is permanent and permanent secrets are a design failure.

517
00:21:50,720 --> 00:21:55,040
A managed identity is what enterprises should prefer when the execution tier lives in a

518
00:21:55,040 --> 00:21:56,040
Azure.

519
00:21:56,040 --> 00:21:57,960
It removes the secret handling problem entirely.

520
00:21:57,960 --> 00:21:59,520
The runtime gets an identity.

521
00:21:59,520 --> 00:22:01,680
The identity gets permissions.

522
00:22:01,680 --> 00:22:03,240
Tokens are issued automatically.

523
00:22:03,240 --> 00:22:04,640
Rotation is no longer your problem.

524
00:22:04,640 --> 00:22:05,880
That's not convenience.

525
00:22:05,880 --> 00:22:09,880
That's reducing attack surface and operational toilet at the same time, which is rare enough

526
00:22:09,880 --> 00:22:12,360
that you should take it whenever you can.

527
00:22:12,360 --> 00:22:15,720
But whether you use a service principle or manage the identity, the critical behavior

528
00:22:15,720 --> 00:22:16,720
is the same.

529
00:22:16,720 --> 00:22:19,040
Token-based calls with least privilege.

530
00:22:19,040 --> 00:22:22,680
The execution tier should accept only valid tokens from known callers.

531
00:22:22,680 --> 00:22:25,040
It should validate audience issuer and claims.

532
00:22:25,040 --> 00:22:29,480
It should reject anonymous calls, shared keys and its internal assumptions.

533
00:22:29,480 --> 00:22:33,000
And the identity should be scoped to exactly what it needs.

534
00:22:33,000 --> 00:22:36,280
Call this API in this environment with this role.

535
00:22:36,280 --> 00:22:38,480
Not contributor because it was faster.

536
00:22:38,480 --> 00:22:40,600
Not owner because troubleshooting was annoying.

537
00:22:40,600 --> 00:22:42,640
Those shortcuts don't stay temporary.

538
00:22:42,640 --> 00:22:44,000
They become the architecture.

539
00:22:44,000 --> 00:22:48,320
This is also where conditional access enters the chat and quietly ruins people's day.

540
00:22:48,320 --> 00:22:50,920
Non-interactive workloads don't do MFA prompts.

541
00:22:50,920 --> 00:22:52,880
They don't pass device compliance checks.

542
00:22:52,880 --> 00:22:54,120
They don't open a browser.

543
00:22:54,120 --> 00:22:58,080
So the tenant needs conditional access policies that explicitly account for service principles

544
00:22:58,080 --> 00:22:59,640
and managed identities.

545
00:22:59,640 --> 00:23:02,240
Otherwise you'll oscillate between two failure modes.

546
00:23:02,240 --> 00:23:06,520
Either you block the workload and break production, or you exempt it broadly and create a permanent

547
00:23:06,520 --> 00:23:07,520
bypass.

548
00:23:07,520 --> 00:23:09,240
The hybrid mandate doesn't accept either.

549
00:23:09,240 --> 00:23:13,520
The identity boundary must be designed as a first class workload pattern.

550
00:23:13,520 --> 00:23:17,880
Dedicated principles, explicit assignments and policies that treat automation as automation

551
00:23:17,880 --> 00:23:20,280
not as a human pretending to be a robot.

552
00:23:20,280 --> 00:23:21,960
Now the ownership model.

553
00:23:21,960 --> 00:23:25,400
This is the part everyone avoids because it forces accountability.

554
00:23:25,400 --> 00:23:26,880
Who owns the app registration?

555
00:23:26,880 --> 00:23:28,360
Who owns the managed identity?

556
00:23:28,360 --> 00:23:29,880
Who owns the API permissions?

557
00:23:29,880 --> 00:23:33,200
Who owns incident response when the principle starts getting denied?

558
00:23:33,200 --> 00:23:35,760
If the answer is the maker, you've already lost.

559
00:23:35,760 --> 00:23:38,080
Because makers don't own entrapostia.

560
00:23:38,080 --> 00:23:39,480
Security and platform teams do.

561
00:23:39,480 --> 00:23:43,920
If the answer is the cloud team but they don't own the flow logic, you've split responsibility

562
00:23:43,920 --> 00:23:44,920
without a contract.

563
00:23:44,920 --> 00:23:46,440
So define it.

564
00:23:46,440 --> 00:23:52,440
makers own orchestration artifacts, engineers own execution services and platform security

565
00:23:52,440 --> 00:23:54,040
owns identities and policy.

566
00:23:54,040 --> 00:23:55,600
And yes, that sounds bureaucratic.

567
00:23:55,600 --> 00:23:56,600
It's not.

568
00:23:56,600 --> 00:23:58,080
It's survivability engineering.

569
00:23:58,080 --> 00:24:02,400
Because when an employee leaves, a flow authenticated as them becomes an outage with a calendar

570
00:24:02,400 --> 00:24:03,400
invite.

571
00:24:03,400 --> 00:24:07,240
When a flow authenticates as a workload principle, off-boarding is irrelevant.

572
00:24:07,240 --> 00:24:11,240
The system keeps running and the audit trail still points to a non-human identity that

573
00:24:11,240 --> 00:24:16,040
can be governed, rotated and revoked, without rewriting the business process.

574
00:24:16,040 --> 00:24:19,440
This is why identity is the first boundary it determines every other boundary.

575
00:24:19,440 --> 00:24:22,640
But identity without network containment is still an exposed surface.

576
00:24:22,640 --> 00:24:28,480
So next comes the network boundary where private stops being a vibe and becomes a property.

577
00:24:28,480 --> 00:24:31,120
Network boundary, private endpoint for the function.

578
00:24:31,120 --> 00:24:33,040
Network is the part everyone hand waves with.

579
00:24:33,040 --> 00:24:34,760
It's in Azure, so it's fine.

580
00:24:34,760 --> 00:24:38,960
That sentence has funded more incident retrospectives than any attacker ever has.

581
00:24:38,960 --> 00:24:44,320
If the Python execution tier sits behind a public URL, you've created a permanent exception.

582
00:24:44,320 --> 00:24:47,720
Maybe you lock it down with a function key, maybe you add IP restrictions, maybe you stick

583
00:24:47,720 --> 00:24:50,200
it behind a waft later, but the physics don't change.

584
00:24:50,200 --> 00:24:53,960
You made your execution tier internet reachable and now you will spend the rest of its life

585
00:24:53,960 --> 00:24:55,960
explaining why that's acceptable.

586
00:24:55,960 --> 00:24:57,520
This is the uncomfortable truth.

587
00:24:57,520 --> 00:24:59,720
Public endpoints become policy debt.

588
00:24:59,720 --> 00:25:04,000
They start as temporary, then they become dependency, then they become sacred.

589
00:25:04,000 --> 00:25:06,440
Once flows, scripts and systems depend on that endpoint.

590
00:25:06,440 --> 00:25:09,200
Nobody wants to break it to fix it, so it stays public.

591
00:25:09,200 --> 00:25:13,600
Forever, with just one allow list, with just one bypass for a partner, with just one

592
00:25:13,600 --> 00:25:15,280
exception for a test tool.

593
00:25:15,280 --> 00:25:18,880
And then your back to conditional chaos, except now it's network shaped.

594
00:25:18,880 --> 00:25:21,320
Private endpoint exists to stop the argument.

595
00:25:21,320 --> 00:25:23,960
Private endpoint makes reachable a govern property.

596
00:25:23,960 --> 00:25:25,760
The service is still the same function app.

597
00:25:25,760 --> 00:25:29,560
The code is still the same Python, but the front door moves off the public internet and

598
00:25:29,560 --> 00:25:31,520
into your private address space.

599
00:25:31,520 --> 00:25:36,840
That means if something wants to call the execution tier, it has to be inside your network boundary,

600
00:25:36,840 --> 00:25:39,960
or it has to come through an explicitly governed ingress point.

601
00:25:39,960 --> 00:25:44,080
That distinction matters because identity without containment is still an attack surface.

602
00:25:44,080 --> 00:25:46,080
Entra proves who is calling?

603
00:25:46,080 --> 00:25:47,160
Network proves from where?

604
00:25:47,160 --> 00:25:51,200
If you only do identity, you're still betting that nobody can reach the endpoint except

605
00:25:51,200 --> 00:25:52,480
the callers you intended.

606
00:25:52,480 --> 00:25:54,280
That's not an architectural guarantee.

607
00:25:54,280 --> 00:25:55,640
That's a hope with certificates.

608
00:25:55,640 --> 00:25:57,800
So what does this look like in a hybrid mandate?

609
00:25:57,800 --> 00:26:00,680
The Python execution tier runs in Azure Functions.

610
00:26:00,680 --> 00:26:03,480
The function gets vnet integration for outbound.

611
00:26:03,480 --> 00:26:07,240
Then you expose inbound through private endpoint, so the function's runtime is reachable

612
00:26:07,240 --> 00:26:10,360
only via private IP with private DNS resolution.

613
00:26:10,360 --> 00:26:13,520
Now your API isn't on the internet with rules.

614
00:26:13,520 --> 00:26:17,800
It's an internal service with an explicit entry path, and yes, there are multiple patterns

615
00:26:17,800 --> 00:26:21,200
depending on how strict you need to be and what else you're integrating with.

616
00:26:21,200 --> 00:26:22,440
The details change.

617
00:26:22,440 --> 00:26:23,520
The principle doesn't.

618
00:26:23,520 --> 00:26:27,720
You don't make your execution tier public just because it's convenient for a flow action.

619
00:26:27,720 --> 00:26:31,080
There's an immediate pushback here, but power automate is SAS.

620
00:26:31,080 --> 00:26:32,960
How does it call a private endpoint?

621
00:26:32,960 --> 00:26:36,600
That question is exactly why network boundaries force architectural clarity.

622
00:26:36,600 --> 00:26:41,160
If a SAS orchestrator has to call into a private execution tier, you need a deliberate bridge.

623
00:26:41,160 --> 00:26:44,040
Not a leak, not a random public exception.

624
00:26:44,040 --> 00:26:47,480
Sometimes that bridge is API management with private back end connectivity acting as

625
00:26:47,480 --> 00:26:49,960
a controlled ingress with policy enforcement.

626
00:26:49,960 --> 00:26:52,360
Sometimes it's a relay pattern, sometimes it's a queue.

627
00:26:52,360 --> 00:26:56,520
The flow writes a message to a governed broker, and the execution tier pulls it from inside

628
00:26:56,520 --> 00:26:57,520
the network.

629
00:26:57,520 --> 00:27:00,520
The hybrid mandate doesn't require one specific implementation.

630
00:27:00,520 --> 00:27:05,440
It requires that the bridge is explicit, auditable and owned because the failure mode is predictable.

631
00:27:05,440 --> 00:27:10,080
If the flow can't reach the function privately, people will make it public for now.

632
00:27:10,080 --> 00:27:12,280
And for now is how permanent exposure happens.

633
00:27:12,280 --> 00:27:15,520
Private endpoint also forces you to get serious about outbound paths.

634
00:27:15,520 --> 00:27:18,800
A function app can talk to anything by default if you let it.

635
00:27:18,800 --> 00:27:22,880
That means the execution tier can quietly become a data exfiltration engine with legitimate

636
00:27:22,880 --> 00:27:24,120
credentials.

637
00:27:24,120 --> 00:27:28,960
Private networking and controlled egress through naty, firewalls or inspected routes turn

638
00:27:28,960 --> 00:27:34,160
outbound traffic into something you can actually reason about, not perfectly, but architecturally.

639
00:27:34,160 --> 00:27:36,160
And when you do that, you change the operating model.

640
00:27:36,160 --> 00:27:40,320
You stop treating the execution tier like a hobby project and start treating it like production

641
00:27:40,320 --> 00:27:45,560
infrastructure, governed ingress, governed egress, named dependencies, and no surprise internet

642
00:27:45,560 --> 00:27:47,240
reachability.

643
00:27:47,240 --> 00:27:49,840
The business benefit isn't security theater.

644
00:27:49,840 --> 00:27:50,840
It's stability.

645
00:27:50,840 --> 00:27:54,160
When an endpoint is private, random internet noise doesn't hit it.

646
00:27:54,160 --> 00:27:55,760
Scanners don't find it.

647
00:27:55,760 --> 00:27:58,040
Opportunistic probing doesn't become your baseline.

648
00:27:58,040 --> 00:28:02,080
You reduce the ambient attack traffic that everyone pretends doesn't matter until it does.

649
00:28:02,080 --> 00:28:03,760
So the network boundary is simple.

650
00:28:03,760 --> 00:28:06,680
The Python execution tier lives behind private endpoint.

651
00:28:06,680 --> 00:28:10,920
If something needs to reach it, it does so through a designed path, not a public exception.

652
00:28:10,920 --> 00:28:13,760
And once you accept that, the next boundary becomes obvious.

653
00:28:13,760 --> 00:28:17,080
Network controls are pointless if the data path is still unmanaged.

654
00:28:17,080 --> 00:28:18,880
So now you define the data boundary.

655
00:28:18,880 --> 00:28:22,560
Dataverse stays inside the platform and you stop pretending direct external access is

656
00:28:22,560 --> 00:28:24,440
just integration.

657
00:28:24,440 --> 00:28:25,440
Data boundary.

658
00:28:25,440 --> 00:28:28,280
Dataverse never directly exposed externally.

659
00:28:28,280 --> 00:28:32,520
Dataverse is where organizations accidentally confuse accessible with governed.

660
00:28:32,520 --> 00:28:36,800
Because dataverse feels like a database, it has tables, rows, relationships, and an API

661
00:28:36,800 --> 00:28:37,800
surface.

662
00:28:37,800 --> 00:28:39,120
So the instinct is obvious.

663
00:28:39,120 --> 00:28:42,640
If Python needs data, just let Python talk to dataverse directly.

664
00:28:42,640 --> 00:28:44,840
Over the API, get the job done.

665
00:28:44,840 --> 00:28:47,560
That instinct is how the data boundary collapses.

666
00:28:47,560 --> 00:28:51,680
In the hybrid mandate, dataverse is the governed system of record that stays behind

667
00:28:51,680 --> 00:28:52,680
the platform.

668
00:28:52,680 --> 00:28:56,080
It is not an integration playground for every script that wants a shortcut.

669
00:28:56,080 --> 00:28:57,280
The reason isn't moral.

670
00:28:57,280 --> 00:28:58,280
It's mechanical.

671
00:28:58,280 --> 00:29:02,440
The moment you expose dataverse directly to external execution runtimes, you multiply

672
00:29:02,440 --> 00:29:07,200
the number of identities, endpoints, and permission models that can touch your core business data.

673
00:29:07,200 --> 00:29:08,520
You're not integrating.

674
00:29:08,520 --> 00:29:10,320
You're expanding the blast radius.

675
00:29:10,320 --> 00:29:14,280
And blast radius is what audits actually measure even when they pretend they're asking

676
00:29:14,280 --> 00:29:15,280
about policies.

677
00:29:15,280 --> 00:29:16,400
So the rule is simple.

678
00:29:16,400 --> 00:29:20,680
The execution tier talks to a controlled interface, not to dataverse endpoints.

679
00:29:20,680 --> 00:29:23,440
Power platform orchestrates access to dataverse.

680
00:29:23,440 --> 00:29:26,400
And as your governs the interface that crosses tiers.

681
00:29:26,400 --> 00:29:30,520
That means the Python tier should not be running an SDK that casually reads whole tables

682
00:29:30,520 --> 00:29:31,520
because it can.

683
00:29:31,520 --> 00:29:35,240
It should be calling a service contract that gives it only the minimum payload needed to

684
00:29:35,240 --> 00:29:36,240
compute.

685
00:29:36,240 --> 00:29:40,440
If the execution tier needs 12 fields, it gets 12 fields, not the record, not the table,

686
00:29:40,440 --> 00:29:42,800
not a just in case dump because it was convenient.

687
00:29:42,800 --> 00:29:43,800
This might seem restrictive.

688
00:29:43,800 --> 00:29:44,800
It's not.

689
00:29:44,800 --> 00:29:48,640
It's what keeps your tenant from turning into an accidental data lake full of copies, caches,

690
00:29:48,640 --> 00:29:50,280
and half deleted exports.

691
00:29:50,280 --> 00:29:52,000
Think in terms of the authorization graph.

692
00:29:52,000 --> 00:29:56,280
When a flow reads dataverse, it does so through a governed identity and an environment

693
00:29:56,280 --> 00:29:58,680
boundary that your platform team can see.

694
00:29:58,680 --> 00:30:02,760
When a random script reads dataverse, you've shifted access into places you don't inventory

695
00:30:02,760 --> 00:30:08,280
well, developer machines, notebooks, build agents, containers, and service principles that

696
00:30:08,280 --> 00:30:11,560
end up overprivileged because it kept failing.

697
00:30:11,560 --> 00:30:13,200
That is the erosion path.

698
00:30:13,200 --> 00:30:14,520
Complexity creates friction.

699
00:30:14,520 --> 00:30:17,800
Friction creates privilege escalation and privilege escalation becomes permanent.

700
00:30:17,800 --> 00:30:19,760
The clean pattern is mediated access.

701
00:30:19,760 --> 00:30:23,760
Power automate or a governed API tier reads from dataverse and sends a narrow payload to

702
00:30:23,760 --> 00:30:25,400
Python for execution.

703
00:30:25,400 --> 00:30:29,400
Then returns a narrow result, normalized data, validation outcomes, dedupe decisions,

704
00:30:29,400 --> 00:30:33,000
inference scores, whatever the use case requires, then the orchestration tier commits back to

705
00:30:33,000 --> 00:30:35,200
dataverse through its governed pathways.

706
00:30:35,200 --> 00:30:36,520
This is not about performance.

707
00:30:36,520 --> 00:30:39,600
It's about control because commit is where damage happens.

708
00:30:39,600 --> 00:30:44,360
Reads can leak, rights can corrupt, so the architecture has to make rights boring, predictable

709
00:30:44,360 --> 00:30:45,720
and policy bound.

710
00:30:45,720 --> 00:30:50,600
Dataverse rights should happen through patterns you can audit, throttle, and correlate.

711
00:30:50,600 --> 00:30:53,280
Not through scripts that mostly follow the rules.

712
00:30:53,280 --> 00:30:57,480
The other part of the data boundary is DLP alignment, and this is where most organizations

713
00:30:57,480 --> 00:31:00,160
get exposed while thinking they're protected.

714
00:31:00,160 --> 00:31:03,400
DLP policies and power platform can restrict connector pairings.

715
00:31:03,400 --> 00:31:07,160
They can prevent dataverse data from flowing into consumer connectors.

716
00:31:07,160 --> 00:31:11,520
But if you let Python pull data directly from dataverse, then DLP becomes irrelevant.

717
00:31:11,520 --> 00:31:15,160
The data has already crossed the boundary into an uncontrolled runtime.

718
00:31:15,160 --> 00:31:16,560
From there it can go anywhere.

719
00:31:16,560 --> 00:31:21,000
Files, outbound APIs, third-party libraries, random storage accounts, or temporary folders

720
00:31:21,000 --> 00:31:22,960
that somehow last for years.

721
00:31:22,960 --> 00:31:26,480
So if you care about DLP, you have to care about where the data exits.

722
00:31:26,480 --> 00:31:30,160
The execution tier must receive only what it needs, and it must operate inside the same

723
00:31:30,160 --> 00:31:33,680
governance story, identity, network containment, and logged egress.

724
00:31:33,680 --> 00:31:37,400
Otherwise DLP is theater applied after the data already left.

725
00:31:37,400 --> 00:31:41,800
And yes, there are legitimate cases where pro developers will say, "But the dataverse SDK

726
00:31:41,800 --> 00:31:43,320
for Python exists."

727
00:31:43,320 --> 00:31:46,520
Or, "We can use service to service authentication."

728
00:31:46,520 --> 00:31:48,920
Or "We can lock it down with least privilege."

729
00:31:48,920 --> 00:31:49,920
Those are tools.

730
00:31:49,920 --> 00:31:50,920
They are not a boundary.

731
00:31:50,920 --> 00:31:55,000
Then architectural rule that survives staff turnover, project pressure, and the next urgent

732
00:31:55,000 --> 00:31:56,000
request.

733
00:31:56,000 --> 00:31:59,320
If your boundary depends on every engineer remembering to do least privilege perfectly,

734
00:31:59,320 --> 00:32:00,320
you don't have a boundary.

735
00:32:00,320 --> 00:32:01,400
You have good intentions.

736
00:32:01,400 --> 00:32:04,640
The hybrid mandate treats dataverse like a protected core.

737
00:32:04,640 --> 00:32:06,840
It stays inside the power platform trust envelope.

738
00:32:06,840 --> 00:32:10,780
Everything outside talks to it through explicit contracts that you can version, validate,

739
00:32:10,780 --> 00:32:11,780
and monitor.

740
00:32:11,780 --> 00:32:15,480
That reduces the number of paths into your system of record, and it keeps your data movement

741
00:32:15,480 --> 00:32:17,920
explainable when the inevitable question arrives.

742
00:32:17,920 --> 00:32:19,320
Where does this data go?

743
00:32:19,320 --> 00:32:22,800
So the data boundary is the line that keeps your system coherent.

744
00:32:22,800 --> 00:32:26,000
Dataverse never becomes a public dependency of the execution tier.

745
00:32:26,000 --> 00:32:28,560
Once you accept that the next boundary becomes unavoidable.

746
00:32:28,560 --> 00:32:32,680
If Python talks to a controlled interface, then that interface has to be managed as an

747
00:32:32,680 --> 00:32:35,240
API, not as an ad hoc endpoint.

748
00:32:35,240 --> 00:32:37,400
And that's where the API boundary enters.

749
00:32:37,400 --> 00:32:40,960
The function gets wrapped in API management, whether you like it or not.

750
00:32:40,960 --> 00:32:45,000
API, boundary, function wrapped in API management.

751
00:32:45,000 --> 00:32:49,120
Once dataverse stops being directly reachable, you need an interface that can carry the

752
00:32:49,120 --> 00:32:50,680
load of that decision.

753
00:32:50,680 --> 00:32:53,840
And this is where most hybrid designs quietly collapse.

754
00:32:53,840 --> 00:32:58,600
They treat the Azure function like an API product, but they operate it like a shared script.

755
00:32:58,600 --> 00:33:02,880
API management exists to stop that drift, not because APM is magical, it isn't.

756
00:33:02,880 --> 00:33:07,520
It's because enterprises need a place where policy becomes enforceable, consistently, across

757
00:33:07,520 --> 00:33:08,640
every caller.

758
00:33:08,640 --> 00:33:13,600
If you let every flow call the function endpoint directly, you get the same outcome you always

759
00:33:13,600 --> 00:33:15,400
get in power platform.

760
00:33:15,400 --> 00:33:17,360
Hundreds of slightly different clients.

761
00:33:17,360 --> 00:33:22,160
Each with their own expectations, timeouts, payload quirks and retry behaviors.

762
00:33:22,160 --> 00:33:25,240
And then your service isn't a service, it's a compatibility hostage.

763
00:33:25,240 --> 00:33:27,120
APM is the contract boundary.

764
00:33:27,120 --> 00:33:30,160
It's where the execution tier becomes an actual product.

765
00:33:30,160 --> 00:33:34,200
Versioned endpoints, documented schemers, predictable arrow shapes and policies that don't depend

766
00:33:34,200 --> 00:33:36,240
on every maker remembering the rules.

767
00:33:36,240 --> 00:33:39,880
It's also where you centralize throttling and abuse control, so the orchestration tier

768
00:33:39,880 --> 00:33:45,000
can scale without deducing your own back end by accident, because that happens.

769
00:33:45,000 --> 00:33:49,000
Your automated retries are not polite, parallel branches are not polite, applied to each with

770
00:33:49,000 --> 00:33:51,160
concurrency turned on is definitely not polite.

771
00:33:51,160 --> 00:33:54,840
So if you don't have a choke point, your execution tier will get load patterns that feel

772
00:33:54,840 --> 00:33:59,320
like an attack, except they're coming from your own tenant with legitimate permissions.

773
00:33:59,320 --> 00:34:01,600
APM gives you a place to enforce.

774
00:34:01,600 --> 00:34:07,640
Rate limits, quotas, header requirements, payload size and response timeouts, not as guidance

775
00:34:07,640 --> 00:34:08,800
as reality.

776
00:34:08,800 --> 00:34:11,800
It also gives you a place to enforce authentication consistently.

777
00:34:11,800 --> 00:34:15,960
You already decided cross tier calls must use enter based workload identity.

778
00:34:15,960 --> 00:34:18,880
APM lets you make that the default behavior.

779
00:34:18,880 --> 00:34:23,800
Validate tokens, validate audiences, reject unknown issuers, reject missing scopes and

780
00:34:23,800 --> 00:34:26,660
do it before the request ever reaches your function runtime.

781
00:34:26,660 --> 00:34:29,240
That distinction matters because functions are execution.

782
00:34:29,240 --> 00:34:33,840
They should spend CPU doing compute, not doing gatekeeping for every caller variation.

783
00:34:33,840 --> 00:34:35,200
You forgot to constrain.

784
00:34:35,200 --> 00:34:38,760
Then there's the part everyone ignores until the platform turns on them.

785
00:34:38,760 --> 00:34:44,240
Flows live for years, makers copy them, departments fork them, someone saves a template in a team

786
00:34:44,240 --> 00:34:46,800
and suddenly you have 50 clones calling your endpoint.

787
00:34:46,800 --> 00:34:50,600
If your function API changes shape and you don't have a versioning story, you'll break

788
00:34:50,600 --> 00:34:53,000
production in places you can't even find.

789
00:34:53,000 --> 00:34:55,560
APM is where you make versioning boring.

790
00:34:55,560 --> 00:34:57,360
V1 does what it always did.

791
00:34:57,360 --> 00:34:59,400
V2 exists when you need to evolve.

792
00:34:59,400 --> 00:35:03,080
Deprecation becomes a planned life cycle event, not a surprise outage.

793
00:35:03,080 --> 00:35:07,120
And you can instrument which clients still hit V1 so you can actually drive retirement.

794
00:35:07,120 --> 00:35:09,120
That's governance, not policy documents.

795
00:35:09,120 --> 00:35:13,840
APM also forces contract discipline which is the opposite of just past JSON.

796
00:35:13,840 --> 00:35:16,760
The execution tier should reject ambiguous payloads.

797
00:35:16,760 --> 00:35:18,640
If a field is required, it's required.

798
00:35:18,640 --> 00:35:20,600
If a schema changed, the caller needs to know.

799
00:35:20,600 --> 00:35:22,600
If an array is too large, fail fast.

800
00:35:22,600 --> 00:35:24,160
If a content type is wrong, reject it.

801
00:35:24,160 --> 00:35:28,800
The point is to stop garbage in from becoming mysterious behavior that you debug at 3am

802
00:35:28,800 --> 00:35:31,560
power platform teams hate this at first because it feels strict.

803
00:35:31,560 --> 00:35:32,760
It's supposed to.

804
00:35:32,760 --> 00:35:35,200
Strict contracts are what keep orchestration maintainable.

805
00:35:35,200 --> 00:35:39,400
When flows can send anything and the backend tries to guess intent, you're back to probabilistic

806
00:35:39,400 --> 00:35:40,400
behavior.

807
00:35:40,400 --> 00:35:42,160
You're back to exceptions being architecture.

808
00:35:42,160 --> 00:35:46,640
APM lets you enforce validation and normalization at the boundary so both sides stay honest.

809
00:35:46,640 --> 00:35:51,440
And yes, APM is also the right place for transformation policies when you absolutely need them.

810
00:35:51,440 --> 00:35:57,120
Rewrite headers, map fields, standardize error responses, even do lightweight filtering.

811
00:35:57,120 --> 00:35:58,120
The rule is simple.

812
00:35:58,120 --> 00:36:00,520
APM enforces interface behavior.

813
00:36:00,520 --> 00:36:02,720
Python executes business compute.

814
00:36:02,720 --> 00:36:05,440
It rebuilds your service logic and policies.

815
00:36:05,440 --> 00:36:07,680
That's just moving the mess to a different UI.

816
00:36:07,680 --> 00:36:12,920
Finally, APM is how you stop every flow from becoming an API client with custom quirks.

817
00:36:12,920 --> 00:36:15,480
Without APM, makers will do what makers always do.

818
00:36:15,480 --> 00:36:21,280
Set weird timeouts, ignore error codes, pass text, and implement retry logic by adding delays.

819
00:36:21,280 --> 00:36:23,400
With APM, you can publish a single standard.

820
00:36:23,400 --> 00:36:24,400
This is the endpoint.

821
00:36:24,400 --> 00:36:25,400
This is the contract.

822
00:36:25,400 --> 00:36:26,400
This is the auth.

823
00:36:26,400 --> 00:36:27,400
This is the expected response.

824
00:36:27,400 --> 00:36:28,400
This is the error model.

825
00:36:28,400 --> 00:36:30,080
And this is the throttling behavior.

826
00:36:30,080 --> 00:36:32,520
Now your hybrid mandate looks like a system.

827
00:36:32,520 --> 00:36:33,680
It's not a diagram.

828
00:36:33,680 --> 00:36:34,960
And this is the actual payoff.

829
00:36:34,960 --> 00:36:39,720
APM becomes the layer that lets the back and evolve while orchestration stays stable.

830
00:36:39,720 --> 00:36:44,000
Engineers can patch dependencies, optimize compute or refactor internal modules without asking

831
00:36:44,000 --> 00:36:46,560
makers to rewrite business process logic.

832
00:36:46,560 --> 00:36:47,560
The contract holds.

833
00:36:47,560 --> 00:36:49,480
The orchestration tier keeps coordinating.

834
00:36:49,480 --> 00:36:51,360
The execution tier keeps executing.

835
00:36:51,360 --> 00:36:54,680
But none of this matters if you can't see failures across tiers.

836
00:36:54,680 --> 00:36:55,840
Governance doesn't fail loudly.

837
00:36:55,840 --> 00:36:57,640
It fails silently.

838
00:36:57,640 --> 00:37:01,800
So the next boundary is the one that makes incident response deterministic.

839
00:37:01,800 --> 00:37:05,800
A system is a system that's related logging across power platform, APM, and the execution

840
00:37:05,800 --> 00:37:07,520
tier.

841
00:37:07,520 --> 00:37:10,280
Logging boundary correlates logs across systems.

842
00:37:10,280 --> 00:37:14,520
Logging is where a hybrid architecture stops being a diagram and starts being debuggable.

843
00:37:14,520 --> 00:37:18,720
And most organizations fail here in the most predictable way possible.

844
00:37:18,720 --> 00:37:23,160
Every system logs its own truth in its own format with its own identifiers, and then the

845
00:37:23,160 --> 00:37:26,320
incident bridge call becomes a group project in interpretation.

846
00:37:26,320 --> 00:37:27,840
Power automate has run history.

847
00:37:27,840 --> 00:37:32,320
The PM has request logs, Azure Functions has application inside traces.

848
00:37:32,320 --> 00:37:34,560
Dataverse has audit logs and its own activity telemetry.

849
00:37:34,560 --> 00:37:36,880
They all tell a story and they just don't tell the same story.

850
00:37:36,880 --> 00:37:39,640
So when a flow run fails, you get the comfortable lie.

851
00:37:39,640 --> 00:37:42,280
The flow failed at the HTTP action.

852
00:37:42,280 --> 00:37:43,280
That's not a root cause.

853
00:37:43,280 --> 00:37:44,360
That's a location marker.

854
00:37:44,360 --> 00:37:49,440
The real question is whether the request hit APM, whether APM rejected it on policy, whether

855
00:37:49,440 --> 00:37:53,360
the function code through an exception, whether the function timed out, whether the downstream

856
00:37:53,360 --> 00:37:58,200
call to dataverse was denied, and whether retries created duplicates that are now someone

857
00:37:58,200 --> 00:37:59,680
else's problem.

858
00:37:59,680 --> 00:38:03,240
If you can't answer those questions in minutes, you don't have observability.

859
00:38:03,240 --> 00:38:04,640
You have distributed journaling.

860
00:38:04,640 --> 00:38:07,720
The logging boundary in the hybrid mandate is explicit.

861
00:38:07,720 --> 00:38:13,160
One correlation ID must cross the orchestration tier, the API boundary, the execution tier,

862
00:38:13,160 --> 00:38:15,040
and any governed data operations.

863
00:38:15,040 --> 00:38:19,240
The same identifier shows up everywhere, every time, no exceptions.

864
00:38:19,240 --> 00:38:22,400
Because once you have that, incident response becomes deterministic.

865
00:38:22,400 --> 00:38:27,280
You stop guessing, you stop screenshot archaeology, you stop fighting over whose logs are right.

866
00:38:27,280 --> 00:38:31,520
You query the correlation ID and you see the end-to-end path of the transaction.

867
00:38:31,520 --> 00:38:35,160
So how does this work mechanically without turning into an implementation lecture?

868
00:38:35,160 --> 00:38:36,200
Start in power automate.

869
00:38:36,200 --> 00:38:41,000
The flow generates a correlation ID at the start of the run, not at the HTTP step, not inside

870
00:38:41,000 --> 00:38:42,000
Python.

871
00:38:42,000 --> 00:38:45,920
At the first moment, the orchestration tier decides, work is happening.

872
00:38:45,920 --> 00:38:50,520
That correlation ID becomes a variable and it gets attached to every cross tier call.

873
00:38:50,520 --> 00:38:55,080
First headers, payload fields were appropriate, and any status records you write to data

874
00:38:55,080 --> 00:38:56,080
verse.

875
00:38:56,080 --> 00:38:57,560
Then APIM enforces it.

876
00:38:57,560 --> 00:39:00,840
If the correlation ID header isn't present, reject the request.

877
00:39:00,840 --> 00:39:03,720
This is the part that feels rude until you remember what you're buying.

878
00:39:03,720 --> 00:39:06,320
The ability to operate at scale without ambiguity.

879
00:39:06,320 --> 00:39:10,360
APM can also generate one if you want, but the stronger pattern is to let orchestration

880
00:39:10,360 --> 00:39:13,120
originated so it maps cleanly to a flow run.

881
00:39:13,120 --> 00:39:17,760
Next as Zuer functions, the function code reads the correlation ID, sets it as the operation

882
00:39:17,760 --> 00:39:22,200
ID in its telemetry context and logs every meaningful step against it.

883
00:39:22,200 --> 00:39:26,400
Validation outcomes, branch decisions, downstream calls, and exceptions.

884
00:39:26,400 --> 00:39:27,880
Not verbose debug noise.

885
00:39:27,880 --> 00:39:29,840
Evidence then, downstream data operations.

886
00:39:29,840 --> 00:39:33,400
If the function writes to data verse through a governed interface, that interface must also

887
00:39:33,400 --> 00:39:35,280
log with the same correlation ID.

888
00:39:35,280 --> 00:39:39,880
If the orchestration tier writes to data verse directly, it writes the correlation ID into

889
00:39:39,880 --> 00:39:43,440
the record or an associated tracking table so you can type business data to technical

890
00:39:43,440 --> 00:39:44,600
execution.

891
00:39:44,600 --> 00:39:47,760
Now you can answer questions that otherwise turn into weak long post mortems.

892
00:39:47,760 --> 00:39:51,400
How long did the request spend in power automate before it left the orchestration tier?

893
00:39:51,400 --> 00:39:52,400
Did APM throttle it?

894
00:39:52,400 --> 00:39:53,920
And if so, how many times?

895
00:39:53,920 --> 00:39:57,360
Did the function execute or did it fail authentication at the edge?

896
00:39:57,360 --> 00:40:01,080
Did the function time out or did it throw a handled validation error?

897
00:40:01,080 --> 00:40:04,120
Did retries occur and were they idempotent or duplicative?

898
00:40:04,120 --> 00:40:05,200
How large was the payload?

899
00:40:05,200 --> 00:40:07,880
And did it cross the thresholds that trigger platform limits?

900
00:40:07,880 --> 00:40:11,120
Did permission denials occur and which principle got denied?

901
00:40:11,120 --> 00:40:16,200
Those are the metrics that matter, latency, failures, retries, payload size, throttle events,

902
00:40:16,200 --> 00:40:17,680
and authorization denials.

903
00:40:17,680 --> 00:40:21,200
Not because dashboards are fun, but because these signals tell you where architectural erosion

904
00:40:21,200 --> 00:40:22,200
is happening.

905
00:40:22,200 --> 00:40:24,840
And yes, you should push these logs somewhere central.

906
00:40:24,840 --> 00:40:27,360
Azure monitor and application insights are fine.

907
00:40:27,360 --> 00:40:31,120
Sentinel becomes relevant once you care about threat detection and not just troubleshooting.

908
00:40:31,120 --> 00:40:33,800
The product choice matters less than the behavior.

909
00:40:33,800 --> 00:40:38,560
Centralize, correlate, retain, and make it searchable by correlation ID and principle.

910
00:40:38,560 --> 00:40:42,840
This is also where you learn the uncomfortable truth about low-code observability.

911
00:40:42,840 --> 00:40:44,720
Power automate will show you what it did.

912
00:40:44,720 --> 00:40:47,520
It will not show you what your execution tier did.

913
00:40:47,520 --> 00:40:49,200
Your execution tier will show you what it did.

914
00:40:49,200 --> 00:40:51,640
It will not show you what power automate assumed.

915
00:40:51,640 --> 00:40:54,800
Without correlation, those gaps fill with human narratives.

916
00:40:54,800 --> 00:40:56,720
With correlation, they fill with data.

917
00:40:56,720 --> 00:41:00,840
And once you have correlated logs, the rest of the hybrid mandate becomes enforceable.

918
00:41:00,840 --> 00:41:02,160
You can prove boundaries exist.

919
00:41:02,160 --> 00:41:03,880
You can prove calls or authenticated.

920
00:41:03,880 --> 00:41:05,760
You can prove data didn't move where it shouldn't.

921
00:41:05,760 --> 00:41:08,000
You can prove which tier failed and why.

922
00:41:08,000 --> 00:41:10,440
That's what governance looks like when it's real.

923
00:41:10,440 --> 00:41:12,160
Evidence, not confidence.

924
00:41:12,160 --> 00:41:17,320
Now you can finally define the workflow end to end as a system, not a collection of parts.

925
00:41:17,320 --> 00:41:22,120
Conceptual flow, event, reasoning, orchestration, execution, commit.

926
00:41:22,120 --> 00:41:27,120
Now stitch the tiers together into a single conceptual flow, not because architects love diagrams,

927
00:41:27,120 --> 00:41:31,560
because your incident response team needs a mental model that survives pressure.

928
00:41:31,560 --> 00:41:32,840
The sequence is simple.

929
00:41:32,840 --> 00:41:37,040
Event reasoning, orchestration, execution, commit, five verbs, five places where intent

930
00:41:37,040 --> 00:41:39,040
can drift if you don't pin it down.

931
00:41:39,040 --> 00:41:40,440
Start with the event.

932
00:41:40,440 --> 00:41:43,360
The event is the moment reality changes and the system notices.

933
00:41:43,360 --> 00:41:48,400
It can be an email arriving with an attachment, a team's message, a form submission, a

934
00:41:48,400 --> 00:41:53,120
dataverse row being created or modified, a scheduled trigger that checks for late invoices,

935
00:41:53,120 --> 00:41:55,760
expiring contracts or missing compliance attestations.

936
00:41:55,760 --> 00:41:58,800
The event source doesn't matter what matters is that events are noisy.

937
00:41:58,800 --> 00:42:01,960
They contain malformed inputs, duplicates and human chaos.

938
00:42:01,960 --> 00:42:04,480
So the event isn't where you do the work.

939
00:42:04,480 --> 00:42:06,600
It's where you decide whether work should happen.

940
00:42:06,600 --> 00:42:08,320
The decision is the reasoning layer.

941
00:42:08,320 --> 00:42:09,600
Reesoning is not execution.

942
00:42:09,600 --> 00:42:11,400
Reesoning is classification and rooting.

943
00:42:11,400 --> 00:42:14,560
It answers, is this request valid enough to process?

944
00:42:14,560 --> 00:42:15,920
And who owns the outcome?

945
00:42:15,920 --> 00:42:17,000
Do we need an approval?

946
00:42:17,000 --> 00:42:18,600
Which environment does this belong in?

947
00:42:18,600 --> 00:42:20,440
What category of work is this?

948
00:42:20,440 --> 00:42:24,360
Simple automation, high-risk change or something that requires escalation?

949
00:42:24,360 --> 00:42:26,760
This is where power platform earns its keep.

950
00:42:26,760 --> 00:42:29,480
A flow can apply cheap logic fast.

951
00:42:29,480 --> 00:42:32,000
Subject line filters, sender allow lists.

952
00:42:32,000 --> 00:42:33,160
Basic field validation.

953
00:42:33,160 --> 00:42:38,240
A backup of a business owner in dataverse, rooting to an approval or branching based on process state.

954
00:42:38,240 --> 00:42:39,400
Keep it readable.

955
00:42:39,400 --> 00:42:40,720
Keep it explainable.

956
00:42:40,720 --> 00:42:44,560
When a compliance lead asks why a record went down a particular path, the answer should

957
00:42:44,560 --> 00:42:47,160
be visible without reverse engineering Python.

958
00:42:47,160 --> 00:42:51,880
Then comes orchestration, which is the part most people confuse with execution.

959
00:42:51,880 --> 00:42:53,240
Orchestration is coordination over time.

960
00:42:53,240 --> 00:42:54,440
It creates a tracking record.

961
00:42:54,440 --> 00:42:55,720
It sets a correlation ID.

962
00:42:55,720 --> 00:42:57,680
It moves a process from state A to state B.

963
00:42:57,680 --> 00:42:58,680
It informs humans.

964
00:42:58,680 --> 00:42:59,680
It waits.

965
00:42:59,680 --> 00:43:00,960
It retries intentionally.

966
00:43:00,960 --> 00:43:03,120
It escalates when the process sits too long.

967
00:43:03,120 --> 00:43:07,480
It records that something happened even if the execution tier is currently unhealthy.

968
00:43:07,480 --> 00:43:11,200
This is where you enforce that the workflow is the system of record for the process, not

969
00:43:11,200 --> 00:43:13,520
the sidecar script and not the API logs.

970
00:43:13,520 --> 00:43:17,960
If the execution tier dies, the orchestration tier should still know what it asked for, when

971
00:43:17,960 --> 00:43:19,800
it asked for it, and what it's waiting on.

972
00:43:19,800 --> 00:43:21,960
Now you cross the tier boundary into execution.

973
00:43:21,960 --> 00:43:25,480
The execution is where Python runs, but the key is that it runs as a service call, not

974
00:43:25,480 --> 00:43:26,640
as a vibe.

975
00:43:26,640 --> 00:43:31,160
The orchestration tier sends a bounded payload with a correlation ID and a contract version.

976
00:43:31,160 --> 00:43:35,960
The execution tier validates the payload, performs deterministic compute, and returns structured

977
00:43:35,960 --> 00:43:40,680
results, success, failure type, output data, and peritom errors when it's batch work.

978
00:43:40,680 --> 00:43:42,600
This is also where identity belongs.

979
00:43:42,600 --> 00:43:46,640
The execution tier should treat the correlation ID as a first class input, so retries don't

980
00:43:46,640 --> 00:43:47,840
create duplicates.

981
00:43:47,840 --> 00:43:51,080
If you rerun the same request, you should get the same outcome.

982
00:43:51,080 --> 00:43:53,520
Or a clean, already processed response.

983
00:43:53,520 --> 00:43:55,920
Anything else becomes retry storms and data drift.

984
00:43:55,920 --> 00:43:57,240
Then you get to commit.

985
00:43:57,240 --> 00:44:00,120
Commit is where enterprise data changes, and it has to be boring.

986
00:44:00,120 --> 00:44:04,720
It means writes back to dataverse or other systems of record through governed endpoints,

987
00:44:04,720 --> 00:44:07,680
with strict permissions and logged outcomes.

988
00:44:07,680 --> 00:44:10,400
Commit isn't Python writes wherever it wants.

989
00:44:10,400 --> 00:44:13,360
Commit is the system records results through known paths.

990
00:44:13,360 --> 00:44:16,960
Sometimes the orchestration tier does the commit using native connectors and environment bound

991
00:44:16,960 --> 00:44:17,960
permissions.

992
00:44:17,960 --> 00:44:20,640
Sometimes the execution tier commits through an API boundary.

993
00:44:20,640 --> 00:44:22,920
Either way, the rule stays intact.

994
00:44:22,920 --> 00:44:27,360
Rites happen through interfaces that are authenticated, authorized, and observable.

995
00:44:27,360 --> 00:44:30,560
And finally, the orchestration tier closes the loop.

996
00:44:30,560 --> 00:44:35,440
It updates status, notifies stakeholders, and stores the execution output in a form the

997
00:44:35,440 --> 00:44:36,960
business can actually consume.

998
00:44:36,960 --> 00:44:39,440
Not raw logs, not screenshots, data.

999
00:44:39,440 --> 00:44:40,520
So that's the sequence.

1000
00:44:40,520 --> 00:44:42,600
Event decides that something might matter.

1001
00:44:42,600 --> 00:44:44,640
Reasoning decides what should happen.

1002
00:44:44,640 --> 00:44:46,320
Orchestration manages the process.

1003
00:44:46,320 --> 00:44:50,720
Execution does deterministic compute, and commit records the outcome safely.

1004
00:44:50,720 --> 00:44:53,920
If you can't name which tier owns each verb, you don't have a hybrid model.

1005
00:44:53,920 --> 00:44:55,280
You have cross-system guesswork.

1006
00:44:55,280 --> 00:44:58,720
Now make it tangible because abstract models don't survive first contact with spreadsheets

1007
00:44:58,720 --> 00:45:00,480
and bulk updates.

1008
00:45:00,480 --> 00:45:04,920
Scenario one, CSV, Excel processing without flow spaghetti.

1009
00:45:04,920 --> 00:45:09,280
This is the scenario that shows up in every enterprise, regardless of how digital the strategy

1010
00:45:09,280 --> 00:45:10,280
deck looks.

1011
00:45:10,280 --> 00:45:15,280
Spreadsheets, finance lives in Excel, operations lives in CSV exports, vendor send reports that

1012
00:45:15,280 --> 00:45:18,720
are really just semi-structured guesses with a header row.

1013
00:45:18,720 --> 00:45:23,320
And somehow all of that becomes a business process with deadlines and consequences.

1014
00:45:23,320 --> 00:45:25,240
For automate can process these files.

1015
00:45:25,240 --> 00:45:27,080
Of course it can, that's not the debate.

1016
00:45:27,080 --> 00:45:30,840
The debate is what happens after the third small tweak and the fifth edge case and the

1017
00:45:30,840 --> 00:45:32,160
tenth file format.

1018
00:45:32,160 --> 00:45:33,880
That's basically the same.

1019
00:45:33,880 --> 00:45:35,360
This is where flows become spaghetti.

1020
00:45:35,360 --> 00:45:38,560
A maker builds a flow that triggers when a file lands in SharePoint.

1021
00:45:38,560 --> 00:45:40,040
It lists rows in a table.

1022
00:45:40,040 --> 00:45:41,040
It loops.

1023
00:45:41,040 --> 00:45:42,040
It transforms text.

1024
00:45:42,040 --> 00:45:43,040
It converts date formats.

1025
00:45:43,040 --> 00:45:45,200
It tries to handle commas inside quoted fields.

1026
00:45:45,200 --> 00:45:47,520
It tries to ignore blank rows that aren't blank.

1027
00:45:47,520 --> 00:45:50,960
It tries to detect the total line someone left at the bottom.

1028
00:45:50,960 --> 00:45:54,840
Then it tries to build error handling inside scopes because a single bad row shouldn't fail

1029
00:45:54,840 --> 00:45:55,840
the whole run.

1030
00:45:55,840 --> 00:45:59,400
Then someone asks for a summary email with the rows that failed.

1031
00:45:59,400 --> 00:46:03,720
And now you're building an error report generator inside an orchestration engine.

1032
00:46:03,720 --> 00:46:06,640
This is how you end up with a flow that is both fragile and expensive.

1033
00:46:06,640 --> 00:46:08,880
So in the hybrid mandate, the split is clean.

1034
00:46:08,880 --> 00:46:12,880
The flow owns intake, permission checks, status tracking and notifications.

1035
00:46:12,880 --> 00:46:15,120
It does the work that's inherently orchestration.

1036
00:46:15,120 --> 00:46:16,520
A file arrived.

1037
00:46:16,520 --> 00:46:17,600
This is the owner.

1038
00:46:17,600 --> 00:46:19,200
This is the process state.

1039
00:46:19,200 --> 00:46:20,640
This needs approval.

1040
00:46:20,640 --> 00:46:22,280
Certify these people.

1041
00:46:22,280 --> 00:46:24,280
Record that we attempted processing.

1042
00:46:24,280 --> 00:46:26,520
Record that processing succeeded or failed.

1043
00:46:26,520 --> 00:46:31,880
The flow does not own passing, normalization, deduplication, validation, logic or report formatting.

1044
00:46:31,880 --> 00:46:33,640
Not because makers aren't capable.

1045
00:46:33,640 --> 00:46:37,720
Because that logic needs determinism and testability and those are not native properties of a flow

1046
00:46:37,720 --> 00:46:38,720
run history.

1047
00:46:38,720 --> 00:46:43,200
So the flow receives the file, stores it in a governed location and calls the execution

1048
00:46:43,200 --> 00:46:44,200
tier.

1049
00:46:44,200 --> 00:46:47,800
The execution tier Python as a service does what Python is good at.

1050
00:46:47,800 --> 00:46:51,120
It reads the CSV or Excel with a real library built for it.

1051
00:46:51,120 --> 00:46:53,760
It normalizes columns into a defined schema.

1052
00:46:53,760 --> 00:46:55,720
It handles date passing with strict rules.

1053
00:46:55,720 --> 00:46:58,920
It detects duplicates using keys you can explain in unit test.

1054
00:46:58,920 --> 00:47:04,280
It validates every row against an explicit rule set and returns results as structured data.

1055
00:47:04,280 --> 00:47:07,880
Accepted rows, rejected rows, and why each row failed.

1056
00:47:07,880 --> 00:47:10,080
And here's the part most organizations miss.

1057
00:47:10,080 --> 00:47:12,520
Errors come back as data, not as screenshots.

1058
00:47:12,520 --> 00:47:16,600
In the low-code only approach, a failure becomes an exception with a blob of context.

1059
00:47:16,600 --> 00:47:17,760
It's hard to aggregate.

1060
00:47:17,760 --> 00:47:21,840
In the hybrid approach, the Python service returns a result contract that looks boring

1061
00:47:21,840 --> 00:47:26,240
on purpose, counts, row-level status, a list of validation messages with line numbers

1062
00:47:26,240 --> 00:47:29,200
or record identifiers and a correlation ID.

1063
00:47:29,200 --> 00:47:30,920
Now the flow can do what it's actually good at.

1064
00:47:30,920 --> 00:47:32,440
It can root those errors.

1065
00:47:32,440 --> 00:47:37,560
If there are rejections, it can notify the submitter with a formatted summary and a link

1066
00:47:37,560 --> 00:47:41,400
to the rejected rows stored in a controlled table or file.

1067
00:47:41,400 --> 00:47:45,480
If the batch is clean, it can progress the process state and trigger downstream work.

1068
00:47:45,480 --> 00:47:49,760
If the batch fails validation beyond a threshold, it can escalate to a human review step.

1069
00:47:49,760 --> 00:47:54,000
And all of that remains readable in the flow because the heavy logic is not embedded in a

1070
00:47:54,000 --> 00:47:55,440
maze of expressions.

1071
00:47:55,440 --> 00:47:58,000
This is also where you stop leaking data through convenience.

1072
00:47:58,000 --> 00:48:02,960
If the execution tier is a governed service behind APIM authenticated via Entra, contained

1073
00:48:02,960 --> 00:48:07,840
by network boundaries and emitting correlated logs, then processing a spreadsheet no longer

1074
00:48:07,840 --> 00:48:11,720
means dump business data into random files and hope it's fine.

1075
00:48:11,720 --> 00:48:14,040
It means the file enters a controlled pipeline.

1076
00:48:14,040 --> 00:48:17,160
It can take execution and commit with evidence at every step.

1077
00:48:17,160 --> 00:48:20,520
And the output contract matters more than the implementation.

1078
00:48:20,520 --> 00:48:24,360
Because once you have a stable contract, you can evolve the Python logic without rewriting

1079
00:48:24,360 --> 00:48:25,360
the business process.

1080
00:48:25,360 --> 00:48:27,080
You can add a new column mapping.

1081
00:48:27,080 --> 00:48:28,760
You can update a validation rule.

1082
00:48:28,760 --> 00:48:30,040
You can patch a dependency.

1083
00:48:30,040 --> 00:48:31,360
You can optimize performance.

1084
00:48:31,360 --> 00:48:32,520
The flow doesn't change.

1085
00:48:32,520 --> 00:48:34,920
The process owners don't have to relearn the automation.

1086
00:48:34,920 --> 00:48:37,320
The orchestration tier stays stable while execution improves.

1087
00:48:37,320 --> 00:48:39,160
And that's the actual maturity move.

1088
00:48:39,160 --> 00:48:41,000
Not we used Python.

1089
00:48:41,000 --> 00:48:45,080
We separated orchestration from execution and we made the boundary explicit.

1090
00:48:45,080 --> 00:48:49,000
So the spreadsheet scenario stops being a maker right of passage and becomes a governed

1091
00:48:49,000 --> 00:48:53,080
service pattern, flow coordinates, Python computes, Azure enforces.

1092
00:48:53,080 --> 00:48:56,760
And once you've done it for CSV and Excel, the next failure mode becomes obvious.

1093
00:48:56,760 --> 00:48:58,000
It's not formatting.

1094
00:48:58,000 --> 00:49:00,240
It's scale.

1095
00:49:00,240 --> 00:49:04,560
bulk updates, throttles, partial success and the classic enterprise question.

1096
00:49:04,560 --> 00:49:08,200
Can we do 10,000 of these by tomorrow without corrupting our data?

1097
00:49:08,200 --> 00:49:12,360
Inario 2, bulk data verse updates with deterministic validation.

1098
00:49:12,360 --> 00:49:17,440
bulk updates are where power automates it worked in testing story usually ends.

1099
00:49:17,440 --> 00:49:19,360
Not because flows can't update data verse.

1100
00:49:19,360 --> 00:49:20,360
They can.

1101
00:49:20,360 --> 00:49:24,360
The failure happens when you move from dozens of records to thousands and the platform stops

1102
00:49:24,360 --> 00:49:29,640
behaving like a friendly assistant and starts behaving like a rate limited distributed system.

1103
00:49:29,640 --> 00:49:30,640
Because that's what it is.

1104
00:49:30,640 --> 00:49:32,720
The usual pattern is painfully consistent.

1105
00:49:32,720 --> 00:49:36,520
Someone builds a flow that queries data verse for a set of rows, then runs an apply to

1106
00:49:36,520 --> 00:49:41,400
each, then does an update a row action, maybe with concurrency turned on to speed it up and

1107
00:49:41,400 --> 00:49:45,080
then adds a couple scopes with run after conditions to catch failures.

1108
00:49:45,080 --> 00:49:46,600
It ships, it runs.

1109
00:49:46,600 --> 00:49:48,000
It even succeeds a few times.

1110
00:49:48,000 --> 00:49:53,280
Then scale shows up, throttling shows up, intermittent connector errors show up, runs timeout,

1111
00:49:53,280 --> 00:49:54,640
retries kick in.

1112
00:49:54,640 --> 00:49:58,640
Some updates succeed some fail and the flow has no coherent concept of rollback.

1113
00:49:58,640 --> 00:50:02,800
Now you have partial success with no deterministic replay strategy, which is another way of saying

1114
00:50:02,800 --> 00:50:04,880
you have data drift with a progress bar.

1115
00:50:04,880 --> 00:50:08,600
And the real damage isn't the failures, it's the ambiguity when someone asks which records

1116
00:50:08,600 --> 00:50:12,000
were updated, the answer turns into, well, mostly.

1117
00:50:12,000 --> 00:50:14,040
That's not acceptable in the system of record.

1118
00:50:14,040 --> 00:50:18,720
So the hybrid mandate treats bulk updates as an execution tier problem, not an orchestration

1119
00:50:18,720 --> 00:50:19,720
tier hobby.

1120
00:50:19,720 --> 00:50:21,120
Here's what stays in the flow.

1121
00:50:21,120 --> 00:50:25,880
In take of the request, approval if it's a high impact change, creation of a tracking record

1122
00:50:25,880 --> 00:50:28,760
in data verse and communication to stakeholders.

1123
00:50:28,760 --> 00:50:33,760
The flow also decides the boundary conditions, which table, which segment, which job type,

1124
00:50:33,760 --> 00:50:37,320
which correlation idea and what done means, the flow owns the business meaning.

1125
00:50:37,320 --> 00:50:41,160
Then it calls Python as a service and Python does two things that flows are structurally

1126
00:50:41,160 --> 00:50:44,560
bad at, deterministic validation and deterministic batching.

1127
00:50:44,560 --> 00:50:45,840
Start with validation.

1128
00:50:45,840 --> 00:50:50,160
Before Python writes anything, it validates the entire candidate set, not one row at a time,

1129
00:50:50,160 --> 00:50:54,880
mid loop, with half the records already committed, the execution tier pulls the necessary fields

1130
00:50:54,880 --> 00:51:00,080
through a controlled interface, applies strict rules and produces a validation report.

1131
00:51:00,080 --> 00:51:03,600
Counts, categories of failure and per record reasons.

1132
00:51:03,600 --> 00:51:07,360
This is where you stop confusing update logic with data quality.

1133
00:51:07,360 --> 00:51:11,440
If you're about to update 10,000 records, you don't want to discover on record 7,000 Simp

1134
00:51:11,440 --> 00:51:16,280
1.32 that the data violates a rule and now you're in the middle of a half applied change.

1135
00:51:16,280 --> 00:51:18,680
Validation first is what makes the operation reversible.

1136
00:51:18,680 --> 00:51:21,320
You can reject the whole job before it mutates reality.

1137
00:51:21,320 --> 00:51:22,320
Then batching.

1138
00:51:22,320 --> 00:51:24,320
Python controls how rights happen.

1139
00:51:24,320 --> 00:51:27,720
Chunk sizes back off retry strategy and id impotency keys.

1140
00:51:27,720 --> 00:51:33,000
It writes in batches you can reason about and it records outcomes per batch and per record.

1141
00:51:33,000 --> 00:51:35,840
If a batch fails, you know exactly which records were in it.

1142
00:51:35,840 --> 00:51:40,040
If a retry happens, it doesn't create duplicate updates because the execution tier treats the

1143
00:51:40,040 --> 00:51:44,600
correlation id plus record identity as a first class id impotency input.

1144
00:51:44,600 --> 00:51:45,920
That's the deterministic part.

1145
00:51:45,920 --> 00:51:50,320
The same job produces the same outcomes even under retries, even under throttling, even

1146
00:51:50,320 --> 00:51:52,120
under transient failures.

1147
00:51:52,120 --> 00:51:55,040
And now the critical governance rule from earlier stays intact.

1148
00:51:55,040 --> 00:51:57,840
No direct external dataverse exposure.

1149
00:51:57,840 --> 00:52:02,040
Python doesn't get to talk to dataverse, however at once, just because it's back end code.

1150
00:52:02,040 --> 00:52:03,920
You still enforce the mediated pattern.

1151
00:52:03,920 --> 00:52:08,600
The execution tier writes through governed endpoints that might be an API behind APIM that

1152
00:52:08,600 --> 00:52:12,080
performs the rights with a controlled identity and logs every operation.

1153
00:52:12,080 --> 00:52:16,200
Or it might be the orchestration tier doing the final commit based on the execution tiers

1154
00:52:16,200 --> 00:52:17,200
output.

1155
00:52:17,200 --> 00:52:20,480
Either way, you don't let bulk mutation become a free for all just because the code is in

1156
00:52:20,480 --> 00:52:24,840
Python because bulk mutation is where least privilege gets murdered first.

1157
00:52:24,840 --> 00:52:28,160
Engineers get tired of permission errors, so they grant broad roles.

1158
00:52:28,160 --> 00:52:29,400
Makers get tired of failures.

1159
00:52:29,400 --> 00:52:31,280
So they ask for exceptions.

1160
00:52:31,280 --> 00:52:33,320
It's become permanent entropy wins.

1161
00:52:33,320 --> 00:52:38,240
So you design the right path to be narrow by default, specific tables, specific actions,

1162
00:52:38,240 --> 00:52:40,760
specific scopes and audited principles.

1163
00:52:40,760 --> 00:52:44,240
Now look at what becomes possible once the execution tier owns the bulk job.

1164
00:52:44,240 --> 00:52:50,800
You can return per record status as actual data, updated, skipped, rejected, already compliant,

1165
00:52:50,800 --> 00:52:52,160
conflict detected, and why.

1166
00:52:52,160 --> 00:52:54,960
You can store that in a dataverse table as the job ledger.

1167
00:52:54,960 --> 00:52:58,480
You can produce a human readable summary for the business, but you still keep machine

1168
00:52:58,480 --> 00:53:00,320
readable truth for incident response.

1169
00:53:00,320 --> 00:53:04,200
And you can do the one thing enterprises always pretend they don't need until they do.

1170
00:53:04,200 --> 00:53:05,200
Prove what happened.

1171
00:53:05,200 --> 00:53:06,800
Not the flow ran successfully.

1172
00:53:06,800 --> 00:53:11,280
What records changed, what values changed, who authorized it, what job produced it, and

1173
00:53:11,280 --> 00:53:16,920
what correlation ID ties every step together across flow, APM and function logs.

1174
00:53:16,920 --> 00:53:19,480
That's how bulk operations stop being terrifying.

1175
00:53:19,480 --> 00:53:22,080
The flow becomes the request and approval layer.

1176
00:53:22,080 --> 00:53:25,200
Python becomes the deterministic execution engine.

1177
00:53:25,200 --> 00:53:30,040
Azure becomes the enforcement layer that makes identity network and logging non-negotiable.

1178
00:53:30,040 --> 00:53:33,760
And once you've solved bulk updates, the next question shows up immediately, usually from

1179
00:53:33,760 --> 00:53:37,160
the same teams that caused the bulk update problem in the first place.

1180
00:53:37,160 --> 00:53:39,960
They want to add just a little AI to the process.

1181
00:53:39,960 --> 00:53:45,600
That's where things go from expensive to unpredictable if you don't keep the same tier boundaries.

1182
00:53:45,600 --> 00:53:46,600
Scenario 3.

1183
00:53:46,600 --> 00:53:49,480
ML inference as a govern service, not a maker experiment.

1184
00:53:49,480 --> 00:53:54,920
AI is where the hybrid mandate gets tested because AI tempts people into skipping architecture.

1185
00:53:54,920 --> 00:54:00,240
A maker sees an action that can call an LLM, drops it into a flow, and suddenly the workflow thinks.

1186
00:54:00,240 --> 00:54:02,200
It demos well, it even ships.

1187
00:54:02,200 --> 00:54:04,520
Then finance asks why the monthly bill doubled.

1188
00:54:04,520 --> 00:54:06,840
Legal asks how decisions get explained.

1189
00:54:06,840 --> 00:54:10,520
Security asks where prompts and outputs went, and the business asks the only question that

1190
00:54:10,520 --> 00:54:14,120
matters, why did the same input produce a different result this week?

1191
00:54:14,120 --> 00:54:15,120
This is the risk.

1192
00:54:15,120 --> 00:54:19,880
Embedding inference directly in flows turns cost, behavior, and governance into a probabilistic

1193
00:54:19,880 --> 00:54:20,880
mess.

1194
00:54:20,880 --> 00:54:23,920
The orchestration tier was never designed to be your model runtime, your prompt management

1195
00:54:23,920 --> 00:54:25,440
system, and your safety layer.

1196
00:54:25,440 --> 00:54:28,680
When you force it to be, you get conditional chaos with a token budget.

1197
00:54:28,680 --> 00:54:31,320
So the rule stays consistent with everything else.

1198
00:54:31,320 --> 00:54:34,280
Infrance belongs in the execution tier, not in the orchestration tier.

1199
00:54:34,280 --> 00:54:35,280
The Y is simple.

1200
00:54:35,280 --> 00:54:37,520
ML inference is compute with consequences.

1201
00:54:37,520 --> 00:54:41,480
It needs deterministic rappers even when the model itself is stochastic.

1202
00:54:41,480 --> 00:54:42,480
It needs versioning.

1203
00:54:42,480 --> 00:54:43,480
It needs guardrails.

1204
00:54:43,480 --> 00:54:45,400
It needs auditable inputs and outputs.

1205
00:54:45,400 --> 00:54:48,960
And it needs a place where engineers can fix it without opening 50 flows and praying they

1206
00:54:48,960 --> 00:54:50,560
all use the same prompt.

1207
00:54:50,560 --> 00:54:53,680
In the hybrid pattern, power automate still does what it's good at.

1208
00:54:53,680 --> 00:54:55,400
It roots the business process.

1209
00:54:55,400 --> 00:54:59,520
It decides when inference should happen, who is allowed to request it, what record it's

1210
00:54:59,520 --> 00:55:01,280
tied to, and what happens next.

1211
00:55:01,280 --> 00:55:05,440
It does not own the prompt, the retry logic, or the passing of untrusted model output.

1212
00:55:05,440 --> 00:55:06,440
That's execution.

1213
00:55:06,440 --> 00:55:11,280
So the flow calls a Python service behind APIM, authenticated via Entra, with network

1214
00:55:11,280 --> 00:55:13,680
containment and correlated logging.

1215
00:55:13,680 --> 00:55:15,820
Same as the CSV and bulk update scenarios.

1216
00:55:15,820 --> 00:55:17,440
The only difference is the workload.

1217
00:55:17,440 --> 00:55:21,320
Instead of passing files or batching updates, Python is invoking a model preparing features

1218
00:55:21,320 --> 00:55:22,880
and returning stable outputs.

1219
00:55:22,880 --> 00:55:24,640
Here's what Python does that flows shouldn't.

1220
00:55:24,640 --> 00:55:26,400
First, input shaping.

1221
00:55:26,400 --> 00:55:32,000
Most AI in flows failures start with garbage inputs, unbounded text, missing context, sensitive

1222
00:55:32,000 --> 00:55:36,600
fields accidentally included, or random formatting that makes the model behave differently.

1223
00:55:36,600 --> 00:55:39,000
The Python tier normalizes and constraints input.

1224
00:55:39,000 --> 00:55:45,120
It can trim, redact, classify, and reject payloads before they ever hit a model endpoint.

1225
00:55:45,120 --> 00:55:46,120
That's not optional.

1226
00:55:46,120 --> 00:55:47,120
That's your safety boundary.

1227
00:55:47,120 --> 00:55:50,080
Second, prompt and configuration versioning.

1228
00:55:50,080 --> 00:55:51,800
Infraints isn't just call the model.

1229
00:55:51,800 --> 00:55:55,720
It's a specific prompt template with a specific system message, with specific tool settings,

1230
00:55:55,720 --> 00:56:00,160
with specific temperature and max tokens, and often with specific grounding data.

1231
00:56:00,160 --> 00:56:04,440
If that configuration lives inside a flow action, you've turned prompt engineering into a distributed

1232
00:56:04,440 --> 00:56:06,480
configuration drift problem.

1233
00:56:06,480 --> 00:56:09,280
Every copied flow becomes its own model behavior fork.

1234
00:56:09,280 --> 00:56:12,440
Nobody can answer which version is running where entropy wins again.

1235
00:56:12,440 --> 00:56:15,920
So the execution tier owns prompt versions like code artifacts.

1236
00:56:15,920 --> 00:56:18,920
Version identifiers become part of the response contract.

1237
00:56:18,920 --> 00:56:20,920
And the business asks why behavior changed?

1238
00:56:20,920 --> 00:56:22,200
You don't argue about vibes.

1239
00:56:22,200 --> 00:56:24,560
You point to a version and a change record.

1240
00:56:24,560 --> 00:56:27,960
Third, guard rails and deterministic post processing.

1241
00:56:27,960 --> 00:56:31,160
Model outputs are not structured until you force them to be.

1242
00:56:31,160 --> 00:56:34,760
Flows tend to treat model text like truth and then pass it with expressions that break

1243
00:56:34,760 --> 00:56:36,680
the moment the model gets creative.

1244
00:56:36,680 --> 00:56:41,840
The Python tier can enforce strict output schemers, validate fields, reject malformed results,

1245
00:56:41,840 --> 00:56:44,240
and apply deterministic rules that the business can audit.

1246
00:56:44,240 --> 00:56:46,680
The model can suggest the service decides what counts.

1247
00:56:46,680 --> 00:56:49,280
The uncomfortable truth executives need to hear.

1248
00:56:49,280 --> 00:56:50,920
AI doesn't replace policy.

1249
00:56:50,920 --> 00:56:55,120
AI needs policy codified because otherwise it becomes a liability generator.

1250
00:56:55,120 --> 00:56:56,320
So what comes back to the flow?

1251
00:56:56,320 --> 00:56:57,320
Not raw text.

1252
00:56:57,320 --> 00:56:59,400
Not here's the model's opinion.

1253
00:56:59,400 --> 00:57:02,960
The execution tier returns stable bounded outputs.

1254
00:57:02,960 --> 00:57:08,080
A classification label, a confident score if you choose to expose it, a set of extracted fields,

1255
00:57:08,080 --> 00:57:11,480
a decision recommendation with a reason code and any warnings.

1256
00:57:11,480 --> 00:57:15,320
If the model can't comply with the schema, the service returns a failure type.

1257
00:57:15,320 --> 00:57:20,280
The orchestration tier can root, retry, human review or reject.

1258
00:57:20,280 --> 00:57:22,640
Now you get the enterprise behaviors you actually want.

1259
00:57:22,640 --> 00:57:26,960
Cost becomes governable because APM can throttle and you can measure token usage at the service

1260
00:57:26,960 --> 00:57:29,760
boundary instead of hiding it in flow runs.

1261
00:57:29,760 --> 00:57:34,560
Behavior becomes governable because prompts and passing live in one place with version control.

1262
00:57:34,560 --> 00:57:39,120
Auditability becomes possible because you can log inputs, outputs and config versions

1263
00:57:39,120 --> 00:57:42,720
with correlation IDs tied back to the flow run and the dataverse record.

1264
00:57:42,720 --> 00:57:47,480
And you can finally separate AI experimentation from AI in production without pretending

1265
00:57:47,480 --> 00:57:48,800
makers won't experiment.

1266
00:57:48,800 --> 00:57:50,520
They will, they should.

1267
00:57:50,520 --> 00:57:54,920
But experiments belong in a green zone with controls, not in production flows that approve

1268
00:57:54,920 --> 00:57:59,840
payments, update customer records or trigger compliance actions.

1269
00:57:59,840 --> 00:58:02,600
The hybrid mandate doesn't ban AI in power platform.

1270
00:58:02,600 --> 00:58:07,440
It contains it.flowstays orchestration, python does execution, Azure enforces governance.

1271
00:58:07,440 --> 00:58:12,000
And inference becomes a service with contracts, boundaries and evidence rather than a maker

1272
00:58:12,000 --> 00:58:16,200
experiment that slowly turns into policy drift with an invoice.

1273
00:58:16,200 --> 00:58:20,520
Scaling reality, limits, throttles and the conditional chaos tax.

1274
00:58:20,520 --> 00:58:24,480
Scale is where your architecture stops being an opinion and starts being a physics problem.

1275
00:58:24,480 --> 00:58:28,880
Power automate has run limits, connectors have throttles, dataverse has service protection

1276
00:58:28,880 --> 00:58:34,080
limits, APM has quotas, functions have execution timeouts, none of these are negotiable and none

1277
00:58:34,080 --> 00:58:37,080
of them care that a VP needs the workflow by Friday.

1278
00:58:37,080 --> 00:58:40,880
So when an automation succeeds and gets adopted it immediately becomes a scaling test

1279
00:58:40,880 --> 00:58:42,560
you didn't design for.

1280
00:58:42,560 --> 00:58:48,160
Volume rises, concurrency rises, payload sizes creep up because someone adds just one more

1281
00:58:48,160 --> 00:58:49,160
field.

1282
00:58:49,160 --> 00:58:52,520
And then the platform does what distributed systems always do.

1283
00:58:52,520 --> 00:58:56,720
It applies back pressure, starts rejecting calls and forces you to learn the difference between

1284
00:58:56,720 --> 00:58:59,440
a deterministic system and a probabilistic one.

1285
00:58:59,440 --> 00:59:01,200
Here's what most people miss.

1286
00:59:01,200 --> 00:59:03,560
Throttling doesn't just slow you down, it changes behavior.

1287
00:59:03,560 --> 00:59:06,200
A flow that hits connector limits doesn't fail cleanly.

1288
00:59:06,200 --> 00:59:10,400
It retries, it delays, it sometimes partially completes depending on where it hits the limit.

1289
00:59:10,400 --> 00:59:14,000
And because many flows don't implement identity, retries don't replay safely.

1290
00:59:14,000 --> 00:59:18,000
They duplicate work, they create conflicting updates, they send duplicate emails, they create

1291
00:59:18,000 --> 00:59:21,480
double approvals, they write the same record twice with two different values depending

1292
00:59:21,480 --> 00:59:23,360
on what data changed between attempts.

1293
00:59:23,360 --> 00:59:25,080
That's the conditional chaos tax.

1294
00:59:25,080 --> 00:59:29,000
Not because power platform is unreliable, because your design assumed linear execution in

1295
00:59:29,000 --> 00:59:31,720
a world that enforces distributed constraints.

1296
00:59:31,720 --> 00:59:35,960
So treat scaling as a first class boundary decision, not a performance afterthought.

1297
00:59:35,960 --> 00:59:37,880
The orchestration tier needs to stay lean.

1298
00:59:37,880 --> 00:59:41,440
That is not a preference, it's the only way the system remains survivable when adoption

1299
00:59:41,440 --> 00:59:42,600
doubles.

1300
00:59:42,600 --> 00:59:46,520
Every extra action in a flow is an additional failure surface.

1301
00:59:46,520 --> 00:59:50,560
Another timeout, another connector dependency, another retry path, another place where errors

1302
00:59:50,560 --> 00:59:53,280
can be swallowed and re-emitted as unknown.

1303
00:59:53,280 --> 00:59:57,040
And yes, people will argue that low code makes it easy to add steps.

1304
00:59:57,040 --> 01:00:00,720
That's exactly the problem, it's easy to add steps that become permanent, it's easy to

1305
01:00:00,720 --> 01:00:05,000
add exceptions that become policy, it's easy to keep shipping until you have a flow that

1306
01:00:05,000 --> 01:00:10,000
is effectively a data pipeline with a UI and those fail in slow, expensive, non-deterministic

1307
01:00:10,000 --> 01:00:11,000
ways.

1308
01:00:11,000 --> 01:00:15,000
So the hybrid mandate applies the same pressure as before, move compute out, keep coordination

1309
01:00:15,000 --> 01:00:16,000
in.

1310
01:00:16,000 --> 01:00:19,800
If you're doing bulk operations, don't loop through 10,000 records in power automate because

1311
01:00:19,800 --> 01:00:20,800
you can.

1312
01:00:20,800 --> 01:00:24,800
You will hit limits and the work around engineering will turn into the real logic.

1313
01:00:24,800 --> 01:00:29,240
Put the bulk work in Python behind APIM where you can control batching, apply back off and

1314
01:00:29,240 --> 01:00:31,160
implement idempotency properly.

1315
01:00:31,160 --> 01:00:36,720
If you're doing heavy transforms, don't pass and normalize in a flow because it's just strings.

1316
01:00:36,720 --> 01:00:41,400
That turns into nested loops, fragile expressions and a debugging model that depends on run

1317
01:00:41,400 --> 01:00:42,960
history archaeology.

1318
01:00:42,960 --> 01:00:47,320
Do it in the execution tier where you can test it, version it and run it deterministically.

1319
01:00:47,320 --> 01:00:50,920
And when you do keep orchestration in the flow, you still need to respect the platform's

1320
01:00:50,920 --> 01:00:51,920
shape.

1321
01:00:51,920 --> 01:00:53,600
Retrieves should be intentional, not accidental.

1322
01:00:53,600 --> 01:00:57,800
If a call fails, you should know whether you're retrying because it was transient or because

1323
01:00:57,800 --> 01:01:00,080
you hit a quarter that won't clear for an hour.

1324
01:01:00,080 --> 01:01:04,840
That means your execution tier needs failure types that the orchestration tier can root,

1325
01:01:04,840 --> 01:01:10,400
throttled, unauthorized, validation error, downstream unavailable timeout.

1326
01:01:10,400 --> 01:01:13,680
If everything comes back as 500, the flow can't behave intelligently.

1327
01:01:13,680 --> 01:01:16,640
It will just try again until it makes the situation worse.

1328
01:01:16,640 --> 01:01:18,280
This is where APIM earns its keep again.

1329
01:01:18,280 --> 01:01:21,840
You throttle at the edge, you don't let a thousand parallel flows stampede the back end

1330
01:01:21,840 --> 01:01:24,040
because someone turned on concurrency.

1331
01:01:24,040 --> 01:01:26,200
You apply quotas per client identity.

1332
01:01:26,200 --> 01:01:30,280
You enforce payload limits so you don't get surprised 20 metabyte JSON bodies because someone

1333
01:01:30,280 --> 01:01:33,480
decided to send the entire spreadsheet to be safe.

1334
01:01:33,480 --> 01:01:38,240
You reject early, consistently and with a reason code, the orchestration tier can understand.

1335
01:01:38,240 --> 01:01:40,040
And when you need real scale, you queue.

1336
01:01:40,040 --> 01:01:41,040
Cues aren't trendy.

1337
01:01:41,040 --> 01:01:45,520
They're how you turn bursty, user-driven orchestration into steady, governable execution.

1338
01:01:45,520 --> 01:01:46,760
The flow submits a job.

1339
01:01:46,760 --> 01:01:49,800
The execution tier pulls and processes at a controlled rate.

1340
01:01:49,800 --> 01:01:51,840
The flow track state and notifies humans.

1341
01:01:51,840 --> 01:01:55,720
That's orchestration behaving like orchestration instead of pretending to be a compute engine.

1342
01:01:55,720 --> 01:02:00,960
Now the cost part because cost is where executives suddenly develop strong opinions about architecture.

1343
01:02:00,960 --> 01:02:04,760
Power automate costs scale with runs and premium connectors.

1344
01:02:04,760 --> 01:02:07,160
Execution tier costs scale with compute and throughput.

1345
01:02:07,160 --> 01:02:11,640
If you keep pushing compute into flows, you pay for orchestration to do execution poorly.

1346
01:02:11,640 --> 01:02:16,200
And if you push compute into Python services, you pay for execution to do execution well.

1347
01:02:16,200 --> 01:02:20,120
The hybrid mandate is the only model that lets you choose deliberately which cost curve

1348
01:02:20,120 --> 01:02:21,440
you want.

1349
01:02:21,440 --> 01:02:22,880
Scaling doesn't reward optimism.

1350
01:02:22,880 --> 01:02:24,120
It rewards boundaries.

1351
01:02:24,120 --> 01:02:28,120
And if you don't enforce those boundaries early, you'll enforce them later under incident

1352
01:02:28,120 --> 01:02:30,800
pressure with angry stakeholders and unclear ownership.

1353
01:02:30,800 --> 01:02:35,240
That's the most expensive way to learn platform limits, so treat limits and throttles as architectural

1354
01:02:35,240 --> 01:02:40,960
inputs designed for back pressure, enforce id-impotency, throttle at APIM.

1355
01:02:40,960 --> 01:02:43,440
Queue when volume isn't polite.

1356
01:02:43,440 --> 01:02:49,200
Otherwise success becomes a tax you pay in conditional C, alum discipline, versioned contracts,

1357
01:02:49,200 --> 01:02:50,680
not version screenshots.

1358
01:02:50,680 --> 01:02:53,920
Once the hybrid pattern works, the enterprise does what it always does.

1359
01:02:53,920 --> 01:02:58,360
It promotes the prototype to production by continuing to rely on it, not through a formal

1360
01:02:58,360 --> 01:03:00,040
release, through repetition.

1361
01:03:00,040 --> 01:03:04,120
And that's where ALM either shows up as architecture or disappears as ceremony.

1362
01:03:04,120 --> 01:03:08,840
Power platform teams often treat life cycle management as export the flow and import the flow.

1363
01:03:08,840 --> 01:03:10,600
That's not life cycle management.

1364
01:03:10,600 --> 01:03:14,040
That's file movement, it preserves an artifact, but it doesn't preserve intent, it doesn't

1365
01:03:14,040 --> 01:03:17,520
preserve dependency versions, it doesn't preserve contracts, and it absolutely doesn't

1366
01:03:17,520 --> 01:03:21,000
preserve the one thing that matters when systems drift, a traceable change story.

1367
01:03:21,000 --> 01:03:25,200
In hybrid automation, ALM has to be centered on contracts, not canvas, because the boundary

1368
01:03:25,200 --> 01:03:28,280
between orchestration and execution is not a diagram.

1369
01:03:28,280 --> 01:03:33,480
It's an agreement, what inputs are allowed, what outputs are guaranteed, what failures mean,

1370
01:03:33,480 --> 01:03:34,800
and what gets logged.

1371
01:03:34,800 --> 01:03:39,320
If that agreement changes silently, you don't get a neat failure, you get a slow governance

1372
01:03:39,320 --> 01:03:44,560
leak, makers implement workarounds, engineers add temporary compatibility, and within a

1373
01:03:44,560 --> 01:03:48,520
quarter you've rebuilt the same entropy generator you were trying to escape.

1374
01:03:48,520 --> 01:03:52,880
So the first rule is blunt version the API contract like it's a product because it is.

1375
01:03:52,880 --> 01:03:58,280
If the Python execution tier exposes endpoints behind APIM, those endpoints need explicit versions,

1376
01:03:58,280 --> 01:04:02,320
not will keep it compatible, not it's just internal.

1377
01:04:02,320 --> 01:04:06,160
Internal API is break more businesses than external ones because nobody budgets for internal

1378
01:04:06,160 --> 01:04:07,160
change management.

1379
01:04:07,160 --> 01:04:12,080
So you publish V1, you add V2 when behavior changes, you deprecate with the timeline, and

1380
01:04:12,080 --> 01:04:16,480
you use APM analytics to see which flows still call V1 because otherwise your deprecation

1381
01:04:16,480 --> 01:04:18,000
plan is a fantasy.

1382
01:04:18,000 --> 01:04:22,760
The second rule, treat Python dependencies as production supply chain, not as a maker convenience.

1383
01:04:22,760 --> 01:04:27,720
A requirements dot dxt that changes because someone ran PIP install locally is not an enterprise

1384
01:04:27,720 --> 01:04:28,840
artifact.

1385
01:04:28,840 --> 01:04:34,240
The execution tier needs PINT dependencies, repeatable builds, and a controlled promotion path.

1386
01:04:34,240 --> 01:04:38,080
Otherwise you will eventually redeploy a function and discover that a transitive dependency

1387
01:04:38,080 --> 01:04:43,560
updated behavior changed and the workflow now produces different outputs for the same input.

1388
01:04:43,560 --> 01:04:48,080
That's not Python being risky, that's you refusing to manage the execution tier as software

1389
01:04:48,080 --> 01:04:52,960
and yes the same applies to prompts and model configurations if you're doing inference.

1390
01:04:52,960 --> 01:04:53,960
Configuration is code.

1391
01:04:53,960 --> 01:04:58,760
If it can change behavior it must be versioned, reviewed, and released with traceability.

1392
01:04:58,760 --> 01:05:03,200
Now the power platform side, flows, apps, and solutions also need discipline but not

1393
01:05:03,200 --> 01:05:04,680
the fake kind.

1394
01:05:04,680 --> 01:05:08,760
And screenshots are what teams produce when they don't have deployment artifacts.

1395
01:05:08,760 --> 01:05:11,800
Here's the run history, here's the screenshot of the trigger.

1396
01:05:11,800 --> 01:05:15,600
Here's the expression that worked, that's documentation of accidents, not documentation

1397
01:05:15,600 --> 01:05:16,600
of design.

1398
01:05:16,600 --> 01:05:22,600
Real ALM means the flow lives in a solution, uses environment variables, references connections

1399
01:05:22,600 --> 01:05:27,360
that are created intentionally, and gets promoted through dev, test, and prod like any other

1400
01:05:27,360 --> 01:05:28,840
artifact.

1401
01:05:28,840 --> 01:05:31,320
Environment separation is not negotiable in the hybrid mandate.

1402
01:05:31,320 --> 01:05:35,760
If Dev and prod share the same API, the same identities in the same dataverse tables you

1403
01:05:35,760 --> 01:05:40,040
don't have environments, you have one environment with extra steps.

1404
01:05:40,040 --> 01:05:41,560
Separation is what makes change safe.

1405
01:05:41,560 --> 01:05:46,640
Dev calls dev APM, test calls test APM, prod calls prod APM, and the identities are separate

1406
01:05:46,640 --> 01:05:50,520
because permissions drift differently in each place and you need the ability to revoke,

1407
01:05:50,520 --> 01:05:54,000
rotate, and audit without breaking everything at once.

1408
01:05:54,000 --> 01:05:57,960
Promotion should be gated on traceability, which flow version, which API version, which

1409
01:05:57,960 --> 01:06:03,160
Python build, which dependency set, which APIM policy set, if you can't answer that on demand,

1410
01:06:03,160 --> 01:06:07,200
you are not operating a system, you are operating a coincidence, and here's the part that makes

1411
01:06:07,200 --> 01:06:09,080
people uncomfortable.

1412
01:06:09,080 --> 01:06:13,080
Ownership has to follow the artifacts, makers can own orchestration logic, but they cannot

1413
01:06:13,080 --> 01:06:16,040
be the release managers for execution code.

1414
01:06:16,040 --> 01:06:19,360
Engineers can own execution services, but they cannot be the only people who understand

1415
01:06:19,360 --> 01:06:21,240
when a business process changes.

1416
01:06:21,240 --> 01:06:25,160
Platform teams can own identities and policies, but they cannot be the catch all for every,

1417
01:06:25,160 --> 01:06:27,040
it stopped working complaint.

1418
01:06:27,040 --> 01:06:32,360
So you need a release contract across teams, makers, change flows, engineers, change services,

1419
01:06:32,360 --> 01:06:37,440
platform governs the boundaries, and everyone agrees on what constitutes a breaking change,

1420
01:06:37,440 --> 01:06:40,200
because the hybrid mandate is not low code plus Python.

1421
01:06:40,200 --> 01:06:42,640
It's a system where drift is expected and managed.

1422
01:06:42,640 --> 01:06:44,400
RM is the drift management layer.

1423
01:06:44,400 --> 01:06:47,760
Without it, your 3-tier model collapses back into improvisation.

1424
01:06:47,760 --> 01:06:50,840
With it, you get the only real enterprise outcome.

1425
01:06:50,840 --> 01:06:54,840
Stable orchestration, deterministic execution, and governance that survives the next

1426
01:06:54,840 --> 01:06:55,840
year.

1427
01:06:55,840 --> 01:06:59,600
Operating model, COE as entropy management, not a maker police.

1428
01:06:59,600 --> 01:07:03,280
The hybrid mandate dies in most organizations for a boring reason.

1429
01:07:03,280 --> 01:07:08,720
Nobody owns the space between maker velocity and enterprise responsibility, so the platform

1430
01:07:08,720 --> 01:07:09,720
drifts.

1431
01:07:09,720 --> 01:07:13,680
Not because people are reckless, because the operating model doesn't exist, and systems

1432
01:07:13,680 --> 01:07:16,080
will always root around missing structure.

1433
01:07:16,080 --> 01:07:19,320
That's what the center of excellence is for, not as a governance theatre committee,

1434
01:07:19,320 --> 01:07:22,800
not as the department of note as entropy management.

1435
01:07:22,800 --> 01:07:26,680
Because the work isn't stopping makers from building things, that's impossible.

1436
01:07:26,680 --> 01:07:29,880
The work is preventing the accumulation of invisible exceptions.

1437
01:07:29,880 --> 01:07:34,840
One-off connectors, ad hoc service accounts, copy pasted flows, unversioned endpoints, and

1438
01:07:34,840 --> 01:07:38,080
temporary bypasses that become permanent dependencies.

1439
01:07:38,080 --> 01:07:41,840
A COE that behaves like maker police creates a predictable failure mode.

1440
01:07:41,840 --> 01:07:46,000
Makers go quiet, they build anyway, they stop asking questions, solutions move into the dark,

1441
01:07:46,000 --> 01:07:50,040
and the first time leadership hears about it is during an audit or after an incident,

1442
01:07:50,040 --> 01:07:51,760
when the response is always the same.

1443
01:07:51,760 --> 01:07:53,560
How did we not know this existed?

1444
01:07:53,560 --> 01:07:55,720
So the COE has to be positioned differently.

1445
01:07:55,720 --> 01:08:00,360
The COE is the team that curates the enterprise patterns that make hybrid safe.

1446
01:08:00,360 --> 01:08:04,680
Reference architectures, approved integration paths, and the boundary rules that keep orchestration

1447
01:08:04,680 --> 01:08:09,120
low-code, execution deterministic, and governance enforceable in Azure.

1448
01:08:09,120 --> 01:08:13,240
The COE doesn't build every solution, it builds the rails that make solutions survivable,

1449
01:08:13,240 --> 01:08:15,760
that means the COE owns four concrete things.

1450
01:08:15,760 --> 01:08:17,600
First, portfolio visibility.

1451
01:08:17,600 --> 01:08:18,840
Not a vanity inventory.

1452
01:08:18,840 --> 01:08:21,320
An actual portfolio with classification.

1453
01:08:21,320 --> 01:08:22,320
Productivity.

1454
01:08:22,320 --> 01:08:23,680
Team workflow.

1455
01:08:23,680 --> 01:08:25,160
Departmental system.

1456
01:08:25,160 --> 01:08:26,360
Enterprise workflow.

1457
01:08:26,360 --> 01:08:27,360
Regulated workflow.

1458
01:08:27,360 --> 01:08:29,880
Those categories determine which guard rails apply.

1459
01:08:29,880 --> 01:08:32,800
If everything gets treated like production, delivery freezes.

1460
01:08:32,800 --> 01:08:35,960
If nothing gets treated like production, entropy wins.

1461
01:08:35,960 --> 01:08:38,000
Classification is the compromise that scales.

1462
01:08:38,000 --> 01:08:39,840
Second, patterns and reusable assets.

1463
01:08:39,840 --> 01:08:42,920
The COE should publish the standard integration contracts.

1464
01:08:42,920 --> 01:08:44,600
How a flow calls an API?

1465
01:08:44,600 --> 01:08:48,880
Where correlation IDs come from, what APIM policies are non-negotiable, what an error response

1466
01:08:48,880 --> 01:08:51,960
looks like, what "ident potent" means in this tenant.

1467
01:08:51,960 --> 01:08:53,640
Makers don't need a 90-page document.

1468
01:08:53,640 --> 01:08:57,520
They need a template and a reference implementation that works the first time.

1469
01:08:57,520 --> 01:08:59,480
Third, the escalation path.

1470
01:08:59,480 --> 01:09:00,840
Fusion teams are not a slogan.

1471
01:09:00,840 --> 01:09:02,320
They're an operating model.

1472
01:09:02,320 --> 01:09:06,080
Citizen makers own orchestration logic and business process.

1473
01:09:06,080 --> 01:09:08,000
Engineers own the execution tier.

1474
01:09:08,000 --> 01:09:09,000
Python services.

1475
01:09:09,000 --> 01:09:10,000
Packaging.

1476
01:09:10,000 --> 01:09:11,000
Dependency pinning.

1477
01:09:11,000 --> 01:09:12,000
Performance.

1478
01:09:12,000 --> 01:09:13,000
And reliability.

1479
01:09:13,000 --> 01:09:15,360
Platform teams own the governance tier.

1480
01:09:15,360 --> 01:09:20,920
For identities, network boundaries, secrets, logging and policy enforcement, the COE coordinates

1481
01:09:20,920 --> 01:09:24,120
those handoffs, so work doesn't stall in politics.

1482
01:09:24,120 --> 01:09:27,080
Fourth, enforcement mechanisms that don't rely on willpower.

1483
01:09:27,080 --> 01:09:31,680
DLP policies, environment strategy, managed environments, connector governance, APM policy

1484
01:09:31,680 --> 01:09:33,800
baselines and identity standards.

1485
01:09:33,800 --> 01:09:37,920
The COE should define what's allowed but also make the allowed path the easiest path.

1486
01:09:37,920 --> 01:09:42,360
If the secure path is harder than the insecure path, you've built a lesson in architectural

1487
01:09:42,360 --> 01:09:43,360
erosion.

1488
01:09:43,360 --> 01:09:45,080
Now the part everyone avoids, intake.

1489
01:09:45,080 --> 01:09:48,840
We need a clear rule for when a flow qualifies for the execution tier.

1490
01:09:48,840 --> 01:09:53,120
Not because makers can't do it, because the organization can't afford every complex automation

1491
01:09:53,120 --> 01:09:55,440
to become a bespoke debugging exercise.

1492
01:09:55,440 --> 01:09:57,880
So the intake criteria should be mechanical.

1493
01:09:57,880 --> 01:10:00,320
Data transforms beyond simple mapping.

1494
01:10:00,320 --> 01:10:05,280
bulk operations above a threshold, long running jobs, ML inference, cross-environment integration,

1495
01:10:05,280 --> 01:10:08,800
and anything that touches regulated data with non-trivial logic.

1496
01:10:08,800 --> 01:10:13,600
When those triggers appear, the COE roots the work into the hybrid pattern.

1497
01:10:13,600 --> 01:10:17,560
It's a great, empowered platform, execute in Python, govern in Azure, and the COE has

1498
01:10:17,560 --> 01:10:20,000
to defend that boundary in both directions.

1499
01:10:20,000 --> 01:10:24,160
It needs to stop makers from smuggling execution into flows through scopes, baguette and connector

1500
01:10:24,160 --> 01:10:25,160
abuse.

1501
01:10:25,160 --> 01:10:29,680
But it also needs to stop engineers from bypassing orchestration and building shadow services

1502
01:10:29,680 --> 01:10:32,480
that mutate dataverse without process visibility.

1503
01:10:32,480 --> 01:10:36,720
Both our entropy generators both feel justified in the moment, both become permanent.

1504
01:10:36,720 --> 01:10:39,320
This is why the COE's job is not approval.

1505
01:10:39,320 --> 01:10:41,760
It's alignment.

1506
01:10:41,760 --> 01:10:47,200
It means every hybrid solution has an accountable orchestration owner, an accountable execution

1507
01:10:47,200 --> 01:10:49,360
owner, and an accountable governance owner.

1508
01:10:49,360 --> 01:10:51,840
If any of those are missing, the solution is already abandoned.

1509
01:10:51,840 --> 01:10:53,000
It just hasn't failed yet.

1510
01:10:53,000 --> 01:10:55,480
And finally, the COE needs to measure drift.

1511
01:10:55,480 --> 01:10:58,080
Not with vanity metrics like number of apps.

1512
01:10:58,080 --> 01:11:02,680
With indicators that predict incidents, number of public endpoints, number of flows using

1513
01:11:02,680 --> 01:11:07,360
personal connections, number of APIs without versioning, number of workloads without correlated

1514
01:11:07,360 --> 01:11:12,000
logging and number of solutions living only in the default environment.

1515
01:11:12,000 --> 01:11:15,480
Those are the cracks where chaos enters, and mature COE doesn't block progress.

1516
01:11:15,480 --> 01:11:20,320
It makes progress repeatable, and that's the entire point of the hybrid mandate, agility,

1517
01:11:20,320 --> 01:11:24,280
but with discipline enforced by design, not by optimism.

1518
01:11:24,280 --> 01:11:28,480
Executive risk framing, what you're actually buying with the hybrid mandate.

1519
01:11:28,480 --> 01:11:30,640
Executives don't approve architecture diagrams.

1520
01:11:30,640 --> 01:11:34,280
They approve risk trades, and the reason the hybrid mandate matters is that it converts

1521
01:11:34,280 --> 01:11:38,160
a set of vague emotional arguments into measurable enforceable properties.

1522
01:11:38,160 --> 01:11:41,720
Mostly, the ship teams think they're buying speed when they fund power platform.

1523
01:11:41,720 --> 01:11:42,720
They're not.

1524
01:11:42,720 --> 01:11:45,800
They're buying the ability to push decision making closer to the business without collapsing

1525
01:11:45,800 --> 01:11:46,800
governance.

1526
01:11:46,800 --> 01:11:51,200
That distinction matters because if the only lever you pull is velocity, the organization

1527
01:11:51,200 --> 01:11:53,080
will absolutely achieve velocity.

1528
01:11:53,080 --> 01:11:57,200
It will just be velocity towards sprawl inconsistent controls and operational ambiguity.

1529
01:11:57,200 --> 01:11:58,640
The platform doesn't stop that.

1530
01:11:58,640 --> 01:11:59,640
It enables it.

1531
01:11:59,640 --> 01:12:02,480
So here's what you're actually buying with the hybrid mandate.

1532
01:12:02,480 --> 01:12:05,520
Lower incident costs through determinism.

1533
01:12:05,520 --> 01:12:07,440
Incidents are expensive for one reason.

1534
01:12:07,440 --> 01:12:09,160
Nobody can agree on what happened.

1535
01:12:09,160 --> 01:12:13,640
The bridge called Bern's time reconstructing reality across flow runs, connect the behavior,

1536
01:12:13,640 --> 01:12:15,480
and back end logs that don't line up.

1537
01:12:15,480 --> 01:12:20,400
The hybrid mandate forces a deterministic execution tier with correlated logging and enforced

1538
01:12:20,400 --> 01:12:21,400
contracts.

1539
01:12:21,400 --> 01:12:23,440
That means failures stop being interpretive.

1540
01:12:23,440 --> 01:12:24,640
They become traceable.

1541
01:12:24,640 --> 01:12:28,120
Your mean time to innocence drops, which is the only metric leadership should care about

1542
01:12:28,120 --> 01:12:29,360
during an outage.

1543
01:12:29,360 --> 01:12:31,440
And your mean time to resolution follows.

1544
01:12:31,440 --> 01:12:32,440
Second.

1545
01:12:32,440 --> 01:12:36,320
You lose security exposure through fewer parts to sensitive data.

1546
01:12:36,320 --> 01:12:38,280
Security teams don't lose sleep over a flow.

1547
01:12:38,280 --> 01:12:42,560
They lose sleep over uncontrolled identities sprawl and unmanaged egress.

1548
01:12:42,560 --> 01:12:46,560
When data verse becomes directly reachable from arbitrary runtimes, you increase the number

1549
01:12:46,560 --> 01:12:49,680
of principles, endpoints, and data paths that can leak.

1550
01:12:49,680 --> 01:12:51,200
That is the blast radius problem.

1551
01:12:51,200 --> 01:12:56,600
The hybrid mandate shrinks the blast radius by forcing data access through governed boundaries.

1552
01:12:56,600 --> 01:13:00,680
Entra workload identity, network containment, and policy enforcement at the API edge.

1553
01:13:00,680 --> 01:13:03,040
You don't eliminate risk, you stop multiplying it.

1554
01:13:03,040 --> 01:13:04,040
Third.

1555
01:13:04,040 --> 01:13:06,760
Reduced compliance, pain through evidence, not assertions.

1556
01:13:06,760 --> 01:13:08,920
Compliance audits are not asking whether you have policies.

1557
01:13:08,920 --> 01:13:11,280
They're asking whether policies survive pressure.

1558
01:13:11,280 --> 01:13:14,680
The hybrid mandate gives you artifacts auditors can understand.

1559
01:13:14,680 --> 01:13:19,120
Versioned APIs, consistent authentication, bounded payloads, and end-to-end traces tied to

1560
01:13:19,120 --> 01:13:20,800
a correlation ID.

1561
01:13:20,800 --> 01:13:25,720
That turns, we think it's controlled into, here is the record of the decision, the execution,

1562
01:13:25,720 --> 01:13:27,000
and the commit.

1563
01:13:27,000 --> 01:13:30,640
Audit conversations get shorter when you can show proof without assembling a narrative.

1564
01:13:30,640 --> 01:13:34,840
Both reduced vendor lock-in risk by separating orchestration from execution.

1565
01:13:34,840 --> 01:13:36,280
This is where people get confused.

1566
01:13:36,280 --> 01:13:38,440
The hybrid mandate is not anti-power platform.

1567
01:13:38,440 --> 01:13:39,440
It's pro-boundary.

1568
01:13:39,440 --> 01:13:43,000
When you bury your business logic inside flows, you hardwire your enterprise behavior

1569
01:13:43,000 --> 01:13:46,240
to a tool that was designed for orchestration, not compute.

1570
01:13:46,240 --> 01:13:50,200
Over time, every workaround becomes a dependency and migration becomes a rewrite.

1571
01:13:50,200 --> 01:13:54,640
When you externalize deterministic logic into Python services behind stable contracts,

1572
01:13:54,640 --> 01:13:57,800
you create portability, not because you plan to leave, because you want the option

1573
01:13:57,800 --> 01:13:59,720
to evolve without ransom.

1574
01:13:59,720 --> 01:14:01,800
And can change platforms later.

1575
01:14:01,800 --> 01:14:05,040
Execution services can be reused, governance controls remain enforceable.

1576
01:14:05,040 --> 01:14:08,800
Fifth, better talent alignment and lower organizational friction.

1577
01:14:08,800 --> 01:14:12,680
Citizen makers should not be carrying the responsibility for runtime patching, dependency

1578
01:14:12,680 --> 01:14:14,800
management, and API design.

1579
01:14:14,800 --> 01:14:18,720
Engineers should not be writing approval workflows and chasing inbox rules.

1580
01:14:18,720 --> 01:14:21,560
The hybrid mandate makes ownership legible.

1581
01:14:21,560 --> 01:14:26,560
Makers own process orchestration, engineers own execution services, and the platform team

1582
01:14:26,560 --> 01:14:28,160
owns the governance tier.

1583
01:14:28,160 --> 01:14:33,280
It reduces the constant debate over who broke it, because the boundaries define accountability

1584
01:14:33,280 --> 01:14:35,640
before production defines it for you.

1585
01:14:35,640 --> 01:14:39,560
Now the uncomfortable executive reality, you are not funding a feature.

1586
01:14:39,560 --> 01:14:41,600
You are funding an operating model.

1587
01:14:41,600 --> 01:14:46,680
If leadership mandates low-code only, they are choosing a probabilistic security model,

1588
01:14:46,680 --> 01:14:50,280
because policy exceptions will accumulate until the system behaves differently in each

1589
01:14:50,280 --> 01:14:51,960
corner of the tenant.

1590
01:14:51,960 --> 01:14:55,800
If leadership mandates pro-code only, they are choosing a delivery bottleneck, because

1591
01:14:55,800 --> 01:14:59,760
the backlog will outgrow the team and shadow IT will happen anyway.

1592
01:14:59,760 --> 01:15:04,080
The hybrid mandate is the only position that survives entropy, low-code where orchestration

1593
01:15:04,080 --> 01:15:08,400
is the value, Python where determinism is required, and Azure where governance must

1594
01:15:08,400 --> 01:15:10,080
be enforced by design.

1595
01:15:10,080 --> 01:15:14,920
And that's the executive purchase, fewer ambiguous outages, fewer uncontrolled data paths,

1596
01:15:14,920 --> 01:15:20,480
fewer audit surprises, and a platform that scales without turning into conditional chaos.

1597
01:15:20,480 --> 01:15:24,320
Closing executive positioning, a maturity model, not a rebellion.

1598
01:15:24,320 --> 01:15:28,240
The executive positioning has to be explicit, because otherwise the organization will hear

1599
01:15:28,240 --> 01:15:29,560
what it wants to hear.

1600
01:15:29,560 --> 01:15:32,320
Some will hear "power platform is unsafe."

1601
01:15:32,320 --> 01:15:35,160
Others will hear "engineering" wants to take control back.

1602
01:15:35,160 --> 01:15:38,480
And makers will hear "it is about to slow everything down again."

1603
01:15:38,480 --> 01:15:40,040
All of those interpretations are wrong.

1604
01:15:40,040 --> 01:15:42,120
The hybrid mandate is not anti-power platform.

1605
01:15:42,120 --> 01:15:44,160
Power platform is the orchestration tier.

1606
01:15:44,160 --> 01:15:45,480
That's not a consolation price.

1607
01:15:45,480 --> 01:15:49,600
That's the part of the system that touches humans, process, approvals and business state.

1608
01:15:49,600 --> 01:15:51,080
It's where intent gets expressed.

1609
01:15:51,080 --> 01:15:52,600
It's where work becomes accountable.

1610
01:15:52,600 --> 01:15:56,600
It's where you can see why a decision happened without decompiling a service.

1611
01:15:56,600 --> 01:15:58,640
So the mandate doesn't reduce power platform.

1612
01:15:58,640 --> 01:16:01,040
It rescues it from being forced to do execution work.

1613
01:16:01,040 --> 01:16:02,280
It was never built to do.

1614
01:16:02,280 --> 01:16:04,200
It's also not pro-developer elitism.

1615
01:16:04,200 --> 01:16:07,760
This isn't about real developers versus citizen developers.

1616
01:16:07,760 --> 01:16:11,360
That's a childish framing, and it's usually deployed by people who benefit from keeping the

1617
01:16:11,360 --> 01:16:12,520
boundary blurry.

1618
01:16:12,520 --> 01:16:16,840
The enterprise needs both skill sets because the enterprise has both kinds of work, human

1619
01:16:16,840 --> 01:16:19,040
process and deterministic compute.

1620
01:16:19,040 --> 01:16:24,280
The mandate simply assigns those responsibilities to the tiers that can carry them without collapsing.

1621
01:16:24,280 --> 01:16:28,720
Orchestration stays in low code where it's observable and editable by the business.

1622
01:16:28,720 --> 01:16:31,560
Execution moves to Python where it's testable and repeatable.

1623
01:16:31,560 --> 01:16:36,080
And governance sits in a zoo where identity, network, policy and logging can be enforced

1624
01:16:36,080 --> 01:16:37,400
by design.

1625
01:16:37,400 --> 01:16:39,520
And it is not a workaround for bad design.

1626
01:16:39,520 --> 01:16:43,160
A workaround is what happens when someone cannot get the platform to do what it should.

1627
01:16:43,160 --> 01:16:48,000
So they root around controls, file watches, local scripts, service accounts, random endpoints,

1628
01:16:48,000 --> 01:16:50,680
just let it through firewall rules.

1629
01:16:50,680 --> 01:16:52,360
The hybrid mandate does the opposite.

1630
01:16:52,360 --> 01:16:57,080
It builds the supported, auditable path on purpose so people stop inventing shadow paths.

1631
01:16:57,080 --> 01:17:00,320
That distinction matters because enterprises always enter hybrid.

1632
01:17:00,320 --> 01:17:04,160
The only question is whether they do it intentionally or whether they do it accidentally

1633
01:17:04,160 --> 01:17:05,160
in the dark.

1634
01:17:05,160 --> 01:17:06,160
Now, what is it?

1635
01:17:06,160 --> 01:17:10,360
It is a maturity model for enterprises that want both agility and engineering discipline.

1636
01:17:10,360 --> 01:17:13,680
At low maturity, everything lives in the orchestration tier.

1637
01:17:13,680 --> 01:17:17,560
Flows become pipelines, connectors become integration strategy, and run history becomes

1638
01:17:17,560 --> 01:17:19,200
the primary debugging tool.

1639
01:17:19,200 --> 01:17:20,880
It works until it doesn't.

1640
01:17:20,880 --> 01:17:25,000
Then every exception becomes permanent and you've converted low code speed into long term

1641
01:17:25,000 --> 01:17:26,080
governance debt.

1642
01:17:26,080 --> 01:17:30,640
At the next maturity level, the organization introduces the execution tier intentionally.

1643
01:17:30,640 --> 01:17:32,800
Python becomes a service, not a sidecar.

1644
01:17:32,800 --> 01:17:34,320
Compute moves out of flows.

1645
01:17:34,320 --> 01:17:35,320
Contracts become explicit.

1646
01:17:35,320 --> 01:17:36,320
Edympotency exists.

1647
01:17:36,320 --> 01:17:37,320
Versioning exists.

1648
01:17:37,320 --> 01:17:38,720
Payloads are bounded.

1649
01:17:38,720 --> 01:17:42,440
Makeers stop writing brittle, passing logic and start consuming stable outputs.

1650
01:17:42,440 --> 01:17:46,360
At the mature level, the governance tier hardens the boundaries so the model survives

1651
01:17:46,360 --> 01:17:47,360
pressure.

1652
01:17:47,360 --> 01:17:52,160
Entra workload identity, private endpoints, dataverse behind the platform, APM enforcing

1653
01:17:52,160 --> 01:17:56,240
policy and contracts and correlated logging, so incidents are evidence-based.

1654
01:17:56,240 --> 01:17:57,240
That's not bureaucracy.

1655
01:17:57,240 --> 01:18:00,080
That's how systems stay stable while adoption scales.

1656
01:18:00,080 --> 01:18:03,040
So the hybrid mandate is an enterprise contract, not a diagram.

1657
01:18:03,040 --> 01:18:05,000
Power platform is the orchestration tier.

1658
01:18:05,000 --> 01:18:06,480
Python is the execution tier.

1659
01:18:06,480 --> 01:18:09,680
Azure is the governance tier and the point is not to make things harder.

1660
01:18:09,680 --> 01:18:11,520
The point is to make success survivable.

1661
01:18:11,520 --> 01:18:14,400
This is also the executive instruction stated plainly.

1662
01:18:14,400 --> 01:18:17,240
Mandate boundaries, fund the governance tier and measure drift.

1663
01:18:17,240 --> 01:18:20,240
Because drift is what kills you, not the first deployment.

1664
01:18:20,240 --> 01:18:21,240
Drift.

1665
01:18:21,240 --> 01:18:25,960
The slow accumulation of exceptions, personal connections, public endpoints, unversioned

1666
01:18:25,960 --> 01:18:30,720
APIs and temporary bypasses that quietly become the real architecture.

1667
01:18:30,720 --> 01:18:34,840
If leadership wants a platform that scales without becoming conditional chaos, then leadership

1668
01:18:34,840 --> 01:18:37,960
has to stop rewarding outcomes that ignore boundaries.

1669
01:18:37,960 --> 01:18:41,000
Don't reward we shipped it if it shipped through a backdoor identity.

1670
01:18:41,000 --> 01:18:45,080
Don't reward we automated it if it exposed dataverse directly.

1671
01:18:45,080 --> 01:18:50,440
Don't reward we added AI if nobody can explain the prompt, the version, the cost or the

1672
01:18:50,440 --> 01:18:51,720
audit story.

1673
01:18:51,720 --> 01:18:55,920
Reward the pattern, reward the contract, reward the evidence.

1674
01:18:55,920 --> 01:19:01,200
That's what maturity looks like the organization can move fast and still prove it stayed in control.

1675
01:19:01,200 --> 01:19:05,400
The only takeaway that matter, the only takeaway that matters is this.

1676
01:19:05,400 --> 01:19:10,720
Keep orchestration in power platform, put deterministic compute in Python and enforce the boundaries

1677
01:19:10,720 --> 01:19:14,360
in Azure or the system degrades into conditional chaos.

1678
01:19:14,360 --> 01:19:19,400
If you want, I can do a follow-up episode that maps this to a concrete reference architecture,

1679
01:19:19,400 --> 01:19:24,600
enter a workload identity, APIM policies, private endpoints, correlation IDs and the minimum

1680
01:19:24,600 --> 01:19:26,880
viable contracts that stop drift.

1681
01:19:26,880 --> 01:19:30,720
Subscribe for that and send me the hybrid pattern that's failing in your tenant right now,

1682
01:19:30,720 --> 01:19:33,320
what boundary collapsed and what exception became normal.