- ONBOARDING.md (Hebrew, lawyer-facing): corrected install URL (.vsto), ribbon group name (Marcus-Law), first-run flow (open from ribbon), added the four feature sections, troubleshooting table, file locations, support channels - IT-SETUP.md (English, admin-facing): runner provisioning, codesign cert generation + Infisical storage + Gitea Actions secrets, GPO push of the public cert to attorney machines, nginx config for ClickOnce serving, per-user API key provisioning, release procedure, URL protocol install, monitoring + off-boarding + troubleshooting playbook Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.1 KiB
IT setup guide — Marcus-Law OutlookAddin
Audience: IT admin. Walks you through deploying the ClickOnce installer, distributing the code-signing certificate via GPO, provisioning per-user EspoCRM API keys, and operating the add-in for the 10 attorneys at Marcus-Law.
Prerequisites
- Windows AD domain (
marcus-law.local) with GPO push to all attorney machines. - EspoCRM admin access on
https://crm.prod.marcus-law.co.il. - SSH access to
platform.dev.marcus-law.co.ilfor serving the ClickOnce artifacts. - Mattermost webhook for release notifications (
#git-verelasim). - Infisical project
outlook-addinfor storing secrets.
1. Build infrastructure
Windows CI runner (one-time)
A Windows 11 Pro VM on Coolify hosts the Gitea Actions runner. It needs:
- Visual Studio Build Tools 2022 with workloads:
Microsoft.VisualStudio.Workload.Office(for the VSTO MSBuild targets)Microsoft.VisualStudio.Workload.ManagedDesktop(for WPF)
- Windows SDK 10.0.22621 (for
signtool.exe). - .NET Framework 4.8 Developer Pack (
mage.exe). - WSL Ubuntu (for the
rsyncstep of the publish job). - Gitea act_runner registered as a Windows service with label
windows.
Code-signing certificate
The runner needs a code-signing certificate in Cert:\CurrentUser\My. Generate a self-signed cert once:
$cert = New-SelfSignedCertificate `
-Subject "CN=Marcus-Law OutlookAddin" `
-Type CodeSigningCert `
-CertStoreLocation Cert:\CurrentUser\My `
-NotAfter (Get-Date).AddYears(3) `
-KeyAlgorithm RSA `
-KeyLength 2048
$thumbprint = $cert.Thumbprint
$pfxPassword = ConvertTo-SecureString -String "<generated-password>" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath C:\codesign.pfx -Password $pfxPassword
Export-Certificate -Cert $cert -FilePath C:\codesign.cer # public part — for GPO push
Store these in Infisical /outlook-addin/:
| Key | Value |
|---|---|
CODESIGN_THUMBPRINT |
The cert thumbprint (40 hex chars) |
CODESIGN_PFX_BASE64 |
[Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\codesign.pfx")) |
CODESIGN_PASSWORD |
The PFX password |
PLATFORM_DEV_SSH_KEY |
Base64 of the rsync target's ed25519 private key |
MATTERMOST_WEBHOOK_RELEASES |
Incoming webhook URL for #git-verelasim |
In Gitea (Settings → Actions → Secrets), add four secrets that reference Infisical:
CODESIGN_THUMBPRINTPLATFORM_DEV_SSH_KEYMATTERMOST_WEBHOOK_RELEASES
(The PFX itself is imported once on the runner; the workflow only uses the thumbprint.)
2. Deploy the certificate via GPO
The ClickOnce installer refuses to launch unless the publishing certificate is in Trusted Publishers on each attorney's machine. Use GPO to push it.
- Copy
codesign.certo\\marcus-law.local\SYSVOL\marcus-law.local\Scripts\certs\codesign.cer. - Open Group Policy Management Console → edit a GPO scoped to the attorney OU.
- Navigate to:
Computer Configuration → Policies → Windows Settings → Security Settings → Public Key Policies - Right-click Trusted Publishers → Import… and pick
codesign.cer. - Repeat under Trusted Root Certification Authorities (self-signed cert).
- Force update everywhere:
Invoke-Command -ComputerName (Get-ADComputer -Filter * -SearchBase "OU=Attorneys,DC=marcus-law,DC=local").Name -ScriptBlock { gpupdate /force } - Verify on one attorney machine:
certutil -store -user TrustedPublisher | findstr /C:"Marcus-Law OutlookAddin"
3. nginx — serve the ClickOnce artifacts
On platform.dev.marcus-law.co.il, add /etc/nginx/sites-available/outlook-addin.conf:
server {
listen 443 ssl http2;
server_name platform.dev.marcus-law.co.il;
ssl_certificate /etc/letsencrypt/live/platform.dev.marcus-law.co.il/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/platform.dev.marcus-law.co.il/privkey.pem;
location /outlook-addin/ {
alias /var/www/outlook-addin/;
autoindex off;
types {
application/x-ms-application application;
application/x-ms-manifest manifest;
application/octet-stream vsto;
application/octet-stream deploy;
}
add_header Cache-Control "no-cache, must-revalidate";
}
}
sudo mkdir -p /var/www/outlook-addin
sudo chown -R www-data:www-data /var/www/outlook-addin
sudo ln -s /etc/nginx/sites-available/outlook-addin.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
The CI runner pushes new versions here on every v* tag via the publish job.
4. EspoCRM API key provisioning
Per attorney:
- EspoCRM admin → Administration → Users → Create.
- User Type: API User (or a Regular User with the "API" role).
- Username: mail prefix (e.g.
chaimforchaim@marcus-law.co.il). - Role: at least: read Contact / Case / Account / Lead / Email; create Email; read EmailAddress.
- Save, then Generate API Key. Copy it.
- Store under Infisical
/outlook-addin/users/<email>(or send via a 1-time link service).
Send the attorney a personal email matching the ONBOARDING.md template, with their per-user key.
5. Release procedure (each new version)
- Tag the commit on
main:git tag v1.0.0 git push origin v1.0.0 - Gitea Actions builds Release, signs, generates the ClickOnce artifacts, rsyncs to platform.dev, and posts to
#git-verelasim. - Smoke-test from a clean attorney machine (or a Windows 11 sandbox):
- Browse to
https://platform.dev.marcus-law.co.il/outlook-addin/OutlookAddin.vsto. - The ClickOnce installer should run with no security warning. If you see "Unknown Publisher", the GPO cert hasn't propagated to that machine.
- First-run Settings → enter creds → Test Connection green.
- Send a test email to a known contact, hit "תייק ל-EspoCRM", confirm it appears in the EspoCRM activity stream.
- Browse to
- If a release goes badly, push a new tag with a fix; ClickOnce will roll all users forward automatically on their next Outlook start.
6. URL protocol handler (optional)
To make EspoCRM "Compose email" links open Outlook with the case pre-filled:
- After the ClickOnce install, run as the user:
& "$env:LOCALAPPDATA\Apps\OutlookAddinProtocolHandler\install-protocol.ps1" - Verify:
start outlookaddin://compose?caseId=<id>should open a new Outlook composer with the case pre-populated. (Outlook must be already running so the named pipe is up.) - To uninstall the registration:
uninstall-protocol.ps1.
7. Monitoring
- CI status — Mattermost
#git-verelasim, plus Gitea Actions UI. - Server-side filing — EspoCRM internal logs (
data/logs/espo-*.log) for/api/v1/MailRouter/filecalls. - Client-side errors — when an attorney reports an issue, ask them to send
%LOCALAPPDATA%\MarcusLaw\OutlookAddin\logs\addin-<date>.log. Logs roll daily, retained 14 days.
8. Off-boarding (attorney leaves)
- In EspoCRM admin, delete or disable the API User.
- Their
creds.daton the laptop is DPAPI-encrypted with their Windows user profile — wiping the profile (or the laptop) is enough. - No central state to remove beyond EspoCRM.
9. Troubleshooting playbook
| Symptom | Likely cause | Fix |
|---|---|---|
| Install dialog says "Unknown Publisher" | GPO cert hasn't reached the machine | gpupdate /force, then certutil -store -user TrustedPublisher to confirm |
| Add-in installs but ribbon group is missing | Microsoft.VSTORuntime.4.0 missing |
ClickOnce normally pulls it; if Office < 2019, install VSTO 4 Runtime manually |
| Mass 401s after a renewal | API keys were rotated | Send new keys, ask users to re-open Settings and paste them |
| Retry queue is growing | EspoCRM unreachable, or 5xx errors | Check EspoCRM health; once it's back, the processor drains the queue on its 30-second loop |
| User says "categories disappeared" | They are local to that PC. If they reinstall Outlook on a new machine, categories don't sync (MAPI limitation) | Re-file is harmless — server-side dedups on messageId (409 returned, counted as success) |