Controlled document · Living standard

Microsoft 365 & Power Platform
Development Standards & Architecture Guide

The authoritative record of how this team designs, builds, secures, and ships solutions across Microsoft 365 and the Power Platform — and, just as importantly, why. Every standard in this document carries its reasoning: the tradeoff it accepts, the constraint it solves, or the failure it prevents.

Doc IDM365-STD-001
Version0.5 — Working draft
OwnerJohn
OrganizationFill in
Applies toM365 · Power Platform · Entra
Review cadenceProposed: quarterly
Contents

I · Foundations

§00How to use this documentReading, updating, and the rule that governs everything below

This is a living document. It is deliberately incomplete: it contains decided standards, drafts awaiting confirmation, and open questions you still need to answer. The empty space is the point — fill it in as decisions get made, and record the date and reasoning when you do.

Decided Ratified. Follow it, or bring a case to change it. Draft A proposed default. Use it, but confirm or revise. Open Undecided. A question that needs an owner and an answer.

The one rule this document enforces

Every standard must carry its “why.” A rule without reasoning can only be obeyed or ignored. A rule with reasoning can be applied — the team can recognize when an edge case breaks the assumption behind the rule and make an informed call instead of a cargo-cult one. If you can’t articulate the tradeoff a standard accepts or the failure it prevents, it isn’t ready to be a standard.

How a decision gets recorded here

Propose it (in writing, with the why) → discuss it with whoever it affects → decide it → record it using the template in Appendix A → log the edit in Appendix B. Decisions get an ID (DA-001, NC-002, …) so flows, app comments, and wiki pages can cite them.

Revisiting decisions

Every decision record has a Revisit when field. Decisions are made against constraints — licensing terms, tenant policy, platform features — and constraints change. Naming the trigger for re-evaluation up front prevents both blind loyalty to stale rules and constant relitigation of settled ones.

II · Platform-wide standards

§01Naming conventionsNames are documentation you can’t avoid reading

Naming is the cheapest form of documentation and the only one guaranteed to be seen at the moment of use. The goal of a convention is not tidiness — it is that any team member can open an unfamiliar app or flow and predict what a thing is, who owns it, and what it touches, from its name alone.

Why conventions beat cleverness: Power Platform has weak refactoring tools. Renaming a control, column, or flow after the fact ranges from tedious to breaking. A convention adopted on day one costs nothing; the same convention retrofitted at month six costs a sprint. Decide once, early, and enforce in review.

DraftProposed defaults — confirm or revise

ObjectProposed patternExampleWhy this shape
Canvas controls3–4 letter type prefix + PascalCase purposebtnSubmitRequest, galApprovalQueue, conHeaderBarThe prefix makes formulas self-describing; you can read galApprovalQueue.Selected without opening the tree view.
Global variablesgbl + PascalCasegblCurrentUserScope is the first thing you need to know when debugging a variable; encode it where you can’t miss it.
Context variablesloc + PascalCaselocShowConfirmDialogSame reason — and it exposes accidental scope-mixing at a glance.
Collectionscol + PascalCasecolMyRequestsCollections behave differently from variables and data sources; the prefix prevents category errors in formulas.
Flows[AppCode] - [Verb][Entity] - [Qualifier]PCARD - GetRequests - ByApproverApp code groups flows in a crowded environment; verb-first tells you read vs. write before you open it.
SharePoint listsPascalCase, singular or plural — pick one, no spaces at creationPCardRequestsThe internal name is frozen at creation and haunts every OData filter forever; spaces become _x0020_.
SharePoint columnsPascalCase internal name created before any renameApprovalStageSame freeze rule as lists. Create with the final internal name, then re-label for display.
ScreensAccessibility convention — see NC-001Request Detail ScreenScreen names are announced by screen readers, which makes screen naming an accessibility decision, not a style one. Recorded as NC-001 below.

BaselineMicrosoft’s positions — from the canvas app standards white paper

Microsoft’s Power Apps canvas app coding standards and guidelines white paper (Appendix D) is the closest thing to an official baseline, and the table above already agrees with most of it: camelCase controls with short type prefixes, gbl/loc/col prefixes, PascalCase data sources. Its additions worth deciding on:

  • Why the variable prefixes exist at all: Power Fx allows a context variable and a global variable to share a name, and formulas silently resolve to the context one unless disambiguated. The gbl/loc prefixes aren’t taste — they make that collision impossible. Worth writing into the standard so the rule survives its author.
  • Reused controls carry a screen suffix: control names must be unique app-wide, so a control repeated across screens gets a short screen tag (their example: a nav gallery suffixed for the home screen). Decide whether to adopt this or forbid cross-screen duplication outright in favor of components (§10) — components didn’t exist when the paper was written.
  • Custom connector naming happens before import: the OpenAPI title attribute becomes the data source name with spaces stripped, so set a clean PascalCase title first — it can’t be fixed afterward. Feeds §08’s custom connector policy.
  • Type prefixes for data-typed variables (e.g., str, b) are explicitly left to team preference by the paper. Decide once below.
Draft Decision NC-001

Screen names use the accessibility convention: plain words with spaces, ending in “Screen”

Proposed standard

Screens are named like Home Screen and Request Detail Screen — real words, spaces, no abbreviations, no scr prefix. This supersedes the prefixed pattern originally drafted in the table above.

Why

Screen readers announce screen names as users navigate. A prefixed, compressed name is announced as noise; “Request Detail Screen” is announced as meaning. In a public-sector environment accessibility is an obligation, and this is one of the cheapest accessibility wins the platform offers — the Microsoft baseline is emphatic on it for exactly this reason.

Tradeoffs accepted

Breaks the “every object gets a type prefix” symmetry and loses prefix-search for screens. Accepted because an app has a handful of screens — all visible at the top of the tree view — and thousands of controls; the prefix earns its keep on controls, not screens.

Status: Draft — ratify with §01Owner: ____Date: ____Revisit when: the platform decouples announced screen names from object names

OpenQuestions you need to answer

  • What is the official app-code registry (e.g., PCARD, TRNRM) and who assigns codes?
  • Singular or plural list names — which one, permanently?
  • Do environment names carry a suffix convention (-DEV / -TEST / -PROD), and do solution and flow names repeat it or rely on the environment context?
  • What is the naming pattern for service accounts and app registrations? (Feeds §03.)
  • Are flow action names inside a flow renamed to describe intent, and is that enforced in peer review?Why it matters: “Apply to each 4” is where debugging goes to die.
  • What naming marks something as deprecated but not yet deleted (e.g., zzOLD_ prefix)?
Confirm the table above (or your amended version), record the date, and note any exceptions granted.
§02Identity & Entra IDThe substrate every other section resolves to

Every rule in this document eventually resolves to an Entra ID object. Who can open an app, whose rows a flow returns, who may approve, who gets the notification, who can run the script — each is a lookup against a user, a group, or a service identity. Get identity wrong and the standards above it are decoration.

Why identity comes before the services that use it: teams usually discover their identity model backwards — an app needs an audience, so someone makes a group; a flow needs to check approvers, so someone hard-codes an email. Six months later access is defined in a dozen places and nobody can answer “who can see this?” without opening three apps. Deciding the model once, up front, makes that question answerable from one place.

DraftProposed practices — each with its why

AreaPracticeWhy
Groups over individualsEvery access grant — app share, run-only, list permission, license, approver role — targets a security group, never a named user.Individual grants are invisible to review and survive the person who left. A group is one object an auditor can read and a joiner/leaver process can touch.
Group namingGroups carry a purpose-encoded name that matches §01’s conventions and states the app or function they serve.A group called Test Group 2 will outlive everyone who knew what it was for. Names are the only documentation a group reliably carries.
Dynamic vs. assignedDecide per audience, and record which. Dynamic groups for HR-shaped audiences (department, title); assigned for exception-shaped ones (project teams, approvers).Dynamic rules stay true automatically but inherit every error in the source attributes. Assigned groups are explicit and rot the moment someone forgets. The failure modes are opposite — pick the one you can live with per case.
Self-service group creationDecide explicitly whether users may create groups and Teams. Record the answer even if the answer is the platform default.Left at default, anyone can create groups, which quietly creates SharePoint sites and mailboxes nobody governs. An undocumented default is still a decision — just one nobody made.
Privileged accessNo standing admin roles on daily-driver accounts. Elevation is just-in-time via PIM, time-bound, justified, and reviewed.Standing admin is the finding every assessment writes first, and the blast radius of one phished session. JIT converts a permanent risk into a logged, bounded event.
Conditional AccessKnow which CA policies apply to the Power Platform, SharePoint, and Graph service principals — and test app and flow behavior against them before release.CA is where identity decisions become runtime behavior. A policy that blocks unmanaged devices will break an app for field staff and the error surfaces as “the app is broken,” not “policy denied.”
Licensing by groupLicense assignment via group membership, not per-user clicks.Makes entitlement auditable in the same place as access, and stops the “why does this person have a premium license” archaeology in §06.
Break-glass accountsEmergency access accounts exist, are excluded from CA, are stored offline, and are tested on a schedule.The one scenario where you cannot log in to fix the thing preventing you from logging in. An untested break-glass account is a story you tell yourself.
GuestsGuest posture decided per workload with an expiry/review path — not left to whatever the tenant default is.Guests accumulate silently and appear in access reviews years later attached to data nobody expected them to reach.
Draft Decision ID-001

