scripts/setup.sh¶
Platforms: macOS, Linux.
Purpose¶
setup.sh is the first-time and additional-device provisioning entry point for
macOS and Linux. It is a thin orchestrator around the Python provisioning
scripts: it verifies prerequisites, bootstraps the dedicated Provisioner app,
creates or attaches the full Agent Identity chain (Blueprint → Agent Identity →
Agent User), generates and registers the Blueprint certificate, writes .env,
optionally provisions cloud-hosted operational memory, registers the entrabot
MCP server, and runs a post-setup smoke check. State is persisted to
.entrabot-state.json, so re-runs are idempotent.
Requirements¶
- macOS or Linux with Bash.
- Azure CLI (
az) onPATH, already signed in (az login). Without an active login the script fails at Step 2. - Python 3.12 or newer (
python3.12,python3.13, or apython3that reports ≥ 3.12). git.- An OS credential store reachable by
keyring(Keychain on macOS, Secret Service / Keyring on Linux). The certificate step fails closed if the active keyring backend is not OS-native, rather than writing the private key to disk. - A Teams-capable M365 license for the Agent User is required for end-to-end Teams operation; license replication can take 10–15 minutes after assignment.
Install the macOS prerequisites with prereqs-macos.sh.
On Linux, install python3.12, git, and the Azure CLI with your package
manager. See also macOS / Linux installation.
Usage¶
# First-time provisioning — create a brand-new identity chain
./scripts/setup.sh --new --with-upn-suffix=workstation
# Create an explicit Agent User UPN
./scripts/setup.sh --new --agent-user-upn=entrabot-agent@yourtenant.onmicrosoft.com
# Attach this machine to an existing Blueprint (multi-device / re-install).
# Adds a certificate for this machine; other machines' certificates are kept.
./scripts/setup.sh --use-blueprint=<BLUEPRINT_APP_ID> --agent-user-upn=<UPN>
# Replace EVERY certificate on the Blueprint with a new one for this machine.
# Other machines stop authenticating until they run setup again.
# This does not delete the Blueprint itself.
./scripts/setup.sh --use-blueprint=<BLUEPRINT_APP_ID> --remove-all-existing-certs
# Opt in to cloud-hosted operational memory (Azure Blob Storage)
./scripts/setup.sh --new --with-upn-suffix=workstation --use-cloud-memory
# Skip the post-setup smoke test (useful in CI with no live tenant)
./scripts/setup.sh --new --with-upn-suffix=ci --skip-smoke
# Re-run diagnostics or the consolidated status view instead of provisioning
./scripts/setup.sh --diagnose
./scripts/setup.sh --status --json
An identity mode is required: pass exactly one of --new or
--use-blueprint=<APP_ID>. --new and --use-blueprint are mutually
exclusive.
Options¶
Identity mode (choose one):
--new— create a completely fresh chain. Existing state is backed up and the identity keys are cleared (the Provisioner app is preserved). Requires a UPN suffix (--with-upn-suffix) or an explicit--agent-user-upn; if neither is given, the script prompts for a suffix.--use-blueprint=ID— attach to an existing Blueprint by App ID. A new cert is generated for this machine and added to the Blueprint; existing certificates are kept, so other machines keep working, and the existing Agent Identity and Agent User are reused. If local state points at a different Blueprint, the stale identity-derived state is wiped (after a timestamped backup) so discovery re-runs cleanly against the new Blueprint. The old machine's cert on the previous Blueprint is not revoked automatically.--remove-all-existing-certs— used with--use-blueprint. Removes every certificate on the Blueprint, including those of other machines, and replaces them with a new one for this machine, even if this machine's current certificate is still valid. It does not delete the Blueprint, its Agent Identities, or its Agent Users. Every other machine stops authenticating until it runs setup again. It lists what will be removed (name and expiry only) and asksy/Non a terminal; without a terminal the flag itself is the consent, and it says so loudly. Nothing on this machine changes until the replacement is confirmed, so declining, or Graph refusing the update, leaves the active key, the state file, and any pending key as they were. This is the only way setup removes a certificate. Mutually exclusive with--new, which creates a Blueprint with no certificates.
Agent User selection:
--with-upn-suffix=NAME— UPN suffix for the Agent User (required with--newunless--agent-user-upnis given). With--use-blueprint, selects an existing suffixed Agent User.--agent-user-upn=UPN— explicit Agent User UPN. With--new, creates exactly that UPN; with--use-blueprint, selects an existing Agent User to reuse.--switch-user— runaz loginfirst so a different signed-in user owns and sponsors the agent.--teams-user=EMAIL— set a different user (or comma-separated users) as the Teams recipient(s); theazCLI user remains the admin/provisioner. Guests (B2B) are detected and resolved to their home tenant. Without it, a re-run for the same Agent User keeps the sponsor already in.env(ENTRABOT_HUMAN_*); with it, the sponsor is changed to the user(s) you name.
Operational memory:
--use-cloud-memory— opt in to Azure Blob Storage for operational data (interaction log, daily summaries, promises, per-chat delivery cursors, the watched-chat list, and the email-poll cursor). Provisions a resource group, storage account, and container scoped to the Agent User, and unsetsENTRABOT_KEEP_MEMORY_LOCAL. A re-run without this flag keeps cloud memory that is already set in.envfor the same Agent User.--keep-memory-local— operational data stays on the local filesystem; setsENTRABOT_KEEP_MEMORY_LOCAL=true. A first run with neither memory flag does this too. On a re-run for the same Agent User whose.envalready has cloud memory, pass this flag to switch back to local; without it the cloud settings are kept.--with-storage-account=NAME— target a named storage account instead of the deterministic per-tenant default (used with--use-cloud-memory). Mutually exclusive with--create-new-storage.--with-container=NAME— target a named blob container instead of theagent-<oid>default.--create-new-storage— force a fresh randomly-suffixed storage account even when the deterministic-name one exists. Mutually exclusive with--with-storage-account.
Agent 365 Work IQ:
--with-a365-work-iq— install or update the Microsoft Agent 365 DevTools CLI (a365) viadotnetglobal tools (requires a .NET SDK onPATH).--configure-a365-work-iq— run the interactive Work IQ Word developer setup against the existing Entrabot Blueprint, materialize the Work IQ OAuth grants, and validate the tooling manifest. Requirespwsh.--a365-agent-name=NAME— deprecated compatibility flag; Work IQ setup now reuses the existing Entrabot Blueprint from state.
Diagnostics and control:
--status— skip setup and delegate tostatus.sh; extra arguments are forwarded toshow_agent_status.py(for example--json,--health-only,--strict).--diagnose— skip provisioning and run a full health check (state file, certificate, three-hop token, Graph identity, Teams scope, MCP wiring, and the optionalpersona-satientry in.mcp.json). Exits non-zero if any check fails. The persona-sati check is advisory: it can reportSKIPorWARNbut never a failure, and it connects only to loopback addresses (see MCP connectivity).--skip-smoke— skip the post-setup smoke test (token + Graph identity + Teams scope).--help,-h— print the full option matrix.
Effects¶
- Verifies
az, Python ≥ 3.12, and required CLI tools; verifies the Azure login. - Bootstraps the Provisioner app via
entra_provisioning.pyand creates or reuses the Blueprint, Agent Identity, and Agent User viacreate_entra_agent_ids.py(using the Provisioner certificate token, neverazCLI tokens). - Reuses this machine's certificate only when
find_local_blueprint_cert.pyshows that the local private key matches a certificate still registered on the Blueprint that hasn't expired. The thumbprint cached in the state file is never trusted on its own: setup uses the thumbprint that check returns, and saves it if it differs from the cached one.verify_blueprint_cert.pyis an operator tool that setup does not call. - Otherwise, including whenever a new private key is still waiting under the pending
keystore entry from an earlier run,
register_blueprint_cert.pygenerates an RSA-2048 self-signed certificate (or reuses the pending key), keeps the private key under a temporary keystore entry, and uploads only the public certificate. Because a GraphPATCH keyCredentialsreplaces the whole list, it sends every existing certificate back along with the new one, then reads the list back to confirm before making the new key active and recording the thumbprint. A normal run never removes a certificate. - Creates
.venv, installs the package with dev extras, and writes.env(chmod 600) with the resulting tenant, Blueprint, Agent Identity, Agent User, and human-user identifiers plus the certificate thumbprint. - Reads the existing
.envbefore rewriting it. When this run is for the same Agent User (ENTRABOT_AGENT_USER_IDis unchanged), it keeps the sponsor (ENTRABOT_HUMAN_*) unless you pass--teams-useror--switch-user, and keeps the cloud-memory settings (ENTRABOT_BLOB_ENDPOINT/ENTRABOT_BLOB_CONTAINER) unless you pass--keep-memory-localor--use-cloud-memory. Cloud memory counts as set only when both blob settings are present andENTRABOT_KEEP_MEMORY_LOCALis nottrue,1, oryes. A run for a different Agent User (--new, or another Blueprint or Agent User) carries nothing over. The rest of.envis written fresh, so other lines you added by hand are dropped. - With
--use-cloud-memoryand an Agent User present, provisions Azure Blob Storage viaprovision_blob_storage.pyand appendsENTRABOT_BLOB_ENDPOINT/ENTRABOT_BLOB_CONTAINERto.env; offers an idempotent, source-preserving migration prompt that copies existing local data (interaction log, daily summaries, promises) and Claude Code persona memory as a one-time artifact copy. If provisioning fails, setup shows the provisioner's output and warns. It keeps the cloud settings already in.envif there were any for this Agent User, and otherwise falls back to local-only memory. The migration doesn't copy local poll state: the watched-chat list, the email cursor, the legacychat_idkey, or the per-chat delivery cursors underchat_cursors/. A machine's copies may be stale or another agent's. The cloud cursor starts at "now", discovery rebuilds the list from Graph about two minutes after boot, and a chat with no cursor surfaces its newest message only if it is under 24 hours old. - Registers the
entrabotMCP server in the project-local.mcp.json(Claude Code) and~/.copilot/mcp-config.json(Copilot CLI) viamcp_config.py; both entries are byte-identical. - Runs a post-setup smoke test (token + Graph identity + Teams scope) unless
--skip-smokeis passed. - Does not create or watch any Teams chat. There is no default chat; every Teams tool requires an explicit chat id supplied at runtime. See Messaging and delivery.
Exit behavior¶
0— setup (or--status/--help) completed successfully; the smoke test either passed or was skipped.1— a fatal setup error (for example a missing prerequisite, no Azure login, a failed provisioning step, a certificate check that couldn't complete, a declined--remove-all-existing-certsprompt,--new/--use-blueprintconflict, or a missing required identity mode).2— mutually exclusive storage flags,--newwith--remove-all-existing-certs, or setup completed provisioning but the cloud-memory migration failed (cloud memory is not in sync with local disk; re-run after fixing the underlying consent error).3— provisioning finished but the post-setup smoke test failed (the agent can't yet authenticate end-to-end, most often because a just-assigned Teams license has not replicated). Re-run./scripts/setup.sh --diagnoseafter replication completes.--diagnoseexits with the diagnostics' own overall exit code (0unless a check fails; warnings and skips do not change it).
Common failures¶
Not logged in to Azure CLI— runaz login(or./scripts/setup.sh --switch-user) and retry.No identity mode specified— pass--new --with-upn-suffix=NAMEor--use-blueprint=APP_ID.Could not check ...— setup couldn't read the Blueprint's certificates or the OS credential store (the pending key entry included), so it doesn't know whether this machine is already registered. It stops rather than generating a certificate, and nothing was changed. Fix the error shown above the message and re-run.Could not confirm the certificate update— the update may have landed but couldn't be read back. The new private key is kept; re-run setup and it picks up where it left off. Seeregister_blueprint_cert.py.- Old certificates building up — a normal run only adds, including when a
certificate has expired. Remove individual certificates in the Entra admin center,
or replace them all with
--remove-all-existing-certs. - Another machine stopped authenticating after
--remove-all-existing-certs— this is what the flag does. Run setup on that machine to add its certificate back. - Smoke test failed (exit 3) — usually license replication latency; wait
10–15 minutes and re-run with
--diagnose. - Migration failed (exit 2) — most often missing storage consent for the
Agent Identity; re-run
setup.sh(idempotent) orcreate_entra_agent_ids.pyto grant it, then retry.
Related commands¶
- Script reference — Setup
prereqs-macos.sh— install the macOS prerequisites.setup_delegated.sh— delegated (MSAL) sign-in instead of the full Agent User chain.setup-windows.ps1— the Windows counterpart.create_entra_agent_ids.py— the underlying identity-chain provisioner.register_blueprint_cert.py— generates and registers this machine's certificate.list_blueprint_certs.py— see which certificates are on the Blueprint.provision_blob_storage.py— cloud-memory provisioning invoked by--use-cloud-memory.status.sh/show_agent_status.py— status and health checks (reached via--status).teardown.sh— remove everythingsetup.shcreates.- Identity and token flow.