Admin Data Maintenance Build Pass
This adds two maintenance features: certificate library metadata so PDFs are identifiable in SharePoint, and an admin-only permanent person removal route that deletes that person's training records and certificate files after a strong confirmation.
0. Why The Reminder Flow Guide Changed
You are currently building the email reminder flow. The guide was changed for two concrete reasons, not just tidying:
| Problem found | What changed | Why it matters |
|---|---|---|
Send an email (V2) was nested at level 9. | The separate Reminder due condition was removed. Ready to send now combines "Reminder Type is not blank" and "log row does not already exist". | Power Automate has a maximum nesting limit of 8. The old shape could not save. |
Power Automate created unwanted For each wrappers. | The guide now says to use expressions for nested-loop values instead of picking vague dynamic tokens like Current item. | The email and log actions must sit directly inside Ready to send - True / If yes, not inside extra array loops. |
If you are halfway through that build, keep following Course Expiry Reminder Emails from the corrected Ready to send section onward. The changes are there because the older branch shape was genuinely invalid.
1. Certificate Library Metadata
The locked certificate library should not be a pile of anonymous PDFs. Add these columns to TT Certificates_LTD / TT Certificates (LTD).
| Column internal name | Type | Required? | Purpose |
|---|---|---|---|
TrainingRecordID | Number | Yes | Links the file to TT TrainingRecords.ID. |
PersonEmail | Single line text | Yes | The person the certificate belongs to. |
PersonName | Single line text | No | Readable staff name for library maintenance. |
CourseID | Number | Yes | Links the file to TT Courses.ID. |
CourseName | Single line text | Yes | Readable course title. |
DateCompleted | Date only | No | Completion date stored against the training record. |
UploadedByEmail | Single line text | No | Who uploaded or replaced the certificate. |
UploadedOn | Date and time | No | When the file was saved. |
IsCurrent | Yes/No | Yes | Current certificate marker. Set to true for the live file. |
1.1 Update TT - Save Certificate Library
Open the save-certificate flow. Inside the authorised branch, inside the HasNewFile - True / If yes branch, the shape should be:
HasNewFile
└── True / If yes
├── Get existing certificate files
├── Apply to each existing certificate
│ └── Delete file
├── Create file
└── Update file properties
If you already have Update file properties, edit it. If not, add it directly under Create file. Do not put it inside the delete loop.
| Update file properties field | Use this value |
|---|---|
| Site Address | Your Training Tracker SharePoint site. |
| Library Name | TT Certificates_LTD / TT Certificates (LTD). |
| Id | ItemId from Create file. |
Title | CourseName from When Power Apps calls a flow (V2). |
TrainingRecordID | RecordID from the trigger. |
PersonEmail | PersonEmail from the trigger. |
CourseID | CourseID from the trigger. |
CourseName | CourseName from the trigger. |
DateCompleted | DateCompleted from the trigger. |
UploadedByEmail | ActorEmail from the trigger. |
UploadedOn | Expression: utcNow() |
IsCurrent | true |
For PersonName, use section 1.2. Do not guess a dynamic token called Title if there are several of them.
1.2 Optional But Recommended: Fill PersonName
To fill the human-readable name, add this before Update file properties, still inside the same HasNewFile - True / If yes branch.
- Add SharePoint - Get items. Name it
Get Person For Metadata. - List:
TT Personnel. - Filter Query: use the expression below.
- Top Count:
1.
concat("Email eq '", PERSON_EMAIL_FROM_TRIGGER, "'")
concat("Email eq '", , then insert PersonEmail from When Power Apps calls a flow (V2), then type , "'"). The exact internal trigger name can differ between flows, so the guide deliberately does not guess it here.
Then set PersonName in Update file properties with this expression.
first(body('Get_Person_For_Metadata')?['value'])?['Title']
2. Build TT - Remove Person
This should be an instant cloud flow called from Power Apps. It performs the destructive cleanup under the flow connection, not by giving every admin direct delete formulas in the app.
2.1 Trigger Inputs
| Order | Type | Name | Notes |
|---|---|---|---|
| 1 | Number | PersonID | TT Personnel.ID. |
| 2 | Text | PersonEmail | The selected person's email. |
| 3 | Text | ConfirmEmail | The email the admin typed into the confirmation box. |
| 4 | Text | ActorEmail | The signed-in admin. |
| 5 | Yes/No | IsAdmin | Must be true. |
2.2 Flow Shape
Power Apps (V2) ├── Initialize varSuccess = false ├── Initialize varMessage = "" ├── Get person ├── Condition - Authorised and confirmed │ ├── True / If yes │ │ ├── Get certificate files │ │ ├── Apply to each certificate file │ │ │ └── Delete file │ │ ├── Get training records │ │ ├── Apply to each training record │ │ │ └── Delete item │ │ ├── Get people using this line manager │ │ ├── Apply to each managed person │ │ │ └── Update item - clear LineManagerEmail │ │ ├── Delete item - TT Personnel │ │ ├── Create item - TT AuditLog │ │ ├── Set varSuccess = true │ │ └── Set varMessage = "Person removed." │ └── False / If no │ └── Set varMessage = "Not authorised or confirmation did not match." └── Respond to a PowerApp or flow
2.3 Authorisation Condition
The condition must check both admin status and typed confirmation. Use this as the left side expression, operator is equal to, right side true.
and(
equals(triggerBody()?['boolean'], true),
equals(toLower(triggerBody()?['text']), toLower(triggerBody()?['text_1']))
)
PersonEmail is usually text, ConfirmEmail is usually text_1, and IsAdmin is usually boolean. If the expression red-lines, open trigger code view and swap in the real names. The logic is still: IsAdmin = true and PersonEmail = ConfirmEmail.
2.4 Delete Certificate Files
Inside Authorised and confirmed - True / If yes, add SharePoint - Get files (properties only).
| Field | Value |
|---|---|
| Library Name | TT Certificates_LTD / TT Certificates (LTD). |
| Filter Query | Expression below. |
concat("PersonEmail eq '", triggerBody()?['text'], "'")
Add Apply to each over the files returned. Inside it add SharePoint - Delete file. Use Identifier from the current certificate file item.
2.5 Delete Training Records
Still inside the True branch, add SharePoint - Get items against TT TrainingRecords.
concat("PersonEmail eq '", triggerBody()?['text'], "'")
Add Apply to each over the returned records. Inside it add SharePoint - Delete item, list TT TrainingRecords, Id from the current training record.
2.6 Sever Line Manager Links
Still inside the True branch, add SharePoint - Get items against TT Personnel to find anyone whose line manager is the removed person.
concat("LineManagerEmail eq '", triggerBody()?['text'], "'")
Add Apply to each. Inside it, Update item on TT Personnel, keep required fields unchanged, and set LineManagerEmail blank. Also set LastStatus to Line manager removed, LastUpdatedByEmail to ActorEmail, and LastUpdatedOn to utcNow().
2.7 Delete The Person And Respond
- Add SharePoint - Delete item, list
TT Personnel, Id =PersonIDfrom the trigger. - Add SharePoint - Create item, list
TT AuditLog, event typePerson permanently removed. - Set
varSuccesstotrue. - Set
varMessagetoPerson removed.. - Put one Respond to a PowerApp or flow after the whole condition, not inside either branch. Return
successandmessage.
3. Add The Power Apps Remove Button
This is a targeted edit to scrAdminManage. Do not replace the whole screen unless you want to discard your manual spacing tweaks.
3.1 Add Confirmation Input
Inside the edit-person area, add a modern text input named txtRemovePersonConfirm.
| Property | Formula |
|---|---|
Default | "" |
Placeholder | "Type email to confirm permanent removal" |
Visible | varTab = "People" && !IsBlank(varEditPerson) && varShowRemovePersonConfirm |
X | 32 |
Y | 410 |
Width | 590 |
Height | 40 |
3.2 Add Remove Button
Add a modern button named btnRemovePersonPermanent.
| Property | Formula |
|---|---|
Text | If(varShowRemovePersonConfirm, "Confirm permanent removal", "Remove person") |
BasePaletteColor | RGBA(226,75,74,1) |
Visible | varTab = "People" && !IsBlank(varEditPerson) && varIsAdmin |
X | 32 |
Y | If(varShowRemovePersonConfirm, 462, 410) |
Width | 284 |
Height | 44 |
Set btnRemovePersonPermanent.OnSelect to this. Formula bar paste: no leading equals sign.
If(
!varShowRemovePersonConfirm,
Set(varShowRemovePersonConfirm, true);
Reset(txtRemovePersonConfirm);
Notify("Type the person's email address to confirm permanent removal.", NotificationType.Warning),
If(
Lower(Trim(txtRemovePersonConfirm.Text)) <> Lower(varEditPerson.Email),
Notify("Email confirmation does not match. Nothing was removed.", NotificationType.Error),
Set(varCertBusy, true);
Set(
varRemovePersonResult,
IfError(
'TT - Remove Person'.Run(
varEditPerson.ID,
Lower(varEditPerson.Email),
Lower(Trim(txtRemovePersonConfirm.Text)),
varMyEmail,
varIsAdmin
),
{success: false, message: "Remove flow could not be reached."}
)
);
Set(varCertBusy, false);
If(
varRemovePersonResult.success,
Refresh('TT Personnel');
Refresh('TT TrainingRecords');
Set(varEditPerson, Blank());
Set(varShowRemovePersonConfirm, false);
Notify(varRemovePersonResult.message, NotificationType.Success),
Notify(varRemovePersonResult.message, NotificationType.Error)
)
)
)
3.3 Add Cancel Remove Button
Add a modern button named btnCancelRemovePerson.
| Property | Formula |
|---|---|
Text | "Cancel removal" |
BasePaletteColor | RGBA(108,117,125,1) |
Visible | varTab = "People" && !IsBlank(varEditPerson) && varShowRemovePersonConfirm |
X | 326 |
Y | 462 |
Width | 296 |
Height | 44 |
OnSelect | Set(varShowRemovePersonConfirm, false); Reset(txtRemovePersonConfirm) |
4. Test Checklist
- Upload or replace one certificate.
- Open
TT Certificates_LTDdirectly in SharePoint and confirm metadata columns show person email, course, date, uploader, and current marker. - Create a disposable test person with one test training record and one test certificate.
- Run
TT - Remove Persononly against that test person first. - Confirm the person row is gone, training records are gone, certificate files are gone, and any LineManagerEmail links pointing at that email are blank.
- Confirm
TT AuditLoghas a permanent-removal entry.
5. References
- Microsoft Learn: Power Fx Remove and RemoveIf functions. This build deliberately uses a flow instead of direct app-side removal because it also deletes library files and writes audit.
- Microsoft Support: delete files from a SharePoint document library.