No standing privileged roles — admin access is activated just-in-time

Proposed standard

No account, human or service, carries a standing privileged role. Admin-level access is activated per task through PIM with a justification and an expiry. Service accounts (§03) are explicitly in scope: they get the permissions their solution needs and nothing role-shaped.

Why — the reasoning
  • A standing admin role is a permanent attack surface bought for an occasional need. It is also the first finding every security assessment writes, and the hardest one to argue with.
  • JIT converts a permanent grant into an expiring, approved request, so a compromised session is measured in hours rather than indefinitely.
  • The activation records are the audit trail. Reviewers under our frameworks (§05) ask who held privilege and when; this answers it without anyone assembling evidence by hand.
Tradeoffs accepted

Activation friction during incidents — which is why a break-glass path must exist and be rehearsed (§03), not improvised at 2 AM · PIM carries licensing requirements; confirm entitlement before committing · approvals only work if someone actually reviews them, otherwise you have bought friction and no security.

Status: DraftOwner: ____Date: ____Revisit when: role tooling changes, or a real incident shows activation latency is unsafe

OpenQuestions you need to answer

  • What is the group naming standard, who may create groups, and is self-service creation on or off today?Why it matters: this one setting determines whether §11’s site architecture is a plan or a suggestion.
  • For app audiences — dynamic or assigned? If dynamic, which attributes are trustworthy enough to drive access, and who owns their accuracy?
  • Which roles are PIM-eligible versus standing, what is the maximum activation window, and who approves activation?
  • Which Conditional Access policies touch Power Platform, SharePoint, and Graph — and does anyone test releases against them before users do?
  • What is the guest access posture per workload, and what expires an unused guest?
  • How many break-glass accounts exist, where are the credentials, and when were they last tested?
  • What does the government-cloud environment change here — which identity features are unavailable or behave differently, and where is that recorded so it isn’t rediscovered quarterly?
  • What is the access review cadence for groups, and does completing it produce evidence an auditor would accept?
Record the group model, naming, creation gate, PIM configuration, CA posture, and review cadence — each with its why.
Log feature gaps and behavioral differences as you hit them, with the date checked. This saves the next person the same afternoon.
§03Service principals & service accountsWho the platform thinks is doing the work

DA-001 makes identity an architectural feature: flows deliberately run as something other than the end user. That “something” must be chosen, secured, documented, and survivable when people leave. Two distinct instruments exist and should not be conflated: service accounts (licensed user identities that own connections and flows) and service principals / app registrations (non-user identities for API-level access, e.g., Microsoft Graph or SharePoint app-only calls).

Why not just build on personal accounts: a flow connection owned by a person breaks the day that person’s account is disabled — usually discovered in production, usually during offboarding week. Beyond continuity, personal ownership means row-level security (DA-001) silently depends on one employee’s permissions, which is neither auditable nor explainable to a compliance reviewer.
Draft Decision SP-001

Production connections are owned by dedicated service accounts, not personal accounts

Proposed standard

Each production solution (or solution family) runs its flows under a named service account (e.g., svc-pp-pcard) that owns the connections and is a co-owner of the flows. Personal accounts may own connections only in dev.

Why

Continuity (survives offboarding), least-privilege clarity (the account’s permissions are the solution’s permissions — auditable in one place), and it is the identity mechanism that makes DA-001’s row-level security enforceable rather than accidental.

Tradeoffs

License cost per service account · credential custody overhead (MFA, password vaulting, sign-in monitoring) · one more identity per solution to include in access reviews.

Status: DraftOwner: ____Date: ____Revisit when: service principal support for the relevant connectors makes account-less ownership viable

OpenQuestions you need to answer

  • One service account per app, per department, or per environment tier? What is the blast radius you are willing to accept if one credential is compromised?
  • Where are service account credentials stored, who can retrieve them, and what is the break-glass procedure?
  • What is the secret/credential rotation cadence, and who owns the calendar reminder that actually makes it happen?
  • When is a true service principal (app registration) required instead of a service account — e.g., Graph API calls, app-only SharePoint access — and who approves new app registrations?
  • Certificates or client secrets for app registrations, and what expiry ceiling is allowed?Why it matters: the default temptation is a 24-month secret created in a hurry; the incident happens in month 25.
  • What Entra ID roles/permissions is a service account allowed to hold, and what is categorically forbidden (e.g., no standing admin roles — use PIM)?
  • How are service accounts and app registrations inventoried and reviewed — and does that review satisfy your compliance frameworks (e.g., CJIS/HIPAA-adjacent audit expectations)?
Record the account model, naming, custody, rotation, and approval workflow — each with its why.
§04Security & permissionsThe permission model DA-001 promises only exists if these settings are right

DA-001 claims row-level security. That claim is only true when three layers agree: the SharePoint lists are locked down, the flows run under the service identity for everyone, and the app is shared to the right people. Get any one wrong and the model silently degrades to “everyone can see everything, just less conveniently.”

Draft Decision SEC-001

App-facing flows are shared run-only, configured to use the owner’s connection — and backing lists grant end users no direct access

Proposed standard

Every DA-001 flow is shared to app users as run-only, with connections set to “Use this connection” (the owner’s / service account’s), never “Provided by run-only user.” The SharePoint lists behind the app grant end users no permissions at all; only the service account (and admins) can touch them.

Why — this is the linchpin
  • If run-only users provide their own connection, the flow executes as them — recreating exactly the direct-connector identity problem DA-001 exists to eliminate, and failing for any user without list access.
  • If users retain direct list permissions, the flow’s filtering is theater: anyone can open the list URL, use built-in search, or query via Graph and see every row and column. Server-side filtering is only security when the server is the only path to the data.
Tradeoffs

The service identity becomes high-value — its credential custody (§03) is now a security control, not housekeeping · out-of-app experiences (list views, exports, Power BI direct) require deliberate alternatives · misconfiguration is invisible in the app UI, so it must be checked, not assumed (deployment checklist, §15).

Status: Draft — ratify alongside DA-001Owner: ____Date: ____Revisit when: platform-native row-level security for SharePoint-backed apps ships

DraftFurther proposed practices

AreaPracticeWhy
App sharingShare apps to security groups, never individual users; group membership is the access request process.Individual shares rot instantly and are invisible to access reviews; groups make joiner/leaver processes real.
Authorization dataRole assignments (who approves what, who sees which department) live in an admin-managed SharePoint list read by flows — not hard-coded in formulas.Authorization changes weekly; app deployments shouldn’t. Data-driven roles also give auditors a single artifact.
Environment rolesMaker/admin roles in each environment reviewed on a schedule; production maker access restricted to the team.Every maker in prod is a person who can edit a live flow at 4:55 PM on Friday.
Admin elevationAdmin-level work happens under PIM-style just-in-time elevation, not standing rights.Standing admin on daily-driver accounts is the finding every security assessment writes first.
Data classificationEvery solution declares its data classification at intake; classification selects the control set (encryption expectations, audit depth, retention).CJIS/HIPAA-class data cannot be a surprise discovered in an incident review. Classify first, then build.

OpenQuestions you need to answer

  • Which compliance frameworks formally apply to which data classes here, and what specific controls do they demand from Power Platform solutions?
  • What audit evidence must solutions produce (who did what, when), and is flow-written stamping (§11) sufficient or is centralized logging required?
  • Who performs periodic access reviews of app shares, group memberships, run-only lists, and service identities — and how is completion evidenced?
  • What is the incident procedure if a service account credential is exposed — rotation steps, connection re-auth, and communication?
  • What is the exception process when someone requests direct list access “just for reporting”?Why it matters: this is the single most common way the SEC-001 model gets quietly broken.
  • Are guest/external identities ever in scope for these apps, and what changes if so?
Record classifications, control sets, review cadence and owners — with the reasoning for each control.
§05Compliance & data governanceRetention, labels, audit, and the two different things called DLP

