service-omni-agent-users-create

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

service-omni-agent-users-create

service-omni-agent-users-create

Create N agent users on a Salesforce org via Anonymous Apex so Omni-Channel has agents to route work to. Usernames follow a deterministic per-org pattern, detection is SOQL-based, and the skill inserts only the users that are missing — so it is safe to re-run. It is invoked by
service-omni-channel-setup-coordinate
after
service-omni-base-settings-configure
enables Omni-Channel; assigning permission sets (
service-omni-permission-set-assign
) and adding users to queues (
service-omni-queue-members-assign
) are separate leaves. Supervisor users come from
service-omni-supervisor-users-create
.
通过Anonymous Apex在Salesforce org上创建N个代理用户,以便Omni-Channel拥有可路由工作的代理。用户名遵循基于org的确定性模式,检测基于SOQL实现,且该技能仅插入缺失的用户——因此可安全重复运行。它会在
service-omni-base-settings-configure
启用Omni-Channel后,由
service-omni-channel-setup-coordinate
调用;分配权限集(
service-omni-permission-set-assign
)和将用户添加到队列(
service-omni-queue-members-assign
)是独立的分支流程。管理员用户由
service-omni-supervisor-users-create
生成。

Inputs

输入参数

Confirm once, up front:
  • org-alias
    (required, no default) — must resolve via
    sf org display
    .
  • User count (optional, default
    3
    , range
    1..10
    ).
  • Profile name (optional, default
    Standard User
    for portability). The coordinator overrides this to a Service Cloud profile so agents consume Service Cloud licenses; on many CDOs the Salesforce license pool is saturated while Service Cloud has free slots.
Usernames and passwords are never accepted from the operator — both are generated (usernames from the org suffix, passwords via Anonymous Apex). Operator-supplied credentials would break re-run detection and risk weak or leaked secrets.
需预先确认以下参数:
  • org-alias
    (必填,无默认值)——必须可通过
    sf org display
    解析。
  • 用户数量(可选,默认值
    3
    ,范围
    1..10
    )。
  • 配置文件名称(可选,默认值为
    Standard User
    以保证可移植性)。协调器会将其覆盖为Service Cloud配置文件,以便代理使用Service Cloud许可证;在许多CDO中,Salesforce许可证池已饱和,但Service Cloud仍有可用额度。
用户名和密码从不接受操作员输入——两者均自动生成(用户名基于org后缀,密码通过Anonymous Apex生成)。操作员提供的凭据会破坏重复运行的检测逻辑,并存在弱密码或密钥泄露风险。

Preconditions and safety

前置条件与安全机制

  • Target org authenticated via
    sf
    CLI (My Domain URL, not
    .lightning.force.com
    ), Service Cloud license present,
    sf
    CLI ≥ 2.139.6.
  • The executing user has
    PermissionsModifyAllData
    and
    PermissionsManagePasswordPolicies
    (standard on System Administrator).
  • Production guardrail: the detect script computes
    safe_to_write
    as
    IsSandbox
    OR
    TrialExpirationDate != null
    OR
    OrganizationType
    in {Developer Edition, Base Edition}, and the skill blocks with no override when it is false. CDOs, scratch orgs, and dev orgs are permitted.
Password handling (fail-closed). Passwords are set by Anonymous Apex
System.setPassword
(
sf user password generate
cannot target Apex-inserted users — it fails with
NamedOrgNotFoundError
). The password literal appears in the inline executeAnonymous debug log and, only when a debug-log TraceFlag is active for the running user, in a queryable
ApexLog
. The wrapper therefore fails closed before the first
System.setPassword
: it proves via a SOQL-filtered Tooling API query (
ExpirationDate > now
) that no active TraceFlag exists. If safety cannot be positively proven — the running user is unresolved, the query fails or is unparseable, or any active TraceFlag exists — it generates no password at all; the user is left ACTIVE, flagged
password_status:"reset_required"
, and a
security_warning
explains why. It never deletes logs, so no plaintext can reach an
ApexLog
and unrelated audit logs are untouched. If
System.setPassword
itself fails for a user, that user is kept ACTIVE and flagged for reset.
  • 目标org已通过
    sf
    CLI认证(使用My Domain URL,而非
    .lightning.force.com
    ),具备Service Cloud许可证,且
    sf
    CLI版本≥2.139.6。
  • 执行用户拥有
    PermissionsModifyAllData
    和
    PermissionsManagePasswordPolicies
    权限(系统管理员默认具备)。
  • 生产环境防护:检测脚本会计算
    safe_to_write
    ,当org为沙箱环境(
    IsSandbox
    )、试用版(
    TrialExpirationDate != null
    )或组织类型为开发者版/基础版时,
    safe_to_write
    为true;若为false,该技能会直接阻止操作且无覆盖选项。CDO、临时org和开发org均允许操作。
