1. IGA とワークフロー — 概要
アイデンティティのガバナンスと管理 (IGA)これは、ユーザーが組織に参加するとき (参加者)、役割を変更するとき (移動者)、退職するとき (退職者) までの ID ライフサイクル管理の領域です。 Keycloakワークフローは機能ですプレビュー。プレビューこれらの IGA プロセスの自動化が可能になります。
1.1 なぜワークフローが必要なのでしょうか?
| 問題 | ワークフローがありません | ワークフローがあります |
|---|---|---|
| 新入社員のオンボーディング | 管理者は手動でアカウントを作成し、ロールを割り当て、グループに追加する必要があります | 自動: アカウントを作成 → 部門に応じて役割を割り当て → グループを追加 → ウェルカムメールを送信 |
| 部署異動 | 管理者は古い役割を手動で削除し、新しい役割を割り当てる必要があります | 部門の変更を自動的に検出 → それに応じて役割/グループを更新 |
| オフボーディング | アクセス権の取り消し忘れ → セキュリティリスク | アカウントを自動的に無効にする → セッションを取り消す → グループから削除 |
| アクセスレビュー | 定期的に見直しができない | 未使用の権限を自動的にチェックして報告する |
1.2 Keycloakワークフローとは何ですか?
Keycloakワークフローはシステムですイベント駆動型、条件ベース以下を含む自動プロセスの定義が可能になります。
- トリガー: ワークフロートリガーイベント (ユーザー作成、属性変更、ログインイベント...)
- 条件: ワークフローを実行するには条件を満たす必要があります
- ステップ: 条件に一致した場合に実行するアクション (ロールの割り当て、グループへの追加、通知の送信など)
2. ワークフローをオンにする (プレビュー機能)
ワークフローは機能ですプレビュー。プレビュー、明示的に有効にする必要があります。
# Bật qua command line
bin/kc.sh start --features=workflows
# Hoặc trong keycloak.conf
features=workflows
# Bật cùng các features khác
bin/kc.sh start --features=workflows,organizations,scripts
# Docker
docker run -e KC_FEATURES=workflows \
quay.io/keycloak/keycloak:latest start-dev
注記:プレビュー機能はバージョン間で API が変更される場合があります。徹底的なテストを行わずに本番環境で使用しないでください。
3. ワークフローの概念
3.1 ワークフローコンポーネント
Workflow Definition
├── Metadata (name, description, enabled)
├── Trigger (event type)
├── Conditions (when to execute)
│ ├── Condition 1 (attribute check)
│ ├── Condition 2 (group membership)
│ └── ... (AND/OR logic)
└── Steps (what to do)
├── Step 1 (assign role)
├── Step 2 (add to group)
├── Step 3 (send notification)
└── ... (sequential execution)
3.2 トリガーの種類
| トリガー | 説明する | ユースケース |
|---|---|---|
| USER_CREATED | 新しいユーザーが作成されました | オンボーディング: 役割/グループの自動割り当て |
| USER_UPDATED | ユーザー属性の変更 | ムーバー: 部門の変更を検出 |
| USER_DELETED | ユーザーが削除されました | クリーンアップ: 外部アクセスを取り消す |
| USER_DISABLED | ユーザーが無効になっています | オフボード: セッションを取り消す |
| GROUP_MEMBERSHIP_CHANGED | ユーザーがグループに追加/グループから削除されました | 関連する役割を自動割り当て |
| ROLE_ASSIGNED | ユーザーには役割が割り当てられています | カスケード権限 |
| ROLE_REMOVED | ユーザーの役割が削除されました | 依存アクセスを取り消す |
4. ワークフローの定義
4.1 ワークフロー定義の構造
{
"name": "Employee Onboarding",
"description": "Automatically provision new employees based on department",
"enabled": true,
"trigger": {
"type": "USER_CREATED"
},
"conditions": [
{
"type": "user_attribute",
"attribute": "employeeType",
"operator": "equals",
"value": "full-time"
}
],
"steps": [
{
"type": "assign_role",
"role": "employee"
},
{
"type": "add_to_group",
"group": "/Company/All-Employees"
},
{
"type": "conditional",
"condition": {
"type": "user_attribute",
"attribute": "department",
"operator": "equals",
"value": "engineering"
},
"thenSteps": [
{
"type": "assign_role",
"role": "developer"
},
{
"type": "add_to_group",
"group": "/Company/Engineering"
}
]
}
]
}
4.2 YAML形式
# workflow-onboarding.yaml
name: Employee Onboarding
description: Auto-provision new employees
enabled: true
trigger:
type: USER_CREATED
conditions:
- type: user_attribute
attribute: employeeType
operator: equals
value: full-time
steps:
- type: assign_role
role: employee
- type: add_to_group
group: /Company/All-Employees
- type: conditional
condition:
type: user_attribute
attribute: department
operator: equals
value: engineering
thenSteps:
- type: assign_role
role: developer
- type: add_to_group
group: /Company/Engineering
- type: conditional
condition:
type: user_attribute
attribute: department
operator: equals
value: marketing
thenSteps:
- type: assign_role
role: marketer
- type: add_to_group
group: /Company/Marketing
5. ワークフロー表現言語
ワークフローは式言語を使用して、動的条件の定義そしてユーザー属性を参照する.
5.1 変数
| 変数 | 説明する | 例えば |
|---|---|---|
ユーザー.ユーザー名 | ユーザーのユーザー名 | ジョン・ドゥ |
ユーザー.メールアドレス | 電子メールアドレス | [email protected] |
ユーザー名.名 | ファーストネーム | ジョン |
ユーザーの姓 | 苗字 | ドウ |
user.attributes.{名前} | カスタム属性 | ユーザー属性部門 |
ユーザー.グループ | グループパスをリストする | ["/エンジニアリング"、"/VPN-ユーザー"] |
ユーザーの役割 | 割り当てられた役割をリストする | [「従業員」、「開発者」] |
イベントの種類 | イベントの種類 | USER_CREATED |
イベント時間 | イベントのタイムスタンプ | 2026-03-30T10:00:00Z |
5.2 演算子
| オペレーター | 説明する | 例えば |
|---|---|---|
等しい | 等しいものを比較する | user.attributes.Department が「エンジニアリング」に等しい |
等しくない | 等しくない | user.attributes.status not_equals "inactive" |
が含まれています。含まれています | 値が含まれています | user.email には「@acme.com」が含まれています |
で始まる | から始める | user.ユーザー名は「svc-」で始まります |
で終わる | で終わる | user.email の末尾は「@acme.com」 |
印刷する | リストに属します | ["HCM"、"HN"、"DN"] の user.attributes.location |
存在します | 属性が存在します | user.attributes.employeeId が存在します |
存在しない | 属性が存在しません | user.attributes.termination_date not_exists |
5.3 機能
# String functions
upper(user.attributes.department) → "ENGINEERING"
lower(user.email) → "[email protected]"
trim(user.attributes.title) → "Senior Developer"
# Date functions
now() → current timestamp
daysAgo(30) → timestamp 30 ngày trước
daysBetween(user.createdTimestamp, now()) → số ngày từ lúc tạo
# Collection functions
size(user.groups) → số groups
contains(user.roles, "admin") → true/false
6. ワークフローの管理
6.1 CRUD操作
# Tạo workflow
curl -X POST "http://localhost:8080/admin/realms/my-realm/workflows" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Employee Onboarding",
"description": "Auto-provision new employees",
"enabled": true,
"trigger": { "type": "USER_CREATED" },
"conditions": [
{
"type": "user_attribute",
"attribute": "employeeType",
"operator": "equals",
"value": "full-time"
}
],
"steps": [
{ "type": "assign_role", "role": "employee" },
{ "type": "add_to_group", "group": "/All-Employees" }
]
}'
# List workflows
curl -s "http://localhost:8080/admin/realms/my-realm/workflows" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'
# Get workflow by ID
WORKFLOW_ID="workflow-uuid"
curl -s "http://localhost:8080/admin/realms/my-realm/workflows/${WORKFLOW_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'
# Update workflow
curl -X PUT "http://localhost:8080/admin/realms/my-realm/workflows/${WORKFLOW_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Employee Onboarding v2",
"enabled": true,
"steps": [
{ "type": "assign_role", "role": "employee" },
{ "type": "add_to_group", "group": "/All-Employees" },
{ "type": "invoke_api", "url": "https://hr.example.com/api/provisioned", "method": "POST" }
]
}'
# Delete workflow
curl -X DELETE "http://localhost:8080/admin/realms/my-realm/workflows/${WORKFLOW_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
# Enable/Disable workflow
curl -X PUT "http://localhost:8080/admin/realms/my-realm/workflows/${WORKFLOW_ID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{ "enabled": false }'
7. 条件の定義
7.1 ユーザー属性の条件
{
"conditions": [
{
"type": "user_attribute",
"attribute": "department",
"operator": "equals",
"value": "engineering"
},
{
"type": "user_attribute",
"attribute": "employeeType",
"operator": "in",
"values": ["full-time", "contract"]
}
]
}
7.2 グループのメンバーシップ条件
{
"conditions": [
{
"type": "group_membership",
"group": "/Engineering",
"operator": "is_member"
}
]
}
7.3 時間ベースの条件
{
"conditions": [
{
"type": "time",
"attribute": "user.createdTimestamp",
"operator": "older_than_days",
"value": 90
}
]
}
7.4 イベントベースの条件
{
"conditions": [
{
"type": "event",
"attribute": "event.details.updated_attribute",
"operator": "equals",
"value": "department"
}
]
}
8. ステップの定義
8.1 役割の割り当て
{
"type": "assign_role",
"role": "employee",
"scope": "realm"
}
// Client role
{
"type": "assign_role",
"role": "editor",
"scope": "client",
"clientId": "my-app"
}
8.2 役割の削除
{
"type": "remove_role",
"role": "contractor",
"scope": "realm"
}
8.3 グループに追加
{
"type": "add_to_group",
"group": "/Company/Engineering/Backend"
}
8.4 グループから削除
{
"type": "remove_from_group",
"group": "/Company/Marketing"
}
8.5 通知の送信
{
"type": "send_email",
"to": "${user.email}",
"template": "welcome-employee",
"params": {
"name": "${user.firstName}",
"department": "${user.attributes.department}",
"manager": "${user.attributes.manager}"
}
}
8.6 外部 API の呼び出し
{
"type": "invoke_api",
"url": "https://hr-system.example.com/api/v1/employees/provisioned",
"method": "POST",
"headers": {
"Content-Type": "application/json",
"X-API-Key": "${env.HR_API_KEY}"
},
"body": {
"employeeId": "${user.attributes.employeeId}",
"email": "${user.email}",
"department": "${user.attributes.department}",
"provisionedAt": "${event.time}"
}
}
8.7 ユーザー属性の設定
{
"type": "set_attribute",
"attribute": "provisionedAt",
"value": "${now()}"
}
{
"type": "set_attribute",
"attribute": "accessLevel",
"value": "standard"
}
9. ジョイナー、ムーバー、リーバー (JML) プロセス
9.1 ジョイナー — 自動オンボーディング
# workflow-joiner.yaml
name: "JML: Joiner - Employee Onboarding"
description: Auto-provision new full-time employees
enabled: true
trigger:
type: USER_CREATED
conditions:
- type: user_attribute
attribute: employeeType
operator: equals
value: full-time
steps:
# Base provisioning cho tất cả employees
- type: assign_role
role: employee
- type: add_to_group
group: /Company/All-Employees
- type: set_attribute
attribute: onboardingStatus
value: completed
- type: set_attribute
attribute: onboardedAt
value: "${now()}"
# Department-specific provisioning
- type: conditional
condition:
type: user_attribute
attribute: department
operator: equals
value: engineering
thenSteps:
- type: assign_role
role: developer
- type: add_to_group
group: /Company/Engineering
- type: assign_role
role: gitlab-user
scope: client
clientId: gitlab
- type: conditional
condition:
type: user_attribute
attribute: department
operator: equals
value: sales
thenSteps:
- type: assign_role
role: sales-rep
- type: add_to_group
group: /Company/Sales
- type: assign_role
role: crm-user
scope: client
clientId: salesforce
# Notify HR system
- type: invoke_api
url: https://hr.example.com/api/onboarding/completed
method: POST
body:
employeeId: "${user.attributes.employeeId}"
email: "${user.email}"
# Send welcome email
- type: send_email
to: "${user.email}"
template: welcome-employee
9.2 異動者 — 部門異動
# workflow-mover.yaml
name: "JML: Mover - Department Transfer"
description: Handle department changes
enabled: true
trigger:
type: USER_UPDATED
conditions:
- type: event
attribute: event.details.updated_attribute
operator: equals
value: department
steps:
# Lấy old department từ event details
# Remove old department group
- type: remove_from_group
group: "/Company/${event.details.previous_value}"
# Add to new department group
- type: add_to_group
group: "/Company/${user.attributes.department}"
# Log transfer
- type: set_attribute
attribute: lastTransferDate
value: "${now()}"
- type: set_attribute
attribute: previousDepartment
value: "${event.details.previous_value}"
# Notify managers
- type: invoke_api
url: https://hr.example.com/api/transfers
method: POST
body:
employeeId: "${user.attributes.employeeId}"
fromDepartment: "${event.details.previous_value}"
toDepartment: "${user.attributes.department}"
transferDate: "${event.time}"
9.3 退職者 — 自動オフボーディング
# workflow-leaver.yaml
name: "JML: Leaver - Employee Offboarding"
description: Auto-deprovision disabled/deleted employees
enabled: true
trigger:
type: USER_DISABLED
conditions:
- type: user_attribute
attribute: employeeType
operator: in
values:
- full-time
- contract
steps:
# Revoke all active sessions
- type: invoke_api
url: "http://localhost:8080/admin/realms/my-realm/users/${user.id}/logout"
method: POST
headers:
Authorization: "Bearer ${admin.token}"
# Remove from all groups (except audit trail groups)
- type: remove_from_group
group: /Company/All-Employees
# Remove sensitive roles
- type: remove_role
role: developer
- type: remove_role
role: admin
# Set offboarding metadata
- type: set_attribute
attribute: offboardingStatus
value: completed
- type: set_attribute
attribute: offboardedAt
value: "${now()}"
- type: set_attribute
attribute: accountDisabledReason
value: employee-departure
# Notify IT and HR
- type: invoke_api
url: https://hr.example.com/api/offboarding/completed
method: POST
body:
employeeId: "${user.attributes.employeeId}"
email: "${user.email}"
offboardedAt: "${event.time}"
# Notify admin via email
- type: send_email
to: [email protected]
template: offboarding-notification
params:
employeeName: "${user.firstName} ${user.lastName}"
department: "${user.attributes.department}"
10. アクセスレビューワークフロー
レビューへのアクセスが許可されています定期的にチェックするユーザーが現在の権限をまだ必要としているかどうかを確認します。
# workflow-access-review.yaml
name: "Access Review: Inactive Users"
description: Review and flag inactive users
enabled: true
trigger:
type: SCHEDULED
schedule: "0 0 1 * *" # Chạy hàng tháng
conditions:
- type: user_attribute
attribute: lastLoginTimestamp
operator: older_than_days
value: 90
steps:
# Flag user for review
- type: set_attribute
attribute: accessReviewStatus
value: pending-review
- type: set_attribute
attribute: accessReviewDate
value: "${now()}"
# Add to review group
- type: add_to_group
group: /Access-Review/Pending
# Notify user's manager
- type: send_email
to: "${user.attributes.managerEmail}"
template: access-review-notification
params:
employeeName: "${user.firstName} ${user.lastName}"
lastLogin: "${user.attributes.lastLoginTimestamp}"
reviewDeadline: "${daysFromNow(14)}"
# If user has sensitive roles, escalate
- type: conditional
condition:
type: role_assigned
role: admin
thenSteps:
- type: send_email
to: [email protected]
template: access-review-escalation
params:
employeeName: "${user.firstName} ${user.lastName}"
sensitiveRoles: "${user.roles}"
11. 一般的な企業のユースケース
11.1 請負業者のライフサイクル管理
# Tự động disable contractor khi hết hợp đồng
name: "Contractor: Auto-disable on contract end"
enabled: true
trigger:
type: SCHEDULED
schedule: "0 0 * * *" # Daily check
conditions:
- type: user_attribute
attribute: employeeType
operator: equals
value: contract
- type: user_attribute
attribute: contractEndDate
operator: before
value: "${now()}"
steps:
- type: disable_user
- type: set_attribute
attribute: disabledReason
value: contract-expired
- type: send_email
to: "${user.attributes.managerEmail}"
template: contractor-expired
11.2 LDAP Sync からの自動プロビジョニング
# Khi user được sync từ LDAP, auto-assign roles
name: "LDAP: Post-sync provisioning"
enabled: true
trigger:
type: USER_CREATED
conditions:
- type: user_attribute
attribute: LDAP_ID
operator: exists
- type: user_attribute
attribute: department
operator: exists
steps:
- type: assign_role
role: ldap-user
- type: conditional
condition:
type: user_attribute
attribute: memberOf
operator: contains
value: "CN=VPN-Users"
thenSteps:
- type: assign_role
role: vpn-access
11.3 コンプライアンス — パスワードローテーションリマインダー
# Nhắc nhở user đổi password sau 90 ngày
name: "Compliance: Password rotation reminder"
enabled: true
trigger:
type: SCHEDULED
schedule: "0 8 * * 1" # Weekly, Monday 8 AM
conditions:
- type: user_attribute
attribute: lastPasswordChange
operator: older_than_days
value: 80
steps:
- type: send_email
to: "${user.email}"
template: password-rotation-reminder
params:
daysRemaining: "${90 - daysBetween(user.attributes.lastPasswordChange, now())}"
- type: conditional
condition:
type: user_attribute
attribute: lastPasswordChange
operator: older_than_days
value: 90
thenSteps:
- type: set_attribute
attribute: passwordExpired
value: "true"
- type: invoke_api
url: "http://localhost:8080/admin/realms/my-realm/users/${user.id}/execute-actions-email"
method: PUT
body: ["UPDATE_PASSWORD"]
12. 監視とトラブルシューティング
12.1 ワークフロー実行ログ
# Bật debug logging cho workflows
bin/kc.sh start \
--features=workflows \
--log-level=org.keycloak.workflow:DEBUG
# Xem workflow execution history
curl -s "http://localhost:8080/admin/realms/my-realm/workflows/${WORKFLOW_ID}/executions" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" | jq '.'
# Response mẫu:
{
"executions": [
{
"id": "exec-uuid-1",
"workflowId": "workflow-uuid",
"userId": "user-uuid",
"status": "COMPLETED",
"triggeredAt": "2026-03-30T10:00:00Z",
"completedAt": "2026-03-30T10:00:02Z",
"steps": [
{ "type": "assign_role", "status": "SUCCESS" },
{ "type": "add_to_group", "status": "SUCCESS" },
{ "type": "send_email", "status": "SUCCESS" }
]
}
]
}
12.2 一般的な問題
| 問題 | 理由 | 解決 |
|---|---|---|
| ワークフローがトリガーされない | 機能が有効になっていない、またはワークフローが無効になっています | チェック--features=ワークフローそして有効: true |
| 条件が一致しません | 属性名または値が間違っています | 管理コンソール経由でユーザー属性を確認する |
| ステップが失敗しました | ロール/グループが存在しません | ワークフローで参照する前にロール/グループを作成してください |
| API呼び出しに失敗しました | 外部サービスは利用できません | ネットワーク接続を確認し、再試行ロジックを追加します |
| メールが送信されませんでした | SMTPはまだ設定されていません | レルム設定を構成 → 電子メール |
13. ベストプラクティス
- シンプルに始める— 最初に基本的な JML を実装し、次に複雑さを追加します
- 本番前にワークフローを徹底的にテストする— これはプレビュー機能であるため、バグがある可能性があります
- 冪等ステップ— 副作用を引き起こすことなくステップを再実行できるようにします
- エラー処理— 外部 API の障害または電子メールの返送に備えて計画する
- 監査証跡— ワークフローの実行を記録するための属性 (タイムスタンプ、ステータス) を常に設定します。
- 関心事の分離— JML を個別のワークフロー (結合者、移動者、離脱者) に分離します。
- バージョン管理— ワークフロー定義を git にエクスポートし、CI/CD を使用してデプロイします
- 実行ログを監視する— 失敗したワークフロー実行に対するアラートを設定する
- 段階的な展開— 最初に小規模なユーザー グループのワークフローを有効にしてから、拡張します