Solutions built here hold public-sector data, some of it regulated. That makes retention, classification, auditability, and discoverability design inputs — not paperwork applied afterward. This section is where those obligations get named, so §04 can select controls and §07 can size its data model against them.

Why “DLP” means two unrelated things, and why the confusion is expensive: Power Platform DLP sorts connectors into Business / Non-Business / Blocked groups and stops an app from combining them (§08). Purview DLP inspects content and acts on what the data actually is — a card number, a record identifier. They share a name, live in different admin centers, and protect against different failures. A team that thinks it “has DLP” because one of them is configured has a gap it can’t see.
Why records retention is not optional here: in a public-sector tenant, data in a SharePoint list is very likely a public record subject to a state retention schedule — the same as a document or an email. The app didn’t create an exemption. If retention hasn’t been decided for a list, the default (keep forever, delete on a whim) is almost certainly wrong in both directions: it holds data past its schedule and offers no defensible disposition.

DraftProposed practices — each with its why

AreaPracticeWhy
Classify at intakeEvery solution declares a data classification before build starts; the classification selects the control set (audit depth, retention, encryption expectations, environment rules).Classification discovered during an incident is discovered too late. It is also the cheapest gate in the process — one question, asked once.
Retention decided per listEach system-of-record list maps to a retention schedule and a disposition path, recorded with the schema (§11).Retention is a legal obligation with a clock on it; “we never decided” is not a defensible disposition.
Both DLPs are configured and documentedPower Platform connector DLP and Purview content DLP each have a recorded state, reviewed together.See the callout above. Whichever one you skipped is the one the auditor asks about.
Sensitivity labelsDecide whether labels apply to app-generated documents and container-level (site/team) labels, and what each label actually enforces.Labels that carry no enforcement train people to ignore labels — worse than not labeling.
Audit logKnow the tenant’s audit retention period and confirm it meets the longest obligation you carry, not the shortest.Audit retention is only discovered to be too short at the moment you need the record.
DiscoverabilityApp data must be findable under eDiscovery/records request; if a store isn’t discoverable, that is a design flaw to record, not a feature.Public records requests reach app data. A store nobody can search is a liability with a deadline attached.
No regulated data in lower environmentsTest data is synthetic or scrubbed; production data does not travel to Dev/Test.A copy in a dev environment is a copy outside the control set — a compliance incident nobody planned and everybody could have predicted.
Draft Decision CMP-001

Every solution declares a data classification before development starts

Proposed standard

No solution enters development without a recorded data classification. The classification selects the control set: retention, audit depth, permitted environments, whether flow-mediated access is sufficient, and what may exist in lower environments.

Why — the reasoning
  • Classification discovered late is discovered during an incident review. It is the input that drives §04’s controls, §07’s data model, §11’s site architecture, and §16’s test-data rules — every one of which is expensive to change after build.
  • One question at intake costs minutes. Retrofitting regulated-grade auditing onto a shipped SharePoint app costs a rebuild.
Tradeoffs accepted

Intake friction · it requires a named person empowered to say what the data actually is — without that, the field gets guessed · over-classification is its own failure: a tool wrapped in controls it does not need trains people to route around them.

Status: DraftOwner: ____Date: ____Revisit when: framework scope changes or a new data class enters the tenant

OpenQuestions you need to answer

  • Which frameworks formally apply to which data classes — CJIS, HIPAA, state records law, PCI-adjacent for payment workflows — and who is the authority that answers this, not guesses at it?Why it matters: the P-Card workflow touches purchasing data; the answer shapes §04’s control set for it.
  • What is the records retention schedule for app-held data, and who is the records custodian for a SharePoint list?
  • What is the current state of Purview DLP, and does it inspect anything in the app-backing sites?
  • Are sensitivity labels in use, and do they enforce anything or merely mark?
  • What is the audit log retention period today, and what is the longest retention obligation any solution carries?
  • Who signs off that a solution is compliant before it ships, and what artifact records that sign-off?
  • What is the breach/incident procedure specific to app data, and how does it differ by classification?
Map data class → framework → required controls → evidence. This is the table §04 and §15 both point at.
List, classification, retention period, disposition method, custodian.
§06Licensing: premium vs. non-premiumA cost-and-capability framework, not a reflex

“Never go premium” and “just buy premium” are both reflexes. The standard here is a framework: premium is a purchase of specific capabilities (Dataverse’s relational integrity and security roles, premium connectors, higher limits) at a specific multiplication factor (per user? per app? per flow?). The decision is legitimate only when the capability gap and the multiplication factor are both written down.

Why the multiplication factor is the whole game: the same premium connector costs radically different amounts depending on where it lives. In an app, it can flag every end user for premium licensing. Contained in a flow (per DA-001), the licensing question narrows to the flow layer — where a single process-style license or one licensed owner may cover the workload. Architecture placement, not the connector itself, sets the bill.

Decision framework — work these in order

#QuestionWhy it’s asked here
1Can standard connectors + flow-mediated access (DA-001) meet the requirement at all?Most form/approval/reporting workloads can. Exhaust the free architecture before pricing the paid one.
2If not, what is the specific capability gap — relational integrity, security roles, volume limits, a particular connector?“Dataverse is more professional” is not a gap. Named gaps can be priced and challenged; vibes cannot.
3What is the multiplication factor — per-user × how many users, per-app, or per-flow?This converts the gap into dollars. It is also where flow containment often collapses the cost by an order of magnitude.
4What does the SharePoint-based alternative cost in engineering effort and accepted limitations?Premium avoided is not free — it’s paid in workarounds. Compare honestly in both directions.
5Who signs off, and where is the decision recorded with its expiry/revisit trigger?Licensing terms change more often than architectures. Every premium decision needs a revisit date.

Precedent worth recording: the P-Card approvals app was re-platformed from Dataverse to SharePoint lists after a licensing review — a live example of question 3 changing the answer. Write up that case below while the details are fresh; institutional memory of why is exactly what this document exists to hold.

OpenQuestions you need to answer

  • What licenses does the organization actually hold today (seeded plans, per-app, per-user, process/flow-level), and where is that inventory kept current?
  • What is the approval threshold — can a maker decide premium for a 5-user tool, or does every premium decision route through the same gate?
  • At what point does an app’s scale or data sensitivity force the Dataverse conversation regardless of cost (security roles, auditing, relational integrity)?
  • How do you monitor for accidental premium — a maker dropping a premium connector into an app and flagging every user?Why it matters: the platform will happily let this happen silently; the license true-up will not be silent.
  • What is the standing answer for AI Builder / Copilot Studio credits, which carry their own consumption model?
Document the original Dataverse design, the licensing discovery, the re-platform decision, and what it cost/saved. Date and sign it.
Record current entitlements and the ratified approval path.

III · Power Platform

§07Data access patternsHow apps read and write data — the decision everything else hangs on

Data access is the highest-leverage architectural decision in a Power Platform solution. It determines licensing exposure, the security model, performance characteristics, and how much of your logic lives server-side versus in the app. It is decided first because most other sections inherit constraints from it.

Decided Decision DA-001

Power Automate flows are the primary method for all data read and write operations to SharePoint lists

Standard

Canvas apps do not query or patch SharePoint lists through direct SharePoint connectors with lookups. Instead, apps call Power Automate flows (Power Apps V2 trigger) that perform the read/write server-side and return only the data the calling user is entitled to see.

Why — the reasoning
  • Licensing containment. It prevents the app from becoming premium if any upstream connectors are premium. When the app itself only ever touches the flow boundary, premium connector usage is isolated to the flow layer instead of flagging every app user for premium licensing. The premium cost surface stays at the flow, where it can be evaluated and licensed deliberately, rather than silently multiplying across the entire user base of the app.
  • Server-side row-level security. Flows allow server-side filtering that enforces row-level security without exposing the entire list to end users. The flow evaluates who is asking and returns only their rows and only the columns the app needs. Direct SharePoint connectors force the logged-in user’s identity and expose all columns and rows the user can reach — which breaks granular permission models. SharePoint list permissions are too coarse to express “managers see their team’s records; submitters see their own,” but a flow can express exactly that.
Tradeoffs accepted
  • Latency. A flow round-trip is slower than a direct connector call. Acceptable for form-and-approval workloads; design galleries around explicit “load” moments, not keystroke-by-keystroke queries.
  • No delegation via galleries. The app receives a JSON payload, not a delegable data source. Pagination, sorting, and filtering must be designed into the flow contract instead of assumed from the connector.
  • Connection ownership matters. The flow runs under its connection owner’s identity, not the end user’s. This is precisely what makes row-level security possible — and precisely why connection ownership must be disciplined (see §03 and §04).
  • Audit trail shifts. SharePoint’s Created By / Modified By will show the connection identity. The actual acting user must be stamped into dedicated columns by the flow.
  • More moving parts. Every data operation is now app + flow + list. The flow envelope standard (DA-002) and naming conventions (§01) exist to keep this navigable.
