Chuyển đến nội dung chính

レッスン 16: ワークフロー - IGA による管理の自動化

Identity Governance and Administration (IGA) 用の Keycloak ワークフロー (プレビュー) を導入します。ワークフロー、ワークフロー定義、ワークフロー表現言語、ワークフローの管理、条件とステップの定義、Joiner-Mover-Leaver (JML) プロセス、自動オンボーディング/オフボーディング、アクセス レビュー、企業の一般的な使用例を理解します。

🔒 DevSecOps — レッスン 16 レッスン 16: ワークフロー - 管理の自動化 IGAと

基本から上級までの Keycloak

パート 4: ユーザー フェデレーション、組織、および認可

xdev.asia

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 を使用してデプロイします
  • 実行ログを監視する— 失敗したワークフロー実行に対するアラートを設定する
  • 段階的な展開— 最初に小規模なユーザー グループのワークフローを有効にしてから、拡張します