{"id":50324230,"url":"https://github.com/carloscommits/groups-console","last_synced_at":"2026-05-29T05:00:47.730Z","repository":{"id":274906085,"uuid":"923254570","full_name":"CarlosCommits/groups-console","owner":"CarlosCommits","description":"Windows-first desktop app for Exchange Online and Microsoft Graph administration, built to help operators manage contacts, guests, distribution lists, mail-enabled security groups, and membership exports.","archived":false,"fork":false,"pushed_at":"2026-05-29T03:14:57.000Z","size":8747,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-29T03:22:24.693Z","etag":null,"topics":["electron","exchange-online","microsoft-365","microsoft-graph","powershell","react","typescript"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CarlosCommits.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-01-27T22:34:23.000Z","updated_at":"2026-05-29T03:14:49.000Z","dependencies_parsed_at":"2025-01-30T03:37:17.927Z","dependency_job_id":null,"html_url":"https://github.com/CarlosCommits/groups-console","commit_stats":null,"previous_names":["ccanas2/rad-app","carloscommits/rad-app","carloscommits/groups-console"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/CarlosCommits/groups-console","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarlosCommits%2Fgroups-console","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarlosCommits%2Fgroups-console/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarlosCommits%2Fgroups-console/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarlosCommits%2Fgroups-console/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CarlosCommits","download_url":"https://codeload.github.com/CarlosCommits/groups-console/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarlosCommits%2Fgroups-console/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33637485,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electron","exchange-online","microsoft-365","microsoft-graph","powershell","react","typescript"],"created_at":"2026-05-29T05:00:46.382Z","updated_at":"2026-05-29T05:00:47.709Z","avatar_url":"https://github.com/CarlosCommits.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"logos/Groups%20Console%20logo%20design%20concept%202%20bottom-trimmed.png\" alt=\"Groups Console logo\" width=\"270\"\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eGroups Console\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  Windows-first desktop app for Exchange Online and Microsoft Graph administration.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/CarlosCommits/groups-console/releases/latest/download/GroupsConsoleSetup.exe\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Download%20for%20Windows-GroupsConsoleSetup.exe-00504a?style=for-the-badge\u0026logo=windows\u0026logoColor=white\" alt=\"Download the latest Windows installer\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n![Groups Console dashboard](images/Dashboard%20image%20rounded.png)\n\n## What it does today\n\nGroups Console is built to make Microsoft 365 contact and group maintenance easier for non-IT administrators, such as office administrators, executive assistants, and operations staff who keep distribution lists up to date. The goal is to reduce routine dependency on IT teams for day-to-day Microsoft contacts, distribution lists, mail-enabled security groups, and guest-user cleanup.\n\nIt focuses on workflows that are awkward in the standard Exchange admin experience, such as adding one contact to multiple groups from a single place. Instead of jumping between admin screens, operators get a focused desktop workspace for finding people, reviewing group membership, updating contacts, inviting guests, and applying common list-management changes.\n\nGroups Console currently supports:\n\n- Exchange group browsing for distribution lists and mail-enabled security groups\n- Group membership reads and membership add/remove flows\n- Unified directory search across Exchange and Graph-backed identities\n- Contact creation and contact company updates\n- Guest search, guest invite, and guest company updates\n- Membership matrix export to `.xlsx`\n- Local system logs and diagnostics export\n\nThe app is still evolving. It is already useful for real tenant workflows, but the project is not yet presented as a finished general-availability product.\n\n## Architecture at a glance\n\n| Layer | Responsibility |\n| --- | --- |\n| Renderer | React UI running in a sandboxed Electron renderer |\n| Preload | Narrow typed `window.groupsConsole` bridge |\n| Main process | Orchestration, dialogs, logging, diagnostics, and Graph integration |\n| Exchange layer | App-owned PowerShell worker/session host for Exchange Online operations |\n| Shared contracts | Zod-validated DTOs and IPC contracts under `src/shared/**` |\n\nExchange remains the write path for distribution lists, mail-enabled security groups, and Exchange contacts. Microsoft Graph is used for guest-user lifecycle and selected directory reads.\n\n## Requirements\n\n### Workstation requirements\n\n| Requirement | Notes |\n| --- | --- |\n| Windows admin workstation | `win32` is the supported runtime target |\n| Windows PowerShell 5.1 | Preferred Exchange runtime |\n| PowerShell 7 (`pwsh`) | May be usable, but Windows PowerShell 5.1 is preferred |\n| Internet access | Required for Microsoft 365 and Microsoft Graph endpoints |\n| Writable app-data directory | Required for logs and local config |\n\n### Required PowerShell module\n\n- `ExchangeOnlineManagement` must be installed and importable for the detected PowerShell runtime\n\nThe app checks for the module at startup/readiness time. If it is missing, the auth panel can offer an `Install Exchange module` action that installs `ExchangeOnlineManagement` for the current Windows user through the app-owned main-process/PowerShell worker path. If the module is installed but not importable, the app blocks Exchange sign-in and shows the import error with IT remediation guidance.\n\nThe current v2 app no longer depends on `ImportExcel` at runtime. Report export is generated on the JavaScript/Electron side.\n\n### Tenant / app requirements\n\nThis app uses the bundled publisher-owned multi-tenant Microsoft Entra app registration and can sign operators into any organizational Microsoft Entra tenant that has authorized the app.\n\nYou will need:\n\n- tenant-admin consent for the bundled Enterprise Application / service principal in the target tenant\n- delegated Graph consent for the scopes your tenant will use\n- an operator account that can both connect to Exchange Online PowerShell and perform the intended Graph operations\n\n## Microsoft User Permissions\n\nFor full operator access in the current app, the practical role set is:\n\n- **Exchange Recipient Administrator** for Exchange Online recipient work, including contacts, recipient lookup, distribution group membership, and mail-enabled security group membership changes.\n- **Microsoft Entra User Administrator** for Microsoft Graph guest work, including guest invitations and guest profile updates.\n- Tenant-admin consent for the delegated Microsoft Graph scopes requested by the app: `User.Read`, `User.Read.All`, `User.ReadWrite.All`, and `User.Invite.All`.\n\nThis is the clean built-in-role answer for an operator who should be able to use every current app workflow. More restrictive setups may be possible with custom Exchange RBAC and tenant-specific Entra policy, but they need to be validated in the target tenant.\n\n### Microsoft Graph side\n\nFor the current guest workflows, the repo documents these practical delegated requirements:\n\n- **Guest search:** directory read capability, typically covered by `User.Read.All`\n- **Guest invite:** `User.Invite.All`, plus whatever tenant invitation policy or Entra role rules your tenant enforces\n- **Guest company update:** `User.ReadWrite.All` in practice for updating other users, plus any tenant role/policy requirements\n\nThe exact Entra role model varies by tenant. Do not assume a universal built-in role name will always be sufficient.\n\n### Exchange side\n\nExchange operations require Exchange Online PowerShell access plus the RBAC rights needed for the specific action.\n\nLikely requirements include:\n\n- read-oriented Exchange roles for listing groups, reading members, and recipient search\n- write-oriented Exchange roles for adding/removing group members\n- recipient-management roles for creating contacts and updating contact company fields\n\nThese are **likely** requirements, not guaranteed universal role group names. Exchange RBAC differs by tenant customization.\n\n## Tenant authorization\n\nGroups Console does not require each tenant to create its own app registration for normal use. The repo ships with `config/tenant.json`, which points at the publisher-owned multi-tenant Microsoft Entra app registration.\n\nGroups Console uses delegated, interactive, system-browser-based Graph authentication through `@azure/msal-node`.\nThe Graph sign-in flow uses MSAL's localhost loopback redirect for the desktop interactive login; it does not require a client secret.\n\nFor a tenant to use Graph-backed workflows, a tenant admin must authorize the app in that tenant. That creates the tenant-local Enterprise Application / service principal for the bundled multi-tenant app registration. Customer tenants should not create client secrets or certificates for this desktop app.\n\nIf you do not override scopes, the app requests these delegated Graph scopes by default:\n\n- `User.Read`\n- `User.Read.All`\n- `User.ReadWrite.All`\n- `User.Invite.All`\n\nIn many tenants, granting these scopes will require tenant-admin consent.\n\n### What the app can and cannot pre-validate\n\nThe app can verify:\n\n- bundled or user-data tenant config exists and parses\n- Exchange tenant matches the active Graph tenant for write-safe workflows\n- local PowerShell/module prerequisites\n\nIf `tenantId` or `graph.allowedTenantIds` is configured, the app can also verify that the signed-in Graph tenant is allowed by local configuration. Without those optional pins, any organizational tenant can sign in once that tenant has authorized the app.\n\nThe app cannot reliably pre-check:\n\n- exact Entra directory roles held by the operator\n- exact Graph scopes actually consented in the current token\n- exact Exchange RBAC assignments\n- tenant invitation policy behavior for every guest-invite scenario\n- per-group ownership/manager restrictions before every Exchange write\n\nIf sign-in succeeds but an operation is denied, the app treats that as an authorization/runtime error and surfaces the backend failure instead of pretending the environment is ready.\n\n## Bootstrap and readiness checks\n\nThe app's local readiness model depends on four checks:\n\n| Check | Purpose |\n| --- | --- |\n| `powershell` | Confirms a supported PowerShell runtime is available |\n| `exchangeModule` | Confirms `ExchangeOnlineManagement` is installed and importable |\n| `logDirectory` | Confirms the app can write local diagnostics |\n| `tenantConfig` | Confirms tenant configuration exists and parses |\n\nIf any of these are missing or degraded, the app should show that state instead of pretending it is fully ready.\n\nThe auth panel uses these checks before Exchange sign-in. It can block Exchange setup when PowerShell is missing, offer the current-user `ExchangeOnlineManagement` install action when the module is missing, and surface installed-but-not-importable module failures without trying to connect Exchange.\n\nTenant mismatch also matters: the app is designed to block writes when Graph and Exchange are connected to different tenants. Multi-tenant Graph sign-in does not loosen that safety check.\n\n## Security and runtime model\n\nSecurity constraints are intentional, not accidental:\n\n- `contextIsolation: true`\n- `sandbox: true`\n- `nodeIntegration: false`\n- renderer code cannot directly import Node/Electron privileged modules\n- PowerShell execution is allowlisted and app-owned; the UI does not send free-form PowerShell\n- the app does not collect or store admin passwords\n- Graph auth uses system-browser interactive flow\n- production packaging is intended to be code-signed\n\nExecution policy stance:\n\n- the app launches its PowerShell workers with **process-scoped** execution policy handling\n- the app does **not** require machine-wide or user-wide execution policy changes as a normal setup step\n- if `MachinePolicy` or `UserPolicy` blocks execution, the app should surface a prerequisite error instead of mutating policy silently\n- the module install remediation path must not call persistent `Set-ExecutionPolicy`\n\n## Development\n\nDeveloper setup, scripts, verification commands, and architecture references live in [DEVELOPMENT.md](DEVELOPMENT.md).\n\nRelease versioning, git tags, GitHub Release assets, and distribution guidance live in [docs/release.md](docs/release.md).\n\n## Supported workflow boundaries\n\n| Area | Current stance |\n| --- | --- |\n| Exchange-backed group administration | Supported |\n| Graph-backed guest workflows | Supported |\n| Contact / guest SMTP overlap handling | Supported with asymmetric safety rules |\n| Local diagnostics and supportability | Supported |\n| Authentication model | Delegated interactive auth only |\n| Server-hosted orchestration | Not in scope |\n| Graph writes for distribution lists or mail-enabled security groups | Not in scope |\n| SMTP as canonical identity key | Not assumed |\n\n## License\n\nCopyright 2026 Carlos Canas.\n\nGroups Console is licensed under the [Apache License 2.0](LICENSE).\nRedistributions should preserve the license text, copyright notice, and [NOTICE](NOTICE) file.\n\nThis project is not affiliated with, endorsed by, or sponsored by Microsoft.\nUse of Microsoft APIs is subject to Microsoft's applicable API terms.\nTenant administrators are responsible for reviewing all changes before applying them.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarloscommits%2Fgroups-console","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarloscommits%2Fgroups-console","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarloscommits%2Fgroups-console/lists"}