Mitigations

Standard response envelope with explicit success/error contract (DA-002) · service-account-owned connections with run-only sharing configured to use the owner’s connection (§04) · user-stamping columns written by the flow · pagination parameters built into every read flow that can return >100 rows.

When direct connectors are still acceptable

Define the exceptions explicitly so this rule doesn’t get eroded case-by-case. Candidates: small, non-sensitive reference/choice lists where every user may legitimately see every row; personal productivity apps with a single user. Confirm and list the sanctioned exceptions below.

Status: DecidedOwner: JohnDate: ____Revisit when: Microsoft changes premium licensing triggers for apps, or delegated row-level security becomes available for SharePoint-backed canvas apps

External corroboration: Microsoft’s own canvas app standards white paper (Appendix D) recommends this same layering for SQL back ends — routing data operations through a flow that calls stored procedures — for performance, for decoupling the app from schema changes, and for tighter security via a restricted execute-only identity. DA-001 applies that reasoning to SharePoint, with licensing containment added on top.

Draft Decision DA-002

Every app-facing flow returns a standard response envelope

Proposed standard

All flows triggered from canvas apps end in a single Respond to a Power App action returning, at minimum: success (boolean), message (text, human-readable), and data (JSON text, parsed in-app with a typed schema).

Why

Because DA-001 routes everything through flows, the app–flow boundary becomes the most-crossed interface in every solution. If each flow invents its own return shape, every screen needs bespoke error handling and nothing is reusable. One envelope means one error-handling pattern in the app, one testing pattern for flows, and new team members learn the contract once.

To confirm

Exact field names and casing · whether data is a JSON string parsed with ParseJSON() or multiple typed output fields · standard error codes, if any.

Status: DraftOwner: ____Date: ____Revisit when: ratified or replaced

OpenQuestions you need to answer

  • What is the pagination contract for read flows — page size, page token or skip count, and who enforces the cap?Why it matters: without a contract, someone will eventually return 5,000 rows in one response and blame the platform for the timeout.
  • What is the app-side caching policy — which datasets get loaded into collections once per session, and which are re-fetched on every screen visit?
  • What is the timeout and retry behavior when a flow call fails mid-session? Silent retry, user-facing notification, or both?
  • How do you handle concurrent edits — last-write-wins, a version/ETag check in the update flow, or record locking via a status column?Why it matters: approval apps invite two approvers acting on the same record within seconds of each other.
  • How are attachments and images handled, given the flow-mediated model? (Attachments often tempt teams back to direct connectors — decide the pattern before that happens.)
  • What payload size is “too big” for a single Respond action, and what is the fallback pattern when you hit it?
  • Do read flows and write flows live in the same flow per entity, or separate flows per operation (Get / Create / Update / Delete)?
Space reserved. When you answer a question above, write the decision — with its why — here or promote it to a numbered decision record.
§08Connector strategyEvery connector is a dependency, a license question, and a data-exfiltration path

A connector is three things at once: a capability, a licensing trigger, and a hole in your data boundary. Strategy means deciding all three deliberately — which connectors are sanctioned, how new ones get approved, and how DLP policy encodes those answers so the platform enforces them instead of relying on memory.

Why an approved-connector list, not ad-hoc choice: connectors are chosen at the moment of maximum optimism (mid-build, deadline looming) and audited at the moment of maximum pain (incident, license true-up, compliance review). A pre-approved list moves the scrutiny to the cheap moment. It also protects DA-001: one unvetted premium connector dropped directly into an app re-triggers exactly the licensing exposure the architecture was designed to contain.

OpenQuestions you need to answer

  • What is the sanctioned connector list for app-layer use vs. flow-layer use? (Under DA-001 these are different lists — the app layer should be close to “SharePoint via flows, Office 365 Users, and little else.”)
  • What do your DLP policies actually say today — which connectors sit in Business vs. Non-Business vs. Blocked, and does that match this document or contradict it?Why it matters: an undocumented DLP policy is a standard someone else wrote for you.
  • Who approves adding a connector to the sanctioned list, and what evidence is required (data touched, auth model, license class, vendor posture)?
  • What is the policy on the HTTP connector and custom connectors — banned, approval-gated, or free? Who reviews the endpoints they call?
  • Are consumer-grade connectors (personal cloud storage, social) blocked tenant-wide, and is that documented here with the reasoning?
  • How do government-cloud constraints affect connector availability, and where do you record “not available in our cloud” findings so they aren’t rediscovered quarterly?
  • What happens when a needed connector is premium — what is the escalation path into the §06 decision framework?
List approved connectors, their layer (app / flow), license class, DLP group, approval date, and the reason each was admitted.
Record the ruling and its why.
§09Flow design patternsUnder DA-001, flows are your API layer — build them like one

Because DA-001 routes all data operations through flows, flow quality is solution quality. These patterns treat each app-facing flow as a small API endpoint: typed inputs, predictable outputs, explicit error handling, and no silent failure.

DraftProposed patterns — each with its why

PatternPracticeWhy
Typed trigger inputsPower Apps V2 trigger with named, typed parameters; never parse a single concatenated string.Typed inputs are self-documenting, validated at the boundary, and don’t shatter when a value contains your delimiter.
Try / Catch / FinallyThree scopes: main logic in Try; Catch runs on failure (configure run-after) and returns the standard envelope with success:false; Finally for cleanup/logging.An unhandled flow failure looks like an infinite spinner to the app user. Catch converts every failure into a message the app can render and a person can report.
Single exitExactly one Respond to a Power App per outcome path, always returning the DA-002 envelope.Multiple ad-hoc respond actions with different shapes make the app’s parsing brittle and reviews unreliable.
Renamed actionsEvery action renamed to intent: Get requests for approver, not Send an HTTP request 2.Run history is your production debugger; its readability is set at design time.
Child flows for shared logicReused logic (audit logging, notification formatting, permission checks) extracted into child flows inside the same solution.Copy-pasted logic across flows drifts; the third copy is where the bug lives.
Deliberate retry policyRetry policy reviewed on every external action; writes that aren’t idempotent get retries disabled or an idempotency check.Default retries on a non-idempotent create can produce duplicate records that look like user error.
Concurrency awarenessLoop concurrency set consciously; ordered operations forced sequential and labeled as such.Parallel “Apply to each” is a free speedup right up until ordering mattered.
Comments at decision pointsNotes on any action embodying a business rule, citing the rule’s source or decision ID.Business rules encoded silently in a condition are unrecoverable when the author leaves.

OpenQuestions you need to answer

  • What is the synchronous budget — how long may an app-facing flow run before it must be redesigned as async (queue + notify) instead of making the user wait?
  • Where do flow errors go besides the envelope — a logging list, email to owners, Teams channel? Who is on the hook to look?
  • Are solutions mandatory for all flows (enabling connection references and environment variables), including quick utility flows?
  • What is the standard for environment variables — which values must never be hard-coded (site URLs, list names, recipient groups)?Why it matters: hard-coded URLs are the #1 reason a “tested” flow fails on the first day in production.
  • Do you require an idempotency strategy for create operations (dedupe key, existence check), and what is the default?
  • What is your policy on nested loops and per-item lookups vs. Filter Query — when is a design sent back in review for performance?
Confirm/amend the table above; record additional house patterns with their reasoning.
§10Canvas app structurePredictable internals: any maker can open any app and know where things live

Canvas apps have no enforced architecture — every app is a folder of screens and formulas arranged however its author was feeling. Structure standards exist so the team’s apps converge on one internal shape: where state lives, how screens talk to each other, how errors surface, and what the user sees while data loads.

DraftProposed structure — each with its why

