v2.46 Built 11 Sep 2026 📋 Screen YAML Vault →

Branch Contact Groups

Live-membership replacement for ~50 Outlook contact groups in the zero mailbox — send to groups with always-current membership, no more manual weekly rebuilds. v2.0 adds a dedicated People screen, Thursday send-splitting into two independent batches, automatic BCC-locking for external addresses, per-group history, and paste-to-replace bulk roster updates.

v2.46 — added a Thursday Batch overview, and fixed GAL-added members skipping JMC/County entirely. Manage Groups now has a Thursday Batches button on the header that opens a read-only, two-column view of every group in Batch 1 and Batch 2 with live member counts, plus a count of groups with no batch set — previously the only way to check a group's batch was opening its Edit panel one group at a time. Separately, picking someone from GAL search inside Manage Members used to add them to the group immediately with blank JMC/County, forcing a second trip through Edit → pencil → JMC/County afterwards; the GAL pick now stages into the existing "Add manually" panel right next to it (prefilled with their name and email), so JMC/County get entered in the same step as the add. No SharePoint schema changes and no App.OnStart changes this round. scrManageGroups is the only screen that changed.

Older release notes have been retired from this page to keep it current and skimmable — every fix through v2.45 is already baked into the screen YAML below, so a fresh paste always reflects the latest state without needing the history. Full commit history remains in the GitHub repo.

Before You Start

  1. Create the four SharePoint lists below (all columns, exact names) on <your SharePoint site URL>.
  2. Add the two maintainer rows to BM Maintainers and your real group/member data (see the Migration Plan card below).
  3. Open Power Apps Studio → New blank canvas app (tablet layout).
  4. Add all data sources listed below (Data panel → Add data).
  5. Set App → OnStart using the code block below.
  6. Create four blank screens (name them exactly as shown), then paste each YAML in order: scrQuickContacts, scrSendEmail, scrManageGroups, scrManagePeople.
  7. These screens use manual X/Y positioning throughout. Anything too tall to fit on one screen (Manage Groups' member panel, Manage People's Add Person/Remove Leaver panels) is a centered lightbox instead of a scrolling section.

Data Connections

Add these in the Data panel before pasting any YAML:

ConnectionType
'BM Groups'SharePoint → your site → BM Groups
'BM Memberships'SharePoint → your site → BM Memberships
'BM Maintainers'SharePoint → your site → BM Maintainers
'BM History'SharePoint → your site → BM History
Office365UsersOffice 365 Users connector

SharePoint Lists

List: BM Groups

Column nameTypeNotes
TitleSingle line of textBuilt-in — the group name, e.g. "All Branch Staff". Not Required.
SortOrderNumberOptional display order. Not Required.
ThursdayBatchChoiceValues: None, Batch 1, Batch 2. Default None. Replaces the old single Thursday flag — the weekly send is split into two independent batches because one BCC of everyone overloads some recipients' mail servers. A group can only be in one batch (or neither), so the two sends never share a recipient.
PrivateYes/NoDefault No. Private groups are hidden from the Send Email screen for anyone not in BM Maintainers, but are still included in Thursday batch sends.
OfficialSensitiveApprovedYes/NoDefault No. Shown as a pill/tag to everyone, not just maintainers — lets anyone see at a glance whether OFFICIAL-SENSITIVE material can go to this group.
OfficialUseOnlyYes/NoDefault No. Independent of OfficialSensitiveApproved — a group can carry either, both, or neither.
NotesMultiple lines of text (plain text, not rich text)Optional. Shown on the group card and to everyone on the Send Email screen so people can sanity-check who they're about to email.
ManuallySetModifiedDate and Time (new)Not Required, leave blank by default. Lets a maintainer override the "Last modified" shown on the card — useful for groups migrated from an old system where the real last-changed date predates this app. Tick "Override last modified date" when editing a group to set it; untick to go back to using SharePoint's own Modified date automatically.

The old Active column has been removed entirely — it's not used anywhere in v2.0. If you built v1.0 already, delete that column.

"Last modified" note: the app shows SharePoint's built-in Modified date, and every formula that changes membership (add/remove/replace) also does a harmless self-Patch on the group's own Title so Modified reflects membership changes too, not just edits to the group record itself.

