Before beginning setup, ensure you have:
- Azure Subscription with Owner or Contributor permissions
- Microsoft 365 Tenant with SharePoint Online
- Azure subscription access (for Logic App deployment)
- Azure AD admin access for app registration
- Anthropic API Key (Claude AI) — optional, AI validation currently disabled
- Development tools:
- Python 3.11+
- Azure Functions Core Tools v4
- Git
- VS Code (recommended)
This app will authenticate the Azure Function to access SharePoint.
-
Navigate to Azure Portal
- Go to: https://portal.azure.com
- Sign in with admin credentials
-
Create App Registration
- Search for "Azure Active Directory" or "Microsoft Entra ID"
- Click App registrations → New registration
Settings:
- Name:
MaceStyleValidator-App - Supported account types: Single tenant
- Redirect URI: Leave blank
- Click Register
-
Note the Application Details
Tenant ID: [Copy from Overview page] Client ID (Application ID): [Copy from Overview page]Save these values - you'll need them later!
-
Create Client Secret
- Click Certificates & secrets → New client secret
- Description:
MaceStyleValidator-Secret - Expires: 24 months (or as per your policy)
- Click Add
- Copy the secret value immediately (won't be shown again)
Client Secret: [Copy the Value, not the Secret ID] -
Configure API Permissions
- Click API permissions → Add a permission
- Select Microsoft Graph → Application permissions
Add this permission:
-
Sites.Selected— Grants access to no sites by default; access is granted per-site via the Graph API -
Click Add permissions
-
Click Grant admin consent for [Tenant Name]
-
Confirm by clicking Yes
✅ Verify: Permission shows "Granted for [Tenant]"
Note:
Sites.Selectedalone does not grant access to any SharePoint site. After completing the App Registration, you must grant per-site access — see docs/azure-admin-setup.md for instructions.
-
Create Function App Resource
- In Azure Portal, click Create a resource
- Search for "Function App"
- Click Create
Basic Settings:
Resource Group: Create new → "rg-macestyle" Function App name: "func-mace-validator-prod" Publish: Code Runtime stack: Python Version: 3.11 Region: [Choose closest to your SharePoint tenant] Operating System: Linux Plan type: Consumption (Serverless)- Click Review + create → Create
- Wait for deployment (2-3 minutes)
-
Configure Application Settings
- Go to Function App → Configuration → Application settings
- Click + New application setting for each:
Name: SHAREPOINT_TENANT_ID Value: [Your Tenant ID from step 1.1] Name: SHAREPOINT_CLIENT_ID Value: [Your Client ID from step 1.1] Name: SHAREPOINT_CLIENT_SECRET Value: [Your Client Secret from step 1.1] Name: SHAREPOINT_SITE_URL Value: https://[yourtenant].sharepoint.com/sites/StyleValidation Name: ANTHROPIC_API_KEY Value: [Your Claude API key - get from console.anthropic.com] (optional — AI currently disabled)- Click Save → Continue
-
Configure CORS (if calling from web app)
- Go to CORS under API section
- Add allowed origins if needed
- Click Save
Note: Claude AI validation is currently disabled via the
ENABLE_CLAUDE_AIflag. This step is only needed if you plan to re-enable AI validation.
-
Sign up for Anthropic
- Go to: https://console.anthropic.com
- Create account or sign in
-
Create API Key
- Navigate to API Keys
- Click Create Key
- Name:
MaceStyleValidator - Click Create
- Copy the key (starts with
sk-ant-...)
-
Fund your account
- Add billing information
- Add credits (recommended: $10 minimum)
- Claude Haiku 4.5 costs ~$0.01 per document (Word only)
-
Create Site Collection (if not exists)
- Go to SharePoint Admin Center
- Sites → Active sites → Create
- Type: Team site
- Site name:
Style Validation - Site address:
/sites/StyleValidation - Primary administrator: [Your account]
- Click Finish
-
Verify Site URL
Expected: https://[tenant].sharepoint.com/sites/StyleValidation
-
Navigate to site
- Go to:
https://[tenant].sharepoint.com/sites/StyleValidation
- Go to:
-
Create List
- Click New → List
- Name:
Style Rules - Description:
Validation rules for style checking - Click Create
-
Add Columns
Column 1: RuleType
- Type: Choice
- Choices:
Font Language Grammar Punctuation Capitalisation Layout - Default: (none)
Column 2: DocumentType
- Type: Choice
- Choices:
Word Visio Excel PowerPoint Both All - Default: Word
Column 3: CheckValue
- Type: Single line of text
Column 4: ExpectedValue
- Type: Single line of text
Column 5: AutoFix
- Type: Yes/No
- Default: Yes
Column 6: UseAI
- Type: Yes/No
- Default: No
Column 7: Priority
- Type: Number
- Min: 1
- Max: 999
- Default: 100
-
Populate Rules
- Option A: Use the
populate_style_rules.pyscript - Option B: Manually add rules from the style guide
To use the script:
cd MaceStyleValidator # Set environment variables export SHAREPOINT_TENANT_ID="[your-tenant-id]" export SHAREPOINT_CLIENT_ID="[your-client-id]" export SHAREPOINT_CLIENT_SECRET="[your-client-secret]" export SHAREPOINT_SITE_URL="https://[tenant].sharepoint.com/sites/StyleValidation" # Run script python3 populate_style_rules.py
This will add ~70 style rules automatically.
- Option A: Use the
-
Create List
- Name:
Validation Results - Description:
History of document validations - Click Create
- Name:
-
Add Columns
Column 1: FileName
- Type: Single line of text
Column 2: ValidationDate
- Type: Date and Time
- Format: Date & Time
Column 3: Status
- Type: Choice
- Choices:
Auto-Fixed — Awaiting Review Passed Review Required Failed - Default: (none)
Important:
Auto-Fixed — Awaiting Reviewmust be present. It is the status the Function writes whenever a document validates with no issues left to fix. If it is missing from this choice list, SharePoint rejects the write, the exception is swallowed, and no Validation Results entry is created at all for those documents — silently losing the audit record for the most common outcome.Column 4: IssuesFound
- Type: Single line of text
- (Stored as text for simplicity)
Column 5: IssuesFixed
- Type: Single line of text
Column 6: ReportLink
- Type: Hyperlink or Picture
-
Get List ID (needed for code)
- Go to List Settings → copy URL
- Extract ID from URL:
List={GUID} - Or run:
python3 inspect_validation_results.py
Note: List IDs are configurable via environment variables:
SHAREPOINT_VALIDATION_RESULTS_ID— GUID of the Validation Results listSHAREPOINT_DOC_LIBRARY_ID— GUID of the Document Library
Set these in Azure Function App Settings for production.
-
Use Default or Create New
- Option A: Use existing "Documents" library
- Option B: Create new library named "Validated Documents"
-
Add Custom Columns
Column 1: ValidationStatus
- Type: Choice
- Choices:
Not Validated Validate Now Validating... Auto-Fixed — Awaiting Review Passed Review Required Failed - Default: Not Validated
- Colour coding (optional):
- Validating... = Yellow
- Auto-Fixed — Awaiting Review = Teal
- Passed = Green
- Review Required = Amber
- Failed = Red
Important:
Validate Nowis an input-only value — a human sets it to request a validation run, and the flow triggers on it. The Function must never write it back as a result, or completing a validation would re-arm its own trigger. When validation finishes with nothing left to fix, the Function writesAuto-Fixed — Awaiting Review(notPassed, which is reserved for a human sign-off). If this choice is missing from the column, SharePoint rejects the write and the result is silently lost.Column 2: ValidationResultLink
- Type: Hyperlink or Picture
Column 3: LastValidated
- Type: Date and Time
- Format: Date & Time
-
Enable Versioning (Important!)
- Library Settings → Versioning settings
- Document Version History: Yes
- Create major versions: Yes
- Keep versions: At least 50 (or more)
- Click OK
-
Set Permissions (if needed)
- Ensure users have:
- Read: To view documents
- Edit: To upload and modify
- Contribute: To trigger validation
- Ensure users have:
# Clone the repository
git clone https://github.com/stephencummins/MaceStyle.git
cd MaceStyle/MaceStyleValidator-
Create virtual environment
python3 -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate
-
Install dependencies
pip install -r requirements.txt
-
Create local.settings.json
cat > local.settings.json << 'EOF' { "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "python", "SHAREPOINT_TENANT_ID": "[your-tenant-id]", "SHAREPOINT_CLIENT_ID": "[your-client-id]", "SHAREPOINT_CLIENT_SECRET": "[your-client-secret]", "SHAREPOINT_SITE_URL": "https://[tenant].sharepoint.com/sites/StyleValidation", "ANTHROPIC_API_KEY": "[your-anthropic-api-key]" } } EOF
Replace all
[...]placeholders with your actual values -
Test locally
func start
You should see:
Functions: ListDocuments: [GET,POST] http://localhost:7071/api/listdocuments TestSharePoint: [GET,POST] http://localhost:7071/api/testsharepoint ValidateDocument: [POST] http://localhost:7071/api/validatedocument
-
Login to Azure
az login
-
Set subscription (if you have multiple)
az account set --subscription "[Your Subscription Name or ID]"
-
Deploy Function
func azure functionapp publish func-mace-validator-prod
Wait for deployment to complete (2-3 minutes).
-
Verify deployment
- Go to Azure Portal → Function App
- Check Functions tab - should show 3 functions
- Check App Keys → copy Host key (default)
-
Get Function URL
https://func-mace-validator-prod.azurewebsites.net/api/validatedocumentSave this URL - needed for Power Automate!
An ARM template is provided at infra/logic-app.json that deploys a Consumption Logic App. This is preferred over Power Automate for production as it lives in the Azure subscription and can be deployed via ARM/DevOps.
az deployment group create \
--resource-group rg-macestyle \
--template-file infra/logic-app.json \
--parameters infra/logic-app.parameters.json \
--parameters functionAppKey="<function-host-key>" \
--parameters sharepointDocLibraryId="<library-guid>"After deployment:
- Go to Azure Portal → Resource Group → API Connections →
sharepointonline - Click Edit API connection
- Click Authorize and sign in with a SharePoint account
- Click Save
The Logic App:
- Triggers on file create/modify in the SharePoint Document Library
- Filters to supported file types (.docx, .xlsx, .pptx, .vsdx, etc.)
- Sets ValidationStatus to "Validating..."
- Gets file content and base64-encodes it
- Calls the Function App (
POST /api/ValidateDocument) with 10-minute timeout - Parses the JSON response
- If fixes were applied, uploads the corrected file back
- Updates ValidationStatus, Description, ValidationReport, ValidationResultLink, and LastValidated
An azure-pipelines.yml is provided in the repo root for automated deployment:
- Build + test on push to
main - Deploy to dev (automatic)
- Deploy to prod (with approval gate)
- Deploy Logic App ARM template
See docs/prod-deployment-guide.md for DevOps setup instructions.
- Upload a test document to the SharePoint library
- Check Logic App run history in Azure Portal
- Verify document status updated, report generated, and results saved
Create test_validation.docx with:
Content to test:
1. American spelling: finalized, color, center, analyze
2. Contractions: can't, don't, won't
3. Symbols: M&S, 50%
4. Wrong font: (Set some text to Calibri or Times New Roman)
5. Numbers: 1000, 2000, 3000
-
Upload to SharePoint
- Upload
test_validation.docx - Wait for "Validating..." status
- Upload
-
Check Results (should complete in 10-20 seconds)
- Status: Should be "Passed"
- ValidationResultLink: Should have link
-
Open HTML Report
- Click ValidationResultLink
- Should show:
- Fixes Applied: Multiple corrections
- Issues: All issues listed
-
Download Fixed Document
- Download the document
- Open in Word
- Verify:
- ✅ finalized → finalised
- ✅ color → colour
- ✅ can't → cannot
- ✅ M&S → M and S
- ✅ 50% → 50 percent
- ✅ All text is Arial
- ✅ Numbers: 1,000, 2,000, 3,000
Test 1: Invalid file type
- Upload
test.txt - Expected: Error or status "Failed"
Test 2: Large file
- Upload 100+ page document
- Expected: Completes (may take 30+ seconds)
Test 3: Corrupted file
- Upload corrupted .docx
- Expected: Status "Failed" with error
Application Insights:
Azure Portal → Function App → Application Insights
Key Metrics:
- Request rate
- Average duration
- Failure rate
- Dependency calls (Graph API, Claude API)
Live Metrics:
Application Insights → Live Metrics
Monitor:
- Real-time requests
- Failures as they happen
- Performance issues
Log Stream:
Function App → Log stream
View:
- Real-time execution logs
- Debugging information
- Error details
Azure Costs:
Azure Portal → Cost Management + Billing → Cost Analysis
Filter by:
- Resource Group: rg-macestyle
- Service: Azure Functions, Application Insights
Anthropic API Costs:
console.anthropic.com → Usage
Track:
- Total API calls
- Tokens used
- Cost per month
Expected Monthly Costs:
Azure Functions: $0-10 (Consumption plan)
Application Insights: $0-5 (Basic tier)
Claude API: ~$0.01 per document
SharePoint: Included in M365
Example:
- 1,000 documents/month = ~$10-20 total
- 100 documents/month = ~$1-5 total
Optimize validation speed:
-
Reduce AI rules (if too slow)
- Disable UseAI for simple rules
- Keep AI for complex language checks
-
Batch operations (for large documents)
- Split very large files
- Process in chunks
-
Cache Graph API tokens
- Already implemented (60-minute cache)
-
Use faster Claude model (if needed)
- Current: Haiku (fast, cheap)
- Alternative: Sonnet (slower, more accurate)
Weekly:
- Check error logs for patterns
- Review failed validations
- Verify costs are within budget
Monthly:
- Review and update style rules
- Analyze validation metrics
- Check for API updates (Graph, Anthropic)
Quarterly:
- Review permissions and security
- Update dependencies
- Rotate secrets if required
Do: ✅ Store secrets in Azure Key Vault ✅ Use managed identities when possible ✅ Rotate secrets every 12 months ✅ Use different secrets for dev/prod
Don't: ❌ Commit secrets to Git ❌ Share secrets via email/chat ❌ Use same secrets across environments ❌ Store secrets in code
Principle of Least Privilege:
-
Azure Function
- Uses
Sites.Selectedwith per-sitewritegrant (not tenant-wide access) - No user impersonation
- Uses
-
SharePoint Users
- Read: View documents and reports
- Edit: Upload and modify documents
- No Contribute to Style Rules list
-
Administrators
- Manage Style Rules
- View logs
- Modify configuration
Data Privacy:
- Documents processed in-memory only
- No persistent storage of content
- Audit trail in Validation Results
GDPR Considerations:
- Personal data in documents (if any)
- Retention policy for Validation Results
- Right to deletion (version history cleanup)
Cause: App registration permissions not granted
Solution:
- Azure Portal → App Registration
- API Permissions → Grant admin consent
- Verify green checkmarks
Cause: Logic App disabled or broken
Solution:
- Azure Portal → Logic App → Check run history
- Review last run error
- Check SharePoint API connection authorisation
Cause: List ID mismatch
Solution:
- Run
inspect_validation_results.py - Get actual list ID
- Update in
sharepoint_results.pyline 35
Note: Claude AI is currently disabled. This section only applies if
ENABLE_CLAUDE_AI = True.
Cause: Invalid API key or insufficient credits
Solution:
- Verify API key in Function App settings
- Check Anthropic console for credits
- Add credits if balance is low
Cause: Missing fileUrl parameter
Solution:
- Check Logic App request body includes fileUrl
- Review Logic App run history for the specific run
SharePoint Lists:
- Export Style Rules to Excel monthly
- Export Validation Results quarterly
- Use SharePoint Online backup (included in M365)
Azure Function Code:
- Git repository (primary backup)
- Azure DevOps / GitHub (secondary)
- Local developer machines
Secrets:
- Document in secure password manager
- Store offline backup in safe
- Keep recovery contacts updated
Scenario: SharePoint site deleted
- Restore from SharePoint Recycle Bin (93 days)
- Or restore from M365 admin center backup
- Re-run populate_style_rules.py to restore rules
Scenario: Azure Function deleted
- Redeploy from Git
- Reconfigure App Settings
- Test with sample document
Scenario: App registration deleted
- Create new app registration
- Update secrets in Function App
- Re-grant API permissions
# Azure Function App Settings
SHAREPOINT_TENANT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
SHAREPOINT_CLIENT_ID="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
SHAREPOINT_CLIENT_SECRET="your-client-secret-value"
SHAREPOINT_SITE_URL="https://tenant.sharepoint.com/sites/StyleValidation"
ANTHROPIC_API_KEY="sk-ant-xxxxxxxxxxxxxxxxxxxxx"Connect-PnPOnline -Url "https://tenant.sharepoint.com/sites/StyleValidation" -Interactive
Get-PnPSite | Select IdConnect-PnPOnline -Url "https://tenant.sharepoint.com/sites/StyleValidation" -Interactive
Get-PnPList | Select Title, IdConnect-PnPOnline -Url "https://tenant.sharepoint.com/sites/StyleValidation" -Interactive
Get-PnPListItem -List "Style Rules" | Export-Csv "StyleRules_Backup.csv"Before going live, verify:
- App registration created with correct permissions
- Azure Function deployed and responding
- SharePoint lists created with all columns
- Style Rules populated
- Document library configured with custom columns
- Logic App deployed and SharePoint connection authorised
- Test document validates successfully
- HTML report generates and uploads
- Validation Results list populates
- Document metadata updates with result link
- All secrets stored securely
- Monitoring and logging configured
- User guide distributed to team
- Admin contacts documented
- Azure Functions GitHub: https://github.com/Azure/azure-functions-python-worker
- Power Automate Community: https://powerusers.microsoft.com/
- Last Updated: March 2026
- Version: 5.0
- Author: [Your Name/Team]
- License: [Your License]