AreaPracticeWhy
StartupPrefer App.Formulas (named formulas) over piling logic into App.OnStart; use App.StartScreen, not a Navigate in OnStart.Named formulas are lazy, cacheable, and declarative — faster startup and no “did OnStart finish yet?” race conditions.
State disciplineGlobal variables for session-wide truth, context variables for screen-local UI state, collections for tabular working data — never interchangeably.Most “mystery bugs” in canvas apps are state written from an unexpected place; scope discipline shrinks the search space.
Data loadingExplicit load pattern per screen: loading spinner state, flow call (per DA-001), IfError handling, empty-state UI.Flow-mediated access makes loading visible; designing the load moment beats pretending it’s instant.
Error surfaceAll flow-call failures route through one reusable notification pattern (component or helper formula) showing the envelope’s message.One error pattern means users report consistent messages and makers debug from one place.
ComponentizationShared chrome (headers, nav, dialogs) built as canvas components — in a component library once used by 2+ apps.Copy-pasted headers drift within a month; a library makes consistency the default instead of a chore.
ThemingModern (Fluent 2) controls with a defined theme object/tokens; no hex values scattered in properties.Central tokens make rebrands and accessibility fixes one change, not three hundred.
ResponsivenessContainers (horizontal/vertical) with explicit min sizes; decide per app whether it must survive phone widths.Bolting responsiveness on later is a rebuild; deciding “desktop-only, and here’s why” is also a valid recorded decision.
AccessibilityAccessibleLabel on interactive controls, tab order verified, contrast checked against tokens.Public-sector tools carry accessibility obligations; retrofits are expensive and morale-sapping.

BaselineMicrosoft’s white paper positions — proposed stance on each

The same Microsoft white paper (Appendix D) covers app construction — but its feature coverage stops at late 2018, before named formulas, modern controls, or components matured. That makes it a perfect exercise for this document’s philosophy: a baseline is an input, not an authority. Proposed stances below; confirm each and record divergences.

Paper’s positionStanceWhy
Initialize apps in OnStart (routing, globals, run-once code)SupersedeThe paper predates App.Formulas and App.StartScreen, which solve the exact pain it warns about — that debugging OnStart requires closing and reopening the app. Our startup row above stands. The paper’s own preface predicts this: it says its practices will age as the platform evolves.
Encapsulate screens — pass what a screen needs as context via Navigate; the screen loads its own dataAdoptScreens with multiple entry paths don’t break, and they stay reusable. Reaching across screens to a gallery’s .Selected couples screens invisibly and fails the moment a second path to the screen exists.
Minimize control count — one control with logic over stacked visibility-toggled variantsAdoptEvery control is package weight and tree-view noise; logic in one property beats archaeology across four overlapping controls.
Comment heavily — comments are stripped from the client packageAdoptZero runtime cost removes the only argument against commenting. §09 already requires comments at decision points; extend the habit into app formulas.
Unlinked, maker-only documentation screens listing variables and collectionsAdaptUseful as an inventory that travels inside the app itself — but it cannot replace the runbook (§16); an app screen can’t hold escalation paths or environment maps.
Hidden configuration screen for app settingsSupersede / AdaptFor environment-shaped values, solution environment variables (§15) win — the paper itself lists the fatal downside (republish to change a value). Keep the hidden-screen idea only as a maker-gated debug panel, with visibility checked against maker/admin identity.
Concurrent() for independent data callsAdoptSequential calls serialize load time; parallelizing independent ones is the cheapest startup win available, and it retires the old hidden-timer workarounds the paper describes.
Cache small, frequently-read datasets in local collectionsAdoptPairs naturally with DA-001: flow responses land in collections once per session. §07’s caching question decides which datasets qualify.
Package hygiene — prefer SVG over PNG, delete unused media and screens, split admin app from user appAdoptLoad time and blast radius: a separate admin app means user-app changes need a smaller test pass, and vice versa.
Formula hygiene — one UpdateContext per event, ClearCollect over Clear;Collect, CountIf over Count(Filter())AdoptSmall individual wins that compound; enforce through the §16 review checklist rather than memory.
Build one form factor, finalize it, then convert to the otherAdopt when both neededKeeps controls, expressions, and variables identically named across phone and tablet variants, halving the support surface.
SaveData / LoadData for cachingCautionWorks only in the mobile player — silently unavailable in browsers. Feeds the offline-stance question below; never let a browser-first app depend on it.
Every control on a screen belongs to a groupAdopt — via containersGroups make intent legible in the tree view and let you move or hide a whole cluster at once. Modern layout containers now serve that purpose and handle responsiveness (see the row above), so treat containers as the current implementation of the paper’s grouping intent rather than a competing idea.
Click targets — lay a transparent rectangle over a control group and use its OnSelectAdoptThe alternative the paper rejects — putting logic in one member control and having its siblings call Select() — breaks whenever the group’s membership changes. A rectangle decouples the clickable region from whatever sits under it and lets you shape the target deliberately.
Relative styling — base one control’s color, fill, and position on another’sAdaptRight instinct, wrong altitude for color. Chaining control-to-control builds a brittle dependency web that breaks silently when the parent is deleted. Use relative styling for position and size within a screen; use theme tokens for color, where one change reaches everything.
Galleries for anything repetitive or linear — including a Gallery as a read-only view form instead of a Display FormAdoptHand-placed control rows are faster to build once and miserable to change forever. The Table()-driven gallery view form is genuinely quicker to restyle than editing separate data cards.
Forms for repetitive data entry fieldsAdapt — see the constraintForms buy grouping, validation, and relative styling, but under DA-001 they cannot submit: SubmitForm() writes through the data source connector we deliberately removed from the app. Use forms for layout, Valid, and Updates — then pass those values into the flow call. Any maker reaching for SubmitForm has left the architecture without noticing.
Avoid nesting — unnecessary DataCards and canvases, nested galleries, nested ForAllAdoptEach level multiplies evaluation work and hides where a formula actually runs. Note the paper corrects itself here: nested galleries were never deprecated — they’re permitted, just costly.
Use Format text; comments and whitespace are stripped from the client packageAdoptRemoves the last excuse for unformatted formulas and thin comments — readability is free at runtime.
Periodically republish apps to pick up platform optimizationsAdopt — fold into §15Some platform improvements apply only to apps published at or after a given version. An app untouched for two years is still running on the runtime it was born with. This belongs in the release cadence, not in anyone’s memory.
Preview and experimental app settings (delayed load, explicit column selection, non-blocking OnStart)Case-by-case — never silentlyThese move real performance numbers, but they change runtime behavior, and the paper’s own warning is to test thoroughly. A preview feature enabled in production is a decision record here, not a checkbox someone flipped on a Friday.

DraftDebugging & diagnostics

The white paper devotes a full section to debug affordances built into the app itself: a semi-transparent canvas panel showing live variable state, toggle controls that let a maker force an error condition and watch the UI react, and an identity check that keeps all of it invisible to end users. That instinct matters more under DA-001, not less — when data arrives as a JSON payload from a flow rather than a bound data source, “what did the app actually receive?” becomes a question you ask constantly, and a debug panel answers it without a republish.

Two of its three patterns still hold. The debug panel (a maker-only overlay listing globals, collection counts, and the last flow envelope) is worth building into the house screen template. The toggle-control error pattern is superseded: it was a workaround from an era before formula-level error handling, and IfError / IsError now express the same thing directly, closer to the failure. The third — how you decide who sees any of it — needs a decision, because the paper’s answer conflicts with a standard already recorded in this document.

Draft Decision AS-001

Debug affordances are gated on an Entra ID security group — never a hard-coded email list

Proposed standard

Apps may ship a maker-only debug panel (global variable state, last flow response envelope, environment label, app version). Its visibility is driven by membership in a named security group, resolved once at startup into a global. Not by a list of email addresses in a formula.

Why
  • The paper’s example hard-codes three email addresses into OnStart. That collides head-on with §15’s rule against hard-coded configuration: adding one person means an edit, a republish, and a deploy through every environment — and removing someone who left requires remembering the formula exists at all. A group check moves the list to data and inherits the joiner/leaver process for free, exactly as §04 does for app sharing.
  • The paper’s cleaner alternative — the Power Apps for Makers connector’s GetAppRoleAssignments — ties debug rights to app-edit rights. That’s the wrong coupling under §04, where production maker access is deliberately restricted: the person on support duty may legitimately need diagnostics without the ability to edit a live app.
Verify before adopting either alternative

Confirm the license class of the Power Apps for Makers connector in our tenant before anyone adds it to an app. If it is premium, dropping it into the app layer for debug convenience would flag every end user for premium licensing — precisely the accidental-premium failure DA-001 and §06 exist to prevent, arriving through the back door of a diagnostics feature. This is a live worked example of §06’s monitoring question, not a hypothetical.

Tradeoffs accepted

One more group to provision and review · a membership check costs one call at startup (cache it in a global; don’t re-check per screen) · debug UI ships inside the production package, so it must be genuinely inert for everyone else — hidden, not merely small.

Status: DraftOwner: ____Date: ____Revisit when: the platform ships a first-class maker diagnostics surface, or Monitor covers the flow-boundary payload well enough to retire the panel