Versioning: List Settings → Versioning Settings → "Create a version each time you edit an item" = Yes.

Permissions: List Settings → Permissions for this list → Stop Inheriting Permissions → grant Edit to the two maintainers, Read to everyone else on the site.

List: BM Memberships

Column nameTypeNotes
TitleSingle line of textBuilt-in — kept as a mirror of Name purely so SharePoint's own default list view still shows something meaningful. The app itself never reads Title — as of v2.34, Name is the real field the app uses everywhere (display, search, sort, rename-cascade). Not Required.
NameSingle line of text (new in v2.34)The person's display name — the field the app actually reads and writes. Not Required.
JMCSingle line of text (new in v2.34)Bde/Area from the standard import sheet. Optional — blank hides the field on cards. Not Required.
County/ContingentSingle line of text (new in v2.34)County/District from the standard import sheet. The column name itself contains a slash, so in Power Fx it must be referenced as ThisItem.'County/Contingent' (quoted). Optional — blank hides the field on cards. Not Required.
EmailSingle line of textStore lowercase. Not Required.
GroupNameSingle line of textMust match a 'BM Groups'.Title value exactly. Plain text, not a Lookup column — this keeps every filter delegable and avoids the Lookup-column delegation issues this app family has hit before. Not Required.
NotesSingle line of textOptional per-person note, editable from Manage Groups or Manage People.

One row per person-per-group (deliberately denormalised — a person in 3 groups is 3 rows). This mirrors how Outlook contact groups actually work and keeps every query in the app a plain, delegable Filter. The new Manage People screen is a view over this same table grouped by email — no separate "people" list needed. JMC and County/Contingent genuinely belong to the membership row (not the person), since the same person could have different regional metadata in different groups — Manage People's per-person card picks whichever membership row happens to be found first as a reasonable approximation.

v2.34 migration step: after adding the new Name column, existing membership rows still have their old display name in Title and a blank Name. Backfill Name from Title once before testing the new screens, otherwise Manage People rows, member popups, search, and name sorting can appear blank even though the membership rows still exist.

Versioning: enabled, same as BM Groups — gives you a native audit trail as a backstop; the app-level BM History list below is the human-readable one you'll actually use day to day.

Permissions: same unique-permission pattern as BM Groups — Edit for the two maintainers, Read for everyone else.

Pasting the standard import sheet (v2.34)

The Replace all (paste) tab inside Manage Members now recognises two formats automatically, based on how many tab-separated columns each pasted row has:

Sheet columnMaps to
Bde / AreaJMC
County / DistrictCounty/Contingent
Rankkept as-is, first word of Name
First Namenormalised to Proper Case, second part of Name
Surnameforced to UPPERCASE, third part of Name
Appointmentignored on import
EmailEmail

Example: Maj / Victor / HUGHES → Name = "Maj Victor HUGHES". A header row is fine to include in the paste — it fails the email-format check and gets silently skipped, the same way blank/invalid rows always have. The old two-column Name/Email paste still works exactly as before for groups that don't have this structured data — the parser just checks the column count per pasted block and picks the matching format.

List: BM Maintainers

Column nameTypeNotes
TitleSingle line of textBuilt-in — the maintainer's name.
EmailSingle line of textStore lowercase. This is what the app checks against the signed-in user to decide whether to show Manage Groups / Manage People.

This list exists specifically so adding a future maintainer is a one-row add to a SharePoint list, not an app edit — no formula change, no republish. Add the two starting rows yourself:

TitleEmail
Maintainer 1maintainer1@yourdomain
Maintainer 2maintainer2@yourdomain

Permissions: anyone who should use the in-app Manage Maintainers button needs Edit access to this list, because Power Apps cannot bypass SharePoint list permissions. Everyone else only needs Read access so the app can check User().Email. The app blocks removing yourself and blocks removing the final maintainer, but SharePoint permissions are still the hard gate.

List: BM History (new in v2.0)