密码处理(默认失败关闭)。密码通过Anonymous Apex的
System.setPassword
设置(
sf user password generate
无法针对Apex插入的用户生效——会触发
NamedOrgNotFoundError
)。密码明文会出现在内联executeAnonymous调试日志中,且仅当运行用户的调试日志TraceFlag处于激活状态时,才会出现在可查询的
ApexLog
中。因此,包装器会在首次调用
System.setPassword
前执行失败关闭检查:通过带SOQL过滤的Tooling API查询(
ExpirationDate > now
)验证是否存在激活的TraceFlag。若无法确认安全性——运行用户身份未解析、查询失败或无法解析、存在任何激活的TraceFlag——则不会生成任何密码;用户会保持ACTIVE状态,标记为
password_status:"reset_required"
,并通过
security_warning
说明原因。该工具从不删除日志,因此明文不会进入
ApexLog
,且不会触及无关的审计日志。若
System.setPassword
对某个用户设置失败,该用户仍会保持ACTIVE状态并标记为需要重置密码。

Run

运行方式

bash
undefined
bash
undefined

read-only preview (never writes)

只读预览模式(永不写入数据)

bash scripts/detect-and-create.sh plan <org-alias> [count] [profile-name]
bash scripts/detect-and-create.sh plan <org-alias> [count] [profile-name]

detect, enforce safe_to_write, then insert only the missing users

检测、验证safe_to_write,仅插入缺失的用户

bash scripts/detect-and-create.sh run <org-alias> [count=3] [profile-name="Standard User"]

`detect-and-create.sh` is the canonical entry point: it re-runs detection, enforces the production guard, and only then inserts. Do not call `scripts/run-create.sh` directly — it is internal and does not enforce the guard on its own.
bash scripts/detect-and-create.sh run <org-alias> [count=3] [profile-name="Standard User"]

`detect-and-create.sh`是标准入口:它会重新执行检测、执行生产环境防护检查,之后才会插入用户。请勿直接调用`scripts/run-create.sh`——它是内部脚本,不会自行执行防护检查。

Behavior

行为逻辑

Detection. The detector derives an 8-char suffix from
Organization.Id
(
substring(10,18)
, lowercased), resolves the profile by name, and queries
User
for
agent{i}.<suffix>@example.com
to find which of the
count
slot indexes are occupied. A stable, deterministic pattern is what makes re-runs idempotent — the suffix is never a timestamp or UUID.
Insertion. The Apex loads
assets/create-users.apex.template
, substitutes
__COUNT__
/
__PROFILE_ID__
/
__SUFFIX__
, and inserts only the missing indexes. It re-checks existing users inside the transaction, which prevents a single run from double-inserting; across concurrent runs the in-transaction check is not a guarantee (both can pass their pre-query before either commits), so duplicate protection there relies on the global username-uniqueness constraint plus the
DUPLICATE_USERNAME
retry (see references/apex-template-notes.md). It enables the Service Cloud feature (
UserPermissionsSupportUser=true
) so users can go online in Omni; if the profile's license does not allow it, the Apex strips the flag and retries (users are still created, but need a Service-Cloud-license profile for full Omni). Each created user is reported via
AGENT_USER_CREATED|<id>|<username>|<email>
(no password in the marker — passwords are set by the separate
System.setPassword
submission).
Verification. After insertion the detector re-runs and must show
missing_count == 0
; otherwise the skill fails (the Apex reported success but the users did not persist).
检测。检测器从
Organization.Id
中提取8字符后缀(
substring(10,18)
,小写),按名称解析配置文件,并查询
User
对象中
agent{i}.<suffix>@example.com
格式的用户,以确定
count
个插槽索引中哪些已被占用。稳定、确定的模式是实现幂等重复运行的关键——后缀绝不会是时间戳或UUID。
插入。Apex加载
assets/create-users.apex.template
,替换
__COUNT__
/
__PROFILE_ID__
/
__SUFFIX__
变量,仅插入缺失的索引。它会在事务内重新检查现有用户,避免单次运行中重复插入;但对于并发运行,事务内的检查无法提供完全保障(两个进程可能在提交前都通过了预查询),因此此处的重复保护依赖全局用户名唯一性约束加上
DUPLICATE_USERNAME
重试机制(详见references/apex-template-notes.md)。它会启用Service Cloud功能(
UserPermissionsSupportUser=true
),以便用户可在Omni中上线;若配置文件的许可证不允许该操作,Apex会移除该标志并重试(用户仍会被创建,但需要具备Service Cloud许可证的配置文件才能实现完整的Omni功能)。每个创建的用户都会通过
AGENT_USER_CREATED|<id>|<username>|<email>
报告(标记中不包含密码——密码由单独的
System.setPassword
操作设置)。
验证。插入完成后,检测器会重新运行,必须显示
missing_count == 0
;否则该技能会执行失败(Apex报告成功但用户未持久化)。

Output contract

输出约定