OpenQuestions you need to answer

  • What is the screen inventory convention — is there a required set (Home, Detail, Admin, About/Version) and a maximum before an app should split?
  • When does the component library become mandatory, and who curates it?
  • What is the versioning display standard — do apps show a version and environment label to users, and where?Why it matters: “which version are you on?” is the first support question; the answer should be on screen.
  • What is the performance budget (startup time, screen-load time) and how is it measured before release?
  • What is the offline stance — explicitly unsupported, or designed for where field work demands it?
  • Which formula patterns are banned or discouraged in review (e.g., heavy logic in visible/OnChange chains, N+1 lookups inside galleries)?
  • Which security group gates debug affordances — one per app family, or one across all Power Platform solutions?Why it matters: one group is easier to run and leaks diagnostics across unrelated apps; per-app groups are precise and multiply the review burden.
  • Is the Power Apps for Makers connector premium in our tenant, and does that alone disqualify it from the app layer? (Feeds §08’s sanctioned list and §06’s accidental-premium monitoring.)
  • May makers enable preview or experimental app settings freely in dev, and what evidence is required to carry one into production?
  • Does the house screen template ship the debug panel by default, or is it added per app on request?
Confirm/amend the table; add the house screen template and component library rules with reasoning.

IV · Microsoft 365 services

§11SharePoint & OneDriveTreat lists as a database you happen to get for free — design them like one

DA-001 makes SharePoint the system of record, accessed through flows. That shifts where SharePoint’s sharp edges cut: delegation warnings in the app matter less, but list design, throughput limits, and site permissions matter more — because the flow layer inherits them all.

Why list design still matters even though flows do the querying: the 5,000-item list view threshold and unindexed-column query failures apply to the flow’s OData calls too. A filterable column that isn’t indexed works fine in the demo with 200 rows and fails at row 5,001 — in production, at quarter-end. Index the columns your flows filter on at design time, because indexes on large lists are painful to add later.

DraftProposed practices — each with its why

AreaPracticeWhy
Site architectureOne dedicated site (or site collection) per solution family; app lists never live in a collaboration site people also use for documents.Permissions boundaries follow sites. Mixing app data with human collaboration makes §04’s lockdown impossible to reason about.
Column creationCreate every column with its final internal name (PascalCase, no spaces), then set display names; record the schema in the solution docs.Internal names are immutable and appear in every OData filter; Approval_x0020_Stage is a lifelong tax on readability.
IndexingIndex every column used in flow Filter Queries before the list grows; document which queries each index serves.See callout above — the failure mode is delayed and production-shaped.
LookupsPrefer storing keys (text/number) resolved by flows over native lookup columns for high-volume lists; keep native lookups under the per-view limits.Lookup columns count against strict view limits and complicate OData; flow-resolved keys keep the schema portable.
User stampingDedicated columns (SubmittedBy, ActionedBy, timestamps) written by flows, since built-in Created/Modified By show the service identity.Restores a true audit trail under DA-001’s identity model; auditors will ask.
Choice vs. reference listVolatile or reportable value sets live in a reference list read by flows; only truly static sets use Choice columns.Choice edits are a schema change requiring a deploy mindset; reference lists make it data.
Versioning & retentionList versioning on for systems of record; retention/trim policy decided per list, not defaulted.Versioning is your cheapest undo and your audit evidence — but unlimited versions on a hot list is a quiet storage leak.
AttachmentsPrefer a document library with metadata linking back to the list item over native list attachments, when files matter.Native attachments are hard to secure granularly, hard to query, and invisible to most governance tooling.

OpenQuestions you need to answer

  • Who provisions app sites — makers, or an admin gate — and from what template?
  • What is the archival strategy when a list approaches size thresholds (yearly archive lists, a rollover flow, or deletion policy)?
  • Where does the canonical schema documentation live for each list, and is it generated or hand-maintained?
  • What is the standard for calculated columns and Power Automate-maintained denormalized fields — allowed, and who owns keeping them true?
  • How are list-level backups/restores handled beyond versioning — is Recycle Bin + versioning officially “enough,” and for which data classes is it not?Why it matters: the honest answer differs for a room-booking list vs. anything touching regulated data.
Confirm/amend the table; attach or link the canonical list-design checklist used at provisioning time.
§12TeamsWhere approvals actually get seen — and a site architecture risk hiding in a checkbox

Teams matters here for two reasons: it is the surface where flow-driven approvals reach a human, and it silently provisions the SharePoint sites §11 tries to govern. Both need decisions.

Why private channels deserve their own decision: a private channel does not live in the parent team’s site — it gets its own site collection, with its own permissions, outside whatever structure §11 established. One checkbox, and a governed site architecture acquires an ungoverned satellite that no one inventoried. Shared channels behave differently again. This is not a Teams preference; it is a SharePoint architecture decision wearing a Teams costume.

DraftProposed practices — each with its why

AreaPracticeWhy
Approval deliveryPick one primary channel for approval requests — adaptive card in Teams, email, or in-app queue — and make the others secondary, consistent across solutions.Approvers learn one habit. Three delivery patterns across three apps means every app gets its own training and its own “I never saw it.”
Adaptive cards carry state honestlyA card that has been actioned updates to show the outcome; cards never leave a stale “Approve / Reject” pair sitting in a chat.A stale actionable card invites a second approval on a record already decided — the concurrency problem from §07 arriving through the UI.
Cards link, apps decideThe card triggers the same flow the app would; approval logic lives in the flow, never duplicated in the card handler.Two implementations of one business rule drift. The card is a doorway, not a second front door with its own lock.
Team provisioningTeam creation is gated and templated, and the resulting site is inventoried under §11.Ungated Teams creation is ungated SharePoint site creation. See §02’s self-service question — same decision, different admin center.
Private channelsDecide: permitted, restricted, or off — and record the site-collection consequence in the reasoning.See the callout above. The default is “on,” which is a decision you inherit rather than make.
LifecycleGroup expiration and an archival path decided for app-associated teams.Teams accumulate faster than any other object in the tenant, each one dragging a site and a mailbox behind it.
Apps embedded as tabsIf a canvas app ships as a Teams tab, decide it up front — the layout, auth context, and deep-link behavior differ from the browser.Retro-fitting an app into a tab surfaces every hard-coded width and every assumption about the URL.
Draft Decision TM-001

Teams-connected sites are never app data stores

Proposed standard

App lists live in a dedicated SharePoint site with no Team attached. Where a solution also wants a Team for conversation, the Team is a separate artifact from the site holding the data.

Why — the reasoning
  • Team membership grants access to the Team’s underlying site. So adding a colleague to a chat silently grants them list access — and SEC-001’s premise is that end users hold no direct list permissions. The row-level security this entire architecture rests on can be undone by someone being helpful in Teams.
  • Worse, it is undone by a Teams action rather than a SharePoint one, so it never crosses an administrator’s desk and appears in no permissions review until someone goes looking.
Tradeoffs accepted

Two artifacts to provision and explain instead of one · users find it counterintuitive that the app’s Team does not hold the app’s data, so the runbook (§16) has to say why.

Status: DraftOwner: ____Date: ____Revisit when: Teams membership stops implying membership of the underlying site

OpenQuestions you need to answer

  • Are approvals delivered by adaptive card, email, or the app itself — and what is the fallback when the primary channel fails?
  • Are private channels permitted? If so, how do their site collections get inventoried and secured under §11 and §04?
  • Who can create teams today, and does that match §02’s answer on self-service group creation?
  • What is the team naming and expiration policy, and who handles the expiry notifications when the owner has left?
  • Do any solutions ship as Teams tabs, and is that a supported pattern here or an exception?
  • Are shared channels (external participants) in scope, and what does that change for §04 and §05?
Record the approval channel, private/shared channel ruling, provisioning gate, and lifecycle policy — with reasoning.
§13Exchange OnlineWho the notification appears to come from, and why it matters

Under §07, flows run as a service identity — which means that by default, every notification an app sends appears to come from a service account nobody recognizes, or worse, from whoever happened to own the connection. Mail is the most visible artifact most solutions produce. It deserves a deliberate answer.

Why app mail sends from a shared mailbox: three failures collapse into one fix. A message from svc-pp-pcard@… looks like phishing and gets ignored or reported. A message from a person’s account makes that person the support desk forever, and breaks the day they leave. Neither has a reply path — and users will reply, because a message from a human invites a reply to a human. A shared mailbox gives the message a recognizable identity, a monitored reply address, and continuity independent of any employee.

DraftProposed practices — each with its why