Column nameTypeNotes
TitleSingle line of textBuilt-in — the human-readable event description, e.g. "Jane Doe added (jane.doe@mod.gov.uk)" or "Bulk replace via paste: 12 removed, 15 added".
GroupNameSingle line of textMatches a 'BM Groups'.Title value — which group this event belongs to.
EventTypeChoiceValues: Created, Added, Removed, Edited, Replaced. Not currently displayed in the app UI but useful for future filtering/reporting.

One row per membership/group event. The app writes to this list every time someone is added, removed, a group is created/edited, or a roster is bulk-replaced. The 🕘 history icon on each group reads this list filtered by GroupName, newest first — this is what answers "who was added or removed, and when" over time. No custom timestamp column needed; the built-in Created date is used directly.

Versioning: not needed — this list is itself the audit trail; individual rows are never edited after creation.

Permissions: same as BM Groups/BM Memberships — Edit for maintainers (the app writes here on their behalf), Read for everyone (the history icon is visible on the Send Email screen too, not just Manage Groups).

Mark every column Not Required in SharePoint on all four lists. Required fields cause network errors — validation (name/email format) is handled inside the app instead.

App.OnStart

Paste into App → OnStart in Power Apps Studio. This is never part of any screen's YAML. Check varModDomain matches your real domain — it's set to mod.gov.uk below.

v2.32 loads BM Memberships group-by-group from SharePoint instead of trying to cache the whole list by item ID. That avoids the row-limit/item-ID problem where later alphabetic groups could show as 0 members even when SharePoint had rows.
v2.32 · 2026-09-11 09:42 BST — stamp added, content unchanged since v2.32
Set(varModDomain, "mod.gov.uk");
Set(varMyEmail, Lower(User().Email));
ClearCollect(colMaintainers, 'BM Maintainers');
Set(varIsMaintainer, CountRows(Filter(colMaintainers, Lower(Email) = varMyEmail)) > 0);
ClearCollect(colGroups, AddColumns(Sort('BM Groups', SortOrder, SortOrder.Ascending), Ticked, false));
ClearCollect(colMemberships, Filter('BM Memberships', false)); ForAll(colGroups As grp, Collect(colMemberships, Filter('BM Memberships', GroupName = grp.Title)));
ClearCollect(colThursdayBatchOptions, ["None", "Batch 1", "Batch 2"]);
Set(varRecipientField, "bcc");
Set(varBccForcedByBatch, false);
Set(varBccForced, false);
Set(varPreviewGroupID, 0);
Set(varShowLightbox, false);
Set(varLightboxMode, "");
Set(varLightboxGroupID, 0);
Set(varLastMembershipRefresh, Now())

Screens

Paste each screen in order. Create a new blank screen first, name it exactly as shown, then paste.

Screen 0
scrQuickContacts v2.43
Quick-copy card screen for one-contact-list clipboard sends. Uses the green/white visual direction, search, filters, member counts, and private-group maintainer visibility.
Open YAML →
Screen 1
scrSendEmail v2.43
Tick groups, preview a group, live recipient count, To/CC/BCC toggle, compact maintainer-only Thursday tools (one batch selector instead of two separate buttons), member/history/batch-preview popup, clear-search button, manual Refresh, and last-loaded timestamp.
Open and copy →
Screen 2
scrManageGroups v2.46
Maintainer-only. Create/edit/delete groups, manage membership, add existing people in bulk, add from GAL (JMC/County captured inline before saving, no separate edit trip needed) or manually, paste-replace rosters with add/remove/unchanged diff counts, manage maintainers, a Thursday Batches overview (Batch 1/Batch 2 groups with live member counts, plus a count of unassigned groups), run a read-only Admin Health Check (blank name, invalid email, orphaned membership rows), and manually refresh the loaded SharePoint cache. Save/edit/delete/add/remove/paste-replace actions update the local cache directly instead of re-fetching everything from SharePoint.
Open and copy →
Screen 3 (new)
scrManagePeople v2.45
Maintainer-only. One record per person, GAL-backed add, edit-person group checklist, leaver cleanup with tickable multi-match search and affected group list, JMC/County fields, plus manual Refresh and last-loaded timestamp. Add/save/purge actions now update the local cache directly instead of re-fetching everything from SharePoint.
Open and copy →

Further Reading

Issues

Found a problem? Report it here.