detect-and-create.sh
emits a single JSON object with
status
∈
created
|
partial
|
reused
|
action_needed
|
blocked
, plus
detect
(mirrors the detector),
create
(mirrors the inserter,
null
when nothing was created), top-level
reused_users
,
created_count
,
reused_count
,
total_present_after
,
users_needing_password_reset
,
action_required
, and
safe_to_write
. The coordinator combines
reused_users
with newly created users so mixed runs configure presence and skills for the complete requested agent set.
  • created
    — every missing index landed with a working password.
  • partial
    — some landed but not all, or any user needs a manual password reset, or inactive occupants were found.
  • reused
    — all requested users already existed; nothing created.
  • action_needed
    — plan mode only; reports missing indexes without writing.
  • blocked
    — precondition failed (
    safe_to_write=false
    , unresolved profile/suffix, or nothing inserted).
create.created_users[].password
is populated only for users created this run whose
System.setPassword
succeeded;
password_status:"reset_required"
means the user is ACTIVE but has no working password yet. Exit code is 0 for
created
/
partial
/
action_needed
/
reused
, 1 for
blocked
.
Generated passwords are a secret: the returned JSON is the only place they appear. Any caller that persists stdout must write it only to a restricted
CREDENTIALS.json
(mode 0600), redact it from every other artifact, and delete it after distribution — the coordinator does this automatically; standalone callers own it. Reused users' passwords are not retrievable; recover via Setup → Users → Reset Password.
detect-and-create.sh
会输出单个JSON对象,其中
status
的取值为
created
|
partial
|
reused
|
action_needed
|
blocked
,此外还包含
detect
(镜像检测器结果)、
create
(镜像插入器结果,未创建任何内容时为
null
)、顶层字段
reused_users
、
created_count
、
reused_count
、
total_present_after
、
users_needing_password_reset
、
action_required
和
safe_to_write
。协调器会将
reused_users
与新创建的用户合并,以便混合运行时为完整的请求代理集配置在线状态和技能。
  • created
    ——所有缺失的索引均已创建且密码可用。
  • partial
    ——部分索引已创建但未全部完成,或存在需要手动重置密码的用户,或发现已占用的非活跃用户。
  • reused
    ——所有请求的用户已存在;未创建任何内容。
  • action_needed
    ——仅计划模式;报告缺失的索引但不写入数据。
  • blocked
    ——前置条件不满足(
    safe_to_write=false
    、配置文件/后缀未解析或未插入任何内容)。
create.created_users[].password
仅会在本次运行创建且
System.setPassword
成功的用户对象中填充;
password_status:"reset_required"
表示用户处于ACTIVE状态但尚无可用密码。退出码:
created
/
partial
/
action_needed
/
reused
时为0,
blocked
时为1。
生成的密码为机密信息:返回的JSON是其唯一出现的位置。任何持久化标准输出的调用方必须仅将其写入受限制的
CREDENTIALS.json
文件(权限模式0600),在所有其他工件中对其进行脱敏,并在分发后删除——协调器会自动执行此操作;独立调用方需自行处理。无法检索复用用户的密码;可通过Setup → Users → Reset Password恢复。

Limitations

限制

  • Username pattern and org suffix are fixed and never operator-configurable — that determinism is what enables idempotent re-runs.
  • Creates only agent users on the given profile; it never deletes, deactivates, or mutates existing users.
  • Common User errors (
    DUPLICATE_USERNAME
    ,
    INVALID_EMAIL
    ,
    LICENSE_LIMIT_EXCEEDED
    ) are translated into operator-friendly messages rather than surfaced raw.
  • 用户名模式和org后缀是固定的,不支持操作员配置——这种确定性是实现幂等重复运行的基础。
  • 仅在指定配置文件上创建代理用户;从不删除、停用或修改现有用户。
  • 常见的用户错误(
    DUPLICATE_USERNAME
    、
    INVALID_EMAIL
    、
    LICENSE_LIMIT_EXCEEDED
    )会转换为操作员友好的提示信息,而非直接显示原始错误。

References

参考文档

FileWhen to read
references/apex-patterns.md
Before running the Apex — Apex structure, User field defaults, password policy, and the profile-localization risk
references/apex-template-notes.md
When user creation returns a duplicate, license, or password error — explains template substitutions and retry behavior
assets/create-users.apex.template
Loaded by
scripts/run-create.sh
when missing agent users must be inserted
scripts/detect-existing.sh
Loaded by the canonical entry point for the read-only org, profile, safety, and existing-user checks
scripts/tests/test_user_create_security.py
Run after changing the user-creation scripts to verify production refusal and password-handling contracts
文件阅读时机
references/apex-patterns.md
运行Apex前——介绍Apex结构、User字段默认值、密码策略和配置文件本地化风险
references/apex-template-notes.md
用户创建返回重复、许可证或密码错误时——解释模板替换和重试行为
assets/create-users.apex.template
需插入缺失代理用户时,由
scripts/run-create.sh
加载
scripts/detect-existing.sh
标准入口加载,用于只读的org、配置文件、安全检查和现有用户检查
scripts/tests/test_user_create_security.py
修改用户创建脚本后运行,验证生产环境拒绝逻辑和密码处理约定