AreaPracticeWhy
Send from a shared mailboxApp notifications send as a solution-specific shared mailbox, with the service account granted explicit Send As permission.See the callout above. It also puts the sending identity in the same governed inventory as everything else in §03.
Send As, not Send On BehalfChoose deliberately and record it. Send As shows only the mailbox; Send On Behalf exposes the service account in the header.“Sent on behalf of svc-pp-pcard” reintroduces exactly the phishing-shaped confusion the shared mailbox removed.
Reply-to is monitoredEvery app-sent message has a reply path that reaches a human, or explicitly states that replies aren’t read and where to go instead.Unmonitored mailboxes turn user replies into silence, and silence into a support ticket about a different thing entirely.
Security groups, not distribution listsApp audiences resolve to the §02 security groups. Distribution lists are for mail, not authorization.Two parallel membership lists always diverge, and the DL is the one nobody reviews.
Mail is not a system of recordNotifications inform; SharePoint holds the record. Nothing important exists only in an inbox.Mailboxes get deleted, filtered, and retained on schedules that have nothing to do with your app’s obligations (§05).
Throttling is a design constraintKnow the per-connection send limits before designing a flow that mails a large audience; batch or use a distribution mechanism built for volume.Throttling surfaces as random partial failures across a run — the hardest class of bug to reproduce and the easiest to prevent.
Draft Decision MX-001

System mail sends from a named shared mailbox with a real owner

Proposed standard

Flow-generated mail sends from a named shared mailbox per solution family, with Send As granted to the service identity. Not from a personal account, and not from an address nobody reads.

Why — the reasoning
  • The From line is the system’s public identity, and mail is the most visible artifact most solutions produce. Sending as a person makes the system unattributable and breaks the week that person is offboarded.
  • An unmonitored no-reply address does not stop people replying — it just means their reply lands nowhere, and they conclude the system is broken. Users will always answer the thing that emailed them.
  • A shared mailbox is the only option where the identity is stable across staff changes, replies reach a human, and the audit trail names the system rather than an employee.
Tradeoffs accepted

One more mailbox to provision and include in access reviews · Send As must be granted to the service identity and reviewed (§03) · “monitored” is a claim that requires a named owner — without one it is simply a no-reply address with better branding.

Status: DraftOwner: ____Date: ____Revisit when: the platform offers a first-class system-sender identity

OpenQuestions you need to answer

  • Which shared mailbox belongs to which solution, and who provisions and monitors it?
  • Send As or Send On Behalf — and is the permission grant documented alongside the service account in §03?
  • May app mail reach external recipients, and what changes for §05 when it does?
  • Do any mail flow rules or connectors silently modify or quarantine app-generated mail?Why it matters: a transport rule written for a different reason is a classic cause of “the flow ran but nobody got the email.”
  • What is the largest audience any single flow mails, and does that sit inside the connector’s send limits?
  • Are notification templates standardized (branding, tone, footer, unsubscribe/where-to-go-for-help), and where do they live?
Record the shared mailbox model, permission choice, monitoring owner, and template location — with reasoning.
§14Microsoft Graph & PowerShell automationThe ungoverned corner of most M365 shops

Scripts are where governance quietly stops. Apps get reviewed, flows live in solutions, and meanwhile a PowerShell file on somebody’s desktop holds an application-permission token that can read every mailbox in the tenant, runs on a scheduled task nobody documented, and is understood by exactly one person. This section exists to make scripts first-class: sourced, scoped, reviewed, and survivable.

Why application permissions get a named approval and delegated permissions usually don’t: a delegated permission can never exceed what the signed-in user could already do — the blast radius is bounded by a human’s own access. An application permission is tenant-wide and unbounded by any user: Sites.Read.All means all, forever, with no one in the loop. That is not a bigger version of the same thing; it is a different category of risk, and it should cost a conversation.

DraftProposed practices — each with its why

AreaPracticeWhy
Scripts live in source controlEvery script that touches production is in a repository with history — not on a desktop, not in a mailbox, not in a chat message.A script without history is a black box: you cannot see what changed, when, or why, and you cannot recover it when the laptop dies. This is the same argument §15 makes for solutions.
Delegated first, application by exceptionPrefer delegated permissions. Application permissions require named approval and a recorded justification.See the callout above. The default should be the one bounded by a human.
Least-privilege scopes, documentedEach script declares the exact scopes it needs and why, in its header. .All scopes are challenged in review, not waved through.Scopes are granted once in a hurry and inherited forever. The header is what a reviewer reads two years later.
Certificates over client secretsUnattended auth uses certificate credentials; secrets only where certificates aren’t supported, with a short expiry and a rotation owner.Secrets leak in transcripts, config files, and screenshots, and expire on a date nobody diarized. Certificates raise the floor on both problems.
One app registration per purposeScripts don’t share a single do-everything registration; scope and lifecycle are per function.A shared registration means the union of every permission any script ever needed — and one compromise reaches all of it. Cf. §03’s blast-radius question.
Writes are idempotent and support a dry runAnything that changes state supports -WhatIf (or an explicit dry-run switch) and can be re-run without duplicating its effect.Bulk scripts are run under pressure against production. A dry run is the cheapest possible confidence, and idempotency makes “did it half-finish?” a survivable question.
Standard headerPurpose · author · permissions required · identity it runs as · where it runs · last verified date.Six lines that answer every question the next person will have, including whether the thing still works.
Module currencyTrack which modules a script depends on and whether they’re still supported; pin versions for unattended runs.The legacy MSOnline and AzureAD modules are retired in favor of the Graph SDK — scripts written against them are on borrowed time, and they fail silently-then-suddenly. Pinning stops an unattended job from breaking on someone else’s release day.
Draft Decision PS-001

Automation authenticates app-only with certificate credentials

Proposed standard

Scheduled and unattended automation authenticates as a registered application using certificate credentials, with Graph permissions scoped to the task. Never as a delegated user, and never with a client secret pasted into the script.

Why — the reasoning
  • Delegated auth inherits a human’s entire permission set — far more than the job needs — and stops working the week that person leaves. The failure surfaces in production, during offboarding, with no obvious cause.
  • App-only lets permissions be scoped to exactly the job, and the consent record becomes a reviewable artifact rather than tribal knowledge.
  • Certificates over secrets: a secret in a script is exfiltrated the moment the file is shared or committed, and its expiry becomes an unplanned outage the day it lapses. Certificate auth keeps the private key out of the script entirely.
Tradeoffs accepted

Certificate lifecycle is real work and must be owned (§03) · app-only Graph permissions are tenant-wide by default, so scoping needs deliberate constraint rather than the convenient wildcard · local development is genuinely more awkward than delegated auth, which is the pressure that breaks this standard if nobody plans for it.

Status: DraftOwner: ____Date: ____Revisit when: managed identity covers the workloads we script

OpenQuestions you need to answer

  • Where do scripts live today — and honestly, how many production-affecting scripts are running from a personal machine on a scheduled task right now?Why it matters: this is the single most common ungoverned-automation finding, and the inventory is usually shorter than feared and scarier than expected. Find it before an offboarding does.
  • Where should they run — Azure Automation, Functions, a managed jump host, a pipeline? What is the cost and the approval path?
  • Who approves an application permission, and what evidence is required?
  • One app registration per script, per function, or per team? What blast radius is acceptable?
  • Certificates or secrets, and what is the maximum expiry allowed for each?
  • Who reviews scripts before they run against production, and against what checklist?
  • What is the inventory of existing app registrations and their granted scopes — and when was it last reconciled against what’s actually in use?
  • Are any scripts still on retired modules, and what is the migration plan and deadline?
Name, purpose, identity, scopes, where it runs, owner, last verified. The gap between this list and reality is the actual finding.
Record the repository, execution host, permission approval path, and credential model — with reasoning.

V · Delivery & practice

§15Testing & deploymentA solution isn’t done when it works — it’s done when it moves safely

Deployment is where good builds go to embarrass their authors. The standard here is boring on purpose: separate environments, solution-based packaging, externalized configuration, and a checklist that verifies the things the platform will not verify for you — especially the SEC-001 settings that silently define your security model.

Why solutions + connection references + environment variables are non-negotiable: without them, “deploying” means manually re-pointing every flow at new URLs and re-creating connections by hand — a process whose error rate is measured in outages. Connection references let the same flow bind to the service account per environment; environment variables carry site URLs and settings across the boundary. This is the difference between a deployment and a re-build.

DraftProposed baseline

AreaPracticeWhy
EnvironmentsMinimum Dev → Test → Prod for anything with real users; personal/default environment is for throwaways only.The default environment is a shared junk drawer with no promotion path; real solutions need a boundary between “being edited” and “being used.”
PackagingAll components (app, flows, connection references, environment variables, custom connectors) travel in one solution; managed in Test/Prod.Managed solutions prevent drive-by edits in production and make “what version is live?” answerable.
ConfigurationZero hard-coded environment-specific values; everything environment-shaped is an environment variable.Every hard-coded URL is a future deployment failure with your name on it.
Testing tiersMaker self-test against a written test script → peer review (§16) → UAT with named business testers and sign-off → deploy.Each tier catches a different failure class: logic, convention/security, and “that’s not how the process actually works.”
Deployment checklistA per-release checklist including: connection references bound to service account · run-only sharing verified as “Use this connection” · list permissions verified locked · environment variables set · smoke test as a real end user.SEC-001’s settings don’t survive export/import automatically and fail invisibly. A checklist is the only tool that reliably remembers.
RollbackPrevious managed version retained and restorable; app versions restorable; rollback rehearsed at least once.A rollback plan that has never been executed is a hypothesis, not a plan.

OpenQuestions you need to answer

  • Manual export/import or pipeline-based ALM — and if pipelines, which tooling is actually available and approved in your cloud/tenant?
  • Where do test scripts live, and what is the minimum test coverage for an app-facing flow (happy path + which failure paths)?
  • Who owns UAT sign-off per solution, and is sign-off recorded somewhere durable?
  • What is the release cadence and change-window policy — can production deploys happen any time, or scheduled windows only?
  • How is test data handled — synthetic only, scrubbed copies, or (decide explicitly) never production data in lower environments?Why it matters: regulated data in a dev environment is a compliance incident nobody planned.
  • What is monitored post-deploy (flow failure rates, app errors) and for how long before a release is “settled”?
Record the pipeline decision, the canonical deployment checklist, and sign-off rules — with reasoning.
§16Team collaboration practicesHow the team builds together without stepping on each other — or leaving with the knowledge

Standards fail socially before they fail technically. This section covers how work is shared, reviewed, documented, and handed off — the practices that make the other ten sections survive personnel changes, deadline pressure, and the fact that canvas apps still effectively allow one editor at a time.

Why peer review is the enforcement mechanism for this entire document: nothing in Power Platform stops a maker from ignoring every standard here. Review is the moment conventions become real — and it is also how reasoning spreads: a reviewer who asks “what’s the why on this?” is teaching the culture this document encodes. Review the why, not just the what.

DraftProposed practices

AreaPracticeWhy
Single-editor realityCanvas app work is claimed visibly (task board or channel post) before opening the app in edit mode; flows can be divided by ownership.Two people editing one canvas app produces lost work and resentment; a 10-second claim ritual prevents both.
Peer reviewEvery production-bound app/flow gets a second set of eyes against a short checklist: naming (§01), envelope (DA-002), error scopes (§09), SEC-001 settings, no hard-coded config (§15).A checklist keeps review consistent across reviewers and makes “passed review” mean something specific.
Documentation homeOne canonical location for solution docs (this guide, per-solution runbooks, schemas); links from app About screens.Documentation that must be hunted for is documentation that doesn’t exist at 2 AM.
Decision hygieneArchitectural choices made mid-build get written into this document (or the solution’s decision log) within the week, with their why.The why evaporates in about seven days; after that you’re reverse-engineering your own intent.
Knowledge spreadRegular internal show-and-tell / training rotation so every solution has at least two people who can support it; pair new makers with a reviewer.Bus-factor-one solutions turn vacations into incidents. Teaching is also the fastest way to harden a standard.
Support handoffEvery production solution ships with a runbook: owner, service identities, environment map, known failure modes, escalation path.The person on support duty should never need the original author online to do triage.

OpenQuestions you need to answer

  • Who are the named reviewers, and what turnaround is promised so review never becomes the excuse to skip review?
  • Where does the claim board / work log live, and is it actually lightweight enough that people use it?
  • What is the training and certification plan per team member, and how is time for it protected?
  • What is the intake process for new app requests — how do ideas get sized, prioritized, and assigned against this standard from day one?
  • How are citizen developers outside the core team handled — free rein in a sandboxed environment, mentorship track, or gated intake?Why it matters: this decision determines whether governance feels like enablement or like a wall.
  • What is the cadence for reviewing this document itself, and who runs that meeting?
Record reviewers, cadences, the review checklist location, and intake process — with reasoning.

Appendices

ADecision record templateCopy, fill, and file under the relevant section

Copy the block below (in the HTML source it’s marked with TEMPLATE — COPY FROM HERE) into the relevant section. Set the class to dr decided, dr draft, or dr openq, and update the stamp text to match. Every field is mandatory except Mitigations — if you can’t write the Why, the decision isn’t ready.

Open Decision XX-000

One-sentence statement of the decision

Standard

What we will do, stated so a new team member could follow it without asking.

Why — the reasoning

The tradeoff this accepts, the constraint it solves, or the failure it prevents. This field is the point of the record.

Tradeoffs accepted

What this costs us, honestly stated.

Mitigations

How the costs above are contained (optional).

Alternatives considered

What was rejected and the one-line reason each lost.

Status: OpenOwner: ____Date: ____Revisit when: ____
BChange logEvery edit to this document, dated and attributed
Why a change log in a “living” document: a living document without history is a document you can’t trust — was this rule always here, or added after the incident? The log turns the document into a record.
DateRevAuthorChange
2026-07-140.1JohnInitial draft. Ratified DA-001 (flow-mediated SharePoint data access). Seeded drafts DA-002, SP-001, SEC-001 and open-question sets for all sections.
2026-07-140.2JohnAdded full-text search. Incorporated the Microsoft canvas app coding standards white paper as baseline input (§07 corroboration note, §01 baseline + NC-001, §10 stance table); added Appendix D (References).
2026-07-140.3JohnSecond pass over the Microsoft white paper: extended the §10 stance table to 21 positions (click targets, grouping, relative styling, galleries, forms, nesting, republishing, preview settings). Flagged the Forms/SubmitForm conflict with DA-001. Added §10 debugging & diagnostics with draft decision AS-001.
2026-07-140.4JohnScope widened from Power Platform to Microsoft 365 (doc ID now M365-STD-001). Restructured into five parts and renumbered; all cross-references updated. New sections: §02 Identity & Entra ID, §05 Compliance & data governance, §12 Teams, §13 Exchange Online, §14 Graph & PowerShell automation. §11 widened to SharePoint & OneDrive. Added theme picker (8 themes) with light/dark/auto.
2026-07-150.5JohnRepaired section numbering: header numbers had drifted out of step with the contents and ids (§15 and §16 were each used twice; §01 and §06 were unused). Contents, anchors and cross-references were already correct and are unchanged. Default theme set to Jade; Graphite renders at zero saturation and is now opt-in. Seeded anchor decisions ID-001, CMP-001, TM-001, MX-001, PS-001 in the sections added at 0.4.
____   
____   
____   
CGlossaryShared vocabulary, so the standards mean the same thing to everyone
TermMeaning here
ALMApplication Lifecycle Management — how solutions move Dev → Test → Prod with versioning and rollback.
DLP policyData Loss Prevention policy — tenant rules grouping connectors into Business / Non-Business / Blocked so they can’t be combined dangerously.
EnvelopeThe standard flow response shape defined in DA-002 (success, message, data).
Flow-mediated accessThe DA-001 pattern: apps never touch lists directly; flows perform all reads/writes server-side.
Run-only userSomeone granted permission to execute a flow without editing it; their connection setting is the linchpin of SEC-001.
RLSRow-level security — users see only the records they’re entitled to, enforced here in the flow layer.
Service accountA licensed, non-personal user identity that owns production connections and flows.
Service principalA non-user Entra ID identity (app registration) used for API-level access such as Microsoft Graph.
DReferencesExternal baselines this standard draws on — and how to treat them
Why references get an appendix: baselines age and links rot. Recording what was consulted, its vintage, and where this team diverged keeps “because Microsoft says so” honest. Once ratified, this document wins over any external source — and every divergence records its why, so the next reader knows it was a decision, not ignorance.
ReferenceInformsNotes
Power Apps canvas app coding standards and guidelines — Microsoft white paper (Baginski & Dunn, with Microsoft IT contributors)§07 corroboration · §01 naming baseline · §10 construction stances · NC-001Feature coverage stops at Dec 2018 (© 2019); predates named formulas, modern controls, and components. Treat as input, not authority — see the stance table in §10. Source PDF
Microsoft Learn — Power Apps and Power Automate documentationCurrent platform behavior; supersedes aged guidance anywhere it conflictsCheck publication dates on everything — the platform moves quarterly.
____________
____________
Record the title, date consulted, what it informed, and any divergence from it — with the why.