mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-05-20 16:38:10 +00:00
Compare commits
34 Commits
1.14.5a4
...
docs/secre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94a7c3dad9 | ||
|
|
7cc1a7bb41 | ||
|
|
09ffe87fbb | ||
|
|
14af56b74d | ||
|
|
35f693cf68 | ||
|
|
da15554d81 | ||
|
|
284533464f | ||
|
|
024e230b2c | ||
|
|
a4c90b6912 | ||
|
|
c50da7a6f2 | ||
|
|
e8aa870f90 | ||
|
|
14cd81eec6 | ||
|
|
a6225da326 | ||
|
|
259d334e38 | ||
|
|
42aa8a777c | ||
|
|
a95d26763f | ||
|
|
65ec783aae | ||
|
|
eefe0e42ac | ||
|
|
75bb882911 | ||
|
|
c36827b45b | ||
|
|
264da8245a | ||
|
|
f2960ccaaf | ||
|
|
bb0bde9518 | ||
|
|
2034f2140a | ||
|
|
3322634625 | ||
|
|
3d95afca41 | ||
|
|
b2cd133f10 | ||
|
|
ba523f46c0 | ||
|
|
63a9e7eb5e | ||
|
|
5d757cb626 | ||
|
|
b0d4dd256d | ||
|
|
e4a91cdc0c | ||
|
|
b9e71b322f | ||
|
|
f495bda016 |
2
.github/workflows/build-uv-cache.yml
vendored
2
.github/workflows/build-uv-cache.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
8
.github/workflows/generate-tool-specs.yml
vendored
8
.github/workflows/generate-tool-specs.yml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
steps:
|
||||
- name: Generate GitHub App token
|
||||
id: app-token
|
||||
uses: tibdex/github-app-token@v2
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app_id: ${{ secrets.CREWAI_TOOL_SPECS_APP_ID }}
|
||||
private_key: ${{ secrets.CREWAI_TOOL_SPECS_PRIVATE_KEY }}
|
||||
app-id: ${{ secrets.CREWAI_TOOL_SPECS_APP_ID }}
|
||||
private-key: ${{ secrets.CREWAI_TOOL_SPECS_PRIVATE_KEY }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
|
||||
4
.github/workflows/linter.yml
vendored
4
.github/workflows/linter.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
uv-main-py3.11-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.11"
|
||||
|
||||
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
|
||||
2
.github/workflows/pr-size.yml
vendored
2
.github/workflows/pr-size.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: codelytv/pr-size-labeler@v1
|
||||
- uses: codelytv/pr-size-labeler@095a41fca88b8764fd9e008ad269bcdb82bb38b9 # v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
xs_label: "size/XS"
|
||||
|
||||
2
.github/workflows/pr-title.yml
vendored
2
.github/workflows/pr-title.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
pr-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: amannn/action-semantic-pull-request@v5
|
||||
- uses: amannn/action-semantic-pull-request@e32d7e603df1aa1ba07e981f2a23455dee596825 # v5
|
||||
continue-on-error: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
6
.github/workflows/publish.yml
vendored
6
.github/workflows/publish.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
||||
python-version: "3.12"
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v4
|
||||
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
|
||||
|
||||
- name: Build packages
|
||||
run: |
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
ref: ${{ inputs.release_tag || github.ref }}
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.12"
|
||||
@@ -159,7 +159,7 @@ jobs:
|
||||
|
||||
- name: Notify Slack
|
||||
if: success()
|
||||
uses: slackapi/slack-github-action@v2.1.0
|
||||
uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
4
.github/workflows/type-checker.yml
vendored
4
.github/workflows/type-checker.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
code: ${{ steps.filter.outputs.code }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
2
.github/workflows/update-test-durations.yml
vendored
2
.github/workflows/update-test-durations.yml
vendored
@@ -38,7 +38,7 @@ jobs:
|
||||
uv-main-py${{ matrix.python-version }}-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
36
.github/workflows/vulnerability-scan.yml
vendored
36
.github/workflows/vulnerability-scan.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
uv-main-py3.11-
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v6
|
||||
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
||||
with:
|
||||
version: "0.11.3"
|
||||
python-version: "3.11"
|
||||
@@ -46,9 +46,39 @@ jobs:
|
||||
- name: Run pip-audit
|
||||
run: |
|
||||
uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json \
|
||||
--ignore-vuln CVE-2026-3219
|
||||
--ignore-vuln PYSEC-2024-277 \
|
||||
--ignore-vuln PYSEC-2026-89 \
|
||||
--ignore-vuln PYSEC-2026-97 \
|
||||
--ignore-vuln PYSEC-2025-148 \
|
||||
--ignore-vuln PYSEC-2025-183 \
|
||||
--ignore-vuln PYSEC-2025-189 \
|
||||
--ignore-vuln PYSEC-2025-190 \
|
||||
--ignore-vuln PYSEC-2025-191 \
|
||||
--ignore-vuln PYSEC-2025-192 \
|
||||
--ignore-vuln PYSEC-2025-193 \
|
||||
--ignore-vuln PYSEC-2025-194 \
|
||||
--ignore-vuln PYSEC-2025-195 \
|
||||
--ignore-vuln PYSEC-2025-196 \
|
||||
--ignore-vuln PYSEC-2025-197 \
|
||||
--ignore-vuln PYSEC-2025-210 \
|
||||
--ignore-vuln PYSEC-2026-139 \
|
||||
--ignore-vuln PYSEC-2025-211 \
|
||||
--ignore-vuln PYSEC-2025-212 \
|
||||
--ignore-vuln PYSEC-2025-213 \
|
||||
--ignore-vuln PYSEC-2025-214 \
|
||||
--ignore-vuln PYSEC-2025-215 \
|
||||
--ignore-vuln PYSEC-2025-216 \
|
||||
--ignore-vuln PYSEC-2025-217 \
|
||||
--ignore-vuln PYSEC-2025-218
|
||||
# Ignored CVEs:
|
||||
# CVE-2026-3219 - pip 26.0.1 (GHSA-58qw-9mgm-455v): no fix available, archive handling issue
|
||||
# PYSEC-2024-277 - joblib 1.5.3: disputed; NumpyArrayWrapper only used with trusted caches
|
||||
# PYSEC-2026-89 - markdown 3.10.2: DoS via malformed HTML; fix 3.8.1 — already past, advisory range is stale
|
||||
# PYSEC-2026-97 - nltk 3.9.4: arbitrary file read in filestring(); no fix available
|
||||
# PYSEC-2025-148 - onnx 1.21.0: path traversal in save_external_data; no fix available
|
||||
# PYSEC-2025-183 - pyjwt 2.12.1: disputed weak-encryption claim; key length is application-chosen
|
||||
# PYSEC-2025-189..197 - torch 2.11.0: memory-corruption/DoS in functions only reachable via untrusted models; no fix available
|
||||
# PYSEC-2025-210, PYSEC-2026-139 - torch 2.11.0: profiler/deserialization issues; no fix available
|
||||
# PYSEC-2025-211..218 - transformers 5.5.4: deserialization/code injection via malicious model checkpoints; no fix available
|
||||
continue-on-error: true
|
||||
|
||||
- name: Display results
|
||||
|
||||
@@ -4,6 +4,115 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="19 مايو 2026">
|
||||
## v1.14.5
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إلغاء استخدام `CrewAgentExecutor`، وتعيين وكلاء الطاقم الافتراضيين إلى `AgentExecutor`
|
||||
- تحسين أدوات صندوق الرمل Daytona
|
||||
- إضافة معلمة بدء `restore_from_state_id`
|
||||
- إضافة تسليط الضوء على `ExaSearchTool`، وإعادة تسميته من `EXASearchTool`
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح تسرب الذاكرة في `git.py` باستخدام `cached_property`
|
||||
- عرض استدعاءات الأدوات المتدفقة عندما تكون `available_functions` غائبة
|
||||
- ضمان تحميل أحداث `skills` للتتبع
|
||||
- تصحيح مسار نقطة النهاية للحالة من `/{kickoff_id}/status` إلى `/status/{kickoff_id}`
|
||||
- استعادة كتلة الشيفرة المفقودة في دليل التدفق الأول للغة البرتغالية (pt-BR)
|
||||
- منع `result_as_answer` من إرجاع رسائل الخطأ أو الكتل المرتبطة كإجابة نهائية
|
||||
- الحفاظ على مخرجات المهام عبر تفريغ الدفعات غير المتزامنة
|
||||
- دائمًا استعادة `task.output_pydantic` في كتلة finally
|
||||
- التعامل مع إدخال `BaseModel` في `convert_to_model`
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.14.5
|
||||
- إضافة دليل ترقية OSS و انتقال الطاقم إلى التدفق
|
||||
- توثيق متغيرات البيئة الإضافية لأدوات المطور
|
||||
- إضافة وثائق لـ `TavilyGetResearch`
|
||||
|
||||
### إعادة الهيكلة
|
||||
- استخراج واجهة سطر الأوامر إلى حزمة مستقلة `crewai-cli`
|
||||
|
||||
## المساهمون
|
||||
|
||||
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="18 مايو 2026">
|
||||
## v1.14.5a7
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الوثائق
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.14.5a6
|
||||
|
||||
### تغييرات كسرية
|
||||
- إلغاء حقل function_calling_llm
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde, @heitorado
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="15 مايو 2026">
|
||||
## v1.14.5a6
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح استدعاءات الأدوات المتدفقة عندما تكون available_functions غائبة
|
||||
- رفع اعتماد langsmith إلى الإصدار >=0.8.0 لمعالجة GHSA-3644-q5cj-c5c7
|
||||
- حل مشاكل الأماكن الشاغرة لكتل التعليمات البرمجية غير المترجمة في وثائق البرتغالية البرازيلية
|
||||
|
||||
### الوثائق
|
||||
- إضافة وثائق لـ TavilyGetResearch
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.14.5a5
|
||||
|
||||
## المساهمون
|
||||
|
||||
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="13 مايو 2026">
|
||||
## v1.14.5a5
|
||||
|
||||
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a5)
|
||||
|
||||
## ما الذي تغير
|
||||
|
||||
### الميزات
|
||||
- إلغاء استخدام CrewAgentExecutor، وتعيين وكلاء Crew الافتراضيين إلى AgentExecutor
|
||||
- تحسين أدوات صندوق الرمل Daytona
|
||||
|
||||
### إصلاحات الأخطاء
|
||||
- إصلاح كتلة الكود المفقودة في دليل التدفق الأول باللغة البرتغالية (pt-BR)
|
||||
- تسجيل أخطاء المراجعة المسبقة والتقطير HITL، إضافة learn_strict
|
||||
- تصحيح urllib3 للثغرات الأمنية
|
||||
- تصحيح gitpython و langchain-core؛ تجاهل CVE paramiko غير المصححة
|
||||
- تحديث جميع حزم مساحة العمل المنشورة على uv lock/sync
|
||||
|
||||
### الوثائق
|
||||
- إضافة دليل ترحيل لـ `inputs.id` إلى `restoreFromStateId`
|
||||
- إضافة دليل ترقية OSS ودليل ترحيل crew-to-flow
|
||||
- تحديث سجل التغييرات والإصدار لـ v1.14.5a4
|
||||
|
||||
## المساهمون
|
||||
|
||||
@akaKuruma, @greysonlalonde, @iris-clawd, @lorenzejay, @mislavivanda
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="9 مايو 2026">
|
||||
## v1.14.5a4
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from crewai.flow.flow import Flow, listen, start
|
||||
from dotenv import load_dotenv
|
||||
from litellm import completion
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class ExampleFlow(Flow):
|
||||
model = "gpt-4o-mini"
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
title: AWS Workload Identity (اتحاد OIDC)
|
||||
description: تكوين AWS Secrets Manager عبر Workload Identity للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
|
||||
sidebarTitle: AWS — Workload Identity
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يُكوِّن هذا الدليل AWS Secrets Manager كمزود أسرار باستخدام **Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على بيانات اعتماد AWS عبر STS، وتقرأ أسرارك — دون تخزين أي مفتاح وصول AWS طويل الأمد في أي مكان.
|
||||
|
||||
<Note>
|
||||
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة ولا تهتم بانتشار التدوير، راجع الدليل الأبسط [AWS — المفاتيح الثابتة / AssumeRole](/ar/enterprise/features/secrets-manager/aws).
|
||||
</Note>
|
||||
|
||||
### كيف يعمل وقت التشغيل
|
||||
|
||||
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
|
||||
2. يستدعي العامل `sts:AssumeRoleWithWebIdentity` على دور IAM الذي ستُعدّه أدناه، مُقدِّماً الـ JWT.
|
||||
3. تتحقق AWS STS من الـ JWT مقابل مُصدر OIDC العام لـ CrewAI Platform (لذا يجب أن يكون تنصيب منصتك قابلاً للوصول من AWS)، ثم تُعيد بيانات اعتماد AWS قصيرة الأمد.
|
||||
4. يستخدم العامل تلك البيانات لاستدعاء `secretsmanager:GetSecretValue`.
|
||||
5. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
|
||||
|
||||
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
|
||||
- حساب AWS لديه إذن إنشاء مزوّدي OIDC وأدوار وسياسات IAM.
|
||||
- منطقة AWS التي تعيش (أو ستعيش) فيها أسرارك، مثلاً `us-east-1`.
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **UUID مؤسسة CrewAI الخاصة بك.** يمكنك العثور عليه في صفحة إعدادات المؤسسة في CrewAI Platform — تُربط سياسة الثقة في الخطوة 3 دور IAM بهذه المؤسسة تحديداً.
|
||||
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من AWS عبر HTTPS** ليتمكّن AWS STS من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت (أو أن AWS يمكنه الوصول إليه شبكياً عبر VPC peering أو ما يعادله).
|
||||
</Note>
|
||||
|
||||
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
|
||||
|
||||
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` في تلك الوثيقة هو الرابط الذي ستُسجِّله AWS كمزود OIDC موثوق.
|
||||
|
||||
افتح الرابط في المتصفح (مع استبدال `<your-platform-host>` بمضيفك الفعلي، مثلاً `app.crewai.com`):
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
ينبغي أن ترى JSON يحتوي على:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
|
||||
|
||||
<Tip>
|
||||
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 2 — تسجيل CrewAI Platform كمزود هوية OIDC في IAM
|
||||
|
||||
افتح [وحدة تحكم IAM ← Identity providers](https://console.aws.amazon.com/iam/home#/identity_providers) وانقر على **Add provider**.
|
||||
|
||||
- **Provider type:** OpenID Connect.
|
||||
- **Provider URL:** قيمة `issuer` من الخطوة 1 (مثلاً `https://app.crewai.com`).
|
||||
- **Audience:** `sts.amazonaws.com`
|
||||
|
||||
انقر على **Add provider**.
|
||||
|
||||
أو عبر CLI:
|
||||
|
||||
```bash
|
||||
aws iam create-open-id-connect-provider \
|
||||
--url "https://<your-platform-host>" \
|
||||
--client-id-list "sts.amazonaws.com" \
|
||||
--thumbprint-list "$(echo | openssl s_client -servername <your-platform-host> -connect <your-platform-host>:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1 | cut -d= -f2 | tr -d ':')"
|
||||
```
|
||||
|
||||
انسخ **OpenIDConnectProviderArn** من المخرجات (أو ARN المزود من الوحدة). ستستخدمه في الخطوة 3.
|
||||
|
||||
<Note>
|
||||
لا تتحقق AWS فعلياً من بصمة الإبهام لاستدعاءات STS WebIdentity — فهي دائماً تُعيد جلب JWKS وقت التحقق — لكن واجهة الـ API تتطلب وجود الحقل.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Add identity provider" form filled with the Platform issuer URL and audience sts.amazonaws.com → /images/secrets-manager/aws-wi/01-add-oidc-provider.png */}
|
||||
{/* SCREENSHOT: Provider detail page showing the provider's ARN → /images/secrets-manager/aws-wi/02-oidc-provider-arn.png */}
|
||||
|
||||
## الخطوة 3 — إنشاء دور IAM
|
||||
|
||||
احفظ كـ `trust-policy.json`، مع استبدال `<YOUR_ACCOUNT_ID>` و `<your-platform-host>` (مضيف المُصدر **بدون** `https://` أو `http://`، مثلاً `app.crewai.com`) و `<YOUR_CREWAI_ORG_UUID>` (من المتطلبات المسبقة):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Federated": "arn:aws:iam::<YOUR_ACCOUNT_ID>:oidc-provider/<your-platform-host>"
|
||||
},
|
||||
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"<your-platform-host>:aud": "sts.amazonaws.com",
|
||||
"<your-platform-host>:sub": "organization:<YOUR_CREWAI_ORG_UUID>"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
أنشئ الدور:
|
||||
|
||||
```bash
|
||||
aws iam create-role \
|
||||
--role-name crewai-secrets-reader \
|
||||
--assume-role-policy-document file://trust-policy.json
|
||||
```
|
||||
|
||||
انسخ **Role Arn** من المخرجات — هذا هو `aws_role_arn` الخاص بك. ستلصقه في CrewAI Platform في الخطوة 6.
|
||||
|
||||
<Tip>
|
||||
يحدّد الشرطان نطاق الثقة بدقة: يقيّد `aud` افتراض الدور إلى الرموز ذات جمهور AWS STS، ويقصر `sub` الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة. تُعيّن CrewAI Platform كلا الادّعاءين دائماً على رموز AWS workload identity.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" with Web Identity trust type, federated provider selector pointing at the CrewAI Platform OIDC provider → /images/secrets-manager/aws-wi/03-create-role-trust.png */}
|
||||
|
||||
## الخطوة 4 — إنشاء وإرفاق سياسة IAM لوصول Secrets Manager + KMS
|
||||
|
||||
احفظ كـ `secrets-policy.json`، مع استبدال العناصر النائبة بمعرّف حسابك ومنطقتك وبادئة اسم السر و ARN(s) مفاتيح KMS التي تُشفّر تلك الأسرار:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerListForUI",
|
||||
"Effect": "Allow",
|
||||
"Action": "secretsmanager:ListSecrets",
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue"
|
||||
],
|
||||
"Resource": "arn:aws:secretsmanager:<REGION>:<YOUR_ACCOUNT_ID>:secret:<SECRET_NAME_PREFIX>-*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "arn:aws:kms:<REGION>:<YOUR_ACCOUNT_ID>:key/<KMS_KEY_ID>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
تُشغّل `SecretsManagerListForUI` ميزة **الاقتراح التلقائي لاسم السر** في نموذج متغيرات البيئة وزر **Test Connection** على بيانات الاعتماد. يقبل `secretsmanager:ListSecrets` فقط `Resource: "*"` — فهو محصور على مستوى الحساب في طبقة IAM.
|
||||
|
||||
أرفق السياسة بالدور إما عبر CLI (سياسة مضمنة، أبسط) أو واجهة الوحدة؛ للبيئات التي تعيد استخدام نفس الأذونات عبر أدوار متعددة، استخدم علامة التبويب **Managed policy** لسياسة مُسمّاة قابلة لإعادة الاستخدام.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="سياسة مضمنة (CLI)">
|
||||
```bash
|
||||
aws iam put-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-name SecretsManagerRead \
|
||||
--policy-document file://secrets-policy.json
|
||||
```
|
||||
|
||||
يُرفق هذا السياسة **مضمنةً** بالدور. السياسات المضمنة مرتبطة بالدور ولا يمكن إعادة استخدامها على أدوار أخرى.
|
||||
</Tab>
|
||||
|
||||
<Tab title="سياسة مُدارة (CLI، قابلة لإعادة الاستخدام)">
|
||||
```bash
|
||||
POLICY_ARN=$(aws iam create-policy \
|
||||
--policy-name CrewAISecretsReader \
|
||||
--policy-document file://secrets-policy.json \
|
||||
--query 'Policy.Arn' --output text)
|
||||
|
||||
aws iam attach-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-arn "$POLICY_ARN"
|
||||
```
|
||||
|
||||
السياسة المُدارة هي مورد IAM مستقل يمكنك إرفاقه بأدوار متعددة.
|
||||
</Tab>
|
||||
|
||||
<Tab title="وحدة التحكم (UI)">
|
||||
1. افتح [وحدة تحكم IAM ← Roles](https://console.aws.amazon.com/iam/home#/roles) واختر **crewai-secrets-reader**.
|
||||
2. في علامة التبويب **Permissions**، انقر على **Add permissions** ← **Create inline policy**.
|
||||
3. بدّل إلى محرر **JSON** والصق محتوى `secrets-policy.json`.
|
||||
4. انقر على **Next**، أعطِ السياسة اسماً (مثلاً `SecretsManagerRead`)، وانقر على **Create policy**.
|
||||
|
||||
لإنشاء سياسة مُدارة قابلة لإعادة الاستخدام بدلاً من ذلك، استخدم **IAM ← Policies ← Create policy** ثم أرفقها بالدور من علامة التبويب **Permissions** الخاصة بالدور.
|
||||
|
||||
{/* SCREENSHOT: IAM Role detail → Permissions → Create inline policy with JSON editor → /images/secrets-manager/aws-wi/03b-attach-inline-policy.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 5 — إنشاء سر واحد على الأقل في AWS
|
||||
|
||||
إذا لم يكن لديك سر للاختبار، أنشئ واحداً الآن:
|
||||
|
||||
```bash
|
||||
aws secretsmanager create-secret \
|
||||
--region <REGION> \
|
||||
--name crewai-test-keyword \
|
||||
--secret-string "hello from aws"
|
||||
```
|
||||
|
||||
أو عبر [وحدة تحكم AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/) ← **Store a new secret**.
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Store a new secret" page with a sample value → /images/secrets-manager/aws-wi/04-create-secret.png */}
|
||||
|
||||
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Workload Identity → /images/secrets-manager/aws-wi/05-amp-settings-wi-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Workload Identity page with "Add Workload Identity Config" button → /images/secrets-manager/aws-wi/06-amp-wi-empty-state.png */}
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `aws-prod`.
|
||||
- **Cloud Provider:** `AWS`.
|
||||
- **AWS Role ARN:** **Role Arn** من الخطوة 3.
|
||||
- **AWS Region:** المنطقة التي تعيش فيها أسرارك، مثلاً `us-east-1`.
|
||||
- (اختياري) حدّد **Set as default for AWS** إذا كنت ترغب في أن يكون تكوين WI هذا هو الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ AWS.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with AWS, role ARN, and region filled in → /images/secrets-manager/aws-wi/07-amp-add-wi-config-aws.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing the new AWS row with "(default)" badge if applicable → /images/secrets-manager/aws-wi/08-amp-wi-list-with-aws.png */}
|
||||
|
||||
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
|
||||
|
||||
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `aws-prod-wi`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Authentication Method:** `Workload Identity` (بدلاً من المفاتيح الثابتة / AssumeRole).
|
||||
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6 (مثلاً `aws-prod`).
|
||||
- (اختياري) حدّد **Set as default credential for this provider**.
|
||||
|
||||
سيطلب النموذج فقط **AWS Region** ضمن Workload Identity — حقول بيانات الاعتماد الثابتة (Access Key ID و Secret Access Key و Role ARN و External ID) مخفية عمداً لأنها لا تنطبق على هذا المسار؛ يأتي ARN الدور من تكوين WI المرتبط.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + Workload Identity + WI config dropdown selected → /images/secrets-manager/aws-wi/09-amp-add-credential-aws-wi.png */}
|
||||
|
||||
## الخطوة 8 — اختبار الاتصال
|
||||
|
||||
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT، وتبادله مع AWS STS عبر `sts:AssumeRoleWithWebIdentity`، وتؤكد أن بيانات الاعتماد الناتجة يمكنها استدعاء `sts:GetCallerIdentity` مقابل الدور المُفترَض. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
|
||||
|
||||
نجاح Test Connection يُثبت أن سياسة الثقة وتسجيل مزود OIDC وشرط الجمهور موصولة جميعها بشكل صحيح. لا يُثبت ذلك أن IAM لكل سر صحيح — يُمارَس `secretsmanager:GetSecretValue` على ARN سر محدد بشكل منفصل عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
|
||||
|
||||
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
|
||||
|
||||
الآن أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
|
||||
|
||||
الفرق الوحيد بين متغيرات البيئة المدعومة بـ WI والمدعومة بمفاتيح ثابتة هو **متى** يُقرأ السر:
|
||||
|
||||
- **مدعوم بـ WI:** تُقرأ قيمة السر طازجة في كل إطلاق أتمتة.
|
||||
- **مدعوم بمفاتيح ثابتة:** تُقرأ قيمة السر وقت النشر وتُدمج في صورة النشر.
|
||||
|
||||
## الخطوة 10 — التحقق من التدوير
|
||||
|
||||
بعد تشغيل عملية النشر، دوّر السر في AWS:
|
||||
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id crewai-test-keyword \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
|
||||
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
|
||||
|
||||
للتأكد في السجلات (إذا كان لديك وصول إلى العامل)، ابحث عن:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (aws): N secret(s) resolved
|
||||
```
|
||||
|
||||
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `GetSecretValue` طازج مقابل AWS.
|
||||
|
||||
## استكشاف الأخطاء
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| يفشل Test Connection بخطأ مصافحة | رُفض استدعاء `sts:AssumeRoleWithWebIdentity`. تحقق من أن ARN الكيان الموحَّد في سياسة الثقة يشير إلى `oidc-provider/<your-platform-host>` (المضيف **بدون** `https://` أو `http://` وبدون شرطة مائلة لاحقة)، وأن شرط الجمهور هو بالضبط `sts.amazonaws.com`، وأن شرط `sub` يطابق UUID مؤسسة CrewAI الخاصة بك، وأن رابط اكتشاف OIDC للمنصة قابل للوصول من AWS عبر الإنترنت العام. |
|
||||
| `InvalidIdentityToken: Couldn't retrieve verification key from your identity provider` | لا يمكن لـ AWS STS الوصول إلى مضيف CrewAI Platform لجلب JWKS. تأكد من أن المضيف متاح عبر الإنترنت من AWS، وأن رابط اكتشاف OIDC يُعيد 200، وأن نقطة نهاية JWKS قابلة للوصول. |
|
||||
| `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity` | عدم تطابق سياسة الثقة. تحقق من الخطوة 3 من جديد: يجب أن يتضمن ARN الكيان الموحَّد `oidc-provider/<your-platform-host>` (المضيف **بدون** `https://` أو `http://` وبدون شرطة مائلة لاحقة)، ويجب أن يكون شرط الجمهور بالضبط `sts.amazonaws.com`، وأن يساوي شرط `sub` بالضبط `organization:<YOUR_CREWAI_ORG_UUID>`. |
|
||||
| يُظهر الاقتراح التلقائي لاسم السر `AccessDenied: secretsmanager:ListSecrets` | يفتقد الدور إلى `secretsmanager:ListSecrets` مع `Resource: "*"`. أضف بيان `SecretsManagerListForUI` من الخطوة 4. |
|
||||
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن IAM المحصور بالمورد مفقود على السر الفاشل. راجع أذونات `secretsmanager:GetSecretValue` و `kms:Decrypt` للدور على ARN ذلك السر بعينه ومفتاح KMS الخاص به. |
|
||||
| `RegionDisabledException` / لم يُعثر على أسرار | لا تطابق المنطقة في تكوين Workload Identity المكان الفعلي للسر. تحقق من الخطوة 6 من جديد. |
|
||||
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
|
||||
|
||||
### روابط مرجعية
|
||||
|
||||
- AWS: [Creating OpenID Connect (OIDC) identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html)
|
||||
- AWS: [Configuring a role for OpenID Connect federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_relying-party.html)
|
||||
- AWS: [STS:AssumeRoleWithWebIdentity API reference](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
|
||||
- للتنوع متعدد السحاب، راجع أيضاً [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) و [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
294
docs/ar/enterprise/features/secrets-manager/aws.mdx
Normal file
294
docs/ar/enterprise/features/secrets-manager/aws.mdx
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: AWS Secrets Manager (بيانات اعتماد ثابتة)
|
||||
description: تكوين AWS Secrets Manager كمزود أسرار لـ CrewAI Platform باستخدام مفاتيح الوصول الثابتة أو AssumeRole
|
||||
sidebarTitle: AWS
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يأخذك هذا الدليل عبر تكوين AWS Secrets Manager كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **بيانات الاعتماد الثابتة** (مفاتيح الوصول، اختيارياً مع AssumeRole). بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في حساب AWS الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة (بدون إعادة نشر)، راجع [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل التكوين من جانب AWS وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- حساب AWS لديه إذن إنشاء مستخدمي IAM وسياسات يديرها العميل و(اختيارياً) أدوار IAM.
|
||||
- منطقة AWS التي تعيش (أو ستعيش) فيها أسرارك، مثلاً `us-east-1`.
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## اختر طريقة المصادقة
|
||||
|
||||
تدعم CrewAI Platform طريقتين لمصادقة المنصة مع AWS Secrets Manager. اختر واحدة قبل أن تبدأ — تختلف الخطوات أدناه بناءً على اختيارك.
|
||||
|
||||
| الطريقة | متى تُستخدم | المقايضات |
|
||||
|---|---|---|
|
||||
| **مفاتيح الوصول الثابتة** | البداية، عمليات نشر بحساب واحد | أبسط إعداد؛ يجب تدوير مفاتيح الوصول يدوياً |
|
||||
| **AssumeRole** | عبر الحسابات، تشديد الإنتاج | بيانات اعتماد قصيرة الأمد؛ يدعم External ID؛ يتطلب دور IAM إضافي |
|
||||
|
||||
تستخدم بقية هذا الدليل علامات تبويب في الخطوات 3–5 لتتمكن من اتباع المسار المطابق لاختيارك.
|
||||
|
||||
## الخطوة 1 — إنشاء مستخدم IAM
|
||||
|
||||
افتح [وحدة تحكم IAM](https://console.aws.amazon.com/iam/)، انتقل إلى **Users**، ثم انقر على **Create user**.
|
||||
|
||||
- الاسم المقترح: `crewai-secrets-reader`.
|
||||
- اترك **Provide user access to the AWS Management Console** بدون تحديد — هذا الكيان تستخدمه CrewAI Platform برمجياً، وليس البشر.
|
||||
- انقر على **Next**.
|
||||
|
||||
في صفحة **Set permissions**، اترك الاختيار الافتراضي. ستُرفق السياسة في الخطوة 3.
|
||||
|
||||
انقر على **Next**، راجع، وانقر على **Create user**.
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق AWS: [Create an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create user" form filled with name "crewai-secrets-reader" → /images/secrets-manager/aws/01-create-iam-user.png */}
|
||||
|
||||
## الخطوة 2 — إنشاء سياسة IAM
|
||||
|
||||
تحتاج CrewAI Platform إلى وصول للقراءة فقط إلى AWS Secrets Manager وإذن لفك تشفير الأسرار عبر KMS. أنشئ سياسة يديرها العميل بـ JSON التالي.
|
||||
|
||||
في وحدة تحكم IAM، انتقل إلى **Policies**، ثم انقر على **Create policy**.
|
||||
|
||||
اختر علامة التبويب **JSON** واستبدل المحتوى بـ:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:ListSecrets",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:DescribeSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:DescribeKey",
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
انقر على **Next**، ثم في صفحة **Review and create**:
|
||||
|
||||
- **Policy name:** `CrewAISecretsManagerRead`
|
||||
- **Description (optional):** `Read-only access to AWS Secrets Manager for CrewAI Platform`
|
||||
|
||||
انقر على **Create policy**.
|
||||
|
||||
<Tip>
|
||||
تمنح السياسة أعلاه `*` على `Resource` للبساطة. في الإنتاج، حدّد نطاق `Resource` إلى ARNs الخاصة بالأسرار التي يجب على CrewAI Platform الوصول إليها، وحدّد نطاق `kms:Decrypt` إلى ARNs مفاتيح KMS التي تُشفّر تلك الأسرار. راجع [إرشادات AWS حول أقل الامتيازات](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html).
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create policy" → JSON tab with the policy above pasted → /images/secrets-manager/aws/02-create-policy-json-editor.png */}
|
||||
{/* SCREENSHOT: AWS IAM "Review and create policy" page with name "CrewAISecretsManagerRead" → /images/secrets-manager/aws/03-policy-review-and-create.png */}
|
||||
|
||||
## الخطوة 3 — إرفاق السياسة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="مفاتيح الوصول الثابتة">
|
||||
1. في وحدة تحكم IAM، انتقل إلى **Users** وانقر على المستخدم الذي أنشأته في الخطوة 1.
|
||||
2. في علامة التبويب **Permissions**، انقر على **Add permissions** ← **Attach policies directly**.
|
||||
3. ابحث عن `CrewAISecretsManagerRead`، حدّدها، وانقر على **Next**.
|
||||
4. انقر على **Add permissions**.
|
||||
|
||||
{/* SCREENSHOT: "Add permissions" → "Attach policies directly" with CrewAISecretsManagerRead selected → /images/secrets-manager/aws/04a-attach-policy-to-user.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
مع AssumeRole، تُرفَق السياسة بـ **دور** IAM منفصل (وليس مباشرة بالمستخدم). يحتاج المستخدم من الخطوة 1 فقط إلى إذن لاستدعاء `sts:AssumeRole` على ذلك الدور.
|
||||
|
||||
**إنشاء الدور:**
|
||||
|
||||
1. في وحدة تحكم IAM، انتقل إلى **Roles** وانقر على **Create role**.
|
||||
2. **Trusted entity type:** AWS account. اختر **This account** (أو **Another AWS account** لإعدادات عبر الحسابات، ثم أدخل معرّف حساب AWS الذي يستضيف مستخدم IAM من الخطوة 1).
|
||||
3. (موصى به) حدّد **Require external ID** وأدخل قيمة تُولّدها بنفسك — هذا سر مشترك ستلصقه في CrewAI Platform في الخطوة 5.
|
||||
4. انقر على **Next**.
|
||||
5. أرفق سياسة `CrewAISecretsManagerRead`.
|
||||
6. انقر على **Next**، سمِّ الدور `CrewAISecretsManagerRole`، وانقر على **Create role**.
|
||||
|
||||
**اسمح لمستخدم IAM بافتراض الدور:**
|
||||
|
||||
1. افتح الدور الذي أنشأته للتو وانسخ **ARN** الخاص به.
|
||||
2. في وحدة تحكم IAM، انتقل إلى **Users**، انقر على المستخدم من الخطوة 1، وفي علامة التبويب **Permissions** انقر على **Add permissions** ← **Create inline policy**.
|
||||
3. في علامة التبويب **JSON**، الصق ما يلي (استبدل `ROLE_ARN_FROM_ABOVE`):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "ROLE_ARN_FROM_ABOVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. سمِّ السياسة `CrewAIAssumeSecretsRole` وانقر على **Create policy**.
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" trust policy step with External ID checkbox enabled → /images/secrets-manager/aws/04b-create-role-trust-policy.png */}
|
||||
{/* SCREENSHOT: Inline sts:AssumeRole policy attached to the IAM user → /images/secrets-manager/aws/04c-attach-assumerole-on-user.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 4 — الحصول على بيانات الاعتماد
|
||||
|
||||
<Tabs>
|
||||
<Tab title="مفاتيح الوصول الثابتة">
|
||||
1. في وحدة تحكم IAM، افتح المستخدم من الخطوة 1.
|
||||
2. انقر على علامة التبويب **Security credentials**.
|
||||
3. تحت **Access keys**، انقر على **Create access key**.
|
||||
4. اختر **Application running outside AWS** (أو **Other**) كحالة استخدام. انقر على **Next**.
|
||||
5. (اختياري) أضف وسماً وصفياً. انقر على **Create access key**.
|
||||
6. انقر على **Show** للكشف عن مفتاح الوصول السري، ثم انسخ كلاً من **Access key ID** و **Secret access key**، أو انقر على **Download .csv file**.
|
||||
|
||||
<Warning>
|
||||
يظهر مفتاح الوصول السري مرة واحدة فقط. إذا أغلقت هذه الصفحة دون نسخه، فستحتاج إلى حذف المفتاح وإنشاء واحد جديد.
|
||||
</Warning>
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق AWS: [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
|
||||
|
||||
{/* SCREENSHOT: Access key use-case selector ("Application running outside AWS") → /images/secrets-manager/aws/05a-create-access-key-use-case.png */}
|
||||
{/* SCREENSHOT: "Retrieve access keys" page with Show/Download buttons → /images/secrets-manager/aws/06a-retrieve-access-keys.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
حتى مع AssumeRole، لا تزال CrewAI Platform تحتاج إلى مفتاح وصول لمستخدم IAM — فهي تستخدم تلك المفاتيح كهوية المتصل لتنفيذ استدعاء `sts:AssumeRole`.
|
||||
|
||||
1. أنشئ مفتاح وصول للمستخدم تماماً كما هو موضح في علامة التبويب **مفاتيح الوصول الثابتة** أعلاه.
|
||||
2. افتح الدور الذي أنشأته في الخطوة 3 وانسخ:
|
||||
- **Role ARN** (من ملخص الدور).
|
||||
- **External ID** الذي كوّنته (إن وُجد) — قد عيّنته بنفسك في الخطوة 3، فتأكد من أنه بحوزتك.
|
||||
|
||||
{/* SCREENSHOT: IAM role detail page showing Role ARN → /images/secrets-manager/aws/05b-role-arn-detail.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 5 — إضافة بيانات الاعتماد في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Secret Provider Credentials page with "Add Credential" button → /images/secrets-manager/usage/02-amp-credentials-empty-state.png */}
|
||||
|
||||
<Tabs>
|
||||
<Tab title="مفاتيح الوصول الثابتة">
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `aws-prod`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** منطقة AWS التي تعيش فيها أسرارك، مثلاً `us-east-1`. يجب أن تطابق منطقة الأسرار التي تريد قراءتها.
|
||||
- **Access Key ID:** القيمة من الخطوة 4.
|
||||
- **Secret Access Key:** القيمة من الخطوة 4.
|
||||
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار AWS بدون تحديد بيانات اعتماد صراحةً.
|
||||
|
||||
اترك **Role ARN** و **External ID** فارغين.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + static access keys filled in → /images/secrets-manager/usage/03a-amp-add-credential-form-aws-static.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `aws-prod-assumerole`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** منطقة AWS التي تعيش فيها أسرارك.
|
||||
- **Access Key ID:** مفتاح وصول مستخدم IAM من الخطوة 4 (يُستخدم لاستدعاء STS).
|
||||
- **Secret Access Key:** مفتاح الوصول السري لمستخدم IAM من الخطوة 4.
|
||||
- **Role ARN:** Role ARN الذي نسخته في الخطوة 4.
|
||||
- **External ID:** External ID الذي عيّنته على سياسة الثقة الخاصة بالدور (احذفه إن لم يوجد).
|
||||
- (اختياري) حدّد **Set as default credential for this provider**.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + AssumeRole fields filled in → /images/secrets-manager/usage/03b-amp-add-credential-form-aws-assumerole.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
**كيف تتصرف الطريقتان وقت التشغيل:**
|
||||
|
||||
- مع **مفاتيح الوصول الثابتة** فقط، تستدعي CrewAI Platform AWS Secrets Manager مباشرةً باستخدام المفاتيح التي قدّمتها.
|
||||
- عند تعيين **Role ARN**، تستدعي CrewAI Platform أولاً `sts:AssumeRole` بمفاتيح الوصول المقدَّمة (و External ID إن كان مكوَّناً)، ثم تستخدم بيانات الاعتماد قصيرة الأمد التي تُعيدها STS لقراءة أسرارك.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: Credentials list showing the new AWS row, with "(default)" badge if applicable → /images/secrets-manager/usage/04-amp-credential-created.png */}
|
||||
|
||||
## الخطوة 6 — إنشاء سر واحد على الأقل في AWS
|
||||
|
||||
إذا لم يكن لديك بالفعل أسرار في AWS Secrets Manager، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 7.
|
||||
|
||||
في [وحدة تحكم AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/)، انقر على **Store a new secret**.
|
||||
|
||||
- **Secret type:** اختر **Other type of secret**.
|
||||
- **Key/value pairs** — إما:
|
||||
- إدخال زوج أو أكثر من مفتاح/قيمة (موصى به للأسرار المهيكلة)، أو
|
||||
- استخدام علامة التبويب **Plaintext** لقيمة نصية واحدة.
|
||||
- **Encryption key:** استخدم `aws/secretsmanager` (المفتاح الذي يديره AWS) ما لم تكن لديك متطلبات محددة لمفتاح KMS.
|
||||
|
||||
انقر على **Next**، ثم أدخل:
|
||||
|
||||
- **Secret name:** اسم فريد، مثلاً `crewai/openai-api-key`.
|
||||
- **Description (optional):** ملاحظة قصيرة عن غرض السر.
|
||||
|
||||
انقر على **Next** عبر خطوات التدوير والمراجعة، ثم انقر على **Store**.
|
||||
|
||||
<Note>
|
||||
**صيغة الإشارة بمفتاح JSON.** إذا خزّنت سراً بأزواج مفتاح/قيمة متعددة (كائن JSON)، يمكن لـ CrewAI Platform استخراج حقل محدد باستخدام صيغة `secret-name#json_key` في إشارات متغيرات البيئة. على سبيل المثال، يمكن الإشارة إلى سر باسم `database-credentials` بـ `{"username": "...", "password": "..."}` باسم `database-credentials#password`. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
|
||||
</Note>
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق AWS: [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html).
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Choose secret type" page → /images/secrets-manager/aws/07-create-secret-store-type.png */}
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Configure secret" page with name and description → /images/secrets-manager/aws/08-create-secret-name.png */}
|
||||
|
||||
## الخطوة 7 — اختبار الاتصال
|
||||
|
||||
عُد إلى CrewAI Platform، في صفحة **Secret Provider Credentials**، اعثر على بيانات الاعتماد التي أنشأتها للتو وانقر على **Test Connection**.
|
||||
|
||||
تؤكد رسالة نجاح أن CrewAI Platform يمكنها المصادقة مع AWS وقراءة الأسرار من حسابك.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" → /images/secrets-manager/usage/05-amp-test-connection-success.png */}
|
||||
|
||||
إذا فشل الاختبار، تحقق من الأسباب الأكثر شيوعاً:
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| `AccessDenied` على `secretsmanager:ListSecrets` | السياسة غير مُرفقة، أو المستخدم خاطئ. تحقق من الخطوة 3 من جديد. |
|
||||
| `AccessDenied` على `kms:Decrypt` | بيان `KMSDecrypt` مفقود، أو أن أسرارك تستخدم مفتاح KMS يديره العميل لا يغطّيه `Resource: "*"`. |
|
||||
| `InvalidClientTokenId` / `SignatureDoesNotMatch` | معرّف مفتاح الوصول أو مفتاح الوصول السري خاطئ. تحقق من الخطوتين 4 و 5 من جديد. |
|
||||
| `RegionDisabledException` / لم يُعثر على أسرار | لا تطابق **Region** الخاصة ببيانات الاعتماد المكان الفعلي لأسرارك. |
|
||||
| `AccessDenied` على `sts:AssumeRole` (AssumeRole فقط) | سياسة `sts:AssumeRole` المضمنة مفقودة على مستخدم IAM، أو لا تسمح سياسة الثقة الخاصة بالدور بهذا الكيان، أو لا يتطابق External ID. |
|
||||
| ينجح الاختبار فوراً بعد إنشاء مستخدم IAM، لكنه يفشل في المرة التالية | تستغرق بيانات اعتماد IAM أحياناً دقيقة أو دقيقتين للانتشار عالمياً. أعد المحاولة. |
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
الآن وقد اتصلت AWS، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
|
||||
|
||||
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
|
||||
- الإشارة إلى أسرار AWS الخاصة بك من متغيرات بيئة CrewAI Platform.
|
||||
|
||||
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) — نفس مخزن الأسرار، بدون بيانات اعتماد ثابتة، وتُجلب الأسرار في كل إطلاق.
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Azure Workload Identity Federation
|
||||
description: تكوين Azure Key Vault عبر Microsoft Entra Workload Identity Federation للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
|
||||
sidebarTitle: Azure — Workload Identity
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يُكوِّن هذا الدليل Azure Key Vault كمزود أسرار باستخدام **Microsoft Entra Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على رمز وصول Entra عبر منصة هوية Microsoft، وتقرأ أسرارك — دون تخزين أي سر عميل في أي مكان.
|
||||
|
||||
<Note>
|
||||
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة، راجع الدليل الأبسط [Azure Key Vault — سر العميل](/ar/enterprise/features/secrets-manager/azure).
|
||||
</Note>
|
||||
|
||||
### كيف يعمل وقت التشغيل
|
||||
|
||||
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
|
||||
2. يُقدّم العامل الـ JWT إلى Microsoft Entra على `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token` كـ `client_assertion` (`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`)، مع الإشارة إلى App Registration الذي يطابق **Federated Identity Credential** الخاص به مُصدر الـ JWT وموضوعه.
|
||||
3. تتحقق Entra من الـ JWT مقابل وثيقة اكتشاف OIDC و JWKS لمنصتك، ثم تُعيد رمز وصول قصير الأمد محصور بـ `https://vault.azure.net/.default`.
|
||||
4. يستدعي العامل Azure Key Vault لقراءة السر.
|
||||
5. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
|
||||
|
||||
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
|
||||
- اشتراك Azure ومستأجر Microsoft Entra يمكنك إدارته.
|
||||
- إذن في المستأجر لإنشاء App Registrations وإضافة Federated Identity Credentials.
|
||||
- Key Vault يستخدم **Azure RBAC** للترخيص (وليس النموذج القديم لسياسة الوصول).
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من Microsoft Entra عبر HTTPS** ليتمكّن Entra من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت.
|
||||
</Note>
|
||||
|
||||
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
|
||||
|
||||
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` هناك هو الرابط الذي ستُسجِّله Microsoft Entra كمُصدر اتحاد موثوق.
|
||||
|
||||
افتح الرابط في المتصفح:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
ينبغي أن ترى JSON يحتوي على:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
|
||||
|
||||
<Tip>
|
||||
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 2 — إنشاء App Registration
|
||||
|
||||
في [بوابة Microsoft Entra](https://entra.microsoft.com)، انتقل إلى **App registrations** وانقر على **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- اترك **Redirect URI** فارغاً.
|
||||
|
||||
انقر على **Register**. سجّل **Application (client) ID** و **Directory (tenant) ID** في لوحة نظرة عامة التطبيق — ستستخدمها في الخطوة 6.
|
||||
|
||||
{/* SCREENSHOT: Azure portal "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure-wi/01-register-app.png */}
|
||||
|
||||
## الخطوة 3 — إضافة Federated Identity Credential
|
||||
|
||||
يُخبر Federated Identity Credential Microsoft Entra: *ثِق برموز JWT المُصدَرة من هذا المُصدر، بهذا الموضوع، عندما تُقدَّم كتأكيد عميل لهذا App Registration.*
|
||||
|
||||
في App Registration، انتقل إلى **Certificates & secrets** ← **Federated credentials** ← **Add credential**.
|
||||
|
||||
- **Federated credential scenario:** `Other issuer`.
|
||||
- **Issuer:** رابط مُصدر CrewAI Platform من الخطوة 1، مثلاً `https://<your-platform-host>`.
|
||||
- **Subject identifier:** `organization:<YOUR_CREWAI_ORG_UUID>` — قيمة ادّعاء `sub` في JWT بالضبط. اعثر على UUID مؤسستك في إعدادات مؤسسة CrewAI Platform. يقصر هذا الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة.
|
||||
- **Name:** أي تسمية وصفية، مثلاً `crewai-org-prod`.
|
||||
- **Audience:** `api://AzureADTokenExchange`. هذا هو الجمهور الثابت الذي تتطلبه Microsoft Entra للبيانات الموحَّدة، وهو ما تُعيّنه CrewAI Platform في ادّعاء `aud` في JWT.
|
||||
|
||||
انقر على **Add**.
|
||||
|
||||
<Tip>
|
||||
**العزل لكل مؤسسة.** يقيّد معرّف الموضوع (`organization:<UUID>`) Federated Identity Credential لرموز مؤسسة CrewAI محددة. إذا كان من المفترض أن تتشارك مؤسسات CrewAI متعددة App Registration واحداً، أضف Federated Identity Credential لكل مؤسسة (كل منها بـ UUID المؤسسة).
|
||||
</Tip>
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust).
|
||||
|
||||
{/* SCREENSHOT: "Add credential" panel with scenario = "Other issuer", issuer URL, subject "organization:<uuid>", audience "api://AzureADTokenExchange" → /images/secrets-manager/azure-wi/02-add-federated-credential.png */}
|
||||
|
||||
## الخطوة 4 — منح App Registration وصولاً إلى Key Vault
|
||||
|
||||
امنح App Registration دور **Key Vault Secrets User** على الخزنة المستهدفة — نفس الدور الذي تستخدمه لمسار بيانات الاعتماد الثابتة. استخدم إما على مستوى الخزنة (أبسط) أو لكل سر (أقل الامتيازات).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="على مستوى الخزنة (أبسط)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
يمنح النطاق على مستوى الخزنة إذن `secrets/list` الذي يعتمد عليه **الاقتراح التلقائي لاسم السر** في نموذج متغير البيئة لـ CrewAI Platform. اختر هذه التبويبة إذا أردت أن يعمل الاقتراح التلقائي.
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure-wi/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="لكل سر (أقل الامتيازات)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
تُعطّل الارتباطات لكل سر **الاقتراح التلقائي لاسم السر** في نموذج متغير البيئة لـ CrewAI Platform (يتطلب الاقتراح التلقائي `secrets/list`، وهو محصور بنطاق الخزنة فقط). اكتب اسم السر الكامل بدلاً من ذلك.
|
||||
|
||||
{/* SCREENSHOT: Per-secret IAM panel with the App Registration assigned **Key Vault Secrets User** at the secret resource scope → /images/secrets-manager/azure-wi/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="البوابة (UI)">
|
||||
لتعيين **على مستوى الخزنة**:
|
||||
|
||||
1. افتح Key Vault الخاص بك في بوابة Azure.
|
||||
2. انقر على **Access control (IAM)** ← **Add** ← **Add role assignment**.
|
||||
3. اختر الدور **Key Vault Secrets User** ← **Next**.
|
||||
4. انقر على **Select members**، ابحث عن App Registration `crewai-secrets-reader`، انقر على **Select**.
|
||||
5. انقر على **Review + assign**.
|
||||
|
||||
لتعيين **لكل سر**، استخدم نفس التدفق لكن ابدأ من **Objects** ← **Secrets** ← اختر السر ← لوحة **Access control (IAM)** الخاصة به. تُعطّل الارتباطات لكل سر الاقتراح التلقائي (راجع تبويبة لكل سر أعلاه).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 5 — إنشاء سر واحد على الأقل في Key Vault
|
||||
|
||||
إذا لم يكن لديك سر للاختبار، أنشئ واحداً عبر Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
أو عبر بوابة Azure:
|
||||
|
||||
1. افتح Key Vault الخاص بك وانتقل إلى **Objects** ← **Secrets**.
|
||||
2. انقر على **Generate/Import**.
|
||||
3. **Upload options:** `Manual`. **Name:** اسم السر (مثلاً `openai-api-key`). **Secret value:** الصق القيمة.
|
||||
4. انقر على **Create**.
|
||||
|
||||
<Note>
|
||||
**اصطلاحات اسم السر.** لا يمكن أن تحتوي أسماء أسرار Azure Key Vault على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`)، لذا يمكنك الاحتفاظ بأسماء متغيرات بيئة بنمط الشرطة السفلية — لكن السر الأساسي في Key Vault يجب أن يستخدم الشرطات.
|
||||
</Note>
|
||||
|
||||
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `azure-prod`.
|
||||
- **Cloud Provider:** `Azure`.
|
||||
- **Tenant ID:** **Directory (tenant) ID** الخاص بـ Microsoft Entra من الخطوة 2.
|
||||
- **Client ID:** **Application (client) ID** الخاص بـ App Registration من الخطوة 2.
|
||||
- (اختياري) حدّد **Set as default for Azure** إذا كنت ترغب في أن يكون هذا هو تكوين WI الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ Azure.
|
||||
|
||||
**Audience** ثابت على `api://AzureADTokenExchange` — تتطلب Microsoft Entra هذا الجمهور بالضبط للبيانات الموحَّدة، لذا لا يظهر حقل Audience في النموذج.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with Azure, tenant ID, client ID populated → /images/secrets-manager/azure-wi/05-amp-add-wi-config-azure.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing AWS, GCP, and Azure rows → /images/secrets-manager/azure-wi/06-amp-wi-list-with-azure.png */}
|
||||
|
||||
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
|
||||
|
||||
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `azure-prod-wi`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6.
|
||||
- **Key Vault URL:** اسم مضيف DNS للخزنة، مثلاً `https://my-vault.vault.azure.net`.
|
||||
- (اختياري) حدّد **Set as default credential for this provider**.
|
||||
|
||||
سيطلب النموذج فقط **Key Vault URL** ضمن Workload Identity — حقول بيانات الاعتماد الثابتة (Tenant ID و Client ID و Client Secret) مخفية عمداً لأنها لا تنطبق على هذا المسار؛ يأتي المستأجر والعميل من تكوين WI المرتبط.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
<Tip>
|
||||
**App Registration واحد، خزائن متعددة.** يعيش Key Vault URL على بيانات الاعتماد، وليس على تكوين WI. لذا يمكن لـ App Registration واحد (وتكوين WI واحد) خدمة عدة Key Vaults — فقط أنشئ بيانات اعتماد مزود أسرار واحدة لكل خزنة، جميعها مرتبطة بنفس تكوين WI.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure + Workload Identity + WI config dropdown + vault URL → /images/secrets-manager/azure-wi/07-amp-add-credential-azure-wi.png */}
|
||||
|
||||
## الخطوة 8 — اختبار الاتصال
|
||||
|
||||
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT، وتُقدّمه إلى Microsoft Entra كـ `client_assertion` موحَّد، وتؤكد أن Entra تُعيد رمز وصول محصور بالخزنة. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
|
||||
|
||||
نجاح Test Connection يُثبت أن مُصدر Federated Identity Credential وموضوعه وجمهوره كلها متطابقة، وأن App Registration قابل للوصول. لا يُثبت ذلك أن RBAC لكل سر في Key Vault صحيح — يُمارَس `getSecret` على سر محدد بشكل منفصل عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
|
||||
|
||||
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
|
||||
|
||||
أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
|
||||
|
||||
## الخطوة 10 — التحقق من التدوير
|
||||
|
||||
بعد تشغيل عملية النشر، دوّر السر في Key Vault:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "rotated value"
|
||||
```
|
||||
|
||||
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
|
||||
|
||||
للتأكد في سجلات العامل، ابحث عن:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (azure): N secret(s) resolved
|
||||
```
|
||||
|
||||
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `getSecret` طازج مقابل Azure Key Vault.
|
||||
|
||||
للتحقق من البداية إلى النهاية باستخدام البصمة، راجع [التحقق من التدوير من البداية إلى النهاية](/ar/enterprise/features/secrets-manager/verify-rotation).
|
||||
|
||||
## استكشاف الأخطاء
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| يفشل Test Connection بخطأ مصافحة | رفضت Microsoft Entra `client_assertion` الموحَّد. تحقق من أن **Issuer** في Federated Identity Credential يطابق قيمة `issuer` للمنصة بالضبط، وأن **Subject** هو `organization:<your-org-uuid>` (يطابق ادّعاء `sub` في JWT)، وأن **Audience** هو `api://AzureADTokenExchange`، وأن رابط اكتشاف OIDC للمنصة قابل للوصول من Entra عبر الإنترنت العام. |
|
||||
| `AADSTS70021: No matching federated identity record found for presented assertion` | لا يتطابق **Issuer** + **Subject** + **Audience** في Federated Identity Credential مع الـ JWT بالضبط. تحقق من الخطوة 3 من جديد: يجب أن يكون الموضوع `organization:<your-org-uuid>` (يطابق ادّعاء `sub` في JWT)، ويجب أن يكون الجمهور `api://AzureADTokenExchange`. |
|
||||
| `AADSTS700024: Client assertion is not within its valid time range` | ساعة مضيف CrewAI Platform منحرفة بشكل كبير عن الوقت الحقيقي. تحقق من NTP على المضيف. |
|
||||
| `AADSTS50013: Assertion failed signature validation` | لم تستطع Microsoft Entra التحقق من توقيع الـ JWT. تأكد من أن `https://<your-platform-host>/oauth2/jwks` قابل للوصول من الإنترنت العام ويُقدّم JWKS صالحاً. |
|
||||
| يُظهر الاقتراح التلقائي لاسم السر `Forbidden — does not have permission to perform action 'Microsoft.KeyVault/vaults/secrets/.../list'` | دور **Key Vault Secrets User** الخاص بـ App Registration محصور بسر واحد. امنح الدور على نطاق الخزنة ليُسمح بإجراء `list` في مستوى البيانات. راجع الخطوة 4. |
|
||||
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن RBAC لكل سر في Key Vault مفقود على السر الفاشل. راجع **Key Vault Secrets User** على ذلك السر تحديداً (أو وسّع تعيين الدور إلى نطاق الخزنة). |
|
||||
| `Forbidden — request was not authorized` (الخزنة تستخدم سياسات الوصول القديمة) | لم يتم تحويل الخزنة إلى Azure RBAC. ضمن **Access configuration** للخزنة، عيّن نموذج الإذن إلى **Azure role-based access control** وأعد منح الدور من الخطوة 4. |
|
||||
| `azure_vault_url is required for Azure secret resolution` (سجلات العامل) | تفتقد بيانات اعتماد مزود الأسرار إلى **Key Vault URL**. تحقق من الخطوة 7 من جديد. |
|
||||
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
|
||||
|
||||
### روابط مرجعية
|
||||
|
||||
- Microsoft: [Microsoft Entra Workload Identity Federation overview](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation)
|
||||
- Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust)
|
||||
- Microsoft: [Azure Key Vault RBAC guide](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide)
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
|
||||
- للتنوع متعدد السحاب، إعداد ما يعادله لـ AWS موجود في [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) وما يعادله لـ GCP في [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
|
||||
## مرجع لقطات الشاشة
|
||||
|
||||
تُربط العناصر النائبة أعلاه بـ:
|
||||
|
||||
- `01-register-app.png` — نموذج "Register an application" في بوابة Azure مع `crewai-secrets-reader`.
|
||||
- `02-add-federated-credential.png` — App Registration ← Certificates & secrets ← Federated credentials ← Add credential، مع **Other issuer**، رابط مُصدر المنصة، الموضوع `organization:<uuid>`، الجمهور `api://AzureADTokenExchange`.
|
||||
- `03-grant-vault-rbac.png` — Key Vault ← Access control (IAM) ← Add role assignment، مع **Key Vault Secrets User** و App Registration المختار.
|
||||
- `04-per-secret-rbac.png` — نفس النموذج لكن في نطاق IAM سر واحد (مسار أقل الامتيازات البديل).
|
||||
- `05-amp-add-wi-config-azure.png` — نموذج "Add Workload Identity Config" في CrewAI Platform مع Cloud Provider = Azure و Tenant ID و Client ID مأهولين.
|
||||
- `06-amp-wi-list-with-azure.png` — صفحة قائمة Workload Identity بعد الإنشاء، تُظهر صفوفاً لـ AWS و GCP وتكوين Azure الجديد.
|
||||
- `07-amp-add-credential-azure-wi.png` — نموذج "Add Secret Provider Credential" مع Provider = Azure Key Vault، Auth = Workload Identity، تكوين WI المختار، و Key Vault URL مأهول.
|
||||
195
docs/ar/enterprise/features/secrets-manager/azure.mdx
Normal file
195
docs/ar/enterprise/features/secrets-manager/azure.mdx
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: Azure Key Vault
|
||||
description: تكوين Azure Key Vault كمزود أسرار لـ CrewAI Platform من البداية إلى النهاية
|
||||
sidebarTitle: Azure
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يأخذك هذا الدليل عبر تكوين Azure Key Vault كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **App Registration في Microsoft Entra مع سر عميل**. بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في Azure Key Vault الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة، راجع [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل التكوين من جانب Azure وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- اشتراك Azure لديه إذن إنشاء App Registrations في Microsoft Entra ومنح تعيينات أدوار على موارد Key Vault.
|
||||
- Key Vault يستخدم **Azure RBAC** للترخيص (وليس النموذج القديم لسياسة الوصول). إذا كان الخزنة لا تزال تستخدم سياسات الوصول، فحوّلها إلى RBAC ضمن لوحة **Access configuration** للخزنة.
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## الخطوة 1 — إنشاء App Registration
|
||||
|
||||
App Registration هي الهوية من جانب Microsoft Entra التي ستُصادق بها CrewAI Platform.
|
||||
|
||||
في [بوابة Microsoft Entra](https://entra.microsoft.com)، انتقل إلى **App registrations** وانقر على **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- اترك **Redirect URI** فارغاً.
|
||||
|
||||
انقر على **Register**. سجّل **Application (client) ID** و **Directory (tenant) ID** في لوحة نظرة عامة التطبيق — ستلصق كليهما في CrewAI Platform في الخطوة 4.
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق Microsoft: [Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).
|
||||
|
||||
{/* SCREENSHOT: Azure "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure/01-register-app.png */}
|
||||
|
||||
## الخطوة 2 — إنشاء سر عميل
|
||||
|
||||
في App Registration، انتقل إلى **Certificates & secrets** ← **Client secrets** ← **New client secret**.
|
||||
|
||||
- **Description:** `crewai-platform`
|
||||
- **Expires:** اختر مدة تتطابق مع سياسة التدوير لديك (تحدّد Microsoft هذا بـ 24 شهراً كحد أقصى).
|
||||
|
||||
انقر على **Add**. انسخ عمود **Value** فوراً — لا يمكن إعادة عرضه أبداً بمجرد مغادرة الصفحة.
|
||||
|
||||
<Warning>
|
||||
أسرار العميل هي بيانات اعتماد ثابتة طويلة الأمد. خزّن القيمة بأمان (في مدير كلمات مرور أو مخزن أسرارك الخاص) ودوّرها قبل انتهاء الصلاحية. للقضاء على بيانات الاعتماد الثابتة تماماً، استخدم [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity) بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: "Client secrets" tab with the new secret row and the "Value" column highlighted → /images/secrets-manager/azure/02-create-client-secret.png */}
|
||||
|
||||
## الخطوة 3 — منح App Registration وصولاً إلى Key Vault
|
||||
|
||||
تحتاج CrewAI Platform إلى وصول قراءة للأسرار في Key Vault الخاص بك. استخدم أحد نطاقين — **على مستوى الخزنة** للبساطة، أو **لكل سر** لأقل الامتيازات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="على مستوى الخزنة (أبسط)">
|
||||
في [وحدة تحكم Key Vault](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.KeyVault%2Fvaults)، افتح الخزنة الهدف، ثم انتقل إلى **Access control (IAM)** ← **Add** ← **Add role assignment**.
|
||||
|
||||
- **Role:** **Key Vault Secrets User**
|
||||
- **Assign access to:** User, group, or service principal
|
||||
- **Members:** ابحث عن App Registration الخاص بك (`crewai-secrets-reader`) واختره.
|
||||
|
||||
انقر على **Review + assign**.
|
||||
|
||||
أو عبر Azure CLI:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="لكل سر (أقل الامتيازات)">
|
||||
امنح الدور على مستوى سر فردي. كرّر لكل سر ينبغي أن تصل إليه CrewAI Platform:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Access control (IAM)" panel showing role assignment scoped to one secret → /images/secrets-manager/azure/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
يسمح دور **Key Vault Secrets User** بقراءة قيم الأسرار لكن ليس سرد جميع الأسرار في الخزنة. يستدعي الاقتراح التلقائي لاسم السر في CrewAI Platform أيضاً `list` — هذا الإذن مُضمَّن في الدور على نطاق الخزنة، لكن **ليس** على نطاق لكل سر. مع ارتباطات لكل سر، لن يقترح الإكمال التلقائي أسراراً؛ اكتب اسم السر الكامل بدلاً من ذلك.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 4 — إضافة بيانات الاعتماد في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `azure-prod`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Key Vault URL:** اسم مضيف DNS للخزنة، مثلاً `https://my-vault.vault.azure.net`.
|
||||
- **Tenant ID:** **Directory (tenant) ID** الخاص بـ Microsoft Entra من الخطوة 1.
|
||||
- **Client ID:** **Application (client) ID** الخاص بـ App Registration من الخطوة 1.
|
||||
- **Client Secret:** **Value** الذي نسخته في الخطوة 2.
|
||||
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار Azure بدون تحديد بيانات اعتماد صراحةً.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure fields filled in → /images/secrets-manager/azure/05-amp-add-credential-form-azure.png */}
|
||||
|
||||
## الخطوة 5 — إنشاء سر واحد على الأقل في Azure Key Vault
|
||||
|
||||
إذا لم يكن لديك بالفعل أسرار في Key Vault، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 6.
|
||||
|
||||
في وحدة تحكم Key Vault، انتقل إلى **Objects** ← **Secrets** ← **Generate/Import**.
|
||||
|
||||
- **Upload options:** `Manual`
|
||||
- **Name:** مثلاً `openai-api-key`
|
||||
- **Secret value:** الصق قيمة سرّك
|
||||
- اترك الباقي على القيم الافتراضية.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
أو عبر Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
<Note>
|
||||
**اصطلاحات اسم السر.** لا يمكن أن تحتوي أسماء أسرار Azure Key Vault على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`)، لذا يمكنك الاحتفاظ بأسماء متغيرات بيئة بنمط الشرطة السفلية — لكن السر الأساسي في Key Vault يجب أن يستخدم الشرطات.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**صيغة الإشارة بمفتاح JSON.** يتعامل Key Vault مع قيم الأسرار كسلاسل معتمة. إذا حدث أن كانت قيمة سرّك كائن JSON، يمكن لـ CrewAI Platform استخراج حقل واحد باستخدام صيغة `secret-name#json_key` (مثلاً `database-credentials#password`). راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
|
||||
</Note>
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق Microsoft: [Set and retrieve a secret](https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli).
|
||||
|
||||
{/* SCREENSHOT: Azure Key Vault "Create a secret" form with name and value → /images/secrets-manager/azure/06-create-secret.png */}
|
||||
|
||||
## الخطوة 6 — اختبار الاتصال
|
||||
|
||||
عُد إلى CrewAI Platform، في صفحة **Secret Provider Credentials**، اعثر على بيانات الاعتماد التي أنشأتها للتو وانقر على **Test Connection**.
|
||||
|
||||
تؤكد رسالة نجاح أن CrewAI Platform يمكنها المصادقة مع Microsoft Entra وقراءة الأسرار من خزنتك.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the Azure credential → /images/secrets-manager/azure/07-test-connection-success.png */}
|
||||
|
||||
إذا فشل الاختبار، تحقق من الأسباب الأكثر شيوعاً:
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| `AADSTS7000215: Invalid client secret provided` | **Client Secret** الملصوق خاطئ أو منتهي الصلاحية. أعد إنشاء السر (الخطوة 2) وحدّث بيانات الاعتماد. |
|
||||
| `AADSTS700016: Application not found in the directory` | لا يطابق **Tenant ID** أو **Client ID** الـ App Registration. تحقق من الخطوة 4 من جديد. |
|
||||
| `Forbidden — caller does not have permission` | يفتقد App Registration إلى دور **Key Vault Secrets User** على الخزنة (أو لكل سر). تحقق من الخطوة 3 من جديد. |
|
||||
| `Vault not found` / أخطاء DNS | **Key Vault URL** خاطئ، أو أن خزنتك لديها نقاط نهاية خاصة تمنع الوصول العام. تأكد من أن المضيف يستجيب لـ `curl https://<vault-name>.vault.azure.net/secrets?api-version=7.4`. |
|
||||
| `Forbidden — request was not authorized` (الخزنة تستخدم سياسات الوصول القديمة) | لم يتم تحويل الخزنة إلى Azure RBAC. ضمن **Access configuration** للخزنة، عيّن نموذج الإذن إلى **Azure role-based access control** وأعد منح الدور من الخطوة 3. |
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
الآن وقد اتصل Azure Key Vault، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
|
||||
|
||||
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
|
||||
- الإشارة إلى أسرار Azure الخاصة بك من متغيرات بيئة CrewAI Platform.
|
||||
|
||||
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity) — نفس الخزنة، بدون سر عميل للتدوير، وتُجلب الأسرار في كل إطلاق.
|
||||
|
||||
## مرجع لقطات الشاشة
|
||||
|
||||
تُربط العناصر النائبة أعلاه بـ:
|
||||
|
||||
- `01-register-app.png` — نموذج "Register an application" في بوابة Azure مع `crewai-secrets-reader`.
|
||||
- `02-create-client-secret.png` — App Registration ← Certificates & secrets ← Client secrets، مع صف السر المُنشأ حديثاً (عمود Value مُميَّز قبل تمويهه).
|
||||
- `03-grant-vault-rbac.png` — Key Vault ← Access control (IAM) ← Add role assignment، مع اختيار **Key Vault Secrets User** و App Registration كعضو.
|
||||
- `04-per-secret-rbac.png` — نفس اللوحة لكن بنطاق سر واحد (مسار أقل الامتيازات البديل).
|
||||
- `05-amp-add-credential-form-azure.png` — نموذج "Add Secret Provider Credential" في CrewAI Platform: Provider = Azure Key Vault، جميع الحقول الخمسة مأهولة.
|
||||
- `06-create-secret.png` — لوحة "Create a secret" في Azure Key Vault مع `openai-api-key` وقيمة ملصوقة.
|
||||
- `07-test-connection-success.png` — رسالة نجاح / حالة صف في CrewAI Platform بعد النقر على **Test Connection** على بيانات الاعتماد.
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: GCP Workload Identity Federation
|
||||
description: تكوين Google Cloud Secret Manager عبر Workload Identity Federation للوصول إلى الأسرار بشكل مراعٍ للتدوير وبدون بيانات اعتماد
|
||||
sidebarTitle: GCP — Workload Identity
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يُكوِّن هذا الدليل Google Cloud Secret Manager كمزود أسرار باستخدام **Workload Identity Federation**: تُصدر CrewAI Platform رموز OIDC قصيرة الأمد، وتُبادلها للحصول على بيانات اعتماد Google Cloud عبر خدمة Security Token Service، وتقرأ أسرارك — دون تخزين أي مفتاح حساب خدمة طويل الأمد في أي مكان.
|
||||
|
||||
<Note>
|
||||
**لماذا هذا المسار:** تُحَلّ الأسرار وقت تنفيذ الأتمتة، لذا **تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر**. إن كنت تحتاج فقط بيانات اعتماد ثابتة، راجع الدليل الأبسط [GCP — مفتاح حساب الخدمة](/ar/enterprise/features/secrets-manager/gcp).
|
||||
</Note>
|
||||
|
||||
### كيف يعمل وقت التشغيل
|
||||
|
||||
1. يطلب عامل النشر JWT OIDC طازج من CrewAI Platform.
|
||||
2. يبادل العامل الـ JWT للحصول على بيانات اعتماد Google موحَّدة عبر [Security Token Service](https://cloud.google.com/iam/docs/reference/sts/rest)، مع الإشارة إلى Workload Identity Pool Provider الذي ستُعدّه أدناه.
|
||||
3. يستدعي العامل `secretmanager.googleapis.com:accessSecretVersion` لقراءة السر، باستخدام بيانات الاعتماد الموحَّدة مباشرةً (يمتلك الكيان الموحَّد `roles/secretmanager.secretAccessor` — راجع الخطوة 4).
|
||||
4. تُحقن القيمة المجلوبة كقيمة لمتغير البيئة لإطلاق الأتمتة ذاك.
|
||||
|
||||
تُخزَّن رموز موضوع OIDC مؤقتاً لنحو ساعة لتفادي إعادة الإصدار في كل إطلاق. تُجلب قيم الأسرار طازجة في كل إطلاق بغض النظر عن حالة ذاكرة OIDC المؤقتة، وهذا ما يجعل هذا المسار مراعياً للتدوير.
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- يجب أن تتضمن صورة حاوية الأتمتة إصدار CrewAI runtime رقم `1.14.5` أو أحدث.
|
||||
- مشروع Google Cloud مع تفعيل **Secret Manager API** و **Security Token Service API** و **IAM Credentials API**. فعّلها عبر الوحدة أو:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com sts.googleapis.com iamcredentials.googleapis.com \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
- إذن في المشروع لإنشاء Workload Identity Pools وأدوار IAM وحسابات الخدمة و(إن لزم) الأسرار.
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذني `workload_identity_configs: manage` و `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **يجب أن يكون تنصيب CrewAI Platform قابلاً للوصول من Google Cloud عبر HTTPS** ليتمكّن GCP STS من جلب وثيقة اكتشاف OIDC و JWKS أثناء التحقق من الرمز. تأكد مع مسؤول المنصة من أن المضيف متاح عبر الإنترنت.
|
||||
</Note>
|
||||
|
||||
## الخطوة 1 — العثور على عنوان مُصدر OIDC لـ CrewAI Platform
|
||||
|
||||
ينشر تنصيب CrewAI Platform وثيقة اكتشاف OpenID Connect على `https://<your-platform-host>/.well-known/openid-configuration`. الحقل `issuer` هناك هو الرابط الذي ستُسجِّله Google كمزود OIDC موثوق.
|
||||
|
||||
افتح الرابط في المتصفح:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
ينبغي أن ترى JSON يحتوي على:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
سجّل القيمة الدقيقة لـ `issuer` — ستستخدمها في الخطوة 3.
|
||||
|
||||
<Tip>
|
||||
إذا أعاد الرابط 404 أو 503، اتصل بمسؤول المنصة. يتطلب مُصدر OIDC تكوين مفتاح توقيع خاص وقت التنصيب. راجع دليل تنصيب المنصة لتكوين `OIDC_PRIVATE_KEY` و `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 2 — إنشاء Workload Identity Pool
|
||||
|
||||
Workload Identity Pool هو حاوية من جانب Google Cloud للهويات الخارجية الموثوقة. ستُسجِّل CrewAI Platform كمزود داخل هذه الحوض.
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools create crewai-pool \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--display-name="CrewAI Platform"
|
||||
```
|
||||
|
||||
أو في [وحدة تحكم Workload Identity Pools](https://console.cloud.google.com/iam-admin/workload-identity-pools)، انقر على **Create Pool**.
|
||||
|
||||
{/* SCREENSHOT: GCP "Create Workload Identity Pool" form with name "crewai-pool" → /images/secrets-manager/gcp-wi/01-create-pool.png */}
|
||||
|
||||
## الخطوة 3 — إضافة CrewAI Platform كمزود OIDC في الحوض
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--display-name="CrewAI Platform OIDC" \
|
||||
--issuer-uri="https://<your-platform-host>" \
|
||||
--attribute-mapping="google.subject=assertion.sub,attribute.organization=assertion.organization_id" \
|
||||
--attribute-condition="assertion.organization_id != ''"
|
||||
```
|
||||
|
||||
يُخبر `--attribute-mapping` Google كيفية ربط ادّعاءات JWT بسمات Google:
|
||||
- `google.subject` هو معرّف الكيان — نربطه بادّعاء `sub` في JWT، الذي تُعيّنه CrewAI Platform إلى `organization:<uuid>`.
|
||||
- `attribute.organization` هو سمة مخصصة — نربطها بادّعاء `organization_id` في JWT لتتمكّن من الإشارة إليها في ارتباطات IAM لاحقاً.
|
||||
|
||||
`--attribute-condition` هو فحص دفاع في العمق يرفض الرموز التي تفتقد لادّعاء `organization_id`.
|
||||
|
||||
احصل على **اسم مورد المزود** (ستحتاجه للجمهور وارتباطات IAM):
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers describe crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--format="value(name)"
|
||||
```
|
||||
|
||||
يبدو الناتج هكذا:
|
||||
|
||||
```
|
||||
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider
|
||||
```
|
||||
|
||||
هذه هي قيمة **Workload Identity Provider** الخاصة بك في CrewAI Platform في الخطوة 6. تحسب CrewAI Platform تلقائياً جمهور OIDC كـ `//iam.googleapis.com/<this-resource-name>` عند إصدار الرموز.
|
||||
|
||||
{/* SCREENSHOT: "Add provider to pool" form with OIDC selected, issuer URI, audience defaults, attribute mapping → /images/secrets-manager/gcp-wi/02-add-oidc-provider.png */}
|
||||
|
||||
## الخطوة 4 — منح الوصول إلى Secret Manager للكيان الموحَّد
|
||||
|
||||
اربط دوري Secret Manager كليهما على نطاق المشروع بالكيان الموحَّد — دور يُفعّل الاقتراح التلقائي لاسم السر في نموذج متغير البيئة، والآخر يسمح بقراءة قيم الأسرار عند إطلاق الأتمتة. كلاهما مطلوبان لتعمل الميزة من البداية إلى النهاية.
|
||||
|
||||
```bash
|
||||
PRINCIPAL_SET="principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/attribute.organization/<YOUR_CREWAI_ORG_UUID>"
|
||||
|
||||
# Required for the Secret Name autocomplete (calls secretmanager.secrets.list)
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.viewer"
|
||||
|
||||
# Required to read secret values at kickoff
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
استبدل `<PROJECT_NUMBER>` برقم المشروع الرقمي (`gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)'`) و `<YOUR_CREWAI_ORG_UUID>` بـ UUID مؤسسة CrewAI Platform التي يجب أن يُسمح لها بقراءة أسرارك. يمكنك العثور على UUID المؤسسة في واجهة المنصة في صفحة إعدادات المؤسسة، أو عبر الـ API. يقصر هذا الاتحاد على مؤسسة CrewAI محددة — تُقبل فقط الرموز المُصدَرة لأتمتات تلك المؤسسة.
|
||||
|
||||
أو عبر وحدة تحكم Google Cloud:
|
||||
|
||||
1. افتح **IAM & Admin** ← **IAM** لمشروعك.
|
||||
2. انقر على **GRANT ACCESS**.
|
||||
3. **New principals:** الصق سلسلة `principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID>` الكاملة.
|
||||
4. عيّن الدور **Secret Manager Viewer** (`roles/secretmanager.viewer`).
|
||||
5. انقر على **SAVE**.
|
||||
6. انقر على **GRANT ACCESS** مرة أخرى وكرّر مع الدور **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
<Tip>
|
||||
**العزل لكل مؤسسة.** يقيّد النمط `principalSet://...attribute.organization/<UUID>` الوصول إلى رموز مؤسسة محددة. إذا كانت لديك مؤسسات CrewAI متعددة تتشارك مشروع Google Cloud واحد، كرّر كلا الارتباطين لكل مؤسسة بالـ UUID الصحيح — أو استخدم شرط سمة أقل تقييداً إن لم يكن العزل ضرورياً.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**تحديد نطاق `secretAccessor` لكل سر (اختياري).** إذا كنت تفضّل عدم منح `roles/secretmanager.secretAccessor` على نطاق المشروع، احذف الارتباط الثاني أعلاه واربط لكل سر بدلاً من ذلك:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <SECRET_NAME> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
أبقِ `roles/secretmanager.viewer` على نطاق المشروع في كلا الحالتين — `secretmanager.secrets.list` (الذي يعتمد عليه الاقتراح التلقائي) لا يمكن منحه لكل سر.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 5 — إنشاء سر واحد على الأقل في GCP
|
||||
|
||||
إذا لم يكن لديك سر للاختبار، أنشئ واحداً عبر CLI `gcloud`:
|
||||
|
||||
```bash
|
||||
echo -n "hello from gcp" | gcloud secrets create crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
أو عبر [وحدة تحكم Secret Manager](https://console.cloud.google.com/security/secret-manager):
|
||||
|
||||
1. افتح **Secret Manager** في مشروع GCP الخاص بك.
|
||||
2. انقر على **+ CREATE SECRET**.
|
||||
3. **Name:** `crewai-test-keyword`. **Secret value:** الصق قيمتك.
|
||||
4. انقر على **CREATE SECRET**.
|
||||
|
||||
## الخطوة 6 — إضافة تكوين Workload Identity في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Workload Identity** وانقر على **Add Workload Identity Config**.
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `gcp-prod`.
|
||||
- **Cloud Provider:** `GCP`.
|
||||
- **Workload Identity Provider:** اسم مورد المزود من الخطوة 3، مثلاً `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider`.
|
||||
- (اختياري) بدّل **Default Configuration** إذا كنت ترغب في أن يكون هذا هو تكوين WI الافتراضي المُحدَّد عند إنشاء بيانات اعتماد سر مدعومة بـ GCP.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with GCP and provider resource name → /images/secrets-manager/gcp-wi/03-amp-add-wi-config-gcp.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing both AWS and GCP rows → /images/secrets-manager/gcp-wi/04-amp-wi-list-with-gcp.png */}
|
||||
|
||||
## الخطوة 7 — إضافة بيانات اعتماد مزود أسرار مرتبطة بتكوين WI
|
||||
|
||||
انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `gcp-prod-wi`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** اختر التكوين الذي أنشأته في الخطوة 6.
|
||||
- **Project ID:** معرّف مشروع GCP الخاص بك (نفس المشروع الذي يملك الأسرار).
|
||||
- (اختياري) حدّد **Set as default credential for this provider**.
|
||||
|
||||
سيطلب النموذج فقط **Project ID** ضمن Workload Identity — حقل **Service Account JSON** مخفي عمداً لأنه لا ينطبق على هذا المسار؛ تأتي الهوية الموحَّدة من تكوين WI المرتبط.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP + Workload Identity + WI config dropdown → /images/secrets-manager/gcp-wi/05-amp-add-credential-gcp-wi.png */}
|
||||
|
||||
## الخطوة 8 — اختبار الاتصال
|
||||
|
||||
بعد حفظ بيانات الاعتماد، انقر على **Test Connection**. لبيانات اعتماد workload-identity، يتحقق هذا من مصافحة OIDC: تُصدر CrewAI Platform JWT وتبادله عبر Security Token Service للحصول على رمز وصول Google موحَّد. نتيجة خضراء تعني أن ارتباط الاتحاد سليم.
|
||||
|
||||
نجاح Test Connection يُثبت أن Workload Identity Pool ومزود OIDC وربط السمات وشرط السمة موصولة جميعها بشكل صحيح. لا يُثبت ذلك أن IAM في Secret Manager صحيح — يُمارَس `secretmanager.secrets.list` و `secretmanager.versions.access` بشكل منفصل عند تحميل الاقتراح التلقائي لاسم السر أو عندما يُحَلّ متغير بيئة عند الإطلاق. راجع [استكشاف الأخطاء](#troubleshooting) لأنماط فشل المصافحة.
|
||||
|
||||
## الخطوة 9 — الإشارة إلى السر في متغير بيئة
|
||||
|
||||
أَشِر إلى السر على أتمتة، تماماً كما تفعل مع أي متغير بيئة مدعوم بمدير أسرار. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) لحقول النموذج والسلوك.
|
||||
|
||||
## الخطوة 10 — التحقق من التدوير
|
||||
|
||||
بعد تشغيل عملية النشر، دوّر السر في GCP بإضافة إصدار جديد (يقرأ Secret Manager دائماً أحدث إصدار مفعَّل افتراضياً):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
أطلق إطلاق أتمتة جديداً. ستكون بيئة الإطلاق ترى `"rotated value"` — بدون إعادة نشر ولا إعادة تشغيل عامل ولا انتظار TTL.
|
||||
|
||||
للتأكد في سجلات العامل، ابحث عن:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (gcp): N secret(s) resolved
|
||||
```
|
||||
|
||||
يظهر هذا السطر لكل إطلاق ويُشير إلى استدعاء `accessSecretVersion` طازج مقابل GCP.
|
||||
|
||||
## استكشاف الأخطاء
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| يفشل Test Connection بخطأ مصافحة | رُفض تبادل رمز STS. تحقق من وجود Workload Identity Pool، وأن مُصدر مزود OIDC يطابق قيمة `issuer` للمنصة، وأن شرط السمة يقبل ادّعاءات JWT. تأكد من أن رابط اكتشاف OIDC للمنصة قابل للوصول من GCP عبر الإنترنت العام. |
|
||||
| `Could not refresh access token: invalid_target` | لا يطابق ادّعاء الجمهور الجمهور المتوقع لمزود Workload Identity. تُعيّن CrewAI Platform الجمهور تلقائياً؛ إذا خصّصته، فتأكد من أنه يطابق `//iam.googleapis.com/<provider-resource-name>`. |
|
||||
| `Failed to fetch JWKS from issuer` | لا يمكن لـ GCP STS الوصول إلى مضيف CrewAI Platform. تأكد من أن المضيف متاح عبر الإنترنت وأن `/.well-known/openid-configuration` يُعيد 200. |
|
||||
| `Attribute condition rejected token` | يتطلب شرط السمة لمزود OIDC (الخطوة 3) `organization_id`. تُعيّن CrewAI Platform هذا الادّعاء دائماً، لذا يعني هذا عادةً تكوين حوض/مزود خاطئاً. تحقق من شرط السمة للمزود من جديد. |
|
||||
| يُظهر الاقتراح التلقائي لاسم السر `PERMISSION_DENIED: secretmanager.secrets.list` | يفتقد الكيان الموحَّد إلى `roles/secretmanager.viewer` على نطاق المشروع. إذن `secretmanager.secrets.list` محصور بنطاق المشروع فقط ولا يمكن منحه لكل سر. راجع الخطوة 4. |
|
||||
| يفشل الإطلاق في حلّ سر رغم نجاح Test Connection | ارتباط WI سليم، لكن `secretmanager.versions.access` مفقود على السر الفاشل. راجع `roles/secretmanager.secretAccessor` (على نطاق المشروع، أو لكل سر إذا حدّدت النطاق بهذه الطريقة في الخطوة 4). |
|
||||
| لا تُلتقط القيمة المُدوَّرة في الإطلاق التالي | تأكد من أن متغير البيئة على الأتمتة يشير إلى بيانات اعتماد مدعومة بـ Workload Identity (وليس بيانات اعتماد بمفاتيح ثابتة). يدمج المسار الثابت القيم في صورة النشر. |
|
||||
|
||||
### روابط مرجعية
|
||||
|
||||
- GCP: [Workload Identity Federation overview](https://cloud.google.com/iam/docs/workload-identity-federation)
|
||||
- GCP: [Configure Workload Identity Federation with OIDC](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
|
||||
- GCP: [Secret Manager IAM roles](https://cloud.google.com/secret-manager/docs/access-control)
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
|
||||
- للتنوع متعدد السحاب، راجع أيضاً [AWS Workload Identity (اتحاد OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) و [Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
188
docs/ar/enterprise/features/secrets-manager/gcp.mdx
Normal file
188
docs/ar/enterprise/features/secrets-manager/gcp.mdx
Normal file
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: Google Cloud Secret Manager
|
||||
description: تكوين Google Cloud Secret Manager كمزود أسرار لـ CrewAI Platform من البداية إلى النهاية
|
||||
sidebarTitle: GCP
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يأخذك هذا الدليل عبر تكوين Google Cloud Secret Manager كمزود أسرار لمؤسستك على CrewAI Platform، باستخدام **بيانات اعتماد حساب خدمة**. بنهاية الدليل، ستتمكن CrewAI Platform من قراءة الأسرار المخزّنة في مشروع Google Cloud الخاص بك وحقنها كقيم متغيرات بيئة وقت التشغيل.
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل مسار **بيانات الاعتماد الثابتة** — تُحَلّ الأسرار وقت النشر وتُدمج في صورة النشر. تتطلب القيم المُدوَّرة إعادة نشر. إذا أردت أسراراً مراعية للتدوير تُحدَّث في كل إطلاق أتمتة، راجع [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
يغطي هذا الدليل التكوين من جانب GCP وإعداد بيانات الاعتماد في CrewAI Platform. للإشارة بعدها إلى سر من متغير بيئة، راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
<Note>
|
||||
قبل البدء، تأكد من امتلاكك:
|
||||
|
||||
- مشروع Google Cloud مع تفعيل **Secret Manager API**. فعّله في [وحدة تحكم APIs & Services](https://console.cloud.google.com/apis/library/secretmanager.googleapis.com) أو عبر `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com --project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
- إذن في المشروع لإنشاء حسابات خدمة ومنح أدوار IAM و(إن لزم) إنشاء الأسرار.
|
||||
- مؤسسة على CrewAI Platform يمتلك مستخدمك فيها إذن `secret_providers: manage`. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## الخطوة 1 — إنشاء حساب خدمة
|
||||
|
||||
حساب الخدمة هو الهوية من جانب GCP التي ستُصادق بها CrewAI Platform.
|
||||
|
||||
في [وحدة تحكم IAM & Admin ← Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts)، انقر على **Create Service Account**.
|
||||
|
||||
- **Service account name:** `crewai-secrets-reader`
|
||||
- **Service account ID:** يُملأ تلقائياً من الاسم (مثلاً `crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com`)
|
||||
- **Description (optional):** "Read-only access to Secret Manager for CrewAI Platform"
|
||||
|
||||
انقر على **Create and Continue**. تخطَّ المنح الاختيارية في هذه الشاشة — ستُرفق الدور في الخطوة 2. انقر على **Done**.
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق GCP: [Create service accounts](https://cloud.google.com/iam/docs/service-accounts-create).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create service account" form with name "crewai-secrets-reader" → /images/secrets-manager/gcp/01-create-service-account.png */}
|
||||
|
||||
## الخطوة 2 — منح الوصول إلى Secret Manager
|
||||
|
||||
تحتاج CrewAI Platform إلى إذن لسرد وقراءة الأسرار في مشروعك. استخدم أحد نطاقين — **على مستوى المشروع** للبساطة، أو **لكل سر** لأقل الامتيازات.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="على مستوى المشروع (أبسط)">
|
||||
في [وحدة تحكم IAM](https://console.cloud.google.com/iam-admin/iam)، انقر على **Grant Access** و:
|
||||
|
||||
- **New principals:** بريد حساب الخدمة من الخطوة 1.
|
||||
- **Role:** **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
انقر على **Save**.
|
||||
|
||||
أو عبر `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: GCP IAM "Grant access" panel with the service account and Secret Manager Secret Accessor role → /images/secrets-manager/gcp/02-iam-grant-access.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="لكل سر (أقل الامتيازات)">
|
||||
امنح الدور فقط على الأسرار المحددة التي ينبغي أن تصل إليها CrewAI Platform. كرّر لكل سر:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding YOUR_SECRET_NAME \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
أو في الوحدة: افتح كل سر في [Secret Manager](https://console.cloud.google.com/security/secret-manager)، انقر على **Permissions** في اللوحة اليمنى، وامنح **Secret Manager Secret Accessor** لحساب الخدمة.
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Permissions" panel in Secret Manager with the service account granted accessor role → /images/secrets-manager/gcp/03-per-secret-permissions.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
يمنح دور `roles/secretmanager.secretAccessor` وصول قراءة فقط لقيم الأسرار. تستدعي CrewAI Platform أيضاً `secretmanager.secrets.list` لتجربة الاقتراح التلقائي في نموذج متغير البيئة — هذا الإذن مُضمَّن في الدور على نطاق المشروع، لكن **ليس** على نطاق لكل سر. مع ارتباطات لكل سر، لن يقترح الإكمال التلقائي أسراراً؛ ستحتاج إلى كتابة اسم السر الكامل.
|
||||
</Tip>
|
||||
|
||||
## الخطوة 3 — إنشاء مفتاح حساب الخدمة
|
||||
|
||||
افتح حساب الخدمة من الخطوة 1 في [وحدة تحكم IAM & Admin ← Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts).
|
||||
|
||||
- انقر على علامة التبويب **Keys**.
|
||||
- انقر على **Add Key** ← **Create new key**.
|
||||
- **Key type:** JSON.
|
||||
- انقر على **Create**. يُنزّل المتصفح ملف JSON — احتفظ به بأمان؛ لا يمكن إعادة تنزيله.
|
||||
|
||||
أو عبر `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts keys create ./crewai-secrets-reader.json \
|
||||
--iam-account=crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
<Warning>
|
||||
مفتاح حساب الخدمة هو بيانات اعتماد ثابتة طويلة الأمد. خزّنه بأمان (في مدير كلمات مرور أو مخزن أسرارك الخاص) ودوّره بشكل منتظم. للقضاء على بيانات الاعتماد الثابتة تماماً، استخدم [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) بدلاً من ذلك.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: Service account "Keys" tab with the "Create new key" → JSON option → /images/secrets-manager/gcp/04-create-service-account-key.png */}
|
||||
|
||||
## الخطوة 4 — إضافة بيانات الاعتماد في CrewAI Platform
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Settings** ← **Secret Provider Credentials** وانقر على **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Name:** اسم وصفي، مثلاً `gcp-prod`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Project ID:** معرّف مشروع GCP الخاص بك (مثلاً `my-crewai-prod`).
|
||||
- **Service Account JSON:** الصق المحتوى الكامل لملف JSON الذي نزّلته في الخطوة 3.
|
||||
- (اختياري) حدّد **Set as default credential for this provider**. تُستخدم بيانات الاعتماد الافتراضية بواسطة متغيرات البيئة التي تشير إلى أسرار GCP بدون تحديد بيانات اعتماد صراحةً.
|
||||
|
||||
انقر على **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP fields filled in → /images/secrets-manager/gcp/05-amp-add-credential-form-gcp.png */}
|
||||
|
||||
## الخطوة 5 — إنشاء سر واحد على الأقل في GCP
|
||||
|
||||
إذا لم يكن لديك بالفعل أسرار في GCP Secret Manager، أنشئ واحداً الآن لتتمكن من التحقق من الاتصال في الخطوة 6.
|
||||
|
||||
في [وحدة تحكم Secret Manager](https://console.cloud.google.com/security/secret-manager)، انقر على **Create secret**.
|
||||
|
||||
- **Name:** اسم فريد، مثلاً `openai-api-key`.
|
||||
- **Secret value:** إما لصق قيمة خام أو رفع ملف.
|
||||
- اترك إعدادات التدوير والتكرار وغيرها على القيم الافتراضية ما لم تكن لديك متطلبات محددة.
|
||||
|
||||
انقر على **Create secret**.
|
||||
|
||||
أو عبر `gcloud`:
|
||||
|
||||
```bash
|
||||
echo -n "sk-your-actual-key" | gcloud secrets create openai-api-key \
|
||||
--data-file=- \
|
||||
--project=YOUR_PROJECT_ID \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
<Note>
|
||||
**صيغة الإشارة بمفتاح JSON.** يتعامل GCP Secret Manager مع قيم الأسرار كبيانات معتمة. إذا حدث أن كانت قيمة سرّك سلسلة JSON، يمكن لـ CrewAI Platform استخراج حقل واحد باستخدام صيغة `secret-name#json_key` (مثلاً `database-credentials#password`). راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) للتفاصيل.
|
||||
</Note>
|
||||
|
||||
للتفاصيل الكاملة، راجع وثائق GCP: [Create a secret](https://cloud.google.com/secret-manager/docs/create-secret-quickstart).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create secret" form with name and value → /images/secrets-manager/gcp/06-create-secret.png */}
|
||||
|
||||
## الخطوة 6 — اختبار الاتصال
|
||||
|
||||
عُد إلى CrewAI Platform، في صفحة **Secret Provider Credentials**، اعثر على بيانات الاعتماد التي أنشأتها للتو وانقر على **Test Connection**.
|
||||
|
||||
تؤكد رسالة نجاح أن CrewAI Platform يمكنها المصادقة مع GCP وقراءة الأسرار من مشروعك.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the GCP credential → /images/secrets-manager/gcp/07-test-connection-success.png */}
|
||||
|
||||
إذا فشل الاختبار، تحقق من الأسباب الأكثر شيوعاً:
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| `PERMISSION_DENIED` عند سرد الأسرار | يفتقد حساب الخدمة إلى `roles/secretmanager.secretAccessor`، أو حدّدت نطاقه لكل سر (لا يُمنح `list`). تحقق من الخطوة 2 من جديد. |
|
||||
| `PERMISSION_DENIED` على `secretmanager.secrets.access` | نفس ما سبق، لكن لسر محدد. تأكد من أن حساب الخدمة يمتلك دور accessor على السر المعني. |
|
||||
| `unauthorized_client` / `invalid_grant` | ملف Service Account JSON الملصوق غير صالح أو منتهي الصلاحية أو لحساب خدمة محذوف. أعد إنشاء المفتاح (الخطوة 3) والصقه من جديد. |
|
||||
| `Project ID does not match` | لا يطابق حقل Project ID في CrewAI Platform المشروع الذي يملك حساب الخدمة / الأسرار. تحقق من الخطوة 4 من جديد. |
|
||||
| `API not enabled` | Secret Manager API غير مفعَّل في المشروع. راجع المتطلبات المسبقة. |
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
الآن وقد اتصل GCP، توجّه إلى [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage) من أجل:
|
||||
|
||||
- منح أعضاء المؤسسة الأذونات الصحيحة لاستخدام (أو إدارة) مدير الأسرار.
|
||||
- الإشارة إلى أسرار GCP الخاصة بك من متغيرات بيئة CrewAI Platform.
|
||||
|
||||
إذا كنت تريد أسراراً **مراعية للتدوير** تنتشر دون إعادة نشر، انتقل إلى [GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) — نفس مخزن الأسرار، بدون بيانات اعتماد ثابتة، وتُجلب الأسرار في كل إطلاق.
|
||||
81
docs/ar/enterprise/features/secrets-manager/overview.mdx
Normal file
81
docs/ar/enterprise/features/secrets-manager/overview.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: نظرة عامة على مدير الأسرار
|
||||
description: ربط مخازن الأسرار الخارجية بمنصة CrewAI Platform والإشارة إلى الأسرار المُدارة من متغيرات البيئة
|
||||
sidebarTitle: نظرة عامة
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
تُتيح ميزة مدير الأسرار لمؤسستك ربط مخزن أسرار خارجي — AWS Secrets Manager أو Google Cloud Secret Manager أو (قريباً) Azure Key Vault — والإشارة إلى تلك الأسرار مباشرةً من متغيرات البيئة على الأتمتات والطواقم لديك. بدلاً من لصق قيم نصية صريحة في المنصة، فإنك تخزّن مجموعة واحدة من بيانات الاعتماد لكل مزود وتُشير إلى الأسرار بالاسم.
|
||||
|
||||
يمنحك هذا:
|
||||
|
||||
- **تخزين مركزي** — إدارة الأسرار في مزوّدك بدلاً من تعديل إعدادات CrewAI Platform. لا تحتفظ CrewAI Platform بأي نسخة نصية صريحة من قيمة السر.
|
||||
- **تقليل التعرّض** — لا تظهر القيم الحساسة أبداً كنص صريح في إعدادات CrewAI Platform.
|
||||
- **قابلية تدقيق سحابية المنشأ** — يسجّل سجل التدقيق الخاص بمزوّدك كل قراءة لسر.
|
||||
|
||||
<Note>
|
||||
يتطلب مدير الأسرار (مساران: بيانات الاعتماد الثابتة و Workload Identity) إصدار CrewAI runtime رقم `1.14.5` أو أحدث في صورة حاوية الأتمتة.
|
||||
</Note>
|
||||
|
||||
## مساران: بيانات اعتماد ثابتة مقابل Workload Identity
|
||||
|
||||
هناك طريقتان لربط CrewAI Platform بمخزن أسرار السحابة لديك. **يختلفان اختلافاً كبيراً في سلوك التدوير**، لذا اختر بناءً على مدى تكرار تدوير أسرارك ومدى صرامة وضعك الأمني.
|
||||
|
||||
| الجانب | بيانات الاعتماد الثابتة | Workload Identity (اتحاد OIDC) |
|
||||
|---|---|---|
|
||||
| **المصادقة** | مفاتيح وصول / ملف JSON لحساب خدمة طويلة الأمد مخزّنة في CrewAI Platform | رموز قصيرة الأمد تُصدر لكل عملية عامل؛ لا تُخزَّن بيانات اعتماد ثابتة في أي مكان |
|
||||
| **انتشار التدوير** | تُحَلّ وقت النشر و**تُدمج في صورة حاوية النشر** — تتطلب القيم المُدوَّرة إعادة نشر | تُحَلّ **وقت تنفيذ الأتمتة** — تنتشر القيم المُدوَّرة إلى الإطلاق التالي بدون إعادة نشر |
|
||||
| **جهد الإعداد** | أقل — لصق المفاتيح / رفع ملف JSON لحساب الخدمة | أعلى — تسجيل CrewAI Platform كمزود OIDC في سحابتك وتكوين سياسات الثقة |
|
||||
| **الأنسب لـ** | البداية، الأسرار قليلة التدوير، عمليات نشر بحساب واحد | الإنتاج، الأسرار كثيرة التدوير، البيئات التي تحكمها الامتثال وتمنع بيانات الاعتماد طويلة الأمد |
|
||||
|
||||
<Note>
|
||||
**يستخدم كلا المسارين نفس تدفق الواجهة** للإشارة إلى الأسرار في متغيرات البيئة (راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage)). الفرق بالكامل في كيفية مصادقة المنصة لسحابتك ومتى تقرأ قيمة السر.
|
||||
</Note>
|
||||
|
||||
### اختر دليل الإعداد الخاص بك
|
||||
|
||||
| المزود | بيانات الاعتماد الثابتة | Workload Identity |
|
||||
|---|---|---|
|
||||
| AWS Secrets Manager | [AWS — المفاتيح الثابتة / AssumeRole](/ar/enterprise/features/secrets-manager/aws) | [AWS — Workload Identity (OIDC)](/ar/enterprise/features/secrets-manager/aws-workload-identity) |
|
||||
| Google Cloud Secret Manager | [GCP — مفتاح حساب الخدمة](/ar/enterprise/features/secrets-manager/gcp) | [GCP — Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity) |
|
||||
| Azure Key Vault | [Azure — السر المُعرَّف للعميل](/ar/enterprise/features/secrets-manager/azure) | [Azure — Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity) |
|
||||
|
||||
<Note>
|
||||
واجهتا مدير الأسرار و Workload Identity مُوسومتان حالياً بـ **Beta** في CrewAI Platform.
|
||||
</Note>
|
||||
|
||||
## كيف تتلاءم الأجزاء معاً
|
||||
|
||||
إعداد مدير الأسرار هو تدفق من ثلاث خطوات يشمل كلاً من مزود السحابة و CrewAI Platform:
|
||||
|
||||
1. **يُكوِّن المسؤول بيانات اعتماد المزود.** هذا هو العمل من جانب السحابة — ويختلف العمل اعتماداً على المسار (بيانات الاعتماد الثابتة أو Workload Identity) الذي تختاره. تغطي أدلة المزودين هذا من البداية إلى النهاية.
|
||||
2. **يُشير المسؤول (أو عضو مصرَّح له) إلى سر في متغير بيئة.** من صفحة متغيرات البيئة، يختار المستخدم بيانات اعتماد المزود ويُحدّد اسم السر. راجع [استخدام مدير الأسرار](/ar/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables).
|
||||
3. **تتلقى الأتمتة القيمة المحلولة وقت التشغيل.** عندما يعمل طاقم أو أتمتة، تجلب CrewAI Platform السر من مزوّدك وتحقنه كقيمة لمتغير البيئة. مع Workload Identity، يحدث هذا الجلب في كل إطلاق (مراعٍ للتدوير). مع بيانات الاعتماد الثابتة، يحدث الجلب وقت النشر وتُدمج القيمة في صورة النشر.
|
||||
|
||||
## الأذونات
|
||||
|
||||
تتحكم ميزتان في CrewAI Platform بالوصول إلى مدير الأسرار:
|
||||
|
||||
- `secret_providers` — تتحكم بمن يستطيع عرض أو إدارة بيانات اعتماد المزودين.
|
||||
- `environment_variables` — تتحكم بمن يستطيع إنشاء وتحرير متغيرات البيئة (بما فيها تلك التي تُشير إلى أسرار).
|
||||
|
||||
تتحكم ميزة ثالثة بإعداد Workload Identity:
|
||||
|
||||
- `workload_identity_configs` — تتحكم بمن يستطيع عرض أو إدارة تكوينات Workload Identity. مطلوبة فقط إذا كنت تستخدم مسار Workload Identity.
|
||||
|
||||
يتمتع المالكون دائماً بالوصول الكامل. لا يحصل الأعضاء على وصول إلى `secret_providers` أو `workload_identity_configs` افتراضياً ويجب منحهم الإذن عبر دور مخصص. راجع [الأذونات (RBAC)](/ar/enterprise/features/secrets-manager/usage#permissions-rbac) للحصول على المصفوفة الكاملة والتعليمات خطوة بخطوة.
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
اختر مسارك:
|
||||
|
||||
- **بيانات الاعتماد الثابتة** (أبسط، تتطلب إعادة نشر عند التدوير):
|
||||
- [تكوين AWS Secrets Manager](/ar/enterprise/features/secrets-manager/aws)
|
||||
- [تكوين Google Cloud Secret Manager](/ar/enterprise/features/secrets-manager/gcp)
|
||||
- [تكوين Azure Key Vault](/ar/enterprise/features/secrets-manager/azure)
|
||||
- **Workload Identity** (مراعٍ للتدوير، بدون إعادة نشر):
|
||||
- [تكوين AWS Workload Identity](/ar/enterprise/features/secrets-manager/aws-workload-identity)
|
||||
- [تكوين GCP Workload Identity Federation](/ar/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- [تكوين Azure Workload Identity Federation](/ar/enterprise/features/secrets-manager/azure-workload-identity)
|
||||
- ثم: [استخدام الأسرار في متغيرات البيئة وإدارة الأذونات](/ar/enterprise/features/secrets-manager/usage)
|
||||
136
docs/ar/enterprise/features/secrets-manager/usage.mdx
Normal file
136
docs/ar/enterprise/features/secrets-manager/usage.mdx
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: استخدام مدير الأسرار
|
||||
description: إدارة الأذونات والإشارة إلى الأسرار المُدارة من متغيرات البيئة في CrewAI Platform
|
||||
sidebarTitle: الاستخدام والأذونات
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
هذا الدليل محايد تجاه المزود. يفترض أنك (أو مسؤول آخر) قد كوّنت بالفعل بيانات اعتماد واحدة على الأقل لمزود أسرار. اختر دليل الإعداد الخاص بك بناءً على المسار الذي تريده:
|
||||
|
||||
- بيانات الاعتماد الثابتة: [AWS](/ar/enterprise/features/secrets-manager/aws) · [GCP](/ar/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (مراعٍ للتدوير): [AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
|
||||
استخدم هذا الدليل من أجل:
|
||||
|
||||
- منح الأذونات الصحيحة لأعضاء المؤسسة.
|
||||
- الإشارة إلى الأسرار من متغيرات البيئة على أتمتاتك.
|
||||
- التحقق من أن كل شيء يُحَلّ بشكل صحيح وقت التشغيل.
|
||||
|
||||
## الأذونات (RBAC)
|
||||
|
||||
ثلاث ميزات في CrewAI Platform ذات صلة عند العمل مع مدير الأسرار:
|
||||
|
||||
- `secret_providers` — تتحكم بالوصول إلى صفحة **بيانات اعتماد مزود الأسرار**.
|
||||
- `workload_identity_configs` — تتحكم بالوصول إلى صفحة **Workload Identity** (ذات صلة فقط إذا كنت تستخدم مسار WI).
|
||||
- `environment_variables` — تتحكم بمن يستطيع إنشاء أو تحرير متغيرات البيئة.
|
||||
|
||||
لكل ميزة مستويا إجراء: `read` و `manage`. منح `manage` يستلزم تلقائياً `read`.
|
||||
|
||||
### ما يجب منحه
|
||||
|
||||
| الهدف | `secret_providers` | `workload_identity_configs` | `environment_variables` |
|
||||
|---|---|---|---|
|
||||
| استخدام بيانات اعتماد ثابتة موجودة في متغيرات البيئة (بدون تعديل المزود) | `read` | — | `manage` |
|
||||
| إنشاء أو تحرير أو حذف بيانات الاعتماد الثابتة | `manage` | — | `manage` |
|
||||
| استخدام بيانات اعتماد مدعومة بـ Workload Identity موجودة في متغيرات البيئة | `read` | — | `manage` |
|
||||
| إنشاء أو تحرير أو حذف تكوينات Workload Identity (وبيانات الاعتماد التي تشير إليها) | `manage` | `manage` | `manage` |
|
||||
|
||||
<Note>
|
||||
يتمتع **المالكون** تلقائياً بالوصول الكامل إلى كل ميزة. يستبعد دور **العضو** الافتراضي عمداً `secret_providers` و `workload_identity_configs` — يجب على المسؤولين تضمين الأعضاء صراحةً عبر دور مخصص.
|
||||
</Note>
|
||||
|
||||
### كيفية التعيين
|
||||
|
||||
1. في CrewAI Platform، انتقل إلى **Settings** ← **Roles**. من هذه الصفحة يمكنك إنشاء أدوار جديدة وتحرير أذونات كل دور وتعيين الأدوار للأعضاء الحاليين في المؤسسة.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Roles → /images/secrets-manager/usage/06-amp-settings-roles-nav.png */}
|
||||
{/* SCREENSHOT: Roles list page with "Create Role" button visible → /images/secrets-manager/usage/07-amp-roles-list.png */}
|
||||
|
||||
2. انقر على **Create Role** لإنشاء دور جديد، أو افتح دوراً موجوداً لتحرير أذوناته.
|
||||
|
||||
3. في محرر أذونات الدور، بدّل الميزات ذات الصلة وفق الجدول أعلاه:
|
||||
|
||||
- `secret_providers`: اختر **read** إذا كان هذا الدور يحتاج فقط إلى استخدام بيانات الاعتماد الموجودة، أو **manage** إذا كان ينبغي أن يكون قادراً أيضاً على إنشاء بيانات الاعتماد وتحريرها وحذفها.
|
||||
- `environment_variables`: اختر **manage** ليتمكن الدور من إنشاء متغيرات بيئة تُشير إلى الأسرار.
|
||||
|
||||
{/* SCREENSHOT: Role editor showing the secret_providers feature with read/manage toggles → /images/secrets-manager/usage/08-amp-role-editor-secret-providers-toggles.png */}
|
||||
{/* SCREENSHOT: Role editor showing environment_variables toggles → /images/secrets-manager/usage/09-amp-role-editor-env-vars-toggles.png */}
|
||||
|
||||
4. احفظ الدور.
|
||||
|
||||
5. عيّن الدور للأعضاء ذوي الصلة من نفس صفحة Roles (أو قائمة أعضاء المؤسسة).
|
||||
|
||||
{/* SCREENSHOT: Member assignment screen where the new role is applied to a user → /images/secrets-manager/usage/10-amp-assign-role-to-member.png */}
|
||||
|
||||
## الإشارة إلى الأسرار في متغيرات البيئة
|
||||
|
||||
بمجرد وجود بيانات اعتماد للمزود وامتلاك دورك للأذونات الصحيحة، يمكنك الإشارة إلى الأسرار المُدارة من أي متغير بيئة.
|
||||
|
||||
في CrewAI Platform، انتقل إلى **Environment Variables** وانقر على **Add Environment Variables**.
|
||||
|
||||
{/* SCREENSHOT: Environment Variables empty state with "Add" button → /images/secrets-manager/usage/11-amp-env-vars-empty.png */}
|
||||
|
||||
املأ النموذج:
|
||||
|
||||
- **Key** — اسم متغير البيئة. يجب أن يبدأ بحرف أو شرطة سفلية ويحتوي فقط على حروف وأرقام وشرطات سفلية. عادةً بأحرف كبيرة، مثل `OPENAI_API_KEY`.
|
||||
|
||||
- **Value Source** — اختر من أين تأتي القيمة:
|
||||
- **Direct Value** — قيمة نصية صريحة تكتبها. استخدم هذا عندما لا ترغب في إشراك مزود.
|
||||
- **Use AWS default** (أو ما يعادله لمزوّدك) — تستخدم بيانات الاعتماد المُعلَّمة حالياً كافتراضية لذلك النوع من المزود.
|
||||
- **بيانات اعتماد مُسمَّاة محددة** — اختر بيانات الاعتماد بالاسم. استخدم هذا إذا كانت لديك بيانات اعتماد متعددة لنفس المزود (مثلاً `aws-prod` و `aws-staging`) وتريد اختيار واحدة صراحةً.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the "Value Source" dropdown open, showing "AWS default" + named credentials → /images/secrets-manager/usage/12-amp-env-var-form-source-selector.png */}
|
||||
|
||||
- **Secret Name** — اسم السر في مزوّدك. بمجرد اختيار بيانات الاعتماد، يُقدّم هذا الحقل اقتراحاً تلقائياً: ابدأ بالكتابة، وتستعلم CrewAI Platform مزوّدك عن أسماء الأسرار المطابقة.
|
||||
|
||||
استخدم الصيغة `secret-name#json_key` لاستخراج حقل واحد من سر مهيكل (JSON). على سبيل المثال، عند وجود سر `database-credentials` بقيمة `{"username": "...", "password": "..."}`، أَشِر إلى `database-credentials#password` لحقن كلمة المرور فقط.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the secret name autocomplete dropdown showing live results → /images/secrets-manager/usage/13-amp-env-var-form-secret-name-autocomplete.png */}
|
||||
|
||||
<Note>
|
||||
**ملاحظة Azure Key Vault:** لا يمكن أن تحتوي أسماء أسرار Azure على شرطات سفلية. تُحوّل CrewAI Platform تلقائياً الشرطات السفلية في حقل **Secret Name** إلى شرطات عند استدعاء Azure (مثلاً، `db_password` تُرسل كـ `db-password`).
|
||||
</Note>
|
||||
|
||||
انقر على **Create** لحفظ المتغير.
|
||||
|
||||
{/* SCREENSHOT: Env var list with the new variable showing masked value and a "secret" indicator → /images/secrets-manager/usage/14-amp-env-var-created.png */}
|
||||
|
||||
<Tip>
|
||||
عند تحرير متغير بيئة موجود، يحافظ ترك حقل **Value** فارغاً على القيمة الحالية. هذا مقصود — فهو يتيح لك تغيير حقول أخرى (مثل اسم السر أو بيانات الاعتماد) دون إعادة إدخال القيمة.
|
||||
</Tip>
|
||||
|
||||
## التحقق من العمل
|
||||
|
||||
للتحقق من البداية إلى النهاية:
|
||||
|
||||
1. أَشِر إلى متغير البيئة على أتمتة أو طاقم أو عملية نشر تماماً كما تفعل مع أي متغير بيئة آخر.
|
||||
2. انشر الأتمتة.
|
||||
3. أطلق تشغيلاً وتأكد من اكتماله بنجاح.
|
||||
|
||||
### يعتمد سلوك التدوير على مسار بيانات الاعتماد
|
||||
|
||||
| مسار بيانات الاعتماد | متى يُقرأ السر | ما يتطلبه التدوير |
|
||||
|---|---|---|
|
||||
| **بيانات الاعتماد الثابتة** (مفاتيح AWS، ملف JSON لحساب خدمة GCP) | **وقت النشر** — تُدمج القيمة في صورة النشر | إعادة نشر الأتمتة بعد تدوير السر |
|
||||
| **Workload Identity** (اتحاد OIDC، AWS أو GCP) | **في كل إطلاق أتمتة** — تُجلب القيمة طازجة من سحابتك | لا شيء — يرى الإطلاق التالي بعد التدوير القيمة الجديدة |
|
||||
|
||||
<Note>
|
||||
**إذا كنت تحتاج أسراراً مراعية للتدوير** (بدون إعادة نشر عند التدوير)، استخدم مسار Workload Identity: [AWS WI](/ar/enterprise/features/secrets-manager/aws-workload-identity) أو [GCP WI](/ar/enterprise/features/secrets-manager/gcp-workload-identity). المقايضة هي مزيد من جهد الإعداد مقدماً (تسجيل CrewAI Platform كمزود OIDC في سحابتك) ولكن عمليات أبسط على المدى الطويل.
|
||||
</Note>
|
||||
|
||||
إذا فشل النشر أو التشغيل بخطأ متعلق بسرك، تحقق من الأسباب الأكثر شيوعاً:
|
||||
|
||||
| العَرَض | السبب المحتمل |
|
||||
|---|---|
|
||||
| `no credential found` | يُشير متغير البيئة إلى مزود ولكن لم تُحدَّد بيانات اعتماد بعينها، ولا توجد بيانات اعتماد افتراضية مُعيّنة لذلك النوع من المزود. إما اختر بيانات اعتماد صراحةً على المتغير، أو علِّم بيانات اعتماد كافتراضية على صفحة **Secret Provider Credentials**. |
|
||||
| `secret not found` | خطأ مطبعي في **Secret Name**، أو أن السر غير موجود في حساب/منطقة المزود التي تشير إليها بيانات الاعتماد. تحقق من كليهما. |
|
||||
| تعمل الأتمتة بالقيمة القديمة بعد التدوير (مسار بيانات الاعتماد الثابتة) | القيمة السابقة مدمجة في صورة حاوية النشر. أعد نشر الأتمتة لاستيعاب القيمة المُدوَّرة. لتجنّب ذلك تماماً، حوّل بيانات الاعتماد إلى مسار Workload Identity. |
|
||||
| تعمل الأتمتة بالقيمة القديمة بعد التدوير (مسار Workload Identity) | تأكد من أن متغير البيئة يُشير إلى بيانات اعتماد مدعومة بـ WI (وليس مفاتيح ثابتة). مع WI، ينبغي أن يرى الإطلاق التالي بعد التدوير القيمة الجديدة. إن لم يحدث ذلك، تحقق من أن السر قد تم تحديثه فعلاً في سحابتك (مثلاً، `aws secretsmanager get-secret-value`). |
|
||||
| `JSON key not found` | عند استخدام `secret-name#json_key`، يجب أن يكون السر الأساسي كائن JSON صالحاً يحتوي على ذلك المفتاح. تحقق بقراءة السر مباشرة في مزوّدك. |
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [العودة إلى نظرة عامة على مدير الأسرار](/ar/enterprise/features/secrets-manager/overview)
|
||||
- بيانات الاعتماد الثابتة: [AWS](/ar/enterprise/features/secrets-manager/aws) · [GCP](/ar/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (مراعٍ للتدوير): [AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
260
docs/ar/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
260
docs/ar/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: التحقق من التدوير
|
||||
description: مثال طاقم مستقل يُثبت أن تدوير الأسرار ينتشر إلى عمليات النشر الجارية دون إعادة نشر.
|
||||
sidebarTitle: التحقق من التدوير
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
يوضّح لك هذا الدليل كيفية التحقق من أن **السر المُدوَّر في مزود السحابة لديك يُلتقط في أول إطلاق أتمتة لاحق** — بدون إعادة نشر ولا إعادة تشغيل عامل. هذا ذو صلة فقط عندما تكون قد كوّنت بيانات اعتماد مدعومة بـ Workload Identity ([AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity)، [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)، [Azure](/ar/enterprise/features/secrets-manager/azure-workload-identity)). تتطلب عمليات نشر بيانات الاعتماد الثابتة إعادة نشر بعد التدوير؛ ليس هناك ما يجب التحقق منه هنا.
|
||||
|
||||
تستخدم الوصفة أدناه طاقماً صغيراً مستقلاً بأداة واحدة ووكيل واحد ومهمة واحدة. لا يُشير موجه الطاقم أبداً إلى قيمة السر — بدلاً من ذلك، تقرأ أداة القيمة من `os.environ` وتُفيد ببصمة SHA-256 لما تراه. دوّر السر في مزود السحابة، أطلق مرة أخرى، وتتغير البصمة.
|
||||
|
||||
<Note>
|
||||
لماذا بصمة وليس القيمة الخام؟ وضع الأسرار الخام في إخراج LLM وسجلات التتبع هو متجه تسرب. البصمة كافية لتأكيد "أن القيمة تغيّرت" دون كتابة القيمة الفعلية في أي مكان يمكن رصده.
|
||||
</Note>
|
||||
|
||||
## المتطلبات المسبقة
|
||||
|
||||
قبل تشغيل هذا التحقق:
|
||||
|
||||
- بيانات اعتماد مزود أسرار مدعومة بـ WI مكوَّنة ([AWS](/ar/enterprise/features/secrets-manager/aws-workload-identity)، [GCP](/ar/enterprise/features/secrets-manager/gcp-workload-identity)، [Azure](/ar/enterprise/features/secrets-manager/azure-workload-identity)).
|
||||
- متغير بيئة على عملية النشر بـ `Secret = true`، المفتاح `API_KEY` (أو أي اسم تفضّله — اضبط الأداة أدناه لتطابقه)، يُشير إلى سر في مزود السحابة.
|
||||
- طريقة لتحديث قيمة السر في مزود السحابة (وصول CLI أو وحدة تحكم السحابة).
|
||||
- طريقة لإطلاق عملية النشر عبر HTTP (curl أو Postman أو علامة التبويب **Run** في CrewAI Platform).
|
||||
|
||||
## الخطوة 1 — هيكلة طاقم التحقق
|
||||
|
||||
أنشئ مشروع طاقم جديد. يُهيكل CrewAI CLI البنية:
|
||||
|
||||
```bash
|
||||
crewai create crew rotation_verifier --skip_provider
|
||||
cd rotation_verifier
|
||||
```
|
||||
|
||||
## الخطوة 2 — إضافة أداة صدى بيانات الاعتماد
|
||||
|
||||
استبدل `src/rotation_verifier/tools/custom_tool.py` بأداة تقرأ متغير البيئة المدعوم بسر وتُعيد بصمة:
|
||||
|
||||
```python src/rotation_verifier/tools/credential_echo_tool.py
|
||||
"""Tool that verifies a runtime-injected secret without leaking the value.
|
||||
|
||||
Reads the secret-backed env var (populated by the workload-identity
|
||||
secrets manager at kickoff time) and returns a stable fingerprint. Never
|
||||
echo raw credential values into LLM output or logs in production code —
|
||||
the fingerprint alone is sufficient to confirm rotation worked.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
# Match the deployment environment variable's `key` field.
|
||||
ENV_VAR_NAME = "API_KEY"
|
||||
|
||||
|
||||
class CredentialEchoTool(BaseTool):
|
||||
name: str = "credential_echo"
|
||||
description: str = (
|
||||
"Read the API credential from the worker's environment and return a "
|
||||
"fingerprint summary. Use this exactly once when asked to verify the "
|
||||
"current credential. Takes no arguments."
|
||||
)
|
||||
|
||||
def _run(self) -> str:
|
||||
value = os.environ.get(ENV_VAR_NAME)
|
||||
if not value:
|
||||
return (
|
||||
f"ERROR: {ENV_VAR_NAME} env var is not set. The workload-"
|
||||
"identity secret fetch did not run, or the deployment is "
|
||||
"missing the secret-backed env var."
|
||||
)
|
||||
fingerprint = hashlib.sha256(value.encode()).hexdigest()[:12]
|
||||
return f"Authenticated. credential.fingerprint=sha256:{fingerprint}"
|
||||
```
|
||||
|
||||
## الخطوة 3 — استبدال تكوينات الوكيل والمهمة الافتراضية
|
||||
|
||||
يضم الطاقم وكيلاً واحداً ومهمة واحدة — كلاهما بأوصاف **لا تذكر أبداً** قيمة السر، لذا تبقى مفاتيح المهام مستقرة عبر عمليات التدوير.
|
||||
|
||||
```yaml src/rotation_verifier/config/agents.yaml
|
||||
credential_checker:
|
||||
role: >
|
||||
Credential Verifier
|
||||
goal: >
|
||||
Confirm that the workload-identity-backed secret reached this worker
|
||||
process and report a fingerprint of the current value.
|
||||
backstory: >
|
||||
You are a no-nonsense reliability engineer responsible for verifying
|
||||
that secrets fetched at runtime via workload identity are present
|
||||
and fresh. You always use the credential_echo tool exactly once and
|
||||
report the result verbatim — you never make up values.
|
||||
```
|
||||
|
||||
```yaml src/rotation_verifier/config/tasks.yaml
|
||||
verify_credential_task:
|
||||
description: >
|
||||
Use the credential_echo tool to read the runtime-injected credential
|
||||
and produce a one-line confirmation. The current year is {current_year}
|
||||
(use it only in the timestamp; do not transform the credential output).
|
||||
expected_output: >
|
||||
A single line in the form:
|
||||
"[{current_year}] <credential_echo tool's exact output>"
|
||||
agent: credential_checker
|
||||
```
|
||||
|
||||
## الخطوة 4 — توصيل فئة الطاقم
|
||||
|
||||
```python src/rotation_verifier/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
from rotation_verifier.tools.credential_echo_tool import CredentialEchoTool
|
||||
|
||||
|
||||
@CrewBase
|
||||
class RotationVerifierCrew():
|
||||
"""Single-task crew that verifies a workload-identity-backed secret
|
||||
was successfully fetched at runtime.
|
||||
|
||||
Rotate the underlying secret in the cloud provider, kickoff again, and
|
||||
the credential fingerprint in the agent's report changes — without any
|
||||
re-deploy, worker restart, or input change. The crew prompt itself
|
||||
never references the secret value.
|
||||
"""
|
||||
|
||||
agents: list[BaseAgent]
|
||||
tasks: list[Task]
|
||||
|
||||
@agent
|
||||
def credential_checker(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config["credential_checker"],
|
||||
tools=[CredentialEchoTool()],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
@task
|
||||
def verify_credential_task(self) -> Task:
|
||||
return Task(config=self.tasks_config["verify_credential_task"])
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
)
|
||||
```
|
||||
|
||||
## الخطوة 5 — نشر الطاقم وتكوين متغير بيئة السر
|
||||
|
||||
انشر هذا الطاقم على CrewAI Platform تماماً كما تنشر أي طاقم آخر. ثم على صفحة **Environment Variables** الخاصة بعملية النشر:
|
||||
|
||||
- **Key:** `API_KEY` (يجب أن يطابق `ENV_VAR_NAME` في الأداة)
|
||||
- **Value Source:** بيانات الاعتماد المدعومة بـ WI التي أعدّتها في [AWS WI](/ar/enterprise/features/secrets-manager/aws-workload-identity) أو [GCP WI](/ar/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- **Secret Name:** اسم السر في Secret Manager الخاص بمزود السحابة لديك
|
||||
|
||||
{/* SCREENSHOT: Environment Variables form with key=API_KEY, secret-backed value source selected, secret name filled → /images/secrets-manager/verify-rotation/01-env-var-form.png */}
|
||||
|
||||
## الخطوة 6 — تشغيل الإطلاق الأول
|
||||
|
||||
استبدل `<DEPLOYMENT_AUTH_TOKEN>` و `<DEPLOYMENT_HOST>` بالقيم من علامة التبويب **Run** الخاصة بعملية النشر.
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
عندما يكتمل الإطلاق (بضع ثوان)، تحقق من إخراج الوكيل. سترى:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:004421b993c9
|
||||
```
|
||||
|
||||
سجّل البصمة. هذا التجزئة مرتبط بشكل فريد بأي قيمة سر موجودة حالياً في مزود السحابة لديك.
|
||||
|
||||
## الخطوة 7 — تدوير السر في مزود السحابة
|
||||
|
||||
<Tabs>
|
||||
<Tab title="AWS">
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id <SECRET_NAME> \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="GCP">
|
||||
أضف إصداراً جديداً (يقرأ Secret Manager دائماً `latest`):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add <SECRET_NAME> \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Azure">
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name <SECRET_NAME> \
|
||||
--value "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## الخطوة 8 — تشغيل إطلاق ثانٍ والمقارنة
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
يُظهر إخراج الوكيل الآن **بصمة مختلفة**:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:e2fc89848f72
|
||||
```
|
||||
|
||||
يُثبت هذا أن التدوير التُقط بواسطة عملية النشر الجارية دون إعادة نشر ولا إعادة تشغيل عامل ولا أي إجراء آخر من قِبل المشغّل.
|
||||
|
||||
## ما يتحقق منه هذا — وما لا يتحقق منه
|
||||
|
||||
**يتحقق من:**
|
||||
- يعمل إصدار رمز OIDC الخاص بـ WI من CrewAI Platform.
|
||||
- تقبل الثقة من جانب السحابة (مزود IAM OIDC لـ AWS، Workload Identity Pool لـ GCP، Federated Identity Credential لـ Azure) الرمز.
|
||||
- تمتلك الهوية من جانب السحابة (IAM Role / حساب خدمة GCP / Entra App Registration) وصولاً لقراءة السر.
|
||||
- تصل قيمة السر إلى `os.environ` لعملية العامل وقت الإطلاق.
|
||||
- تنتشر عمليات التدوير اللاحقة إلى الإطلاق التالي.
|
||||
|
||||
**لا يتحقق من:**
|
||||
- أن طواقم الإنتاج الفعلية لديك تتعامل مع التدوير بسلاسة — مثلاً، المهام طويلة الأمد التي تقرأ متغير البيئة مرة واحدة عند البدء ستستمر في استخدام القيمة القديمة حتى تنتهي المهمة. خطّط وفقاً لذلك: اقرأ الأسرار عند نقطة الاستخدام، وليس عند استيراد الوحدة.
|
||||
|
||||
## لماذا لا نُشير إلى السر مباشرةً في الموجه؟
|
||||
|
||||
سيضع عرض توضيحي يبدو أبسط قيمة السر مباشرةً في وصف مهمة (مثلاً، "البحث عن `{api_key}`") ويتفحص الموجه. **لا تفعل ذلك.** لسببين:
|
||||
|
||||
1. **يُسرّب السر إلى تتبعات استدعاء LLM والسجلات من جانب المزود.** يمكن لأي شخص لديه وصول للتتبعات قراءته.
|
||||
2. **يُغيّر وصف المهمة في كل إطلاق.** تُحدّد CrewAI Platform المهام بتجزئة MD5 للوصف؛ القيمة المُدوَّرة تعني أن التجزئة تتغير لكل إطلاق، مما يكسر ربط المهمة من وقت النشر إلى وقت التشغيل. العَرَض: تُسجَّل سجلات المهام كـ `pending_run` إلى الأبد، أو تُسجَّل بعض مهام طاقم متعدد المهام فقط.
|
||||
|
||||
يتجاوز النمط القائم على الأداة في هذا الدليل كلتا المشكلتين: الموجه ثابت، تقرأ الأداة متغير البيئة وقت التشغيل، وتصل فقط بصمة القيمة إلى LLM.
|
||||
|
||||
## الخطوات التالية
|
||||
|
||||
- [العودة إلى نظرة عامة على مدير الأسرار](/ar/enterprise/features/secrets-manager/overview)
|
||||
- بمجرد التحقق، أَسقط طاقم التحقق. يجب أن تتبع الطواقم الفعلية النمط نفسه: الوصول إلى الأسرار عبر `os.environ` داخل أداة، وعدم استبدالها أبداً في الموجهات.
|
||||
@@ -146,7 +146,6 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="الإجابة على الأسئلة">
|
||||
أجب على أسئلة التوضيح من مساعد الطاقم لتنقيح
|
||||
متطلباتك.
|
||||
@@ -161,12 +160,10 @@ Crew Studio هو طريقة مبتكرة لإنشاء طواقم وكلاء ال
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="الموافقة أو التعديل">
|
||||
وافق على الخطة أو اطلب تغييرات إذا لزم الأمر.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="التنزيل أو النشر">
|
||||
نزّل الكود للتخصيص أو انشر مباشرة على المنصة.
|
||||
</Step>
|
||||
|
||||
102
docs/ar/guides/flows/inputs-id-deprecation.mdx
Normal file
102
docs/ar/guides/flows/inputs-id-deprecation.mdx
Normal file
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: "الانتقال من inputs.id إلى restore_from_state_id"
|
||||
description: "نقل تدفقات @persist من ترطيب inputs.id المهجور إلى حقل restore_from_state_id المدعوم"
|
||||
icon: "arrow-right-arrow-left"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
تمرير `id` داخل `inputs` لترطيب تدفق `@persist` هو **مهجور** ومقرر إزالته في إصدار مستقبلي. البديل، `restore_from_state_id`، متاح في CrewAI **v1.14.5 وما بعده** — الخطوات أدناه تنطبق بمجرد أن تقوم بالتحديث.
|
||||
</Warning>
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
الطريقة الموثقة لترطيب تدفق `@persist` من تنفيذ سابق هي تمرير UUID لذلك التنفيذ كـ `inputs.id`. الآن، تكشف CrewAI عن حقل مخصص، `restore_from_state_id`، الذي يقوم بنفس الترطيب دون تحميل حمولة `inputs` — ودون ربط مفتاح الترطيب بهوية التنفيذ الجديد.
|
||||
|
||||
## الانتقال
|
||||
|
||||
إذا كنت حالياً تبدأ تدفق `@persist` باستخدام `inputs={"id": ...}`:
|
||||
|
||||
```python
|
||||
# مهجور
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(inputs={"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv"})
|
||||
```
|
||||
|
||||
انتقل إلى `restore_from_state_id`:
|
||||
|
||||
```python
|
||||
# مدعوم
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(restore_from_state_id="abcd1234-5678-90ef-ghij-klmnopqrstuv")
|
||||
```
|
||||
|
||||
تتمتع الوضعيتان بمعاني سلالة مختلفة:
|
||||
|
||||
- `inputs={"id": <uuid>}` (مهجور) — **استئناف**: تكتب الكتابات تحت المعرف المقدم، مما يمدد نفس تاريخ `flow_uuid`.
|
||||
- `restore_from_state_id=<uuid>` — **تفرع**: يترطب الحالة من اللقطة، ثم يكتب تحت `state.id` جديدة. يتم الحفاظ على تاريخ التدفق المصدر.
|
||||
|
||||
لأغلب سيناريوهات الإنتاج — إعادة تشغيل تدفق تم تهيئته من حالة سابقة — فإن التفرع هو ما تريده. راجع [إتقان حالة التدفق](/ar/guides/flows/mastering-flow-state) للحصول على النموذج الذهني الكامل.
|
||||
|
||||
إذا كنت تبدأ تدفقك عبر واجهة برمجة تطبيقات CrewAI AMP REST، راجع [AMP](#amp) أدناه لهجرة الحمولة المعادلة.
|
||||
|
||||
## لماذا نقوم بإهمال `inputs.id` لـ `@persist`
|
||||
|
||||
`inputs.id` هو حالياً الطريقة الموثقة لاستئناف تدفق `@persist` من تنفيذ سابق. المشكلة هي أن نفس UUID يقوم بوظيفتين في وقت واحد:
|
||||
|
||||
1. **يحدد أي لقطة يترطب منها `@persist`** — تحميل الحالة المحفوظة تحت ذلك UUID.
|
||||
2. **يصبح معرف تنفيذ التدفق الجديد** (`state.id` في SDK؛ يظهر كـ `flow_id` في بعض السياقات) — كل كتابة `@persist` من هذه البداية أيضاً تقع تحت نفس UUID.
|
||||
|
||||
هذه الوظيفة المزدوجة هي السبب الجذري للمشاكل التي يصفها هذا الدليل. لأن UUID المقدم هو أيضاً معرف التنفيذ الجديد، فإن بدايتين تمرران نفس `inputs.id` ليست تنفيذين متميزين — إنهما تشتركان في معرف، وتشاركان في سجل الاستمرارية، و(على AMP) تشتركان في صف في قائمة التنفيذات. لا توجد طريقة للقول "ترطب من هذه اللقطة، ولكن سجل هذا التشغيل بشكل منفصل" دون تقسيم المسؤوليتين.
|
||||
|
||||
`restore_from_state_id` هو هذا الانقسام. إنه يخبر `@persist` من أي لقطة يترطب، بينما يترك التنفيذ الجديد حراً لاستلام `state.id` جديدة. لم يعد مصدر الترطيب والتشغيل المسجل نفس UUID — وهو ما تريده معظم سيناريوهات الإنتاج فعلياً.
|
||||
|
||||
## جدول إزالة
|
||||
|
||||
من المقرر إزالة `inputs.id` لترطيب `@persist` في إصدار مستقبلي من CrewAI. لا يوجد قطع صارم فوري — تظل التدفقات الحالية تعمل — ولكن بمجرد أن تقوم بالتحديث إلى v1.14.5 أو ما بعده، يجب أن يستخدم الكود الجديد `restore_from_state_id`، ويجب أن تهاجر التدفقات الحالية في الفرصة المناسبة التالية.
|
||||
|
||||
## AMP
|
||||
|
||||
إذا كنت تنشر تدفقك إلى CrewAI AMP، فإن الهجرة تمتد إلى الحمولة التي تبدأ بها المرسلة إلى طاقمك المنشور، وتظهر الأعراض المرئية لإعادة استخدام `inputs.id` على لوحة معلومات النشر. تغطي القسمان الفرعيان أدناه كلاهما.
|
||||
|
||||
### هجرة حمولة البداية
|
||||
|
||||
إذا كنت حالياً تبدأ تدفقاً منشوراً عن طريق تضمين `id` في `inputs`:
|
||||
|
||||
```bash
|
||||
# مهجور
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{"inputs": {"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv", "topic": "AI Agent Frameworks"}}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
نقل UUID إلى حقل `restoreFromStateId` في المستوى الأعلى:
|
||||
|
||||
```bash
|
||||
# مدعوم
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{
|
||||
"inputs": {"topic": "AI Agent Frameworks"},
|
||||
"restoreFromStateId": "abcd1234-5678-90ef-ghij-klmnopqrstuv"
|
||||
}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
يجلس `restoreFromStateId` بجانب `inputs` في حمولة البداية، وليس داخلها. الآن، يحمل كائن `inputs` فقط القيم التي تستهلكها تدفقك فعلياً.
|
||||
|
||||
### ماذا يحدث عند إعادة استخدام `inputs.id`
|
||||
|
||||
عندما تتلقى AMP بداية لتدفق يتطابق `inputs.id` الخاص به مع تنفيذ موجود، فإنه يحل إلى السجل الموجود بدلاً من إنشاء سجل جديد. من لوحة معلومات النشر سترى:
|
||||
|
||||
- **حالة التنفيذ** — حالة التشغيل الجديد تحل محل حالة التشغيل السابق. يمكن أن تعود تنفيذات مكتملة إلى `جارية`، أو يمكن أن تتحول تشغيلات `مكتملة` إلى `خطأ` إذا فشلت البداية الجديدة — في كلتا الحالتين، لم تعد لوحة المعلومات تعكس التشغيل الأصلي.
|
||||
- **التتبع** — تتراكم تتبعات OTel عبر البدايات لأنها تشترك في نفس معرف التنفيذ؛ تتبعات التشغيل السابق إما تُستبدل بـ، أو تُخلط مع، تشغيل الجديد. لم يعد إعادة التشغيل خطوة بخطوة يتوافق مع تنفيذ واحد.
|
||||
- **قائمة التنفيذات** — البدايات التي يجب أن تظهر كصفوف منفصلة تتقلص إلى إدخال واحد، مما يخفي التاريخ.
|
||||
|
||||
تساعد الهجرة إلى `restoreFromStateId` في الحفاظ على كل بداية كتنفيذ خاص بها — مع حالتها الخاصة، وتتبعها، وصفها في القائمة — بينما لا تزال ترطب الحالة من تشغيل سابق.
|
||||
|
||||
<Card title="هل تحتاج مساعدة؟" icon="headset" href="mailto:support@crewai.com">
|
||||
اتصل بفريق الدعم لدينا إذا لم تكن متأكداً من أي وضع يحتاجه تدفقك أو واجهت مشاكل أثناء الهجرة.
|
||||
</Card>
|
||||
190
docs/ar/guides/migration/upgrading-crewai.mdx
Normal file
190
docs/ar/guides/migration/upgrading-crewai.mdx
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: "ترقية CrewAI"
|
||||
description: "كيفية ترقية CrewAI في مشروعك والتكيّف مع التغييرات الجذرية بين الإصدارات."
|
||||
icon: "arrow-up-circle"
|
||||
---
|
||||
|
||||
## نظرة عامة
|
||||
|
||||
تجلب إصدارات CrewAI قدرات جديدة بانتظام. يرشدك هذا الدليل خلال الخطوات العملية للحفاظ على تثبيتك محدّثًا — سواء أداة سطر الأوامر أو البيئة الافتراضية لمشروعك.
|
||||
|
||||
إذا كنت تبدأ من الصفر، راجع [التثبيت](/ar/installation). إذا كنت قادمًا من إطار عمل آخر، راجع [الترحيل من LangGraph](/ar/guides/migration/migrating-from-langgraph).
|
||||
|
||||
---
|
||||
|
||||
## الشيئان اللذان قد ترغب في ترقيتهما
|
||||
|
||||
يوجد CrewAI في مكانين على جهازك، ويتم ترقيتهما بشكل مستقل:
|
||||
|
||||
| ماذا | كيف يُثبَّت | كيف تتم الترقية |
|
||||
|---|---|---|
|
||||
| **أداة سطر الأوامر العامة `crewai`** | `uv tool install crewai` | `uv tool install crewai --upgrade` |
|
||||
| **بيئة venv للمشروع** (حيث يعمل الكود) | `crewai install` / `uv sync` | `uv add "crewai[...]>=X.Y.Z"` ثم `crewai install` |
|
||||
|
||||
يمكن لهما — وغالبًا ما يحدث — أن يخرجا عن التزامن. تشغيل `crewai --version` يُظهر إصدار سطر الأوامر. تشغيل `uv pip show crewai` داخل مشروعك يُظهر إصدار venv. إذا اختلفا، فهذا طبيعي؛ ما يهم بالنسبة للكود قيد التشغيل هو إصدار venv.
|
||||
|
||||
## لماذا لا يقوم `crewai install` وحده بالترقية
|
||||
|
||||
`crewai install` هو غلاف رفيع حول `uv sync`. يُثبّت بالضبط ما يقوله ملف `uv.lock` الحالي — وهو **لا** يرفع أي قيود إصدار.
|
||||
|
||||
إذا كان `pyproject.toml` يقول `crewai>=1.11.1` وقد قام ملف القفل بحلّه إلى `1.11.1`، فإن تشغيل `crewai install` سيُبقيك على `1.11.1` للأبد، حتى وإن كان الإصدار `1.14.4` متاحًا.
|
||||
|
||||
للترقية فعلًا، عليك:
|
||||
|
||||
1. تحديث قيد الإصدار في `pyproject.toml`
|
||||
2. إعادة حلّ ملف القفل
|
||||
3. مزامنة venv
|
||||
|
||||
`uv add` يقوم بالثلاثة في خطوة واحدة.
|
||||
|
||||
## كيفية ترقية مشروعك
|
||||
|
||||
```bash
|
||||
# يرفع القيد ويعيد القفل في أمر واحد
|
||||
uv add "crewai[tools]>=1.14.4"
|
||||
|
||||
# يزامن venv (crewai install يستدعي uv sync تحت الغطاء)
|
||||
crewai install
|
||||
|
||||
# تحقّق
|
||||
uv pip show crewai
|
||||
# → Version: 1.14.4
|
||||
```
|
||||
|
||||
استبدل `[tools]` بأي إضافات يستخدمها مشروعك (مثلًا `[tools,anthropic]`). تحقّق من قائمة `dependencies` في `pyproject.toml` إن لم تكن متأكدًا.
|
||||
|
||||
<Note>
|
||||
يحدّث `uv add` كلا من `pyproject.toml` **و** `uv.lock` بشكل ذرّي. إذا قمت بتحرير `pyproject.toml` يدويًا، فإنك لا تزال بحاجة إلى تشغيل `uv lock --upgrade-package crewai` لإعادة حلّ ملف القفل قبل أن يلتقط `crewai install` الإصدار الجديد.
|
||||
</Note>
|
||||
|
||||
## ترقية أداة سطر الأوامر العامة
|
||||
|
||||
أداة سطر الأوامر العامة منفصلة عن مشروعك. قم بترقيتها عبر:
|
||||
|
||||
```bash
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
|
||||
إذا حذّرك الـ shell بشأن `PATH` بعد الترقية، قم بتحديثه:
|
||||
|
||||
```bash
|
||||
uv tool update-shell
|
||||
```
|
||||
|
||||
هذا **لا** يمسّ بيئة venv الخاصة بمشروعك — لا تزال بحاجة إلى `uv add` + `crewai install` داخل المشروع.
|
||||
|
||||
## التحقق من تزامن الاثنين
|
||||
|
||||
```bash
|
||||
# إصدار سطر الأوامر العام
|
||||
crewai --version
|
||||
|
||||
# إصدار venv للمشروع
|
||||
uv pip show crewai | grep Version
|
||||
```
|
||||
|
||||
ليس من الضروري أن يتطابقا — لكن إصدار venv للمشروع هو ما يهم لسلوك التشغيل.
|
||||
|
||||
<Note>
|
||||
يتطلب CrewAI `Python >=3.10, <3.14`. إذا كان `uv` مثبَّتًا مقابل مفسّر أقدم، فأعد إنشاء venv للمشروع باستخدام إصدار Python مدعوم قبل تشغيل `crewai install`.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## التغييرات الجذرية وملاحظات الترحيل
|
||||
|
||||
تتطلب معظم الترقيات تعديلات صغيرة فقط. المناطق أدناه هي تلك التي تنكسر بصمت أو بتتبعات مكدّس مربكة.
|
||||
|
||||
### مسارات الاستيراد: tools و`BaseTool`
|
||||
|
||||
الموقع الرسمي لاستيراد الـ tools هو `crewai.tools`. لا تزال المسارات القديمة تظهر في الدروس لكن يجب تحديثها.
|
||||
|
||||
```python
|
||||
# قبل
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.agents.tools import tool
|
||||
|
||||
# بعد
|
||||
from crewai.tools import BaseTool, tool
|
||||
```
|
||||
|
||||
كلٌ من المُزخرف `@tool` والفئة الفرعية `BaseTool` يقعان في `crewai.tools`. `AgentFinish` والرموز الأخرى الداخلية للوكيل لم تعد جزءًا من السطح العام — إذا كنت تستوردها، فانتقل إلى event listeners أو callbacks الـ `Task` بدلًا منها.
|
||||
|
||||
### تغييرات معاملات `Agent`
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find authoritative sources on {topic}",
|
||||
backstory="You are a careful, source-driven researcher.",
|
||||
llm="gpt-4o-mini", # اسم نموذج كسلسلة نصية أو كائن LLM
|
||||
verbose=True, # bool وليس مستوى عددي صحيح
|
||||
max_iter=15, # تغيّر الافتراضي بين الإصدارات — حدّده بشكل صريح
|
||||
allow_delegation=False,
|
||||
)
|
||||
```
|
||||
|
||||
- يقبل `llm` إما اسم نموذج كسلسلة نصية (يُحلَّ عبر المزوّد المهيّأ) أو كائن `LLM` للتحكم الدقيق.
|
||||
- `verbose` هو `bool` بسيط. تمرير عدد صحيح لم يعد يبدّل مستويات السجل.
|
||||
- تغيّرت افتراضات `max_iter` بين الإصدارات. إذا توقف وكيلك بصمت عن التكرار بعد أول استدعاء tool، فحدّد `max_iter` صراحةً.
|
||||
|
||||
### معاملات `Crew`
|
||||
|
||||
```python
|
||||
from crewai import Crew, Process
|
||||
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
process=Process.sequential, # أو Process.hierarchical
|
||||
memory=True,
|
||||
cache=True,
|
||||
embedder={"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
)
|
||||
```
|
||||
|
||||
- يتطلب `process=Process.hierarchical` إما `manager_llm=` أو `manager_agent=`. بدون أحدهما، يرفع kickoff خطأً عند التحقّق.
|
||||
- `memory=True` مع مزوّد embedding غير افتراضي يحتاج إلى قاموس `embedder` — راجع [إعداد الذاكرة وembedder](#memory-embedder-config) أدناه.
|
||||
|
||||
### الإخراج المُهيكل لـ `Task`
|
||||
|
||||
استخدم `output_pydantic` أو `output_json` أو `output_file` لإلزام نتيجة المهمة بشكل مكتوب الأنواع:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from crewai import Task
|
||||
|
||||
class Article(BaseModel):
|
||||
title: str
|
||||
body: str
|
||||
|
||||
write = Task(
|
||||
description="Write an article about {topic}",
|
||||
expected_output="A short article with a title and body",
|
||||
agent=writer,
|
||||
output_pydantic=Article, # الفئة، وليس مثيلًا منها
|
||||
output_file="output/article.md",
|
||||
)
|
||||
```
|
||||
|
||||
`output_pydantic` يأخذ **الفئة** نفسها. تمرير `Article(title="", body="")` خطأ شائع ويفشل بخطأ تحقّق مربك.
|
||||
|
||||
### إعداد الذاكرة وembedder {#memory-embedder-config}
|
||||
|
||||
إذا كان `memory=True` وأنت لا تستخدم embeddings الافتراضية الخاصة بـ OpenAI، فيجب أن تمرّر `embedder`:
|
||||
|
||||
```python
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
memory=True,
|
||||
embedder={
|
||||
"provider": "ollama",
|
||||
"config": {"model": "nomic-embed-text"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
ضع بيانات اعتماد المزوّد المعنيّة (`OPENAI_API_KEY`, `OLLAMA_HOST`, إلخ) في ملف `.env`. مسارات تخزين الذاكرة محلية بالنسبة للمشروع افتراضيًا — احذف مجلد ذاكرة المشروع إذا غيّرت embedders، لأن الأبعاد لا تختلط.
|
||||
@@ -802,7 +802,6 @@ The tables below show a representative sample of current top-performing models a
|
||||
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Identify Specialized Needs">
|
||||
Determine if your crew has specific requirements (coding, reasoning, speed)
|
||||
that would benefit from specialized models like **Claude 4 Sonnet** for
|
||||
@@ -810,7 +809,6 @@ The tables below show a representative sample of current top-performing models a
|
||||
consider fast inference providers like **Groq** alongside model selection.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Implement Multi-Model Strategy">
|
||||
Use different models for different agents based on their roles.
|
||||
High-capability models for managers and complex tasks, efficient models for
|
||||
|
||||
@@ -13,7 +13,7 @@ The Daytona sandbox tools give CrewAI agents access to isolated, ephemeral compu
|
||||
|
||||
- **`DaytonaExecTool`** — run any shell command inside a sandbox.
|
||||
- **`DaytonaPythonTool`** — execute a block of Python source code inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox; also supports `move`, `find` (content grep), `search` (filename glob), `chmod` (permissions), `replace` (bulk find-and-replace), and `exists`.
|
||||
|
||||
All three tools share the same sandbox lifecycle controls, so you can mix and match them while keeping state in a single persistent sandbox.
|
||||
|
||||
@@ -55,7 +55,7 @@ from crewai_tools import DaytonaPythonTool
|
||||
tool = DaytonaPythonTool()
|
||||
result = tool.run(code="print(sum(range(10)))")
|
||||
print(result)
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": None}
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": ExecutionArtifacts(stdout="45\n", charts=[])}
|
||||
```
|
||||
|
||||
### Multi-step shell session (persistent)
|
||||
@@ -63,17 +63,22 @@ print(result)
|
||||
```python Code
|
||||
from crewai_tools import DaytonaExecTool, DaytonaFileTool
|
||||
|
||||
# Create the persistent sandbox via the first tool, then attach the second
|
||||
# tool to it so both share state (installed packages, files, env vars).
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Install a package, then write and run a script — all in the same sandbox
|
||||
exec_tool.run(command="pip install httpx -q")
|
||||
file_tool.run(action="write", path="/workspace/fetch.py", content="import httpx; print(httpx.get('https://httpbin.org/get').status_code)")
|
||||
exec_tool.run(command="python /workspace/fetch.py")
|
||||
file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
|
||||
file_tool.run(
|
||||
action="write",
|
||||
path="workspace/script.py",
|
||||
content="import httpx; print(f'httpx loaded, version {httpx.__version__}')",
|
||||
)
|
||||
exec_tool.run(command="python workspace/script.py")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Each tool instance maintains its own persistent sandbox. To share **one** sandbox across two tools, create the first tool, grab its sandbox id via `tool._persistent_sandbox.id`, and pass it to the second tool via `sandbox_id=...`.
|
||||
By default, each tool with `persistent=True` lazily creates its **own** sandbox on first use. The pattern above shares a single sandbox across multiple tools by reading the first tool's `active_sandbox_id` after a `.run()` call and passing it to the others via `sandbox_id=...`. With `persistent=False` (the default), every `.run()` call gets a fresh sandbox that's deleted at the end of that call.
|
||||
</Note>
|
||||
|
||||
### Attach to an existing sandbox
|
||||
@@ -82,7 +87,7 @@ Each tool instance maintains its own persistent sandbox. To share **one** sandbo
|
||||
from crewai_tools import DaytonaExecTool
|
||||
|
||||
tool = DaytonaExecTool(sandbox_id="my-long-lived-sandbox")
|
||||
result = tool.run(command="ls /workspace")
|
||||
result = tool.run(command="ls workspace")
|
||||
```
|
||||
|
||||
### Custom sandbox parameters
|
||||
@@ -102,6 +107,41 @@ tool = DaytonaExecTool(
|
||||
)
|
||||
```
|
||||
|
||||
### Searching, moving, and modifying files
|
||||
|
||||
```python Code
|
||||
from crewai_tools import DaytonaFileTool
|
||||
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Find every TODO in the source tree (grep file contents recursively)
|
||||
file_tool.run(action="find", path="workspace/src", pattern="TODO:")
|
||||
|
||||
# Find all Python files (glob match on filenames)
|
||||
file_tool.run(action="search", path="workspace", pattern="*.py")
|
||||
|
||||
# Make a script executable
|
||||
file_tool.run(action="chmod", path="workspace/run.sh", mode="755")
|
||||
|
||||
# Rename or move a file
|
||||
file_tool.run(
|
||||
action="move",
|
||||
path="workspace/draft.md",
|
||||
destination="workspace/final.md",
|
||||
)
|
||||
|
||||
# Bulk find-and-replace across multiple files
|
||||
file_tool.run(
|
||||
action="replace",
|
||||
paths=["workspace/src/a.py", "workspace/src/b.py"],
|
||||
pattern="old_function",
|
||||
replacement="new_function",
|
||||
)
|
||||
|
||||
# Quick existence check before a destructive op
|
||||
file_tool.run(action="exists", path="workspace/cache.db")
|
||||
```
|
||||
|
||||
### Agent integration
|
||||
|
||||
```python Code
|
||||
@@ -121,7 +161,7 @@ coder = Agent(
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to /workspace/fib.py, and run it.",
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to workspace/fib.py, and run it.",
|
||||
expected_output="The first 10 Fibonacci numbers printed to stdout.",
|
||||
agent=coder,
|
||||
)
|
||||
@@ -168,12 +208,22 @@ All three tools accept these parameters at initialization:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`. |
|
||||
| `path` | `str` | ✓ | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | | Content to write or append. Required for `append`. |
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`, `exists`, `move`, `find`, `search`, `chmod`, `replace`. |
|
||||
| `path` | `str \| None` | ✓ for all actions except `replace` | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | ✓ for `append` | Content to write or append. |
|
||||
| `binary` | `bool` | | If `True`, `content` is base64 on write; returns base64 on read. |
|
||||
| `recursive` | `bool` | | For `delete`: remove directories recursively. |
|
||||
| `mode` | `str` | | For `mkdir`: octal permission string (default `"0755"`). |
|
||||
| `mode` | `str \| None` | | For `mkdir`: octal permissions for the new directory (defaults to `"0755"`). For `chmod`: octal permissions to apply to the target. |
|
||||
| `destination` | `str \| None` | ✓ for `move` | Destination path for `move`. |
|
||||
| `pattern` | `str \| None` | ✓ for `find`, `search`, `replace` | For `find`: substring matched against file CONTENTS. For `search`: glob matched against file NAMES (e.g. `*.py`). For `replace`: text to replace inside files. |
|
||||
| `replacement` | `str \| None` | ✓ for `replace` | Replacement text for `pattern`. |
|
||||
| `paths` | `list[str] \| None` | ✓ for `replace` | List of file paths in which to replace text. |
|
||||
| `owner` | `str \| None` | | For `chmod`: new file owner. |
|
||||
| `group` | `str \| None` | | For `chmod`: new file group. |
|
||||
|
||||
<Note>
|
||||
For `chmod`, pass at least one of `mode`, `owner`, or `group` — any field left as `None` is left unchanged on the target.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
For files larger than a few KB, create the file first with `action="write"` and empty content, then send the body via multiple `action="append"` calls of ~4 KB each to stay within tool-call payload limits.
|
||||
|
||||
2198
docs/docs.json
2198
docs/docs.json
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,115 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="May 19, 2026">
|
||||
## v1.14.5
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Deprecate `CrewAgentExecutor`, default Crew agents to `AgentExecutor`
|
||||
- Improve Daytona sandbox tools
|
||||
- Add `restore_from_state_id` kickoff parameter
|
||||
- Add highlights to `ExaSearchTool`, rename from `EXASearchTool`
|
||||
|
||||
### Bug Fixes
|
||||
- Fix memory leak in `git.py` by using `cached_property`
|
||||
- Surface streamed tool calls when `available_functions` is absent
|
||||
- Ensure `skills` loading events for traces
|
||||
- Correct status endpoint path from `/{kickoff_id}/status` to `/status/{kickoff_id}`
|
||||
- Restore missing code block in pt-BR first-flow guide
|
||||
- Prevent `result_as_answer` from returning hook-block or error messages as final answer
|
||||
- Preserve task outputs across async batch flush
|
||||
- Always restore `task.output_pydantic` in finally block
|
||||
- Handle `BaseModel` input in `convert_to_model`
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.14.5
|
||||
- Add OSS upgrade & crew-to-flow migration guide
|
||||
- Document additional env vars for devtools
|
||||
- Add docs for `TavilyGetResearch`
|
||||
|
||||
### Refactoring
|
||||
- Extract CLI into standalone `crewai-cli` package
|
||||
|
||||
## Contributors
|
||||
|
||||
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="May 18, 2026">
|
||||
## v1.14.5a7
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.14.5a6
|
||||
|
||||
### Breaking Changes
|
||||
- Deprecate function_calling_llm field
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @heitorado
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="May 15, 2026">
|
||||
## v1.14.5a6
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Bug Fixes
|
||||
- Fix streamed tool calls when available_functions is absent
|
||||
- Bump langsmith dependency to version >=0.8.0 to address GHSA-3644-q5cj-c5c7
|
||||
- Resolve untranslated code block placeholders in Brazilian Portuguese documentation
|
||||
|
||||
### Documentation
|
||||
- Add documentation for TavilyGetResearch
|
||||
- Update changelog and version for v1.14.5a5
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="May 13, 2026">
|
||||
## v1.14.5a5
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a5)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Deprecate CrewAgentExecutor, default Crew agents to AgentExecutor
|
||||
- Improve Daytona sandbox tools
|
||||
|
||||
### Bug Fixes
|
||||
- Fix missing code block in pt-BR first-flow guide
|
||||
- Log HITL pre-review and distillation failures, add learn_strict
|
||||
- Patch urllib3 for security vulnerabilities
|
||||
- Patch gitpython and langchain-core; ignore unpatched paramiko CVE
|
||||
- Refresh all published workspace packages on uv lock/sync
|
||||
|
||||
### Documentation
|
||||
- Add migration guide for `inputs.id` to `restoreFromStateId`
|
||||
- Add OSS upgrade and crew-to-flow migration guide
|
||||
- Update changelog and version for v1.14.5a4
|
||||
|
||||
## Contributors
|
||||
|
||||
@akaKuruma, @greysonlalonde, @iris-clawd, @lorenzejay, @mislavivanda
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="May 09, 2026">
|
||||
## v1.14.5a4
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from crewai.flow.flow import Flow, listen, start
|
||||
from dotenv import load_dotenv
|
||||
from litellm import completion
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class ExampleFlow(Flow):
|
||||
model = "gpt-4o-mini"
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
title: AWS Workload Identity (OIDC Federation)
|
||||
description: Configure AWS Secrets Manager via Workload Identity for rotation-aware, credential-free secret access
|
||||
sidebarTitle: AWS — Workload Identity
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide configures AWS Secrets Manager as a secret provider using **Workload Identity Federation**: CrewAI Platform mints short-lived OIDC tokens, exchanges them for AWS credentials via STS, and reads your secrets — without a long-lived AWS access key being stored anywhere.
|
||||
|
||||
<Note>
|
||||
**Why this path:** secrets are resolved at automation execution time, so **rotated values propagate to the next kickoff with no re-deploy**. If you only need static credentials and don't care about rotation propagation, see the simpler [AWS — static keys / AssumeRole](/en/enterprise/features/secrets-manager/aws) guide.
|
||||
</Note>
|
||||
|
||||
### How it works at runtime
|
||||
|
||||
1. The deployment worker requests a fresh OIDC JWT from CrewAI Platform.
|
||||
2. The worker calls `sts:AssumeRoleWithWebIdentity` on the IAM role you set up below, presenting the JWT.
|
||||
3. AWS STS validates the JWT against CrewAI Platform's public OIDC issuer (so your platform installation must be reachable from AWS), then returns short-lived AWS credentials.
|
||||
4. The worker uses those credentials to call `secretsmanager:GetSecretValue`.
|
||||
5. The fetched value is injected as the environment variable's value for that automation kickoff.
|
||||
|
||||
OIDC subject tokens are cached for ~1 hour to avoid re-issuing on every kickoff. Secret values are fetched fresh on every kickoff regardless of OIDC cache state, which is what makes this path rotation-aware.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- The automation pod image must include CrewAI runtime version `1.14.5` or later.
|
||||
- An AWS account with permission to create IAM OIDC providers, IAM roles, and IAM policies.
|
||||
- The AWS region where your secrets live (or will live), e.g. `us-east-1`.
|
||||
- A CrewAI Platform organization where your user has the `workload_identity_configs: manage` and `secret_providers: manage` permissions. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **Your CrewAI organization UUID.** Find it on the organization's settings page in CrewAI Platform — the trust policy in Step 3 binds the IAM role to this specific organization.
|
||||
- **Your CrewAI Platform installation must be reachable from AWS over HTTPS** so that AWS STS can fetch the OIDC discovery document and JWKS during token validation. Confirm with your platform administrator that the host is internet-accessible (or that AWS has network reach to it via VPC peering / equivalent).
|
||||
</Note>
|
||||
|
||||
## Step 1 — Find Your CrewAI Platform OIDC Issuer URL
|
||||
|
||||
Your CrewAI Platform installation publishes an OpenID Connect discovery document at `https://<your-platform-host>/.well-known/openid-configuration`. The `issuer` field in that document is the URL AWS will register as a trusted OIDC provider.
|
||||
|
||||
Open the URL in a browser (replacing `<your-platform-host>` with your actual hostname, e.g. `app.crewai.com`):
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
You should see JSON containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note the exact value of `issuer` — you'll use it in Step 3.
|
||||
|
||||
<Tip>
|
||||
If the URL returns 404 or 503, contact your platform administrator. The OIDC issuer requires a private signing key to be configured at install time. See the platform's installation guide for the `OIDC_PRIVATE_KEY` and `OIDC_ISSUER` configuration.
|
||||
</Tip>
|
||||
|
||||
## Step 2 — Register CrewAI Platform as an IAM OIDC Identity Provider
|
||||
|
||||
Open the [IAM → Identity providers console](https://console.aws.amazon.com/iam/home#/identity_providers) and click **Add provider**.
|
||||
|
||||
- **Provider type:** OpenID Connect.
|
||||
- **Provider URL:** the `issuer` value from Step 1 (e.g. `https://app.crewai.com`).
|
||||
- **Audience:** `sts.amazonaws.com`
|
||||
|
||||
Click **Add provider**.
|
||||
|
||||
Or via CLI:
|
||||
|
||||
```bash
|
||||
aws iam create-open-id-connect-provider \
|
||||
--url "https://<your-platform-host>" \
|
||||
--client-id-list "sts.amazonaws.com" \
|
||||
--thumbprint-list "$(echo | openssl s_client -servername <your-platform-host> -connect <your-platform-host>:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1 | cut -d= -f2 | tr -d ':')"
|
||||
```
|
||||
|
||||
Copy the **OpenIDConnectProviderArn** from the output (or the provider's ARN from the console). You'll use it in Step 3.
|
||||
|
||||
<Note>
|
||||
AWS does not actually validate the thumbprint for STS WebIdentity calls — it always re-fetches the JWKS at validation time — but the API requires the field to be present.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Add identity provider" form filled with the Platform issuer URL and audience sts.amazonaws.com → /images/secrets-manager/aws-wi/01-add-oidc-provider.png */}
|
||||
{/* SCREENSHOT: Provider detail page showing the provider's ARN → /images/secrets-manager/aws-wi/02-oidc-provider-arn.png */}
|
||||
|
||||
## Step 3 — Create the IAM Role
|
||||
|
||||
Save as `trust-policy.json`, replacing `<YOUR_ACCOUNT_ID>`, `<your-platform-host>` (the issuer host **without** `https://` or `http://`, e.g. `app.crewai.com`), and `<YOUR_CREWAI_ORG_UUID>` (from the Prerequisites):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Federated": "arn:aws:iam::<YOUR_ACCOUNT_ID>:oidc-provider/<your-platform-host>"
|
||||
},
|
||||
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"<your-platform-host>:aud": "sts.amazonaws.com",
|
||||
"<your-platform-host>:sub": "organization:<YOUR_CREWAI_ORG_UUID>"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Create the role:
|
||||
|
||||
```bash
|
||||
aws iam create-role \
|
||||
--role-name crewai-secrets-reader \
|
||||
--assume-role-policy-document file://trust-policy.json
|
||||
```
|
||||
|
||||
Copy the **Role Arn** from the output — that's your `aws_role_arn`. You'll paste it into CrewAI Platform in Step 6.
|
||||
|
||||
<Tip>
|
||||
The two conditions scope the trust precisely: `aud` restricts assumption to tokens with the AWS STS audience, and `sub` scopes federation to a specific CrewAI organization — only tokens minted for that org's automations are accepted. CrewAI Platform always sets both claims on AWS workload identity tokens.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" with Web Identity trust type, federated provider selector pointing at the CrewAI Platform OIDC provider → /images/secrets-manager/aws-wi/03-create-role-trust.png */}
|
||||
|
||||
## Step 4 — Create and attach the IAM policy for Secrets Manager + KMS access
|
||||
|
||||
Save as `secrets-policy.json`, replacing the placeholders with your account ID, region, secret-name prefix, and the KMS key ARN(s) that encrypt those secrets:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerListForUI",
|
||||
"Effect": "Allow",
|
||||
"Action": "secretsmanager:ListSecrets",
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue"
|
||||
],
|
||||
"Resource": "arn:aws:secretsmanager:<REGION>:<YOUR_ACCOUNT_ID>:secret:<SECRET_NAME_PREFIX>-*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "arn:aws:kms:<REGION>:<YOUR_ACCOUNT_ID>:key/<KMS_KEY_ID>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`SecretsManagerListForUI` powers the **Secret Name autocomplete** in the Environment Variables form and the **Test Connection** button on the credential. `secretsmanager:ListSecrets` only accepts `Resource: "*"` — it is account-scoped at the IAM layer.
|
||||
|
||||
Attach the policy to the role using either the CLI (inline policy, simplest) or the console UI; for environments that reuse the same permissions across many roles, use the **Managed policy** tab for a reusable, named policy.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Inline policy (CLI)">
|
||||
```bash
|
||||
aws iam put-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-name SecretsManagerRead \
|
||||
--policy-document file://secrets-policy.json
|
||||
```
|
||||
|
||||
This attaches the policy **inline** to the role. Inline policies are tied to the role and cannot be reused on other roles.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Managed policy (CLI, reusable)">
|
||||
```bash
|
||||
POLICY_ARN=$(aws iam create-policy \
|
||||
--policy-name CrewAISecretsReader \
|
||||
--policy-document file://secrets-policy.json \
|
||||
--query 'Policy.Arn' --output text)
|
||||
|
||||
aws iam attach-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-arn "$POLICY_ARN"
|
||||
```
|
||||
|
||||
A managed policy is a standalone IAM resource you can attach to multiple roles.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Console (UI)">
|
||||
1. Open the [IAM → Roles console](https://console.aws.amazon.com/iam/home#/roles) and select **crewai-secrets-reader**.
|
||||
2. On the **Permissions** tab, click **Add permissions** → **Create inline policy**.
|
||||
3. Switch to the **JSON** editor and paste the contents of `secrets-policy.json`.
|
||||
4. Click **Next**, give the policy a name (e.g. `SecretsManagerRead`), and click **Create policy**.
|
||||
|
||||
To create a reusable managed policy instead, use **IAM → Policies → Create policy** and then attach it to the role from the role's **Permissions** tab.
|
||||
|
||||
{/* SCREENSHOT: IAM Role detail → Permissions → Create inline policy with JSON editor → /images/secrets-manager/aws-wi/03b-attach-inline-policy.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 5 — Create at Least One Secret in AWS
|
||||
|
||||
If you don't already have a secret to test against, create one now:
|
||||
|
||||
```bash
|
||||
aws secretsmanager create-secret \
|
||||
--region <REGION> \
|
||||
--name crewai-test-keyword \
|
||||
--secret-string "hello from aws"
|
||||
```
|
||||
|
||||
Or via the [AWS Secrets Manager console](https://console.aws.amazon.com/secretsmanager/) → **Store a new secret**.
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Store a new secret" page with a sample value → /images/secrets-manager/aws-wi/04-create-secret.png */}
|
||||
|
||||
## Step 6 — Add a Workload Identity Configuration in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Workload Identity** and click **Add Workload Identity Config**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Workload Identity → /images/secrets-manager/aws-wi/05-amp-settings-wi-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Workload Identity page with "Add Workload Identity Config" button → /images/secrets-manager/aws-wi/06-amp-wi-empty-state.png */}
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `aws-prod`.
|
||||
- **Cloud Provider:** `AWS`.
|
||||
- **AWS Role ARN:** the **Role Arn** from Step 3.
|
||||
- **AWS Region:** the region where your secrets live, e.g. `us-east-1`.
|
||||
- (Optional) Check **Set as default for AWS** if you'd like this WI config to be the default selected when creating an AWS-backed secret credential.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with AWS, role ARN, and region filled in → /images/secrets-manager/aws-wi/07-amp-add-wi-config-aws.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing the new AWS row with "(default)" badge if applicable → /images/secrets-manager/aws-wi/08-amp-wi-list-with-aws.png */}
|
||||
|
||||
## Step 7 — Add a Secret Provider Credential Bound to the WI Config
|
||||
|
||||
Navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `aws-prod-wi`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Authentication Method:** `Workload Identity` (instead of static keys / AssumeRole).
|
||||
- **Workload Identity Configuration:** select the config you created in Step 6 (e.g. `aws-prod`).
|
||||
- (Optional) Check **Set as default credential for this provider**.
|
||||
|
||||
The form will only ask for **AWS Region** under Workload Identity — the static-credential fields (Access Key ID, Secret Access Key, Role ARN, External ID) are intentionally hidden because they don't apply to this path; the role ARN comes from the linked WI config.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + Workload Identity + WI config dropdown selected → /images/secrets-manager/aws-wi/09-amp-add-credential-aws-wi.png */}
|
||||
|
||||
## Step 8 — Test the Connection
|
||||
|
||||
After saving the credential, click **Test Connection**. For workload-identity credentials this verifies the OIDC handshake: CrewAI Platform mints a JWT, exchanges it with AWS STS via `sts:AssumeRoleWithWebIdentity`, and confirms the resulting credentials can call `sts:GetCallerIdentity` against the assumed role. A green result means the federation binding is healthy.
|
||||
|
||||
A successful Test Connection proves the trust policy, OIDC provider registration, and audience condition are all wired correctly. It does **not** prove per-secret IAM is correct — `secretsmanager:GetSecretValue` on a specific secret ARN is exercised separately when an environment variable resolves at kickoff. See [Troubleshooting](#troubleshooting) for handshake failure modes.
|
||||
|
||||
## Step 9 — Reference the Secret in an Environment Variable
|
||||
|
||||
Now reference the secret on an automation, exactly as you would for any other Secrets Manager-backed env var. See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for the form fields and behavior.
|
||||
|
||||
The only difference between WI-backed and static-keys-backed env vars is **when** the secret is read:
|
||||
|
||||
- **WI-backed:** secret value is read fresh on every automation kickoff.
|
||||
- **Static-keys-backed:** secret value is read at deploy time and baked into the deployment image.
|
||||
|
||||
## Step 10 — Verify Rotation
|
||||
|
||||
After the deployment is running, rotate the secret in AWS:
|
||||
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id crewai-test-keyword \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
|
||||
Trigger a new automation kickoff. The kickoff's environment will see `"rotated value"` — no re-deploy, no worker restart, no waiting on a TTL.
|
||||
|
||||
To confirm in logs (if you have access to the worker), look for:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (aws): N secret(s) resolved
|
||||
```
|
||||
|
||||
This line appears for every kickoff and indicates a fresh `GetSecretValue` call against AWS.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| Test Connection fails with a handshake error | The `sts:AssumeRoleWithWebIdentity` call was rejected. Verify the trust policy's federated principal ARN references `oidc-provider/<your-platform-host>` (host **without** `https://` or `http://`, no trailing slash), the audience condition is exactly `sts.amazonaws.com`, the `sub` condition matches your CrewAI organization UUID, and the platform's OIDC discovery URL is reachable from AWS over the public internet. |
|
||||
| `InvalidIdentityToken: Couldn't retrieve verification key from your identity provider` | AWS STS can't reach your CrewAI Platform host to fetch JWKS. Confirm the host is internet-accessible from AWS, the OIDC discovery URL returns 200, and the JWKS endpoint is reachable. |
|
||||
| `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity` | Trust policy mismatch. Re-check Step 3: the federated principal ARN must include `oidc-provider/<your-platform-host>` (host **without** `https://` or `http://`, no trailing slash), the audience condition must be exactly `sts.amazonaws.com`, and the `sub` condition must equal `organization:<YOUR_CREWAI_ORG_UUID>`. |
|
||||
| Secret Name autocomplete shows `AccessDenied: secretsmanager:ListSecrets` | The role is missing `secretsmanager:ListSecrets` with `Resource: "*"`. Add the `SecretsManagerListForUI` statement from Step 4. |
|
||||
| Kickoff fails to resolve a secret even though Test Connection passes | The WI binding is healthy, but resource-scoped IAM is missing on the failing secret. Audit the role's `secretsmanager:GetSecretValue` and `kms:Decrypt` permissions for that specific secret's ARN and KMS key. |
|
||||
| `RegionDisabledException` / no secrets found | The region in the Workload Identity Config doesn't match where the secret lives. Re-check Step 6. |
|
||||
| Rotated value isn't picked up on the next kickoff | Confirm the env var on the automation is referencing a Workload Identity-backed credential (not a static-keys credential). The static path bakes values into the deploy image. |
|
||||
|
||||
### Reference Links
|
||||
|
||||
- AWS: [Creating OpenID Connect (OIDC) identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html)
|
||||
- AWS: [Configuring a role for OpenID Connect federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_relying-party.html)
|
||||
- AWS: [STS:AssumeRoleWithWebIdentity API reference](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Use secrets in environment variables and manage permissions](/en/enterprise/features/secrets-manager/usage)
|
||||
- For multi-cloud, see also [GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity) and [Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
294
docs/en/enterprise/features/secrets-manager/aws.mdx
Normal file
294
docs/en/enterprise/features/secrets-manager/aws.mdx
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: AWS Secrets Manager (Static Credentials)
|
||||
description: Configure AWS Secrets Manager as a secret provider for CrewAI Platform using static access keys or AssumeRole
|
||||
sidebarTitle: AWS
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide walks you through configuring AWS Secrets Manager as a secret provider for your CrewAI Platform organization, using **static credentials** (access keys, optionally with AssumeRole). By the end, CrewAI Platform will be able to read secrets stored in your AWS account and inject them as environment variable values at runtime.
|
||||
|
||||
<Note>
|
||||
This guide covers the **static credentials** path — secrets are resolved at deploy time and baked into the deployment image. Rotated values require a re-deploy. If you want rotation-aware secrets that update on every automation kickoff (no re-deploy), see [AWS Workload Identity (OIDC Federation)](/en/enterprise/features/secrets-manager/aws-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
This guide covers the AWS-side configuration and the credential setup in CrewAI Platform. To then reference a secret from an environment variable, see [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- An AWS account with permission to create IAM users, customer-managed policies, and (optionally) IAM roles.
|
||||
- The AWS region where your secrets live (or will live), for example `us-east-1`.
|
||||
- A CrewAI Platform organization where your user has the `secret_providers: manage` permission. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Choose an Authentication Method
|
||||
|
||||
CrewAI Platform supports two ways for the platform to authenticate with AWS Secrets Manager. Pick one before you begin — the steps below differ depending on which you choose.
|
||||
|
||||
| Method | When to use | Trade-offs |
|
||||
|---|---|---|
|
||||
| **Static access keys** | Getting started, single-account deployments | Simplest setup; access keys must be rotated manually |
|
||||
| **AssumeRole** | Cross-account, production hardening | Short-lived credentials; supports External ID; requires extra IAM role |
|
||||
|
||||
The rest of this guide uses tabs in Steps 3–5 so you can follow the path that matches your choice.
|
||||
|
||||
## Step 1 — Create an IAM User
|
||||
|
||||
Open the [IAM console](https://console.aws.amazon.com/iam/), navigate to **Users**, then click **Create user**.
|
||||
|
||||
- Suggested name: `crewai-secrets-reader`.
|
||||
- Leave **Provide user access to the AWS Management Console** unchecked — this principal is used programmatically by CrewAI Platform, not by humans.
|
||||
- Click **Next**.
|
||||
|
||||
On the **Set permissions** page, leave the default selection. You will attach the policy in Step 3.
|
||||
|
||||
Click **Next**, review, and click **Create user**.
|
||||
|
||||
For full details, see the AWS documentation: [Create an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create user" form filled with name "crewai-secrets-reader" → /images/secrets-manager/aws/01-create-iam-user.png */}
|
||||
|
||||
## Step 2 — Create the IAM Policy
|
||||
|
||||
CrewAI Platform needs read-only access to AWS Secrets Manager and permission to decrypt secrets via KMS. Create a customer-managed policy with the following JSON.
|
||||
|
||||
In the IAM console, navigate to **Policies**, then click **Create policy**.
|
||||
|
||||
Choose the **JSON** tab and replace the contents with:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:ListSecrets",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:DescribeSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:DescribeKey",
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Click **Next**, then on the **Review and create** page:
|
||||
|
||||
- **Policy name:** `CrewAISecretsManagerRead`
|
||||
- **Description (optional):** `Read-only access to AWS Secrets Manager for CrewAI Platform`
|
||||
|
||||
Click **Create policy**.
|
||||
|
||||
<Tip>
|
||||
The policy above grants `*` on `Resource` for simplicity. In production, scope the `Resource` down to the ARNs of the specific secrets CrewAI Platform should access, and scope `kms:Decrypt` to the specific KMS key ARNs that encrypt those secrets. See the [AWS guidance on least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html).
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create policy" → JSON tab with the policy above pasted → /images/secrets-manager/aws/02-create-policy-json-editor.png */}
|
||||
{/* SCREENSHOT: AWS IAM "Review and create policy" page with name "CrewAISecretsManagerRead" → /images/secrets-manager/aws/03-policy-review-and-create.png */}
|
||||
|
||||
## Step 3 — Attach the Policy
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Static access keys">
|
||||
1. In the IAM console, navigate to **Users** and click the user you created in Step 1.
|
||||
2. On the **Permissions** tab, click **Add permissions** → **Attach policies directly**.
|
||||
3. Search for `CrewAISecretsManagerRead`, select it, and click **Next**.
|
||||
4. Click **Add permissions**.
|
||||
|
||||
{/* SCREENSHOT: "Add permissions" → "Attach policies directly" with CrewAISecretsManagerRead selected → /images/secrets-manager/aws/04a-attach-policy-to-user.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
With AssumeRole, the policy is attached to a separate IAM **role** (not directly to the user). The user from Step 1 only needs permission to call `sts:AssumeRole` on that role.
|
||||
|
||||
**Create the role:**
|
||||
|
||||
1. In the IAM console, navigate to **Roles** and click **Create role**.
|
||||
2. **Trusted entity type:** AWS account. Choose **This account** (or **Another AWS account** for cross-account setups, then enter the AWS account ID hosting the IAM user from Step 1).
|
||||
3. (Recommended) Check **Require external ID** and enter a value you generate yourself — this is a shared secret you will paste into CrewAI Platform in Step 5.
|
||||
4. Click **Next**.
|
||||
5. Attach the `CrewAISecretsManagerRead` policy.
|
||||
6. Click **Next**, name the role `CrewAISecretsManagerRole`, and click **Create role**.
|
||||
|
||||
**Allow the IAM user to assume the role:**
|
||||
|
||||
1. Open the role you just created and copy its **ARN**.
|
||||
2. In the IAM console, navigate to **Users**, click the user from Step 1, and on the **Permissions** tab click **Add permissions** → **Create inline policy**.
|
||||
3. On the **JSON** tab, paste the following (replace `ROLE_ARN_FROM_ABOVE`):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "ROLE_ARN_FROM_ABOVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. Name the policy `CrewAIAssumeSecretsRole` and click **Create policy**.
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" trust policy step with External ID checkbox enabled → /images/secrets-manager/aws/04b-create-role-trust-policy.png */}
|
||||
{/* SCREENSHOT: Inline sts:AssumeRole policy attached to the IAM user → /images/secrets-manager/aws/04c-attach-assumerole-on-user.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 4 — Get Credentials
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Static access keys">
|
||||
1. In the IAM console, open the user from Step 1.
|
||||
2. Click the **Security credentials** tab.
|
||||
3. Under **Access keys**, click **Create access key**.
|
||||
4. Select **Application running outside AWS** (or **Other**) as the use case. Click **Next**.
|
||||
5. (Optional) Add a description tag. Click **Create access key**.
|
||||
6. Click **Show** to reveal the secret access key, then copy both the **Access key ID** and the **Secret access key**, or click **Download .csv file**.
|
||||
|
||||
<Warning>
|
||||
The secret access key is shown only once. If you close this page without copying it, you will need to delete the key and create a new one.
|
||||
</Warning>
|
||||
|
||||
For full details, see the AWS documentation: [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
|
||||
|
||||
{/* SCREENSHOT: Access key use-case selector ("Application running outside AWS") → /images/secrets-manager/aws/05a-create-access-key-use-case.png */}
|
||||
{/* SCREENSHOT: "Retrieve access keys" page with Show/Download buttons → /images/secrets-manager/aws/06a-retrieve-access-keys.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
Even with AssumeRole, CrewAI Platform still needs an access key for the IAM user — it uses those keys as the calling identity to perform the `sts:AssumeRole` call.
|
||||
|
||||
1. Create an access key for the user exactly as described in the **Static access keys** tab above.
|
||||
2. Open the role you created in Step 3 and copy:
|
||||
- The **Role ARN** (from the role summary).
|
||||
- The **External ID** you configured (if any) — you set this yourself in Step 3, so make sure you have it on hand.
|
||||
|
||||
{/* SCREENSHOT: IAM role detail page showing Role ARN → /images/secrets-manager/aws/05b-role-arn-detail.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 5 — Add the Credential in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Secret Provider Credentials page with "Add Credential" button → /images/secrets-manager/usage/02-amp-credentials-empty-state.png */}
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Static access keys">
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `aws-prod`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** The AWS region where your secrets live, e.g. `us-east-1`. This must match the region of the secrets you want to read.
|
||||
- **Access Key ID:** The value from Step 4.
|
||||
- **Secret Access Key:** The value from Step 4.
|
||||
- (Optional) Check **Set as default credential for this provider**. The default credential is used by environment variables that reference AWS secrets without specifying a credential explicitly.
|
||||
|
||||
Leave **Role ARN** and **External ID** blank.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + static access keys filled in → /images/secrets-manager/usage/03a-amp-add-credential-form-aws-static.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `aws-prod-assumerole`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** The AWS region where your secrets live.
|
||||
- **Access Key ID:** The IAM user's access key from Step 4 (used to call STS).
|
||||
- **Secret Access Key:** The IAM user's secret access key from Step 4.
|
||||
- **Role ARN:** The Role ARN you copied in Step 4.
|
||||
- **External ID:** The External ID you set on the role's trust policy (omit if none).
|
||||
- (Optional) Check **Set as default credential for this provider**.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + AssumeRole fields filled in → /images/secrets-manager/usage/03b-amp-add-credential-form-aws-assumerole.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
**How the two modes behave at runtime:**
|
||||
|
||||
- With **static access keys** only, CrewAI Platform calls AWS Secrets Manager directly using the keys you supplied.
|
||||
- When a **Role ARN** is set, CrewAI Platform first calls `sts:AssumeRole` with the supplied access keys (and External ID if configured), then uses the short-lived credentials returned by STS to read your secrets.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: Credentials list showing the new AWS row, with "(default)" badge if applicable → /images/secrets-manager/usage/04-amp-credential-created.png */}
|
||||
|
||||
## Step 6 — Create at Least One Secret in AWS
|
||||
|
||||
If you do not already have secrets in AWS Secrets Manager, create one now so you can verify the connection in Step 7.
|
||||
|
||||
In the [AWS Secrets Manager console](https://console.aws.amazon.com/secretsmanager/), click **Store a new secret**.
|
||||
|
||||
- **Secret type:** Choose **Other type of secret**.
|
||||
- **Key/value pairs** — either:
|
||||
- Enter one or more key/value pairs (recommended for structured secrets), or
|
||||
- Use the **Plaintext** tab for a single string value.
|
||||
- **Encryption key:** Use `aws/secretsmanager` (the AWS-managed key) unless you have a specific KMS key requirement.
|
||||
|
||||
Click **Next**, then enter:
|
||||
|
||||
- **Secret name:** A unique name, e.g. `crewai/openai-api-key`.
|
||||
- **Description (optional):** A short note about what the secret is for.
|
||||
|
||||
Click **Next** through the rotation and review steps, then click **Store**.
|
||||
|
||||
<Note>
|
||||
**JSON-key reference syntax.** If you store a secret with multiple key/value pairs (a JSON object), CrewAI Platform can extract a specific field using the `secret-name#json_key` syntax in environment variable references. For example, a secret named `database-credentials` with `{"username": "...", "password": "..."}` can be referenced as `database-credentials#password`. See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for details.
|
||||
</Note>
|
||||
|
||||
For full details, see the AWS documentation: [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html).
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Choose secret type" page → /images/secrets-manager/aws/07-create-secret-store-type.png */}
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Configure secret" page with name and description → /images/secrets-manager/aws/08-create-secret-name.png */}
|
||||
|
||||
## Step 7 — Test the Connection
|
||||
|
||||
Back in CrewAI Platform, on the **Secret Provider Credentials** page, find the credential you just created and click **Test Connection**.
|
||||
|
||||
A success toast confirms that CrewAI Platform can authenticate to AWS and read secrets from your account.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" → /images/secrets-manager/usage/05-amp-test-connection-success.png */}
|
||||
|
||||
If the test fails, check the most common causes:
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| `AccessDenied` on `secretsmanager:ListSecrets` | Policy not attached, or wrong user. Re-check Step 3. |
|
||||
| `AccessDenied` on `kms:Decrypt` | Missing the `KMSDecrypt` statement, or your secrets use a customer-managed KMS key not covered by `Resource: "*"`. |
|
||||
| `InvalidClientTokenId` / `SignatureDoesNotMatch` | Wrong access key ID or secret access key. Re-check Step 4 and Step 5. |
|
||||
| `RegionDisabledException` / no secrets found | The credential's **Region** does not match where your secrets actually live. |
|
||||
| `AccessDenied` on `sts:AssumeRole` (AssumeRole only) | Inline `sts:AssumeRole` policy missing on the IAM user, or the role's trust policy does not allow this principal, or the External ID does not match. |
|
||||
| Test passes immediately after creating the IAM user, but fails next time | IAM credentials sometimes take a minute or two to propagate globally. Retry. |
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that AWS is connected, head to [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage) to:
|
||||
|
||||
- Grant org members the right permissions to use (or manage) Secrets Manager.
|
||||
- Reference your AWS secrets from CrewAI Platform environment variables.
|
||||
|
||||
If you want **rotation-aware** secrets that propagate without re-deploying, switch to [AWS Workload Identity (OIDC Federation)](/en/enterprise/features/secrets-manager/aws-workload-identity) — same secret store, no static credentials, secrets are fetched per kickoff.
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Azure Workload Identity Federation
|
||||
description: Configure Azure Key Vault via Microsoft Entra Workload Identity Federation for rotation-aware, credential-free secret access
|
||||
sidebarTitle: Azure — Workload Identity
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide configures Azure Key Vault as a secret provider using **Microsoft Entra Workload Identity Federation**: CrewAI Platform mints short-lived OIDC tokens, exchanges them for an Entra access token via the Microsoft identity platform, and reads your secrets — without any client secret being stored anywhere.
|
||||
|
||||
<Note>
|
||||
**Why this path:** secrets are resolved at automation execution time, so **rotated values propagate to the next kickoff with no re-deploy**. If you only need static credentials, see the simpler [Azure Key Vault — client secret](/en/enterprise/features/secrets-manager/azure) guide.
|
||||
</Note>
|
||||
|
||||
### How it works at runtime
|
||||
|
||||
1. The deployment worker requests a fresh OIDC JWT from CrewAI Platform.
|
||||
2. The worker presents the JWT to Microsoft Entra at `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token` as a `client_assertion` (`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`), referencing the App Registration whose **Federated Identity Credential** matches the JWT's issuer + subject.
|
||||
3. Entra validates the JWT against your platform's OIDC discovery document and JWKS, then returns a short-lived access token scoped to `https://vault.azure.net/.default`.
|
||||
4. The worker calls Azure Key Vault to read the secret.
|
||||
5. The fetched value is injected as the environment variable's value for that automation kickoff.
|
||||
|
||||
OIDC subject tokens are cached for ~1 hour to avoid re-issuing on every kickoff. Secret values are fetched fresh on every kickoff regardless of OIDC cache state, which is what makes this path rotation-aware.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- The automation pod image must include CrewAI runtime version `1.14.5` or later.
|
||||
- An Azure subscription and a Microsoft Entra tenant you can manage.
|
||||
- Permission in the tenant to create App Registrations and add Federated Identity Credentials.
|
||||
- A Key Vault using **Azure RBAC** for authorization (not the legacy access-policy model).
|
||||
- A CrewAI Platform organization where your user has the `workload_identity_configs: manage` and `secret_providers: manage` permissions. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **Your CrewAI Platform installation must be reachable from Microsoft Entra over HTTPS** so that Entra can fetch the OIDC discovery document and JWKS during token validation. Confirm with your platform administrator that the host is internet-accessible.
|
||||
</Note>
|
||||
|
||||
## Step 1 — Find Your CrewAI Platform OIDC Issuer URL
|
||||
|
||||
Your CrewAI Platform installation publishes an OpenID Connect discovery document at `https://<your-platform-host>/.well-known/openid-configuration`. The `issuer` field there is the URL Microsoft Entra will register as a trusted federation issuer.
|
||||
|
||||
Open the URL in a browser:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
You should see JSON containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note the exact value of `issuer` — you'll use it in Step 3.
|
||||
|
||||
<Tip>
|
||||
If the URL returns 404 or 503, contact your platform administrator. The OIDC issuer requires a private signing key to be configured at install time. See the platform's installation guide for the `OIDC_PRIVATE_KEY` and `OIDC_ISSUER` configuration.
|
||||
</Tip>
|
||||
|
||||
## Step 2 — Create an App Registration
|
||||
|
||||
In the [Microsoft Entra portal](https://entra.microsoft.com), navigate to **App registrations** and click **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- Leave **Redirect URI** blank.
|
||||
|
||||
Click **Register**. Note the **Application (client) ID** and **Directory (tenant) ID** on the App's overview blade — you'll use them in Step 6.
|
||||
|
||||
{/* SCREENSHOT: Azure portal "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure-wi/01-register-app.png */}
|
||||
|
||||
## Step 3 — Add a Federated Identity Credential
|
||||
|
||||
The Federated Identity Credential tells Microsoft Entra: *trust JWTs minted by this issuer, with this subject, when they're presented as a client assertion for this App Registration.*
|
||||
|
||||
On the App Registration, navigate to **Certificates & secrets** → **Federated credentials** → **Add credential**.
|
||||
|
||||
- **Federated credential scenario:** `Other issuer`.
|
||||
- **Issuer:** the CrewAI Platform issuer URL from Step 1, e.g. `https://<your-platform-host>`.
|
||||
- **Subject identifier:** `organization:<YOUR_CREWAI_ORG_UUID>` — exactly the value of the JWT's `sub` claim. Find your org UUID in CrewAI Platform's organization settings. This scopes federation to a specific CrewAI organization — only tokens minted for that org's automations are accepted.
|
||||
- **Name:** any descriptive label, e.g. `crewai-org-prod`.
|
||||
- **Audience:** `api://AzureADTokenExchange`. This is the fixed audience Microsoft Entra requires for federated credentials and is what CrewAI Platform sets in the JWT's `aud` claim.
|
||||
|
||||
Click **Add**.
|
||||
|
||||
<Tip>
|
||||
**Per-org isolation.** The subject identifier (`organization:<UUID>`) restricts the federated credential to a specific CrewAI organization's tokens. If multiple CrewAI organizations should share one App Registration, add one Federated Identity Credential per organization (each with the org's UUID).
|
||||
</Tip>
|
||||
|
||||
For full details, see the Microsoft documentation: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust).
|
||||
|
||||
{/* SCREENSHOT: "Add credential" panel with scenario = "Other issuer", issuer URL, subject "organization:<uuid>", audience "api://AzureADTokenExchange" → /images/secrets-manager/azure-wi/02-add-federated-credential.png */}
|
||||
|
||||
## Step 4 — Grant the App Registration Access to Key Vault
|
||||
|
||||
Grant the App Registration **Key Vault Secrets User** on the target vault — the same role you'd use for the static-credentials path. Use either vault-wide (simpler) or per-secret (least privilege).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Vault-wide (simpler)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
Vault-wide scope grants the `secrets/list` permission that the **Secret Name autocomplete** in CrewAI Platform's env-var form depends on. Choose this tab if you want autocomplete to work.
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure-wi/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Per-secret (least privilege)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
Per-secret bindings disable the **Secret Name autocomplete** in CrewAI Platform's env-var form (autocomplete requires `secrets/list`, which is vault-scoped only). Type the full secret name instead.
|
||||
|
||||
{/* SCREENSHOT: Per-secret IAM panel with the App Registration assigned **Key Vault Secrets User** at the secret resource scope → /images/secrets-manager/azure-wi/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Portal (UI)">
|
||||
For a **vault-wide** assignment:
|
||||
|
||||
1. Open your Key Vault in the Azure portal.
|
||||
2. Click **Access control (IAM)** → **Add** → **Add role assignment**.
|
||||
3. Select role **Key Vault Secrets User** → **Next**.
|
||||
4. Click **Select members**, search for the App Registration `crewai-secrets-reader`, click **Select**.
|
||||
5. Click **Review + assign**.
|
||||
|
||||
For a **per-secret** assignment, use the same flow but start from **Objects** → **Secrets** → select the secret → its own **Access control (IAM)** panel. Per-secret bindings disable autocomplete (see the Per-secret tab above).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 5 — Create at Least One Secret in Key Vault
|
||||
|
||||
If you don't already have a secret to test against, create one via the Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
Or via the Azure portal:
|
||||
|
||||
1. Open your Key Vault and navigate to **Objects** → **Secrets**.
|
||||
2. Click **Generate/Import**.
|
||||
3. **Upload options:** `Manual`. **Name:** the secret name (e.g. `openai-api-key`). **Secret value:** paste the value.
|
||||
4. Click **Create**.
|
||||
|
||||
<Note>
|
||||
**Secret name conventions.** Azure Key Vault secret names cannot contain underscores. CrewAI Platform automatically converts underscores to hyphens when calling Azure (e.g., `db_password` is sent as `db-password`), so you can keep underscore-style env-var names — but the underlying secret in Key Vault must use hyphens.
|
||||
</Note>
|
||||
|
||||
## Step 6 — Add a Workload Identity Configuration in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Workload Identity** and click **Add Workload Identity Config**.
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `azure-prod`.
|
||||
- **Cloud Provider:** `Azure`.
|
||||
- **Tenant ID:** your Microsoft Entra **Directory (tenant) ID** from Step 2.
|
||||
- **Client ID:** your App Registration's **Application (client) ID** from Step 2.
|
||||
- (Optional) Check **Set as default for Azure** if you'd like this to be the default WI config selected when creating an Azure-backed secret credential.
|
||||
|
||||
The **Audience** is fixed at `api://AzureADTokenExchange` — Microsoft Entra requires this exact audience for federated credentials, so no Audience field is shown on the form.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with Azure, tenant ID, client ID populated → /images/secrets-manager/azure-wi/05-amp-add-wi-config-azure.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing AWS, GCP, and Azure rows → /images/secrets-manager/azure-wi/06-amp-wi-list-with-azure.png */}
|
||||
|
||||
## Step 7 — Add a Secret Provider Credential Bound to the WI Config
|
||||
|
||||
Navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `azure-prod-wi`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** select the config you created in Step 6.
|
||||
- **Key Vault URL:** the vault's DNS hostname, e.g. `https://my-vault.vault.azure.net`.
|
||||
- (Optional) Check **Set as default credential for this provider**.
|
||||
|
||||
The form will only ask for **Key Vault URL** under Workload Identity — the static-credential fields (Tenant ID, Client ID, Client Secret) are intentionally hidden because they don't apply to this path; tenant + client come from the linked WI config.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
<Tip>
|
||||
**One App Registration, many vaults.** The Key Vault URL lives on the credential, not the WI config. So one App Registration (and one WI config) can serve multiple Key Vaults — just create one Secret Provider Credential per vault, all linked to the same WI config.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure + Workload Identity + WI config dropdown + vault URL → /images/secrets-manager/azure-wi/07-amp-add-credential-azure-wi.png */}
|
||||
|
||||
## Step 8 — Test the Connection
|
||||
|
||||
After saving the credential, click **Test Connection**. For workload-identity credentials this verifies the OIDC handshake: CrewAI Platform mints a JWT, presents it to Microsoft Entra as a federated `client_assertion`, and confirms Entra returns a vault-scoped access token. A green result means the federation binding is healthy.
|
||||
|
||||
A successful Test Connection proves the Federated Identity Credential's issuer, subject, and audience all match, and that the App Registration is reachable. It does **not** prove per-secret Key Vault RBAC is correct — `getSecret` against a specific secret is exercised separately when an environment variable resolves at kickoff. See [Troubleshooting](#troubleshooting) for handshake failure modes.
|
||||
|
||||
## Step 9 — Reference the Secret in an Environment Variable
|
||||
|
||||
Reference the secret on an automation, exactly as you would for any other Secrets Manager-backed env var. See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for the form fields and behavior.
|
||||
|
||||
## Step 10 — Verify Rotation
|
||||
|
||||
After the deployment is running, rotate the secret in Key Vault:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "rotated value"
|
||||
```
|
||||
|
||||
Trigger a new automation kickoff. The kickoff's environment will see `"rotated value"` — no re-deploy, no worker restart, no TTL wait.
|
||||
|
||||
To confirm in worker logs, look for:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (azure): N secret(s) resolved
|
||||
```
|
||||
|
||||
This line appears for every kickoff and indicates a fresh `getSecret` call against Azure Key Vault.
|
||||
|
||||
For an end-to-end fingerprint-based verification, see [Verify Rotation End-to-End](/en/enterprise/features/secrets-manager/verify-rotation).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| Test Connection fails with a handshake error | The federated `client_assertion` was rejected by Microsoft Entra. Verify the Federated Identity Credential's **Issuer** matches the platform's `issuer` value exactly, **Subject** is `organization:<your-org-uuid>` (matching the JWT's `sub` claim), **Audience** is `api://AzureADTokenExchange`, and the platform's OIDC discovery URL is reachable from Entra over the public internet. |
|
||||
| `AADSTS70021: No matching federated identity record found for presented assertion` | The Federated Identity Credential's **Issuer** + **Subject** + **Audience** don't all match the JWT exactly. Re-check Step 3: subject must be `organization:<your-org-uuid>` (matching the JWT's `sub` claim), audience must be `api://AzureADTokenExchange`. |
|
||||
| `AADSTS700024: Client assertion is not within its valid time range` | The CrewAI Platform host's clock is significantly skewed from real time. Check NTP on the host. |
|
||||
| `AADSTS50013: Assertion failed signature validation` | Microsoft Entra couldn't verify the JWT's signature. Confirm `https://<your-platform-host>/oauth2/jwks` is reachable from the public internet and serves a valid JWKS. |
|
||||
| Secret Name autocomplete shows `Forbidden — does not have permission to perform action 'Microsoft.KeyVault/vaults/secrets/.../list'` | The App Registration's **Key Vault Secrets User** role is scoped to a single secret. Grant the role at the vault scope so the `list` data-plane action is allowed. See Step 4. |
|
||||
| Kickoff fails to resolve a secret even though Test Connection passes | The WI binding is healthy, but per-secret Key Vault RBAC is missing on the failing secret. Audit **Key Vault Secrets User** on that specific secret (or extend the role assignment to the vault scope). |
|
||||
| `Forbidden — request was not authorized` (vault using legacy access policies) | The vault hasn't been switched to Azure RBAC. Under the vault's **Access configuration**, set permission model to **Azure role-based access control** and re-grant the role from Step 4. |
|
||||
| `azure_vault_url is required for Azure secret resolution` (worker logs) | The Secret Provider Credential is missing **Key Vault URL**. Re-check Step 7. |
|
||||
| Rotated value isn't picked up on the next kickoff | Confirm the env var on the automation is referencing a Workload Identity-backed credential (not a static-keys credential). The static path bakes values into the deploy image. |
|
||||
|
||||
### Reference Links
|
||||
|
||||
- Microsoft: [Microsoft Entra Workload Identity Federation overview](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation)
|
||||
- Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust)
|
||||
- Microsoft: [Azure Key Vault RBAC guide](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Use secrets in environment variables and manage permissions](/en/enterprise/features/secrets-manager/usage)
|
||||
- For multi-cloud, the AWS-equivalent setup is at [AWS Workload Identity (OIDC Federation)](/en/enterprise/features/secrets-manager/aws-workload-identity) and the GCP-equivalent at [GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
|
||||
## Screenshot Reference
|
||||
|
||||
The placeholders above map to:
|
||||
|
||||
- `01-register-app.png` — Azure portal "Register an application" form filled with `crewai-secrets-reader`.
|
||||
- `02-add-federated-credential.png` — App Registration → Certificates & secrets → Federated credentials → Add credential, with **Other issuer**, the platform issuer URL, subject `organization:<uuid>`, audience `api://AzureADTokenExchange`.
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, with **Key Vault Secrets User** and the App Registration selected.
|
||||
- `04-per-secret-rbac.png` — Same form but at a single secret's IAM scope (alternative least-privilege path).
|
||||
- `05-amp-add-wi-config-azure.png` — CrewAI Platform "Add Workload Identity Config" form with Cloud Provider = Azure, Tenant ID, Client ID populated.
|
||||
- `06-amp-wi-list-with-azure.png` — Workload Identity list page after creation, showing rows for AWS, GCP, and the new Azure config.
|
||||
- `07-amp-add-credential-azure-wi.png` — "Add Secret Provider Credential" form with Provider = Azure Key Vault, Auth = Workload Identity, the WI config picked, and Key Vault URL populated.
|
||||
195
docs/en/enterprise/features/secrets-manager/azure.mdx
Normal file
195
docs/en/enterprise/features/secrets-manager/azure.mdx
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: Azure Key Vault
|
||||
description: Configure Azure Key Vault as a secret provider for CrewAI Platform, end-to-end
|
||||
sidebarTitle: Azure
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide walks you through configuring Azure Key Vault as a secret provider for your CrewAI Platform organization, using a **Microsoft Entra App Registration with a client secret**. By the end, CrewAI Platform will be able to read secrets stored in your Azure Key Vault and inject them as environment variable values at runtime.
|
||||
|
||||
<Note>
|
||||
This guide covers the **static credentials** path — secrets are resolved at deploy time and baked into the deployment image. Rotated values require a re-deploy. If you want rotation-aware secrets that update on every automation kickoff, see [Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
This guide covers the Azure-side configuration and the credential setup in CrewAI Platform. To then reference a secret from an environment variable, see [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- An Azure subscription with permission to create App Registrations in Microsoft Entra and to grant role assignments on Key Vault resources.
|
||||
- A Key Vault using **Azure RBAC** for authorization (not the legacy access-policy model). If your vault still uses access policies, switch it to RBAC under the vault's **Access configuration** blade.
|
||||
- A CrewAI Platform organization where your user has the `secret_providers: manage` permission. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Step 1 — Create an App Registration
|
||||
|
||||
The App Registration is the Microsoft Entra-side identity CrewAI Platform will authenticate as.
|
||||
|
||||
In the [Microsoft Entra portal](https://entra.microsoft.com), navigate to **App registrations** and click **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- Leave **Redirect URI** blank.
|
||||
|
||||
Click **Register**. Note the **Application (client) ID** and **Directory (tenant) ID** on the App's overview blade — you'll paste both into CrewAI Platform in Step 4.
|
||||
|
||||
For full details, see the Microsoft documentation: [Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).
|
||||
|
||||
{/* SCREENSHOT: Azure "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure/01-register-app.png */}
|
||||
|
||||
## Step 2 — Create a Client Secret
|
||||
|
||||
On the App Registration, navigate to **Certificates & secrets** → **Client secrets** → **New client secret**.
|
||||
|
||||
- **Description:** `crewai-platform`
|
||||
- **Expires:** pick a duration that matches your rotation policy (Microsoft caps this at 24 months).
|
||||
|
||||
Click **Add**. Copy the **Value** column immediately — it can never be re-displayed once you leave the page.
|
||||
|
||||
<Warning>
|
||||
Client secrets are long-lived static credentials. Store the value securely (in a password manager or your own secret store) and rotate it before expiry. To eliminate static credentials entirely, use [Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity) instead.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: "Client secrets" tab with the new secret row and the "Value" column highlighted → /images/secrets-manager/azure/02-create-client-secret.png */}
|
||||
|
||||
## Step 3 — Grant the App Registration Access to Key Vault
|
||||
|
||||
CrewAI Platform needs read access to secrets in your Key Vault. Use one of two scopes — **vault-wide** for simplicity, or **per-secret** for least privilege.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Vault-wide (simpler)">
|
||||
In the [Key Vault console](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.KeyVault%2Fvaults), open the target vault, then navigate to **Access control (IAM)** → **Add** → **Add role assignment**.
|
||||
|
||||
- **Role:** **Key Vault Secrets User**
|
||||
- **Assign access to:** User, group, or service principal
|
||||
- **Members:** search for and select your App Registration (`crewai-secrets-reader`).
|
||||
|
||||
Click **Review + assign**.
|
||||
|
||||
Or via the Azure CLI:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Per-secret (least privilege)">
|
||||
Grant the role at the level of an individual secret. Repeat for each secret CrewAI Platform should access:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Access control (IAM)" panel showing role assignment scoped to one secret → /images/secrets-manager/azure/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
The **Key Vault Secrets User** role allows reading secret values but not listing all secrets in the vault. CrewAI Platform's secret-name autocomplete also calls `list` — that permission is included by the role at the vault scope, but **not** at the per-secret scope. With per-secret bindings, autocomplete won't suggest secrets; type the full secret name instead.
|
||||
</Tip>
|
||||
|
||||
## Step 4 — Add the Credential in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `azure-prod`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Key Vault URL:** the vault's DNS hostname, e.g. `https://my-vault.vault.azure.net`.
|
||||
- **Tenant ID:** your Microsoft Entra **Directory (tenant) ID** from Step 1.
|
||||
- **Client ID:** your App Registration's **Application (client) ID** from Step 1.
|
||||
- **Client Secret:** the **Value** you copied in Step 2.
|
||||
- (Optional) Check **Set as default credential for this provider**. The default credential is used by environment variables that reference Azure secrets without specifying a credential explicitly.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure fields filled in → /images/secrets-manager/azure/05-amp-add-credential-form-azure.png */}
|
||||
|
||||
## Step 5 — Create at Least One Secret in Azure Key Vault
|
||||
|
||||
If you don't already have secrets in Key Vault, create one now so you can verify the connection in Step 6.
|
||||
|
||||
In the Key Vault console, navigate to **Objects** → **Secrets** → **Generate/Import**.
|
||||
|
||||
- **Upload options:** `Manual`
|
||||
- **Name:** e.g. `openai-api-key`
|
||||
- **Secret value:** paste your secret value
|
||||
- Leave the rest at defaults.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
Or via the Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Secret name conventions.** Azure Key Vault secret names cannot contain underscores. CrewAI Platform automatically converts underscores to hyphens when calling Azure (e.g., `db_password` is sent as `db-password`), so you can keep underscore-style env-var names — but the underlying secret in Key Vault must use hyphens.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**JSON-key reference syntax.** Key Vault treats secret values as opaque strings. If your secret value happens to be a JSON object, CrewAI Platform can extract a single field using the `secret-name#json_key` syntax (e.g. `database-credentials#password`). See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for details.
|
||||
</Note>
|
||||
|
||||
For full details, see the Microsoft documentation: [Set and retrieve a secret](https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli).
|
||||
|
||||
{/* SCREENSHOT: Azure Key Vault "Create a secret" form with name and value → /images/secrets-manager/azure/06-create-secret.png */}
|
||||
|
||||
## Step 6 — Test the Connection
|
||||
|
||||
Back in CrewAI Platform, on the **Secret Provider Credentials** page, find the credential you just created and click **Test Connection**.
|
||||
|
||||
A success toast confirms that CrewAI Platform can authenticate to Microsoft Entra and read secrets from your vault.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the Azure credential → /images/secrets-manager/azure/07-test-connection-success.png */}
|
||||
|
||||
If the test fails, check the most common causes:
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| `AADSTS7000215: Invalid client secret provided` | The pasted **Client Secret** is wrong or expired. Re-create the secret (Step 2) and update the credential. |
|
||||
| `AADSTS700016: Application not found in the directory` | The **Tenant ID** or **Client ID** doesn't match the App Registration. Re-check Step 4. |
|
||||
| `Forbidden — caller does not have permission` | The App Registration is missing the **Key Vault Secrets User** role on the vault (or per-secret). Re-check Step 3. |
|
||||
| `Vault not found` / DNS errors | The **Key Vault URL** is wrong, or your vault has private endpoints that block public access. Confirm the host responds to `curl https://<vault-name>.vault.azure.net/secrets?api-version=7.4`. |
|
||||
| `Forbidden — request was not authorized` (vault using legacy access policies) | The vault hasn't been switched to Azure RBAC. Under the vault's **Access configuration**, set permission model to **Azure role-based access control** and re-grant the role from Step 3. |
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that Azure Key Vault is connected, head to [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage) to:
|
||||
|
||||
- Grant org members the right permissions to use (or manage) Secrets Manager.
|
||||
- Reference your Azure secrets from CrewAI Platform environment variables.
|
||||
|
||||
If you want **rotation-aware** secrets that propagate without re-deploying, switch to [Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity) — same vault, no client secret to rotate, secrets are fetched per kickoff.
|
||||
|
||||
## Screenshot Reference
|
||||
|
||||
The placeholders above map to:
|
||||
|
||||
- `01-register-app.png` — Azure portal "Register an application" form filled with `crewai-secrets-reader`.
|
||||
- `02-create-client-secret.png` — App Registration → Certificates & secrets → Client secrets, with the freshly-created secret row visible (Value column highlighted before it gets masked).
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, with **Key Vault Secrets User** picked and the App Registration selected as a member.
|
||||
- `04-per-secret-rbac.png` — Same panel but scoped to a single secret resource (alternative least-privilege path).
|
||||
- `05-amp-add-credential-form-azure.png` — CrewAI Platform "Add Secret Provider Credential" form: Provider = Azure Key Vault, all five fields populated.
|
||||
- `06-create-secret.png` — Azure Key Vault "Create a secret" panel with `openai-api-key` and a pasted value.
|
||||
- `07-test-connection-success.png` — CrewAI Platform success toast / row state after clicking **Test Connection** on the credential.
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: GCP Workload Identity Federation
|
||||
description: Configure Google Cloud Secret Manager via Workload Identity Federation for rotation-aware, credential-free secret access
|
||||
sidebarTitle: GCP — Workload Identity
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide configures Google Cloud Secret Manager as a secret provider using **Workload Identity Federation**: CrewAI Platform mints short-lived OIDC tokens, exchanges them for Google Cloud credentials via the Security Token Service, and reads your secrets — without a long-lived service account key being stored anywhere.
|
||||
|
||||
<Note>
|
||||
**Why this path:** secrets are resolved at automation execution time, so **rotated values propagate to the next kickoff with no re-deploy**. If you only need static credentials, see the simpler [GCP — service account key](/en/enterprise/features/secrets-manager/gcp) guide.
|
||||
</Note>
|
||||
|
||||
### How it works at runtime
|
||||
|
||||
1. The deployment worker requests a fresh OIDC JWT from CrewAI Platform.
|
||||
2. The worker exchanges the JWT for a federated Google credential via the [Security Token Service](https://cloud.google.com/iam/docs/reference/sts/rest), referencing the Workload Identity Pool Provider you set up below.
|
||||
3. The worker calls `secretmanager.googleapis.com:accessSecretVersion` to read the secret, using the federated credential directly (the federated principal holds `roles/secretmanager.secretAccessor` — see Step 4).
|
||||
4. The fetched value is injected as the environment variable's value for that automation kickoff.
|
||||
|
||||
OIDC subject tokens are cached for ~1 hour to avoid re-issuing on every kickoff. Secret values are fetched fresh on every kickoff regardless of OIDC cache state, which is what makes this path rotation-aware.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- The automation pod image must include CrewAI runtime version `1.14.5` or later.
|
||||
- A Google Cloud project with the **Secret Manager API**, **Security Token Service API**, and **IAM Credentials API** enabled. Enable them via the console or:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com sts.googleapis.com iamcredentials.googleapis.com \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
- Permission in the project to create Workload Identity Pools, IAM roles, service accounts, and (if needed) secrets.
|
||||
- A CrewAI Platform organization where your user has the `workload_identity_configs: manage` and `secret_providers: manage` permissions. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **Your CrewAI Platform installation must be reachable from Google Cloud over HTTPS** so that GCP STS can fetch the OIDC discovery document and JWKS during token validation. Confirm with your platform administrator that the host is internet-accessible.
|
||||
</Note>
|
||||
|
||||
## Step 1 — Find Your CrewAI Platform OIDC Issuer URL
|
||||
|
||||
Your CrewAI Platform installation publishes an OpenID Connect discovery document at `https://<your-platform-host>/.well-known/openid-configuration`. The `issuer` field there is the URL Google will register as a trusted OIDC provider.
|
||||
|
||||
Open the URL in a browser:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
You should see JSON containing:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Note the exact value of `issuer` — you'll use it in Step 3.
|
||||
|
||||
<Tip>
|
||||
If the URL returns 404 or 503, contact your platform administrator. The OIDC issuer requires a private signing key to be configured at install time. See the platform's installation guide for the `OIDC_PRIVATE_KEY` and `OIDC_ISSUER` configuration.
|
||||
</Tip>
|
||||
|
||||
## Step 2 — Create a Workload Identity Pool
|
||||
|
||||
A Workload Identity Pool is a Google Cloud-side container for trusted external identities. You'll register CrewAI Platform as a provider inside this pool.
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools create crewai-pool \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--display-name="CrewAI Platform"
|
||||
```
|
||||
|
||||
Or in the [Workload Identity Pools console](https://console.cloud.google.com/iam-admin/workload-identity-pools), click **Create Pool**.
|
||||
|
||||
{/* SCREENSHOT: GCP "Create Workload Identity Pool" form with name "crewai-pool" → /images/secrets-manager/gcp-wi/01-create-pool.png */}
|
||||
|
||||
## Step 3 — Add CrewAI Platform as an OIDC Provider in the Pool
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--display-name="CrewAI Platform OIDC" \
|
||||
--issuer-uri="https://<your-platform-host>" \
|
||||
--attribute-mapping="google.subject=assertion.sub,attribute.organization=assertion.organization_id" \
|
||||
--attribute-condition="assertion.organization_id != ''"
|
||||
```
|
||||
|
||||
The `--attribute-mapping` tells Google how to map JWT claims into Google attributes:
|
||||
- `google.subject` is the principal identifier — we map it to the JWT's `sub` claim, which CrewAI Platform sets to `organization:<uuid>`.
|
||||
- `attribute.organization` is a custom attribute — we map it to the JWT's `organization_id` claim so you can reference it in IAM bindings later.
|
||||
|
||||
The `--attribute-condition` is a defense-in-depth check that rejects tokens missing an `organization_id` claim.
|
||||
|
||||
Get the **provider resource name** (you'll need it for the audience and IAM bindings):
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers describe crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--format="value(name)"
|
||||
```
|
||||
|
||||
Output looks like:
|
||||
|
||||
```
|
||||
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider
|
||||
```
|
||||
|
||||
This is your **Workload Identity Provider** value for CrewAI Platform in Step 6. CrewAI Platform automatically computes the OIDC audience as `//iam.googleapis.com/<this-resource-name>` when issuing tokens.
|
||||
|
||||
{/* SCREENSHOT: "Add provider to pool" form with OIDC selected, issuer URI, audience defaults, attribute mapping → /images/secrets-manager/gcp-wi/02-add-oidc-provider.png */}
|
||||
|
||||
## Step 4 — Grant Secret Manager Access to the Federated Principal
|
||||
|
||||
Bind both Secret Manager roles at project scope to the federated principal — one role enables the Secret Name autocomplete in the env-var form, the other allows reading secret values at automation kickoff. Both are required for the feature to work end-to-end.
|
||||
|
||||
```bash
|
||||
PRINCIPAL_SET="principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/attribute.organization/<YOUR_CREWAI_ORG_UUID>"
|
||||
|
||||
# Required for the Secret Name autocomplete (calls secretmanager.secrets.list)
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.viewer"
|
||||
|
||||
# Required to read secret values at kickoff
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
Replace `<PROJECT_NUMBER>` with the numeric project number (`gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)'`) and `<YOUR_CREWAI_ORG_UUID>` with the UUID of the CrewAI Platform organization that should be allowed to read your secrets. You can find the org UUID in the platform UI on the organization's settings page, or via the API. This scopes federation to a specific CrewAI organization — only tokens minted for that org's automations are accepted.
|
||||
|
||||
Or via the Google Cloud console:
|
||||
|
||||
1. Open **IAM & Admin** → **IAM** for your project.
|
||||
2. Click **GRANT ACCESS**.
|
||||
3. **New principals:** paste the full `principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID>` string.
|
||||
4. Assign role **Secret Manager Viewer** (`roles/secretmanager.viewer`).
|
||||
5. Click **SAVE**.
|
||||
6. Click **GRANT ACCESS** again and repeat with role **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
<Tip>
|
||||
**Per-org isolation.** The `principalSet://...attribute.organization/<UUID>` pattern restricts access to a specific organization's tokens. If you have multiple CrewAI organizations sharing one Google Cloud project, repeat both bindings per-org with the correct UUID — or use a less restrictive attribute condition if isolation isn't needed.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Scoping `secretAccessor` per-secret (optional).** If you'd rather not grant `roles/secretmanager.secretAccessor` project-wide, omit the second binding above and bind per-secret instead:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <SECRET_NAME> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
Keep `roles/secretmanager.viewer` at the project scope either way — `secretmanager.secrets.list` (which the autocomplete relies on) cannot be granted per-secret.
|
||||
</Tip>
|
||||
|
||||
## Step 5 — Create at Least One Secret in GCP
|
||||
|
||||
If you don't already have a secret to test against, create one via the `gcloud` CLI:
|
||||
|
||||
```bash
|
||||
echo -n "hello from gcp" | gcloud secrets create crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
Or via the [Secret Manager console](https://console.cloud.google.com/security/secret-manager):
|
||||
|
||||
1. Open **Secret Manager** in your GCP project.
|
||||
2. Click **+ CREATE SECRET**.
|
||||
3. **Name:** `crewai-test-keyword`. **Secret value:** paste your value.
|
||||
4. Click **CREATE SECRET**.
|
||||
|
||||
## Step 6 — Add a Workload Identity Configuration in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Workload Identity** and click **Add Workload Identity Config**.
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `gcp-prod`.
|
||||
- **Cloud Provider:** `GCP`.
|
||||
- **Workload Identity Provider:** the provider resource name from Step 3, e.g. `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider`.
|
||||
- (Optional) Toggle **Default Configuration** if you'd like this to be the default WI config selected when creating a GCP-backed secret credential.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with GCP and provider resource name → /images/secrets-manager/gcp-wi/03-amp-add-wi-config-gcp.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing both AWS and GCP rows → /images/secrets-manager/gcp-wi/04-amp-wi-list-with-gcp.png */}
|
||||
|
||||
## Step 7 — Add a Secret Provider Credential Bound to the WI Config
|
||||
|
||||
Navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `gcp-prod-wi`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** select the config you created in Step 6.
|
||||
- **Project ID:** your GCP project ID (the same project that owns the secrets).
|
||||
- (Optional) Check **Set as default credential for this provider**.
|
||||
|
||||
The form will only ask for **Project ID** under Workload Identity — the **Service Account JSON** field is intentionally hidden because it doesn't apply to this path; the federated identity comes from the linked WI config.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP + Workload Identity + WI config dropdown → /images/secrets-manager/gcp-wi/05-amp-add-credential-gcp-wi.png */}
|
||||
|
||||
## Step 8 — Test the Connection
|
||||
|
||||
After saving the credential, click **Test Connection**. For workload-identity credentials this verifies the OIDC handshake: CrewAI Platform mints a JWT and exchanges it via the Security Token Service for a federated Google access token. A green result means the federation binding is healthy.
|
||||
|
||||
A successful Test Connection proves the Workload Identity Pool, OIDC provider, attribute mapping, and attribute condition are all wired correctly. It does **not** prove Secret Manager IAM is correct — `secretmanager.secrets.list` and `secretmanager.versions.access` are exercised separately when the Secret Name autocomplete loads or when an environment variable resolves at kickoff. See [Troubleshooting](#troubleshooting) for handshake failure modes.
|
||||
|
||||
## Step 9 — Reference the Secret in an Environment Variable
|
||||
|
||||
Reference the secret on an automation, exactly as you would for any other Secrets Manager-backed env var. See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for the form fields and behavior.
|
||||
|
||||
## Step 10 — Verify Rotation
|
||||
|
||||
After the deployment is running, rotate the secret in GCP by adding a new version (Secret Manager always reads the latest enabled version by default):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
Trigger a new automation kickoff. The kickoff's environment will see `"rotated value"` — no re-deploy, no worker restart, no TTL wait.
|
||||
|
||||
To confirm in worker logs, look for:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (gcp): N secret(s) resolved
|
||||
```
|
||||
|
||||
This line appears for every kickoff and indicates a fresh `accessSecretVersion` call against GCP.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| Test Connection fails with a handshake error | The STS token exchange was rejected. Verify the Workload Identity Pool exists, the OIDC provider's issuer matches the platform's `issuer` value, and the attribute condition accepts the JWT's claims. Confirm the platform's OIDC discovery URL is reachable from GCP over the public internet. |
|
||||
| `Could not refresh access token: invalid_target` | The audience claim doesn't match the Workload Identity Provider's expected audience. CrewAI Platform sets the audience automatically; if you customized it, ensure it matches `//iam.googleapis.com/<provider-resource-name>`. |
|
||||
| `Failed to fetch JWKS from issuer` | GCP STS can't reach your CrewAI Platform host. Confirm the host is internet-accessible and `/.well-known/openid-configuration` returns 200. |
|
||||
| `Attribute condition rejected token` | The OIDC provider's attribute condition (Step 3) requires `organization_id`. CrewAI Platform always sets this claim, so this usually means a misconfigured pool/provider. Re-check the provider's attribute condition. |
|
||||
| Secret Name autocomplete shows `PERMISSION_DENIED: secretmanager.secrets.list` | The federated principal is missing `roles/secretmanager.viewer` at project scope. The `secretmanager.secrets.list` permission is project-scoped only and cannot be granted per-secret. See Step 4. |
|
||||
| Kickoff fails to resolve a secret even though Test Connection passes | The WI binding is healthy, but `secretmanager.versions.access` is missing on the failing secret. Audit `roles/secretmanager.secretAccessor` (project-scoped, or per-secret if you scoped it that way in Step 4). |
|
||||
| Rotated value isn't picked up on the next kickoff | Confirm the env var on the automation is referencing a Workload Identity-backed credential (not a static-keys credential). The static path bakes values into the deploy image. |
|
||||
|
||||
### Reference Links
|
||||
|
||||
- GCP: [Workload Identity Federation overview](https://cloud.google.com/iam/docs/workload-identity-federation)
|
||||
- GCP: [Configure Workload Identity Federation with OIDC](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
|
||||
- GCP: [Secret Manager IAM roles](https://cloud.google.com/secret-manager/docs/access-control)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Use secrets in environment variables and manage permissions](/en/enterprise/features/secrets-manager/usage)
|
||||
- For multi-cloud, see also [AWS Workload Identity (OIDC Federation)](/en/enterprise/features/secrets-manager/aws-workload-identity) and [Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
188
docs/en/enterprise/features/secrets-manager/gcp.mdx
Normal file
188
docs/en/enterprise/features/secrets-manager/gcp.mdx
Normal file
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: Google Cloud Secret Manager
|
||||
description: Configure Google Cloud Secret Manager as a secret provider for CrewAI Platform, end-to-end
|
||||
sidebarTitle: GCP
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide walks you through configuring Google Cloud Secret Manager as a secret provider for your CrewAI Platform organization, using **service account credentials**. By the end, CrewAI Platform will be able to read secrets stored in your Google Cloud project and inject them as environment variable values at runtime.
|
||||
|
||||
<Note>
|
||||
This guide covers the **static credentials** path — secrets are resolved at deploy time and baked into the deployment image. Rotated values require a re-deploy. If you want rotation-aware secrets that update on every automation kickoff, see [GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
This guide covers the GCP-side configuration and the credential setup in CrewAI Platform. To then reference a secret from an environment variable, see [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
<Note>
|
||||
Before starting, make sure you have:
|
||||
|
||||
- A Google Cloud project with the **Secret Manager API** enabled. Enable it in the [APIs & Services console](https://console.cloud.google.com/apis/library/secretmanager.googleapis.com) or via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com --project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
- Permission in the project to create service accounts, grant IAM roles, and (if needed) create secrets.
|
||||
- A CrewAI Platform organization where your user has the `secret_providers: manage` permission. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Step 1 — Create a Service Account
|
||||
|
||||
A service account is the GCP-side identity CrewAI Platform will authenticate as.
|
||||
|
||||
In the [IAM & Admin → Service Accounts console](https://console.cloud.google.com/iam-admin/serviceaccounts), click **Create Service Account**.
|
||||
|
||||
- **Service account name:** `crewai-secrets-reader`
|
||||
- **Service account ID:** auto-fills from the name (e.g. `crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com`)
|
||||
- **Description (optional):** "Read-only access to Secret Manager for CrewAI Platform"
|
||||
|
||||
Click **Create and Continue**. Skip the optional grants on this screen — you'll attach the role in Step 2. Click **Done**.
|
||||
|
||||
For full details, see the GCP documentation: [Create service accounts](https://cloud.google.com/iam/docs/service-accounts-create).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create service account" form with name "crewai-secrets-reader" → /images/secrets-manager/gcp/01-create-service-account.png */}
|
||||
|
||||
## Step 2 — Grant Secret Manager Access
|
||||
|
||||
CrewAI Platform needs permission to list and read secrets in your project. Use one of two scopes — **project-wide** for simplicity, or **per-secret** for least privilege.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Project-wide (simpler)">
|
||||
In the [IAM console](https://console.cloud.google.com/iam-admin/iam), click **Grant Access** and:
|
||||
|
||||
- **New principals:** the service account's email from Step 1.
|
||||
- **Role:** **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
Click **Save**.
|
||||
|
||||
Or via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: GCP IAM "Grant access" panel with the service account and Secret Manager Secret Accessor role → /images/secrets-manager/gcp/02-iam-grant-access.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Per-secret (least privilege)">
|
||||
Grant the role only on the specific secrets CrewAI Platform should access. Repeat for each secret:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding YOUR_SECRET_NAME \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
Or in the console: open each secret in [Secret Manager](https://console.cloud.google.com/security/secret-manager), click **Permissions** in the right panel, and grant **Secret Manager Secret Accessor** to the service account.
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Permissions" panel in Secret Manager with the service account granted accessor role → /images/secrets-manager/gcp/03-per-secret-permissions.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
The `roles/secretmanager.secretAccessor` role grants read-only access to secret values. CrewAI Platform also calls `secretmanager.secrets.list` for the autocomplete experience in the env-var form — that permission is included in the role at the project scope, but **not** at the per-secret scope. With per-secret bindings, autocomplete won't suggest secrets; you'll need to type the full secret name.
|
||||
</Tip>
|
||||
|
||||
## Step 3 — Create a Service Account Key
|
||||
|
||||
Open the service account from Step 1 in the [IAM & Admin → Service Accounts console](https://console.cloud.google.com/iam-admin/serviceaccounts).
|
||||
|
||||
- Click the **Keys** tab.
|
||||
- Click **Add Key** → **Create new key**.
|
||||
- **Key type:** JSON.
|
||||
- Click **Create**. The browser downloads a JSON file — keep it secure; it cannot be re-downloaded.
|
||||
|
||||
Or via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts keys create ./crewai-secrets-reader.json \
|
||||
--iam-account=crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
<Warning>
|
||||
The service account key is a long-lived static credential. Store it securely (in a password manager or your own secret store) and rotate it on a regular cadence. To eliminate static credentials entirely, use [GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity) instead.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: Service account "Keys" tab with the "Create new key" → JSON option → /images/secrets-manager/gcp/04-create-service-account-key.png */}
|
||||
|
||||
## Step 4 — Add the Credential in CrewAI Platform
|
||||
|
||||
In CrewAI Platform, navigate to **Settings** → **Secret Provider Credentials** and click **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Name:** A descriptive name, e.g. `gcp-prod`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Project ID:** Your GCP project ID (e.g. `my-crewai-prod`).
|
||||
- **Service Account JSON:** Paste the entire contents of the JSON file you downloaded in Step 3.
|
||||
- (Optional) Check **Set as default credential for this provider**. The default credential is used by environment variables that reference GCP secrets without specifying a credential explicitly.
|
||||
|
||||
Click **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP fields filled in → /images/secrets-manager/gcp/05-amp-add-credential-form-gcp.png */}
|
||||
|
||||
## Step 5 — Create at Least One Secret in GCP
|
||||
|
||||
If you don't already have secrets in GCP Secret Manager, create one now so you can verify the connection in Step 6.
|
||||
|
||||
In the [Secret Manager console](https://console.cloud.google.com/security/secret-manager), click **Create secret**.
|
||||
|
||||
- **Name:** A unique name, e.g. `openai-api-key`.
|
||||
- **Secret value:** Either paste a raw value or upload a file.
|
||||
- Leave the rotation, replication, and other settings at their defaults unless you have a specific requirement.
|
||||
|
||||
Click **Create secret**.
|
||||
|
||||
Or via `gcloud`:
|
||||
|
||||
```bash
|
||||
echo -n "sk-your-actual-key" | gcloud secrets create openai-api-key \
|
||||
--data-file=- \
|
||||
--project=YOUR_PROJECT_ID \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
<Note>
|
||||
**JSON-key reference syntax.** GCP Secret Manager treats secret values as opaque blobs. If your secret value happens to be a JSON string, CrewAI Platform can extract a single field using the `secret-name#json_key` syntax (e.g. `database-credentials#password`). See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) for details.
|
||||
</Note>
|
||||
|
||||
For full details, see the GCP documentation: [Create a secret](https://cloud.google.com/secret-manager/docs/create-secret-quickstart).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create secret" form with name and value → /images/secrets-manager/gcp/06-create-secret.png */}
|
||||
|
||||
## Step 6 — Test the Connection
|
||||
|
||||
Back in CrewAI Platform, on the **Secret Provider Credentials** page, find the credential you just created and click **Test Connection**.
|
||||
|
||||
A success toast confirms that CrewAI Platform can authenticate to GCP and read secrets from your project.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the GCP credential → /images/secrets-manager/gcp/07-test-connection-success.png */}
|
||||
|
||||
If the test fails, check the most common causes:
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| `PERMISSION_DENIED` on listing secrets | Service account is missing `roles/secretmanager.secretAccessor`, or you scoped it per-secret (`list` is not granted). Re-check Step 2. |
|
||||
| `PERMISSION_DENIED` on `secretmanager.secrets.access` | Same as above, but for a specific secret. Confirm the service account has accessor role on the secret in question. |
|
||||
| `unauthorized_client` / `invalid_grant` | The pasted Service Account JSON is invalid, expired, or for a deleted service account. Re-create the key (Step 3) and re-paste. |
|
||||
| `Project ID does not match` | The Project ID field in CrewAI Platform doesn't match the project that owns the service account / secrets. Re-check Step 4. |
|
||||
| `API not enabled` | Secret Manager API isn't enabled on the project. See Prerequisites. |
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that GCP is connected, head to [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage) to:
|
||||
|
||||
- Grant org members the right permissions to use (or manage) Secrets Manager.
|
||||
- Reference your GCP secrets from CrewAI Platform environment variables.
|
||||
|
||||
If you want **rotation-aware** secrets that propagate without re-deploying, switch to [GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity) — same secret store, no static credentials, secrets are fetched per kickoff.
|
||||
81
docs/en/enterprise/features/secrets-manager/overview.mdx
Normal file
81
docs/en/enterprise/features/secrets-manager/overview.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Secrets Manager Overview
|
||||
description: Connect external secret stores to CrewAI Platform and reference managed secrets from environment variables
|
||||
sidebarTitle: Overview
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Secrets Manager feature lets your organization connect an external secret store — AWS Secrets Manager, Google Cloud Secret Manager, or (coming soon) Azure Key Vault — and reference those secrets directly from environment variables on your automations and crews. Instead of pasting plaintext values into the platform, you store one set of credentials per provider and refer to secrets by name.
|
||||
|
||||
This gives you:
|
||||
|
||||
- **Centralized storage** — manage secrets in your provider rather than editing CrewAI Platform configuration. CrewAI Platform keeps no plaintext copy of the secret value.
|
||||
- **Reduced exposure** — sensitive values never live in plaintext in your CrewAI Platform configuration.
|
||||
- **Cloud-native auditability** — your provider's audit log records every secret read.
|
||||
|
||||
<Note>
|
||||
Secrets Manager (both the static-credentials and Workload Identity paths) requires CrewAI runtime version `1.14.5` or later in the automation pod image.
|
||||
</Note>
|
||||
|
||||
## Two Paths: Static Credentials vs Workload Identity
|
||||
|
||||
There are two ways to wire CrewAI Platform up to your cloud's secret store. **They differ significantly in rotation behavior**, so choose based on how often your secrets rotate and how strict your security posture is.
|
||||
|
||||
| Aspect | Static Credentials | Workload Identity (OIDC Federation) |
|
||||
|---|---|---|
|
||||
| **Authentication** | Long-lived access keys / service account JSON stored in CrewAI Platform | Short-lived tokens minted per worker process; no static credentials stored anywhere |
|
||||
| **Rotation propagation** | Resolved at deploy time and **baked into the deployment's container image** — rotated values require a re-deploy | Resolved at **automation execution time** — rotated values propagate to the next kickoff with no re-deploy |
|
||||
| **Setup effort** | Lower — paste keys / upload service account JSON | Higher — register CrewAI Platform as an OIDC provider in your cloud, configure trust policies |
|
||||
| **Best for** | Getting started, infrequently-rotated secrets, single-account deployments | Production, frequently-rotated secrets, compliance-driven environments that prohibit long-lived credentials |
|
||||
|
||||
<Note>
|
||||
**Both paths use the same UI flow** to reference secrets in environment variables (see [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage)). The difference is entirely in how the platform authenticates to your cloud and when it reads the secret value.
|
||||
</Note>
|
||||
|
||||
### Choose your setup guide
|
||||
|
||||
| Provider | Static Credentials | Workload Identity |
|
||||
|---|---|---|
|
||||
| AWS Secrets Manager | [AWS — static keys / AssumeRole](/en/enterprise/features/secrets-manager/aws) | [AWS — Workload Identity (OIDC)](/en/enterprise/features/secrets-manager/aws-workload-identity) |
|
||||
| Google Cloud Secret Manager | [GCP — service account key](/en/enterprise/features/secrets-manager/gcp) | [GCP — Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity) |
|
||||
| Azure Key Vault | [Azure — client secret](/en/enterprise/features/secrets-manager/azure) | [Azure — Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity) |
|
||||
|
||||
<Note>
|
||||
The Secrets Manager and Workload Identity UIs are currently labeled **Beta** in CrewAI Platform.
|
||||
</Note>
|
||||
|
||||
## How It Fits Together
|
||||
|
||||
Setting up Secrets Manager is a three-step flow that involves both your cloud provider and CrewAI Platform:
|
||||
|
||||
1. **An admin configures a provider credential.** This is the cloud-side work — and the work differs depending on which path (static credentials or Workload Identity) you choose. Provider-specific guides cover this end to end.
|
||||
2. **An admin (or a permitted member) references a secret in an environment variable.** From the Environment Variables page, the user picks a provider credential and selects the secret name. See [Using the Secrets Manager](/en/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables).
|
||||
3. **The automation receives the resolved value at runtime.** When a crew or automation runs, CrewAI Platform fetches the secret from your provider and injects it as the environment variable's value. With Workload Identity, this fetch happens on every kickoff (rotation-aware). With static credentials, this fetch happens at deploy time and the value is baked into the deployment image.
|
||||
|
||||
## Permissions
|
||||
|
||||
Two CrewAI Platform features control access to Secrets Manager:
|
||||
|
||||
- `secret_providers` — controls who can view or manage provider credentials.
|
||||
- `environment_variables` — controls who can create and edit environment variables (including those that reference secrets).
|
||||
|
||||
A third feature controls Workload Identity setup:
|
||||
|
||||
- `workload_identity_configs` — controls who can view or manage Workload Identity configurations. Required only if you're using the Workload Identity path.
|
||||
|
||||
Owners always have full access. Members do **not** receive access to `secret_providers` or `workload_identity_configs` by default and must be granted permission via a custom role. See [Permissions (RBAC)](/en/enterprise/features/secrets-manager/usage#permissions-rbac) for the full matrix and step-by-step instructions.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Pick your path:
|
||||
|
||||
- **Static credentials** (simpler, requires re-deploy on rotation):
|
||||
- [Configure AWS Secrets Manager](/en/enterprise/features/secrets-manager/aws)
|
||||
- [Configure Google Cloud Secret Manager](/en/enterprise/features/secrets-manager/gcp)
|
||||
- [Configure Azure Key Vault](/en/enterprise/features/secrets-manager/azure)
|
||||
- **Workload Identity** (rotation-aware, no re-deploy):
|
||||
- [Configure AWS Workload Identity](/en/enterprise/features/secrets-manager/aws-workload-identity)
|
||||
- [Configure GCP Workload Identity Federation](/en/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- [Configure Azure Workload Identity Federation](/en/enterprise/features/secrets-manager/azure-workload-identity)
|
||||
- Then: [Use secrets in environment variables and manage permissions](/en/enterprise/features/secrets-manager/usage)
|
||||
136
docs/en/enterprise/features/secrets-manager/usage.mdx
Normal file
136
docs/en/enterprise/features/secrets-manager/usage.mdx
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Using the Secrets Manager
|
||||
description: Manage permissions and reference managed secrets from environment variables in CrewAI Platform
|
||||
sidebarTitle: Usage & Permissions
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide is provider-agnostic. It assumes you (or another admin) have already configured at least one Secret Provider Credential. Pick your setup guide based on the path you want:
|
||||
|
||||
- Static credentials: [AWS](/en/enterprise/features/secrets-manager/aws) · [GCP](/en/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (rotation-aware): [AWS](/en/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/en/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
|
||||
Use this guide to:
|
||||
|
||||
- Grant the right permissions to org members.
|
||||
- Reference secrets from environment variables on your automations.
|
||||
- Verify everything resolves correctly at runtime.
|
||||
|
||||
## Permissions (RBAC)
|
||||
|
||||
Three CrewAI Platform features are relevant when working with Secrets Manager:
|
||||
|
||||
- `secret_providers` — controls access to the **Secret Provider Credentials** page.
|
||||
- `workload_identity_configs` — controls access to the **Workload Identity** page (only relevant if you use the WI path).
|
||||
- `environment_variables` — controls who can create or edit environment variables.
|
||||
|
||||
Each feature has two action levels: `read` and `manage`. Granting `manage` automatically implies `read`.
|
||||
|
||||
### What to Grant
|
||||
|
||||
| Goal | `secret_providers` | `workload_identity_configs` | `environment_variables` |
|
||||
|---|---|---|---|
|
||||
| Use existing static credentials in environment variables (no provider edits) | `read` | — | `manage` |
|
||||
| Create, edit, or delete static credentials | `manage` | — | `manage` |
|
||||
| Use existing Workload Identity-backed credentials in env vars | `read` | — | `manage` |
|
||||
| Create, edit, or delete Workload Identity configs (and credentials referencing them) | `manage` | `manage` | `manage` |
|
||||
|
||||
<Note>
|
||||
**Owners** automatically have full access to every feature. The default **Member** role intentionally excludes `secret_providers` and `workload_identity_configs` — admins must explicitly opt members in via a custom role.
|
||||
</Note>
|
||||
|
||||
### How to Assign
|
||||
|
||||
1. In CrewAI Platform, navigate to **Settings** → **Roles**. From this page you can create new roles, edit each role's permissions, and assign roles to existing members of the organization.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Roles → /images/secrets-manager/usage/06-amp-settings-roles-nav.png */}
|
||||
{/* SCREENSHOT: Roles list page with "Create Role" button visible → /images/secrets-manager/usage/07-amp-roles-list.png */}
|
||||
|
||||
2. Click **Create Role** to make a new role, or open an existing role to edit its permissions.
|
||||
|
||||
3. In the role's permission editor, toggle the relevant features per the table above:
|
||||
|
||||
- `secret_providers`: choose **read** if this role only needs to use existing credentials, or **manage** if it should also be able to create, edit, and delete credentials.
|
||||
- `environment_variables`: choose **manage** so the role can create environment variables that reference secrets.
|
||||
|
||||
{/* SCREENSHOT: Role editor showing the secret_providers feature with read/manage toggles → /images/secrets-manager/usage/08-amp-role-editor-secret-providers-toggles.png */}
|
||||
{/* SCREENSHOT: Role editor showing environment_variables toggles → /images/secrets-manager/usage/09-amp-role-editor-env-vars-toggles.png */}
|
||||
|
||||
4. Save the role.
|
||||
|
||||
5. Assign the role to the relevant members from the same Roles page (or the org Members list).
|
||||
|
||||
{/* SCREENSHOT: Member assignment screen where the new role is applied to a user → /images/secrets-manager/usage/10-amp-assign-role-to-member.png */}
|
||||
|
||||
## Referencing Secrets in Environment Variables
|
||||
|
||||
Once a provider credential exists and your role has the right permissions, you can reference managed secrets from any environment variable.
|
||||
|
||||
In CrewAI Platform, navigate to **Environment Variables** and click **Add Environment Variables**.
|
||||
|
||||
{/* SCREENSHOT: Environment Variables empty state with "Add" button → /images/secrets-manager/usage/11-amp-env-vars-empty.png */}
|
||||
|
||||
Fill the form:
|
||||
|
||||
- **Key** — the name of the environment variable. Must start with a letter or underscore and contain only letters, numbers, and underscores. Conventionally uppercase, e.g. `OPENAI_API_KEY`.
|
||||
|
||||
- **Value Source** — choose where the value comes from:
|
||||
- **Direct Value** — a plaintext value you type in. Use this when you do not want to involve a provider.
|
||||
- **Use AWS default** (or the equivalent for your provider) — uses the credential currently marked as the default for that provider type.
|
||||
- **A specific named credential** — select the credential by name. Use this if you have multiple credentials for the same provider (for example, `aws-prod` and `aws-staging`) and want to pick one explicitly.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the "Value Source" dropdown open, showing "AWS default" + named credentials → /images/secrets-manager/usage/12-amp-env-var-form-source-selector.png */}
|
||||
|
||||
- **Secret Name** — the name of the secret in your provider. Once a credential is selected, this field offers autocomplete: start typing and CrewAI Platform queries your provider for matching secret names.
|
||||
|
||||
Use the `secret-name#json_key` syntax to extract a single field from a structured (JSON) secret. For example, given a secret `database-credentials` with value `{"username": "...", "password": "..."}`, reference `database-credentials#password` to inject just the password.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the secret name autocomplete dropdown showing live results → /images/secrets-manager/usage/13-amp-env-var-form-secret-name-autocomplete.png */}
|
||||
|
||||
<Note>
|
||||
**Azure Key Vault note:** Azure secret names cannot contain underscores. CrewAI Platform automatically converts underscores in your `Secret Name` field to hyphens when calling Azure (e.g., `db_password` is sent as `db-password`).
|
||||
</Note>
|
||||
|
||||
Click **Create** to save the variable.
|
||||
|
||||
{/* SCREENSHOT: Env var list with the new variable showing masked value and a "secret" indicator → /images/secrets-manager/usage/14-amp-env-var-created.png */}
|
||||
|
||||
<Tip>
|
||||
When editing an existing environment variable, leaving the **Value** field blank preserves the current value. This is intentional — it lets you change other fields (like the secret name or credential) without re-entering the value.
|
||||
</Tip>
|
||||
|
||||
## Verifying It Works
|
||||
|
||||
To verify end-to-end:
|
||||
|
||||
1. Reference the environment variable on an automation, crew, or deployment exactly as you would any other environment variable.
|
||||
2. Deploy the automation.
|
||||
3. Trigger a run and confirm it completes successfully.
|
||||
|
||||
### Rotation behavior depends on the credential path
|
||||
|
||||
| Credential path | When the secret is read | What rotation requires |
|
||||
|---|---|---|
|
||||
| **Static credentials** (AWS access keys, GCP service account JSON) | At **deploy time** — value is baked into the deployment image | Re-deploy the automation after rotating the secret |
|
||||
| **Workload Identity** (OIDC federation, AWS or GCP) | At **every automation kickoff** — value is fetched fresh from your cloud | Nothing — the next kickoff after rotation sees the new value |
|
||||
|
||||
<Note>
|
||||
**If you need rotation-aware secrets** (no re-deploy on rotation), use the Workload Identity path: [AWS WI](/en/enterprise/features/secrets-manager/aws-workload-identity) or [GCP WI](/en/enterprise/features/secrets-manager/gcp-workload-identity). The trade-off is more setup effort up front (registering CrewAI Platform as an OIDC provider in your cloud) but simpler operations long-term.
|
||||
</Note>
|
||||
|
||||
If the deploy or run fails with an error related to your secret, check the most common causes:
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---|---|
|
||||
| `no credential found` | The environment variable references a provider but no specific credential was selected, and there is no default credential set for that provider type. Either select a credential explicitly on the variable, or mark a credential as default on the **Secret Provider Credentials** page. |
|
||||
| `secret not found` | Typo in the **Secret Name**, or the secret does not exist in the provider account/region the credential points to. Re-check both. |
|
||||
| Automation runs with the old value after rotating (static-credentials path) | The previous value is baked into the deployment's container image. Re-deploy the automation to pick up the rotated value. To avoid this entirely, switch the credential to the Workload Identity path. |
|
||||
| Automation runs with the old value after rotating (Workload Identity path) | Confirm the env var references a WI-backed credential (not a static-keys one). With WI, the next kickoff after rotation should see the new value. If it doesn't, check that the secret was actually updated in your cloud (e.g., `aws secretsmanager get-secret-value`). |
|
||||
| `JSON key not found` | When using `secret-name#json_key`, the underlying secret must be a valid JSON object containing that key. Verify by reading the secret directly in your provider. |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Back to the Secrets Manager overview](/en/enterprise/features/secrets-manager/overview)
|
||||
- Static credentials: [AWS](/en/enterprise/features/secrets-manager/aws) · [GCP](/en/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (rotation-aware): [AWS](/en/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/en/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
260
docs/en/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
260
docs/en/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: Verify Rotation
|
||||
description: A self-contained example crew that proves secret rotation propagates to running deployments without re-deploy.
|
||||
sidebarTitle: Verify Rotation
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This guide shows you how to verify that **a secret rotated in your cloud provider is picked up on the very next automation kickoff** — no re-deploy, no worker restart. It's only relevant when you've configured a Workload Identity-backed credential ([AWS](/en/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/en/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/en/enterprise/features/secrets-manager/azure-workload-identity)). Static-credential deployments require a re-deploy after rotation; nothing to verify here.
|
||||
|
||||
The recipe below uses a tiny, self-contained crew with one tool, one agent, one task. The crew prompt never references the secret value — instead, a tool reads it from `os.environ` and reports a SHA-256 fingerprint of what it sees. Rotate the secret in your cloud provider, kickoff again, and the fingerprint changes.
|
||||
|
||||
<Note>
|
||||
Why a fingerprint, not the raw value? Putting raw secrets into LLM output and trace logs is a leak vector. The fingerprint is enough to confirm "the value changed" without writing the actual value anywhere observable.
|
||||
</Note>
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before running this verification:
|
||||
|
||||
- A WI-backed Secret Provider Credential is configured ([AWS](/en/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/en/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/en/enterprise/features/secrets-manager/azure-workload-identity)).
|
||||
- An environment variable on your deployment with `Secret = true`, key `API_KEY` (or whatever name you prefer — adjust the tool below to match), referencing a secret in your cloud provider.
|
||||
- A way to update the secret value in your cloud provider (CLI access or the cloud console).
|
||||
- A way to kickoff the deployment via HTTP (curl, Postman, or the **Run** tab in CrewAI Platform).
|
||||
|
||||
## Step 1 — Scaffold a Verification Crew
|
||||
|
||||
Create a new crew project. The CrewAI CLI scaffolds the structure:
|
||||
|
||||
```bash
|
||||
crewai create crew rotation_verifier --skip_provider
|
||||
cd rotation_verifier
|
||||
```
|
||||
|
||||
## Step 2 — Add the Credential Echo Tool
|
||||
|
||||
Replace `src/rotation_verifier/tools/custom_tool.py` with a tool that reads the secret-backed env var and returns a fingerprint:
|
||||
|
||||
```python src/rotation_verifier/tools/credential_echo_tool.py
|
||||
"""Tool that verifies a runtime-injected secret without leaking the value.
|
||||
|
||||
Reads the secret-backed env var (populated by the workload-identity
|
||||
secrets manager at kickoff time) and returns a stable fingerprint. Never
|
||||
echo raw credential values into LLM output or logs in production code —
|
||||
the fingerprint alone is sufficient to confirm rotation worked.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
# Match the deployment environment variable's `key` field.
|
||||
ENV_VAR_NAME = "API_KEY"
|
||||
|
||||
|
||||
class CredentialEchoTool(BaseTool):
|
||||
name: str = "credential_echo"
|
||||
description: str = (
|
||||
"Read the API credential from the worker's environment and return a "
|
||||
"fingerprint summary. Use this exactly once when asked to verify the "
|
||||
"current credential. Takes no arguments."
|
||||
)
|
||||
|
||||
def _run(self) -> str:
|
||||
value = os.environ.get(ENV_VAR_NAME)
|
||||
if not value:
|
||||
return (
|
||||
f"ERROR: {ENV_VAR_NAME} env var is not set. The workload-"
|
||||
"identity secret fetch did not run, or the deployment is "
|
||||
"missing the secret-backed env var."
|
||||
)
|
||||
fingerprint = hashlib.sha256(value.encode()).hexdigest()[:12]
|
||||
return f"Authenticated. credential.fingerprint=sha256:{fingerprint}"
|
||||
```
|
||||
|
||||
## Step 3 — Replace the Default Agent and Task Configs
|
||||
|
||||
The crew has one agent and one task — both with descriptions that **never** mention the secret value, so task keys stay stable across rotations.
|
||||
|
||||
```yaml src/rotation_verifier/config/agents.yaml
|
||||
credential_checker:
|
||||
role: >
|
||||
Credential Verifier
|
||||
goal: >
|
||||
Confirm that the workload-identity-backed secret reached this worker
|
||||
process and report a fingerprint of the current value.
|
||||
backstory: >
|
||||
You are a no-nonsense reliability engineer responsible for verifying
|
||||
that secrets fetched at runtime via workload identity are present
|
||||
and fresh. You always use the credential_echo tool exactly once and
|
||||
report the result verbatim — you never make up values.
|
||||
```
|
||||
|
||||
```yaml src/rotation_verifier/config/tasks.yaml
|
||||
verify_credential_task:
|
||||
description: >
|
||||
Use the credential_echo tool to read the runtime-injected credential
|
||||
and produce a one-line confirmation. The current year is {current_year}
|
||||
(use it only in the timestamp; do not transform the credential output).
|
||||
expected_output: >
|
||||
A single line in the form:
|
||||
"[{current_year}] <credential_echo tool's exact output>"
|
||||
agent: credential_checker
|
||||
```
|
||||
|
||||
## Step 4 — Wire the Crew Class
|
||||
|
||||
```python src/rotation_verifier/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
from rotation_verifier.tools.credential_echo_tool import CredentialEchoTool
|
||||
|
||||
|
||||
@CrewBase
|
||||
class RotationVerifierCrew():
|
||||
"""Single-task crew that verifies a workload-identity-backed secret
|
||||
was successfully fetched at runtime.
|
||||
|
||||
Rotate the underlying secret in the cloud provider, kickoff again, and
|
||||
the credential fingerprint in the agent's report changes — without any
|
||||
re-deploy, worker restart, or input change. The crew prompt itself
|
||||
never references the secret value.
|
||||
"""
|
||||
|
||||
agents: list[BaseAgent]
|
||||
tasks: list[Task]
|
||||
|
||||
@agent
|
||||
def credential_checker(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config["credential_checker"],
|
||||
tools=[CredentialEchoTool()],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
@task
|
||||
def verify_credential_task(self) -> Task:
|
||||
return Task(config=self.tasks_config["verify_credential_task"])
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
)
|
||||
```
|
||||
|
||||
## Step 5 — Deploy and Configure the Secret Env Var
|
||||
|
||||
Deploy this crew to CrewAI Platform exactly as you would any other crew. Then on the deployment's **Environment Variables** page:
|
||||
|
||||
- **Key:** `API_KEY` (must match `ENV_VAR_NAME` in the tool)
|
||||
- **Value Source:** the WI-backed credential you set up in [AWS WI](/en/enterprise/features/secrets-manager/aws-workload-identity) or [GCP WI](/en/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- **Secret Name:** the name of the secret in your cloud provider's Secret Manager
|
||||
|
||||
{/* SCREENSHOT: Environment Variables form with key=API_KEY, secret-backed value source selected, secret name filled → /images/secrets-manager/verify-rotation/01-env-var-form.png */}
|
||||
|
||||
## Step 6 — Run the First Kickoff
|
||||
|
||||
Replace `<DEPLOYMENT_AUTH_TOKEN>` and `<DEPLOYMENT_HOST>` with values from your deployment's **Run** tab.
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
When the kickoff completes (a few seconds), check the agent's output. You'll see:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:004421b993c9
|
||||
```
|
||||
|
||||
Note the fingerprint. That hash is uniquely tied to whatever secret value is currently in your cloud provider.
|
||||
|
||||
## Step 7 — Rotate the Secret in Your Cloud Provider
|
||||
|
||||
<Tabs>
|
||||
<Tab title="AWS">
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id <SECRET_NAME> \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="GCP">
|
||||
Add a new version (Secret Manager always reads `latest`):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add <SECRET_NAME> \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Azure">
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name <SECRET_NAME> \
|
||||
--value "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Step 8 — Run a Second Kickoff and Compare
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
The agent's output now shows a **different fingerprint**:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:e2fc89848f72
|
||||
```
|
||||
|
||||
This proves the rotation was picked up by the running deployment with no re-deploy, worker restart, or other operator action.
|
||||
|
||||
## What This Verifies — and What It Doesn't
|
||||
|
||||
**Verifies:**
|
||||
- WI OIDC token minting from CrewAI Platform works.
|
||||
- Cloud-side trust (IAM OIDC provider for AWS, Workload Identity Pool for GCP, Federated Identity Credential for Azure) accepts the token.
|
||||
- The cloud-side identity (IAM Role / GCP service account / Entra App Registration) has access to read the secret.
|
||||
- The secret value reaches `os.environ` of the worker process at kickoff time.
|
||||
- Subsequent rotations propagate to the next kickoff.
|
||||
|
||||
**Does not verify:**
|
||||
- That your real production crews handle the rotation gracefully — e.g., long-running tasks that read the env var once at startup will keep using the old value until the task ends. Plan accordingly: read secrets at the point of use, not at module import.
|
||||
|
||||
## Why Not Reference the Secret Directly in the Prompt?
|
||||
|
||||
A simpler-looking demo would put the secret value directly into a task description (e.g., "Research about `{api_key}`") and inspect the prompt. **Don't do that.** Two reasons:
|
||||
|
||||
1. **It leaks the secret into LLM call traces and provider-side logs.** Anyone with trace access can read it.
|
||||
2. **It changes the task's description at every kickoff.** CrewAI Platform identifies tasks by an MD5 hash of the description; a rotating value means the hash changes per kickoff, which breaks the deploy-time → runtime task mapping. Symptom: the task records show as `pending_run` indefinitely, or only some of a multi-task crew's tasks register.
|
||||
|
||||
The tool-based pattern in this guide sidesteps both issues: the prompt is static, the tool reads the env var at runtime, and only a fingerprint of the value reaches the LLM.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Back to the Secrets Manager overview](/en/enterprise/features/secrets-manager/overview)
|
||||
- Once verified, drop the verification crew. Real crews should follow the same pattern: secrets accessed via `os.environ` inside a tool, never substituted into prompts.
|
||||
@@ -146,7 +146,6 @@ Here's a typical workflow for creating a crew with Crew Studio:
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Answer Questions">
|
||||
Respond to clarifying questions from the Crew Assistant to refine your
|
||||
requirements.
|
||||
@@ -161,12 +160,10 @@ Here's a typical workflow for creating a crew with Crew Studio:
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Approve or Modify">
|
||||
Approve the plan or request changes if necessary.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Download or Deploy">
|
||||
Download the code for customization or deploy directly to the platform.
|
||||
</Step>
|
||||
|
||||
143
docs/en/guides/flows/inputs-id-deprecation.mdx
Normal file
143
docs/en/guides/flows/inputs-id-deprecation.mdx
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: "Migrating from inputs.id to restore_from_state_id"
|
||||
description: "Move @persist flows off the deprecated inputs.id hydration onto the supported restore_from_state_id field"
|
||||
icon: "arrow-right-arrow-left"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Passing `id` inside `inputs` to hydrate a `@persist` flow is **deprecated** and
|
||||
scheduled for removal in a future release. The replacement, `restore_from_state_id`,
|
||||
is available in CrewAI **v1.14.5 and later** — the steps below apply once you
|
||||
upgrade.
|
||||
</Warning>
|
||||
|
||||
## Overview
|
||||
|
||||
The documented way to hydrate a `@persist` flow from a previous execution is to pass
|
||||
that execution's UUID as `inputs.id`. CrewAI now exposes a dedicated field,
|
||||
`restore_from_state_id`, that performs the same hydration without overloading the
|
||||
`inputs` payload — and without coupling the hydration key to the new execution's
|
||||
identity.
|
||||
|
||||
## Migration
|
||||
|
||||
If you currently kickoff a `@persist` flow with `inputs={"id": ...}`:
|
||||
|
||||
```python
|
||||
# Deprecated
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(inputs={"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv"})
|
||||
```
|
||||
|
||||
Switch to `restore_from_state_id`:
|
||||
|
||||
```python
|
||||
# Supported
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(restore_from_state_id="abcd1234-5678-90ef-ghij-klmnopqrstuv")
|
||||
```
|
||||
|
||||
The two modes have different lineage semantics:
|
||||
|
||||
- `inputs={"id": <uuid>}` (deprecated) — **resume**: writes land under the supplied
|
||||
id, extending the same `flow_uuid` history.
|
||||
- `restore_from_state_id=<uuid>` — **fork**: hydrates state from the snapshot, then
|
||||
writes under a fresh `state.id`. The source flow's history is preserved.
|
||||
|
||||
For most production scenarios — re-running a flow seeded from a previous state — fork
|
||||
is what you want. See [Mastering Flow State](/en/guides/flows/mastering-flow-state)
|
||||
for the full mental model.
|
||||
|
||||
If you kickoff your flow over the CrewAI AMP REST API, see [AMP](#amp) below for the
|
||||
equivalent payload migration.
|
||||
|
||||
## Why we are deprecating `inputs.id` for `@persist`
|
||||
|
||||
`inputs.id` is currently the documented way to resume a `@persist` flow from a
|
||||
previous execution. The problem is that the same UUID does two jobs at once:
|
||||
|
||||
1. **It selects which snapshot `@persist` hydrates from** — load the state saved
|
||||
under that UUID.
|
||||
2. **It becomes the new execution's Flow Execution ID** (`state.id` in the SDK;
|
||||
surfaced as `flow_id` in some contexts) — every `@persist` write from this
|
||||
kickoff also lands under that same UUID.
|
||||
|
||||
This dual role is the root cause of the issues this guide describes. Because the
|
||||
supplied UUID is also the new execution's id, two kickoffs that pass the same
|
||||
`inputs.id` are not two distinct executions — they share an id, share a persistence
|
||||
record, and (on AMP) share a row in the executions list. There is no way to say
|
||||
"hydrate from this snapshot, but record this run separately" without splitting the
|
||||
two responsibilities.
|
||||
|
||||
`restore_from_state_id` is that split. It tells `@persist` which snapshot to hydrate
|
||||
from, while leaving the new execution free to receive a fresh `state.id`. The
|
||||
hydration source and the recorded run are no longer the same UUID — which is what
|
||||
most production scenarios actually want.
|
||||
|
||||
## Removal timeline
|
||||
|
||||
`inputs.id` for `@persist` hydration is scheduled for removal in a future release of
|
||||
CrewAI. There is no immediate hard cut-off — existing flows continue to work — but
|
||||
once you upgrade to v1.14.5 or later, new code should use `restore_from_state_id`, and
|
||||
existing flows should migrate at the next convenient opportunity.
|
||||
|
||||
## AMP
|
||||
|
||||
If you deploy your flow to CrewAI AMP, the migration extends to the kickoff payload
|
||||
sent to your deployed crew, and the visible symptoms of reusing `inputs.id` show up
|
||||
on the deployment dashboard. The two subsections below cover both.
|
||||
|
||||
### Migrating the kickoff payload
|
||||
|
||||
If you currently kickoff a deployed flow by embedding `id` in `inputs`:
|
||||
|
||||
```bash
|
||||
# Deprecated
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{"inputs": {"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv", "topic": "AI Agent Frameworks"}}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
Move the UUID to the top-level `restoreFromStateId` field:
|
||||
|
||||
```bash
|
||||
# Supported
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{
|
||||
"inputs": {"topic": "AI Agent Frameworks"},
|
||||
"restoreFromStateId": "abcd1234-5678-90ef-ghij-klmnopqrstuv"
|
||||
}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
`restoreFromStateId` sits next to `inputs` in the kickoff payload, not inside it. The
|
||||
`inputs` object now only carries values your flow actually consumes.
|
||||
|
||||
### What happens when `inputs.id` is reused
|
||||
|
||||
When AMP receives a kickoff for a flow whose `inputs.id` matches an existing
|
||||
execution, it resolves to the existing record rather than creating a new one. From
|
||||
the deployment dashboard you'll see:
|
||||
|
||||
- **Execution status** — the new run's status overwrites the previous run's. A
|
||||
finished execution can flip back to `running`, or a `completed` run can flip to
|
||||
`error` if the new kickoff fails — either way the dashboard no longer reflects
|
||||
the original run.
|
||||
- **Traces** — OTel traces stack across kickoffs because they share the same
|
||||
execution id; the previous run's traces are either replaced by, or mixed with,
|
||||
the new run's. A step-by-step replay no longer corresponds to a single execution.
|
||||
- **Executions list** — kickoffs that should appear as separate rows collapse into
|
||||
a single entry, hiding history.
|
||||
|
||||
Migrating to `restoreFromStateId` keeps every kickoff as its own execution — with
|
||||
its own status, traces, and row in the list — while still hydrating state from a
|
||||
previous run.
|
||||
|
||||
<Card title="Need Help?" icon="headset" href="mailto:support@crewai.com">
|
||||
Contact our support team if you're unsure which mode your flow needs or hit issues
|
||||
during the migration.
|
||||
</Card>
|
||||
@@ -313,9 +313,9 @@ flow1 = PersistentCounterFlow()
|
||||
result1 = flow1.kickoff()
|
||||
print(f"First run result: {result1}")
|
||||
|
||||
# Second run - state is automatically loaded
|
||||
# Second run - pass the ID to load the persisted state
|
||||
flow2 = PersistentCounterFlow()
|
||||
result2 = flow2.kickoff()
|
||||
result2 = flow2.kickoff(inputs={"id": flow1.state.id})
|
||||
print(f"Second run result: {result2}") # Will be higher due to persisted state
|
||||
```
|
||||
|
||||
|
||||
190
docs/en/guides/migration/upgrading-crewai.mdx
Normal file
190
docs/en/guides/migration/upgrading-crewai.mdx
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: "Upgrading CrewAI"
|
||||
description: "How to upgrade CrewAI in your project and adapt to breaking changes between versions."
|
||||
icon: "arrow-up-circle"
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
CrewAI releases ship new capabilities regularly. This guide walks you through the practical steps to keep your installation up to date — both the CLI and your project's virtual environment.
|
||||
|
||||
If you're starting fresh, see [Installation](/en/installation). If you're coming from another framework, see [Migrating from LangGraph](/en/guides/migration/migrating-from-langgraph).
|
||||
|
||||
---
|
||||
|
||||
## The Two Things You Might Want to Upgrade
|
||||
|
||||
CrewAI lives in two places on your machine, and they upgrade independently:
|
||||
|
||||
| What | How it's installed | How to upgrade |
|
||||
|---|---|---|
|
||||
| The **global `crewai` CLI** | `uv tool install crewai` | `uv tool install crewai --upgrade` |
|
||||
| The **project venv** (what your code runs) | `crewai install` / `uv sync` | `uv add "crewai[...]>=X.Y.Z"` then `crewai install` |
|
||||
|
||||
These can — and often do — get out of sync. Running `crewai --version` tells you the CLI version. Running `uv pip show crewai` inside your project tells you the venv version. If they differ, that's normal; what matters for your running code is the venv version.
|
||||
|
||||
## Why `crewai install` Alone Doesn't Upgrade
|
||||
|
||||
`crewai install` is a thin wrapper around `uv sync`. It installs exactly what the current `uv.lock` file says — it does **not** bump any version constraints.
|
||||
|
||||
If your `pyproject.toml` says `crewai>=1.11.1` and the lock file resolved to `1.11.1`, running `crewai install` will keep you on `1.11.1` forever, even if `1.14.4` is available.
|
||||
|
||||
To actually upgrade, you need to:
|
||||
|
||||
1. Update the version constraint in `pyproject.toml`
|
||||
2. Re-solve the lock file
|
||||
3. Sync the venv
|
||||
|
||||
`uv add` does all three in one shot.
|
||||
|
||||
## How to Upgrade Your Project
|
||||
|
||||
```bash
|
||||
# Bump the constraint and re-lock in one command
|
||||
uv add "crewai[tools]>=1.14.4"
|
||||
|
||||
# Sync the venv (crewai install calls uv sync under the hood)
|
||||
crewai install
|
||||
|
||||
# Verify
|
||||
uv pip show crewai
|
||||
# → Version: 1.14.4
|
||||
```
|
||||
|
||||
Replace `[tools]` with whatever extras your project uses (e.g. `[tools,anthropic]`). Check your `pyproject.toml` `dependencies` list if you're unsure.
|
||||
|
||||
<Note>
|
||||
`uv add` updates both `pyproject.toml` **and** `uv.lock` atomically. If you edit `pyproject.toml` manually, you still need to run `uv lock --upgrade-package crewai` to re-solve the lock file before `crewai install` will pick up the new version.
|
||||
</Note>
|
||||
|
||||
## Upgrading the Global CLI
|
||||
|
||||
The global CLI is separate from your project. Upgrade it with:
|
||||
|
||||
```bash
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
|
||||
If your shell warns about `PATH` after the upgrade, refresh it:
|
||||
|
||||
```bash
|
||||
uv tool update-shell
|
||||
```
|
||||
|
||||
This does **not** touch your project's venv — you still need `uv add` + `crewai install` inside the project.
|
||||
|
||||
## Verify Both Are in Sync
|
||||
|
||||
```bash
|
||||
# Global CLI version
|
||||
crewai --version
|
||||
|
||||
# Project venv version
|
||||
uv pip show crewai | grep Version
|
||||
```
|
||||
|
||||
They don't need to match — but your project venv version is what matters for runtime behavior.
|
||||
|
||||
<Note>
|
||||
CrewAI requires `Python >=3.10, <3.14`. If `uv` was installed against an older interpreter, recreate the project venv with a supported Python before running `crewai install`.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes & Migration Notes
|
||||
|
||||
Most upgrades only require small adjustments. The areas below are the ones that break silently or with confusing tracebacks.
|
||||
|
||||
### Import paths: tools and `BaseTool`
|
||||
|
||||
The canonical import location for tools is `crewai.tools`. Older paths still surface in tutorials but should be updated.
|
||||
|
||||
```python
|
||||
# Before
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.agents.tools import tool
|
||||
|
||||
# After
|
||||
from crewai.tools import BaseTool, tool
|
||||
```
|
||||
|
||||
The `@tool` decorator and `BaseTool` subclass both live in `crewai.tools`. `AgentFinish` and other internal-agent symbols are no longer part of the public surface — if you were importing them, switch to event listeners or `Task` callbacks instead.
|
||||
|
||||
### `Agent` parameter changes
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find authoritative sources on {topic}",
|
||||
backstory="You are a careful, source-driven researcher.",
|
||||
llm="gpt-4o-mini", # string model name OR an LLM object
|
||||
verbose=True, # bool, not an int level
|
||||
max_iter=15, # default has changed across versions — set explicitly
|
||||
allow_delegation=False,
|
||||
)
|
||||
```
|
||||
|
||||
- `llm` accepts either a string model name (resolved via the configured provider) or an `LLM` object for fine-grained control.
|
||||
- `verbose` is a plain `bool`. Passing an integer no longer toggles log levels.
|
||||
- `max_iter` defaults have shifted between releases. If your agent silently stops looping after the first tool call, set `max_iter` explicitly.
|
||||
|
||||
### `Crew` parameters
|
||||
|
||||
```python
|
||||
from crewai import Crew, Process
|
||||
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
process=Process.sequential, # or Process.hierarchical
|
||||
memory=True,
|
||||
cache=True,
|
||||
embedder={"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
)
|
||||
```
|
||||
|
||||
- `process=Process.hierarchical` requires either `manager_llm=` or `manager_agent=`. Without one, kickoff raises at validation time.
|
||||
- `memory=True` with a non-default embedding provider needs an `embedder` dict — see [Memory & embedder config](#memory-embedder-config) below.
|
||||
|
||||
### `Task` structured output
|
||||
|
||||
Use `output_pydantic`, `output_json`, or `output_file` to coerce a task's result into a typed shape:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from crewai import Task
|
||||
|
||||
class Article(BaseModel):
|
||||
title: str
|
||||
body: str
|
||||
|
||||
write = Task(
|
||||
description="Write an article about {topic}",
|
||||
expected_output="A short article with a title and body",
|
||||
agent=writer,
|
||||
output_pydantic=Article, # the class, NOT an instance
|
||||
output_file="output/article.md",
|
||||
)
|
||||
```
|
||||
|
||||
`output_pydantic` takes the **class** itself. Passing `Article(title="", body="")` is a common mistake and fails with a confusing validation error.
|
||||
|
||||
### Memory & embedder config {#memory-embedder-config}
|
||||
|
||||
If `memory=True` and you're not using the default OpenAI embeddings, you must pass an `embedder`:
|
||||
|
||||
```python
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
memory=True,
|
||||
embedder={
|
||||
"provider": "ollama",
|
||||
"config": {"model": "nomic-embed-text"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Set the relevant provider credentials (`OPENAI_API_KEY`, `OLLAMA_HOST`, etc.) in your `.env` file. Memory storage paths are project-local by default — delete the project's memory directory if you change embedders, since dimensions don't mix.
|
||||
@@ -106,6 +106,9 @@ If you haven't installed `uv` yet, follow **step 1** to quickly get it set up on
|
||||
```shell
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
<Note>
|
||||
This upgrades the **global `crewai` CLI tool** only. To upgrade the `crewai` version inside your project's virtual environment, see [Upgrading CrewAI in a project](/en/guides/migration/upgrading-crewai).
|
||||
</Note>
|
||||
<Check>Installation successful! You're ready to create your first crew! 🎉</Check>
|
||||
</Step>
|
||||
|
||||
|
||||
@@ -805,7 +805,6 @@ The tables below show a representative sample of current top-performing models a
|
||||
Begin with well-established models like **GPT-4.1**, **Claude 3.7 Sonnet**, or **Gemini 2.0 Flash** that offer good performance across multiple dimensions and have extensive real-world validation.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Identify Specialized Needs">
|
||||
Determine if your crew has specific requirements (coding, reasoning, speed)
|
||||
that would benefit from specialized models like **Claude 4 Sonnet** for
|
||||
@@ -813,7 +812,6 @@ The tables below show a representative sample of current top-performing models a
|
||||
consider fast inference providers like **Groq** alongside model selection.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Implement Multi-Model Strategy">
|
||||
Use different models for different agents based on their roles.
|
||||
High-capability models for managers and complex tasks, efficient models for
|
||||
|
||||
@@ -13,7 +13,7 @@ The Daytona sandbox tools give CrewAI agents access to isolated, ephemeral compu
|
||||
|
||||
- **`DaytonaExecTool`** — run any shell command inside a sandbox.
|
||||
- **`DaytonaPythonTool`** — execute a block of Python source code inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox; also supports `move`, `find` (content grep), `search` (filename glob), `chmod` (permissions), `replace` (bulk find-and-replace), and `exists`.
|
||||
|
||||
All three tools share the same sandbox lifecycle controls, so you can mix and match them while keeping state in a single persistent sandbox.
|
||||
|
||||
@@ -55,7 +55,7 @@ from crewai_tools import DaytonaPythonTool
|
||||
tool = DaytonaPythonTool()
|
||||
result = tool.run(code="print(sum(range(10)))")
|
||||
print(result)
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": None}
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": ExecutionArtifacts(stdout="45\n", charts=[])}
|
||||
```
|
||||
|
||||
### Multi-step shell session (persistent)
|
||||
@@ -63,17 +63,22 @@ print(result)
|
||||
```python Code
|
||||
from crewai_tools import DaytonaExecTool, DaytonaFileTool
|
||||
|
||||
# Create the persistent sandbox via the first tool, then attach the second
|
||||
# tool to it so both share state (installed packages, files, env vars).
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Install a package, then write and run a script — all in the same sandbox
|
||||
exec_tool.run(command="pip install httpx -q")
|
||||
file_tool.run(action="write", path="/workspace/fetch.py", content="import httpx; print(httpx.get('https://httpbin.org/get').status_code)")
|
||||
exec_tool.run(command="python /workspace/fetch.py")
|
||||
file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
|
||||
file_tool.run(
|
||||
action="write",
|
||||
path="workspace/script.py",
|
||||
content="import httpx; print(f'httpx loaded, version {httpx.__version__}')",
|
||||
)
|
||||
exec_tool.run(command="python workspace/script.py")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Each tool instance maintains its own persistent sandbox. To share **one** sandbox across two tools, create the first tool, grab its sandbox id via `tool._persistent_sandbox.id`, and pass it to the second tool via `sandbox_id=...`.
|
||||
By default, each tool with `persistent=True` lazily creates its **own** sandbox on first use. The pattern above shares a single sandbox across multiple tools by reading the first tool's `active_sandbox_id` after a `.run()` call and passing it to the others via `sandbox_id=...`. With `persistent=False` (the default), every `.run()` call gets a fresh sandbox that's deleted at the end of that call.
|
||||
</Note>
|
||||
|
||||
### Attach to an existing sandbox
|
||||
@@ -82,7 +87,7 @@ Each tool instance maintains its own persistent sandbox. To share **one** sandbo
|
||||
from crewai_tools import DaytonaExecTool
|
||||
|
||||
tool = DaytonaExecTool(sandbox_id="my-long-lived-sandbox")
|
||||
result = tool.run(command="ls /workspace")
|
||||
result = tool.run(command="ls workspace")
|
||||
```
|
||||
|
||||
### Custom sandbox parameters
|
||||
@@ -102,6 +107,41 @@ tool = DaytonaExecTool(
|
||||
)
|
||||
```
|
||||
|
||||
### Searching, moving, and modifying files
|
||||
|
||||
```python Code
|
||||
from crewai_tools import DaytonaFileTool
|
||||
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Find every TODO in the source tree (grep file contents recursively)
|
||||
file_tool.run(action="find", path="workspace/src", pattern="TODO:")
|
||||
|
||||
# Find all Python files (glob match on filenames)
|
||||
file_tool.run(action="search", path="workspace", pattern="*.py")
|
||||
|
||||
# Make a script executable
|
||||
file_tool.run(action="chmod", path="workspace/run.sh", mode="755")
|
||||
|
||||
# Rename or move a file
|
||||
file_tool.run(
|
||||
action="move",
|
||||
path="workspace/draft.md",
|
||||
destination="workspace/final.md",
|
||||
)
|
||||
|
||||
# Bulk find-and-replace across multiple files
|
||||
file_tool.run(
|
||||
action="replace",
|
||||
paths=["workspace/src/a.py", "workspace/src/b.py"],
|
||||
pattern="old_function",
|
||||
replacement="new_function",
|
||||
)
|
||||
|
||||
# Quick existence check before a destructive op
|
||||
file_tool.run(action="exists", path="workspace/cache.db")
|
||||
```
|
||||
|
||||
### Agent integration
|
||||
|
||||
```python Code
|
||||
@@ -121,7 +161,7 @@ coder = Agent(
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to /workspace/fib.py, and run it.",
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to workspace/fib.py, and run it.",
|
||||
expected_output="The first 10 Fibonacci numbers printed to stdout.",
|
||||
agent=coder,
|
||||
)
|
||||
@@ -168,12 +208,22 @@ All three tools accept these parameters at initialization:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`. |
|
||||
| `path` | `str` | ✓ | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | | Content to write or append. Required for `append`. |
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`, `exists`, `move`, `find`, `search`, `chmod`, `replace`. |
|
||||
| `path` | `str \| None` | ✓ for all actions except `replace` | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | ✓ for `append` | Content to write or append. |
|
||||
| `binary` | `bool` | | If `True`, `content` is base64 on write; returns base64 on read. |
|
||||
| `recursive` | `bool` | | For `delete`: remove directories recursively. |
|
||||
| `mode` | `str` | | For `mkdir`: octal permission string (default `"0755"`). |
|
||||
| `mode` | `str \| None` | | For `mkdir`: octal permissions for the new directory (defaults to `"0755"`). For `chmod`: octal permissions to apply to the target. |
|
||||
| `destination` | `str \| None` | ✓ for `move` | Destination path for `move`. |
|
||||
| `pattern` | `str \| None` | ✓ for `find`, `search`, `replace` | For `find`: substring matched against file CONTENTS. For `search`: glob matched against file NAMES (e.g. `*.py`). For `replace`: text to replace inside files. |
|
||||
| `replacement` | `str \| None` | ✓ for `replace` | Replacement text for `pattern`. |
|
||||
| `paths` | `list[str] \| None` | ✓ for `replace` | List of file paths in which to replace text. |
|
||||
| `owner` | `str \| None` | | For `chmod`: new file owner. |
|
||||
| `group` | `str \| None` | | For `chmod`: new file group. |
|
||||
|
||||
<Note>
|
||||
For `chmod`, pass at least one of `mode`, `owner`, or `group` — any field left as `None` is left unchanged on the target.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
For files larger than a few KB, create the file first with `action="write"` and empty content, then send the body via multiple `action="append"` calls of ~4 KB each to stay within tool-call payload limits.
|
||||
|
||||
@@ -54,6 +54,14 @@ These tools enable your agents to search the web, research topics, and find info
|
||||
Extract structured content from web pages using the Tavily API.
|
||||
</Card>
|
||||
|
||||
<Card title="Tavily Research Tool" icon="flask" href="/en/tools/search-research/tavilyresearchtool">
|
||||
Run multi-step research tasks and get cited reports using the Tavily Research API.
|
||||
</Card>
|
||||
|
||||
<Card title="Tavily Get Research Tool" icon="clipboard-list" href="/en/tools/search-research/tavilygetresearchtool">
|
||||
Retrieve the status and results of an existing Tavily research task.
|
||||
</Card>
|
||||
|
||||
<Card title="Arxiv Paper Tool" icon="box-archive" href="/en/tools/search-research/arxivpapertool">
|
||||
Search arXiv and optionally download PDFs.
|
||||
</Card>
|
||||
@@ -76,7 +84,15 @@ These tools enable your agents to search the web, research topics, and find info
|
||||
- **Academic Research**: Find scholarly articles and technical papers
|
||||
|
||||
```python
|
||||
from crewai_tools import SerperDevTool, GitHubSearchTool, YoutubeVideoSearchTool, TavilySearchTool, TavilyExtractorTool
|
||||
from crewai_tools import (
|
||||
GitHubSearchTool,
|
||||
SerperDevTool,
|
||||
TavilyExtractorTool,
|
||||
TavilyGetResearchTool,
|
||||
TavilyResearchTool,
|
||||
TavilySearchTool,
|
||||
YoutubeVideoSearchTool,
|
||||
)
|
||||
|
||||
# Create research tools
|
||||
web_search = SerperDevTool()
|
||||
@@ -84,11 +100,21 @@ code_search = GitHubSearchTool()
|
||||
video_research = YoutubeVideoSearchTool()
|
||||
tavily_search = TavilySearchTool()
|
||||
content_extractor = TavilyExtractorTool()
|
||||
tavily_research = TavilyResearchTool()
|
||||
tavily_get_research = TavilyGetResearchTool()
|
||||
|
||||
# Add to your agent
|
||||
agent = Agent(
|
||||
role="Research Analyst",
|
||||
tools=[web_search, code_search, video_research, tavily_search, content_extractor],
|
||||
tools=[
|
||||
web_search,
|
||||
code_search,
|
||||
video_research,
|
||||
tavily_search,
|
||||
content_extractor,
|
||||
tavily_research,
|
||||
tavily_get_research,
|
||||
],
|
||||
goal="Gather comprehensive information on any topic"
|
||||
)
|
||||
```
|
||||
|
||||
85
docs/en/tools/search-research/tavilygetresearchtool.mdx
Normal file
85
docs/en/tools/search-research/tavilygetresearchtool.mdx
Normal file
@@ -0,0 +1,85 @@
|
||||
---
|
||||
title: "Tavily Get Research Tool"
|
||||
description: "Retrieve the status and results of an existing Tavily research task"
|
||||
icon: "clipboard-list"
|
||||
mode: "wide"
|
||||
---
|
||||
|
||||
The `TavilyGetResearchTool` lets CrewAI agents check an existing Tavily research task by `request_id`. Use it when a research task was started earlier and you need to retrieve its current status or final results.
|
||||
|
||||
If you need to start a new research job, use the [Tavily Research Tool](/en/tools/search-research/tavilyresearchtool). This tool is specifically for looking up an existing Tavily research request after you already have its `request_id`.
|
||||
|
||||
## Installation
|
||||
|
||||
To use the `TavilyGetResearchTool`, install the `tavily-python` library alongside `crewai-tools`:
|
||||
|
||||
```shell
|
||||
uv add 'crewai[tools]' tavily-python
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set your Tavily API key:
|
||||
|
||||
```bash
|
||||
export TAVILY_API_KEY='your_tavily_api_key'
|
||||
```
|
||||
|
||||
Get an API key at [https://app.tavily.com/](https://app.tavily.com/) (sign up, then create a key).
|
||||
|
||||
## Example Usage
|
||||
|
||||
```python
|
||||
from crewai_tools import TavilyGetResearchTool
|
||||
|
||||
tavily_get_research_tool = TavilyGetResearchTool()
|
||||
|
||||
status_result = tavily_get_research_tool.run(
|
||||
request_id="your-research-request-id"
|
||||
)
|
||||
|
||||
print(status_result)
|
||||
```
|
||||
|
||||
## Common Workflow
|
||||
|
||||
Use `TavilyGetResearchTool` when your application or another service has already created a Tavily research task and saved its `request_id`.
|
||||
|
||||
Typical cases include:
|
||||
|
||||
- Polling for completion after kicking off research in a background job.
|
||||
- Looking up the latest status of a long-running research task.
|
||||
- Fetching final research output from a previously created Tavily request.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The `TavilyGetResearchTool` accepts the following argument when calling the `run` method:
|
||||
|
||||
- `request_id` (str): **Required.** The existing Tavily research request ID to retrieve.
|
||||
|
||||
## Async Usage
|
||||
|
||||
Use `_arun` when your application is already running inside an async event loop:
|
||||
|
||||
```python
|
||||
from crewai_tools import TavilyGetResearchTool
|
||||
|
||||
tavily_get_research_tool = TavilyGetResearchTool()
|
||||
|
||||
status_result = await tavily_get_research_tool._arun(
|
||||
request_id="your-research-request-id"
|
||||
)
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Research status retrieval**: Fetch the current status of an existing Tavily research task.
|
||||
- **Result retrieval**: Return available research output once Tavily has completed the task.
|
||||
- **Sync and async**: Use either `_run`/`run` or `_arun` depending on your application's runtime.
|
||||
- **JSON output**: Returns Tavily responses as formatted JSON strings.
|
||||
|
||||
## Response Format
|
||||
|
||||
The tool returns a JSON string containing the current research task status and any available results from Tavily. The exact response shape depends on the task state returned by Tavily, so incomplete tasks may return status information before the final research output is available.
|
||||
|
||||
Refer to the [Tavily API documentation](https://docs.tavily.com/) for full details on the Research API.
|
||||
@@ -4,6 +4,115 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 5월 19일">
|
||||
## v1.14.5
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- `CrewAgentExecutor` 사용 중단, 기본 Crew 에이전트를 `AgentExecutor`로 설정
|
||||
- Daytona 샌드박스 도구 개선
|
||||
- `restore_from_state_id` 시작 매개변수 추가
|
||||
- `ExaSearchTool`에 하이라이트 추가, 이름을 `EXASearchTool`에서 변경
|
||||
|
||||
### 버그 수정
|
||||
- `git.py`에서 `cached_property`를 사용하여 메모리 누수 수정
|
||||
- `available_functions`가 없을 때 스트리밍 도구 호출 표시
|
||||
- 추적을 위한 `skills` 로딩 이벤트 보장
|
||||
- 상태 엔드포인트 경로를 `/{kickoff_id}/status`에서 `/status/{kickoff_id}`로 수정
|
||||
- pt-BR 첫 흐름 가이드에서 누락된 코드 블록 복원
|
||||
- `result_as_answer`가 후크 블록이나 오류 메시지를 최종 답변으로 반환하지 않도록 방지
|
||||
- 비동기 배치 플러시 간 작업 출력 보존
|
||||
- 항상 finally 블록에서 `task.output_pydantic` 복원
|
||||
- `convert_to_model`에서 `BaseModel` 입력 처리
|
||||
|
||||
### 문서화
|
||||
- v1.14.5에 대한 변경 로그 및 버전 업데이트
|
||||
- OSS 업그레이드 및 Crew-투-흐름 마이그레이션 가이드 추가
|
||||
- 개발 도구를 위한 추가 환경 변수 문서화
|
||||
- `TavilyGetResearch`에 대한 문서 추가
|
||||
|
||||
### 리팩토링
|
||||
- CLI를 독립형 `crewai-cli` 패키지로 추출
|
||||
|
||||
## 기여자
|
||||
|
||||
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 5월 18일">
|
||||
## v1.14.5a7
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 문서
|
||||
- v1.14.5a6의 변경 로그 및 버전 업데이트
|
||||
|
||||
### 주요 변경 사항
|
||||
- function_calling_llm 필드 사용 중단
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde, @heitorado
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 5월 15일">
|
||||
## v1.14.5a6
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 버그 수정
|
||||
- available_functions가 없을 때 스트리밍 도구 호출 수정
|
||||
- GHSA-3644-q5cj-c5c7 문제를 해결하기 위해 langsmith 의존성을 버전 >=0.8.0으로 업데이트
|
||||
- 브라질 포르투갈어 문서에서 번역되지 않은 코드 블록 자리 표시자 해결
|
||||
|
||||
### 문서
|
||||
- TavilyGetResearch에 대한 문서 추가
|
||||
- v1.14.5a5에 대한 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 5월 13일">
|
||||
## v1.14.5a5
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a5)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- CrewAgentExecutor 사용 중단, 기본 Crew 에이전트를 AgentExecutor로 설정
|
||||
- Daytona 샌드박스 도구 개선
|
||||
|
||||
### 버그 수정
|
||||
- pt-BR 첫 번째 흐름 가이드에서 누락된 코드 블록 수정
|
||||
- HITL 사전 검토 및 증류 실패 로그 기록, learn_strict 추가
|
||||
- 보안 취약점을 위한 urllib3 패치
|
||||
- gitpython 및 langchain-core 패치; 패치되지 않은 paramiko CVE 무시
|
||||
- uv 잠금/동기화 시 모든 게시된 작업공간 패키지 새로 고침
|
||||
|
||||
### 문서
|
||||
- `inputs.id`에서 `restoreFromStateId`로의 마이그레이션 가이드 추가
|
||||
- OSS 업그레이드 및 crew-to-flow 마이그레이션 가이드 추가
|
||||
- v1.14.5a4의 변경 로그 및 버전 업데이트
|
||||
|
||||
## 기여자
|
||||
|
||||
@akaKuruma, @greysonlalonde, @iris-clawd, @lorenzejay, @mislavivanda
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 5월 9일">
|
||||
## v1.14.5a4
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ from crewai.flow.flow import Flow, listen, start
|
||||
from dotenv import load_dotenv
|
||||
from litellm import completion
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class ExampleFlow(Flow):
|
||||
model = "gpt-4o-mini"
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
title: AWS Workload Identity (OIDC Federation)
|
||||
description: 로테이션 인식, 자격 증명 없는 시크릿 액세스를 위해 Workload Identity를 통해 AWS Secrets Manager를 구성합니다
|
||||
sidebarTitle: AWS — Workload Identity
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **Workload Identity Federation**을 사용하여 AWS Secrets Manager를 시크릿 공급자로 구성합니다: CrewAI Platform이 단기 OIDC 토큰을 발급하고, STS를 통해 이를 AWS 자격 증명과 교환하여 시크릿을 읽습니다 — 장기 AWS 액세스 키를 어디에도 저장하지 않습니다.
|
||||
|
||||
<Note>
|
||||
**이 경로를 선택하는 이유:** 시크릿은 자동화 실행 시점에 해석되므로, **로테이션된 값이 재배포 없이 다음 kickoff에 전파됩니다**. 정적 자격 증명만 필요하고 로테이션 전파에 신경 쓰지 않는다면, 더 간단한 [AWS — 정적 키 / AssumeRole](/ko/enterprise/features/secrets-manager/aws) 가이드를 참조하세요.
|
||||
</Note>
|
||||
|
||||
### 런타임 동작 방식
|
||||
|
||||
1. 배포 워커가 CrewAI Platform에서 새 OIDC JWT를 요청합니다.
|
||||
2. 워커가 JWT를 제시하여 아래에서 설정한 IAM 역할에 대해 `sts:AssumeRoleWithWebIdentity`를 호출합니다.
|
||||
3. AWS STS가 CrewAI Platform의 공개 OIDC 발급자에 대해 JWT를 검증한 다음(따라서 플랫폼 설치가 AWS에서 접근 가능해야 함), 단기 AWS 자격 증명을 반환합니다.
|
||||
4. 워커가 해당 자격 증명을 사용하여 `secretsmanager:GetSecretValue`를 호출합니다.
|
||||
5. 가져온 값이 해당 자동화 kickoff의 환경 변수 값으로 주입됩니다.
|
||||
|
||||
OIDC 주체 토큰은 매 kickoff마다 재발급을 피하기 위해 약 1시간 동안 캐시됩니다. 시크릿 값은 OIDC 캐시 상태와 관계없이 매 kickoff마다 새로 가져오며, 이것이 이 경로를 로테이션 인식으로 만드는 요소입니다.
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- 자동화 파드 이미지에 CrewAI 런타임 버전 `1.14.5` 이상이 포함되어야 합니다.
|
||||
- IAM OIDC 공급자, IAM 역할, IAM 정책을 생성할 권한이 있는 AWS 계정.
|
||||
- 시크릿이 위치한(또는 위치할) AWS 리전(예: `us-east-1`).
|
||||
- 사용자가 `workload_identity_configs: manage` 및 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
- **CrewAI 조직 UUID.** CrewAI Platform의 조직 설정 페이지에서 찾을 수 있습니다 — 3단계의 신뢰 정책이 IAM 역할을 이 특정 조직에 바인딩합니다.
|
||||
- **CrewAI Platform 설치가 AWS에서 HTTPS를 통해 접근 가능해야 합니다.** AWS STS가 토큰 검증 중 OIDC 디스커버리 문서와 JWKS를 가져올 수 있어야 합니다. 플랫폼 관리자에게 호스트가 인터넷에서 접근 가능한지(또는 AWS가 VPC 피어링/동등한 방법을 통해 네트워크에 도달할 수 있는지) 확인하세요.
|
||||
</Note>
|
||||
|
||||
## 1단계 — CrewAI Platform OIDC 발급자 URL 찾기
|
||||
|
||||
CrewAI Platform 설치는 `https://<your-platform-host>/.well-known/openid-configuration`에서 OpenID Connect 디스커버리 문서를 게시합니다. 해당 문서의 `issuer` 필드는 AWS가 신뢰할 수 있는 OIDC 공급자로 등록할 URL입니다.
|
||||
|
||||
브라우저에서 URL을 엽니다(`<your-platform-host>`를 실제 호스트 이름으로 교체, 예: `app.crewai.com`):
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
다음을 포함하는 JSON이 보일 것입니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`issuer`의 정확한 값을 기록하세요 — 3단계에서 사용합니다.
|
||||
|
||||
<Tip>
|
||||
URL이 404 또는 503을 반환하면 플랫폼 관리자에게 문의하세요. OIDC 발급자는 설치 시점에 개인 서명 키가 구성되어 있어야 합니다. `OIDC_PRIVATE_KEY` 및 `OIDC_ISSUER` 구성에 대한 내용은 플랫폼 설치 가이드를 참조하세요.
|
||||
</Tip>
|
||||
|
||||
## 2단계 — CrewAI Platform을 IAM OIDC ID 공급자로 등록
|
||||
|
||||
[IAM → Identity providers 콘솔](https://console.aws.amazon.com/iam/home#/identity_providers)을 열고 **Add provider**를 클릭합니다.
|
||||
|
||||
- **Provider type:** OpenID Connect.
|
||||
- **Provider URL:** 1단계의 `issuer` 값(예: `https://app.crewai.com`).
|
||||
- **Audience:** `sts.amazonaws.com`
|
||||
|
||||
**Add provider**를 클릭합니다.
|
||||
|
||||
또는 CLI를 통해:
|
||||
|
||||
```bash
|
||||
aws iam create-open-id-connect-provider \
|
||||
--url "https://<your-platform-host>" \
|
||||
--client-id-list "sts.amazonaws.com" \
|
||||
--thumbprint-list "$(echo | openssl s_client -servername <your-platform-host> -connect <your-platform-host>:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1 | cut -d= -f2 | tr -d ':')"
|
||||
```
|
||||
|
||||
출력에서 **OpenIDConnectProviderArn**(또는 콘솔에서 공급자의 ARN)을 복사합니다. 3단계에서 사용합니다.
|
||||
|
||||
<Note>
|
||||
AWS는 실제로 STS WebIdentity 호출의 thumbprint를 검증하지 않습니다 — 검증 시점에 항상 JWKS를 다시 가져옵니다 — 그러나 API에서 해당 필드가 존재해야 합니다.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Add identity provider" form filled with the Platform issuer URL and audience sts.amazonaws.com → /images/secrets-manager/aws-wi/01-add-oidc-provider.png */}
|
||||
{/* SCREENSHOT: Provider detail page showing the provider's ARN → /images/secrets-manager/aws-wi/02-oidc-provider-arn.png */}
|
||||
|
||||
## 3단계 — IAM 역할 생성
|
||||
|
||||
`trust-policy.json`으로 저장하고, `<YOUR_ACCOUNT_ID>`, `<your-platform-host>`(발급자 호스트로 `https://` 또는 `http://` **없음**, 예: `app.crewai.com`), `<YOUR_CREWAI_ORG_UUID>`(사전 준비 사항에서)를 교체합니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Federated": "arn:aws:iam::<YOUR_ACCOUNT_ID>:oidc-provider/<your-platform-host>"
|
||||
},
|
||||
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"<your-platform-host>:aud": "sts.amazonaws.com",
|
||||
"<your-platform-host>:sub": "organization:<YOUR_CREWAI_ORG_UUID>"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
역할을 생성합니다:
|
||||
|
||||
```bash
|
||||
aws iam create-role \
|
||||
--role-name crewai-secrets-reader \
|
||||
--assume-role-policy-document file://trust-policy.json
|
||||
```
|
||||
|
||||
출력에서 **Role Arn**을 복사합니다 — 이것이 `aws_role_arn`입니다. 6단계에서 CrewAI Platform에 붙여 넣습니다.
|
||||
|
||||
<Tip>
|
||||
두 조건은 신뢰를 정확하게 범위 지정합니다: `aud`는 AWS STS audience를 가진 토큰으로만 가정을 제한하고, `sub`는 federation을 특정 CrewAI 조직으로 범위 지정합니다 — 해당 조직의 자동화를 위해 발급된 토큰만 수락됩니다. CrewAI Platform은 항상 AWS workload identity 토큰에 두 클레임을 모두 설정합니다.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" with Web Identity trust type, federated provider selector pointing at the CrewAI Platform OIDC provider → /images/secrets-manager/aws-wi/03-create-role-trust.png */}
|
||||
|
||||
## 4단계 — Secrets Manager + KMS 액세스용 IAM 정책 생성 및 연결
|
||||
|
||||
`secrets-policy.json`으로 저장하고 자리 표시자를 계정 ID, 리전, 시크릿 이름 접두사, 그리고 해당 시크릿을 암호화하는 KMS 키 ARN으로 교체합니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerListForUI",
|
||||
"Effect": "Allow",
|
||||
"Action": "secretsmanager:ListSecrets",
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue"
|
||||
],
|
||||
"Resource": "arn:aws:secretsmanager:<REGION>:<YOUR_ACCOUNT_ID>:secret:<SECRET_NAME_PREFIX>-*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "arn:aws:kms:<REGION>:<YOUR_ACCOUNT_ID>:key/<KMS_KEY_ID>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`SecretsManagerListForUI`는 환경 변수 폼의 **Secret Name 자동 완성**과 자격 증명의 **Test Connection** 버튼을 지원합니다. `secretsmanager:ListSecrets`는 `Resource: "*"`만 허용합니다 — IAM 계층에서 계정 범위로 제한됩니다.
|
||||
|
||||
CLI(인라인 정책, 가장 간단함) 또는 콘솔 UI를 사용하여 역할에 정책을 연결합니다. 많은 역할에서 동일한 권한을 재사용하는 환경의 경우 재사용 가능한 명명된 정책을 위해 **Managed policy** 탭을 사용하세요.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="인라인 정책 (CLI)">
|
||||
```bash
|
||||
aws iam put-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-name SecretsManagerRead \
|
||||
--policy-document file://secrets-policy.json
|
||||
```
|
||||
|
||||
이는 정책을 **인라인**으로 역할에 연결합니다. 인라인 정책은 역할에 연결되어 있으며 다른 역할에서 재사용할 수 없습니다.
|
||||
</Tab>
|
||||
|
||||
<Tab title="관리형 정책 (CLI, 재사용 가능)">
|
||||
```bash
|
||||
POLICY_ARN=$(aws iam create-policy \
|
||||
--policy-name CrewAISecretsReader \
|
||||
--policy-document file://secrets-policy.json \
|
||||
--query 'Policy.Arn' --output text)
|
||||
|
||||
aws iam attach-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-arn "$POLICY_ARN"
|
||||
```
|
||||
|
||||
관리형 정책은 여러 역할에 연결할 수 있는 독립형 IAM 리소스입니다.
|
||||
</Tab>
|
||||
|
||||
<Tab title="콘솔 (UI)">
|
||||
1. [IAM → Roles 콘솔](https://console.aws.amazon.com/iam/home#/roles)을 열고 **crewai-secrets-reader**를 선택합니다.
|
||||
2. **Permissions** 탭에서 **Add permissions** → **Create inline policy**를 클릭합니다.
|
||||
3. **JSON** 편집기로 전환하고 `secrets-policy.json`의 내용을 붙여 넣습니다.
|
||||
4. **Next**를 클릭하고 정책 이름(예: `SecretsManagerRead`)을 지정한 다음 **Create policy**를 클릭합니다.
|
||||
|
||||
대신 재사용 가능한 관리형 정책을 만들려면 **IAM → Policies → Create policy**를 사용한 다음, 역할의 **Permissions** 탭에서 역할에 연결합니다.
|
||||
|
||||
{/* SCREENSHOT: IAM Role detail → Permissions → Create inline policy with JSON editor → /images/secrets-manager/aws-wi/03b-attach-inline-policy.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 5단계 — AWS에 최소 하나의 시크릿 생성
|
||||
|
||||
테스트할 시크릿이 아직 없다면 지금 하나 만드세요:
|
||||
|
||||
```bash
|
||||
aws secretsmanager create-secret \
|
||||
--region <REGION> \
|
||||
--name crewai-test-keyword \
|
||||
--secret-string "hello from aws"
|
||||
```
|
||||
|
||||
또는 [AWS Secrets Manager 콘솔](https://console.aws.amazon.com/secretsmanager/) → **Store a new secret**을 통해 만듭니다.
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Store a new secret" page with a sample value → /images/secrets-manager/aws-wi/04-create-secret.png */}
|
||||
|
||||
## 6단계 — CrewAI Platform에 Workload Identity 구성 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Workload Identity**로 이동하여 **Add Workload Identity Config**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Workload Identity → /images/secrets-manager/aws-wi/05-amp-settings-wi-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Workload Identity page with "Add Workload Identity Config" button → /images/secrets-manager/aws-wi/06-amp-wi-empty-state.png */}
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `aws-prod`).
|
||||
- **Cloud Provider:** `AWS`.
|
||||
- **AWS Role ARN:** 3단계의 **Role Arn**.
|
||||
- **AWS Region:** 시크릿이 위치한 리전(예: `us-east-1`).
|
||||
- (선택) AWS 기반 시크릿 자격 증명을 생성할 때 이 WI 구성을 기본으로 선택하려면 **Set as default for AWS**를 체크합니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with AWS, role ARN, and region filled in → /images/secrets-manager/aws-wi/07-amp-add-wi-config-aws.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing the new AWS row with "(default)" badge if applicable → /images/secrets-manager/aws-wi/08-amp-wi-list-with-aws.png */}
|
||||
|
||||
## 7단계 — WI 구성에 바인딩된 Secret Provider Credential 추가
|
||||
|
||||
**Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `aws-prod-wi`).
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Authentication Method:** `Workload Identity`(정적 키 / AssumeRole 대신).
|
||||
- **Workload Identity Configuration:** 6단계에서 만든 구성을 선택합니다(예: `aws-prod`).
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다.
|
||||
|
||||
Workload Identity 아래에서는 폼이 **AWS Region**만 요청합니다 — 정적 자격 증명 필드(Access Key ID, Secret Access Key, Role ARN, External ID)는 이 경로에 적용되지 않으므로 의도적으로 숨겨집니다. 역할 ARN은 연결된 WI 구성에서 가져옵니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + Workload Identity + WI config dropdown selected → /images/secrets-manager/aws-wi/09-amp-add-credential-aws-wi.png */}
|
||||
|
||||
## 8단계 — 연결 테스트
|
||||
|
||||
자격 증명을 저장한 후 **Test Connection**을 클릭합니다. Workload Identity 자격 증명의 경우 OIDC 핸드셰이크를 검증합니다: CrewAI Platform이 JWT를 발급하고, `sts:AssumeRoleWithWebIdentity`를 통해 AWS STS와 교환한 다음, 결과로 받은 자격 증명이 가정된 역할에 대해 `sts:GetCallerIdentity`를 호출할 수 있는지 확인합니다. 녹색 결과는 federation 바인딩이 정상임을 의미합니다.
|
||||
|
||||
성공적인 Test Connection은 신뢰 정책, OIDC 공급자 등록, audience 조건이 모두 올바르게 연결되었음을 증명합니다. 시크릿별 IAM이 올바르다는 것을 증명하지는 **않습니다** — 특정 시크릿 ARN에 대한 `secretsmanager:GetSecretValue`는 환경 변수가 kickoff에 해석될 때 별도로 수행됩니다. 핸드셰이크 실패 모드는 [문제 해결](#troubleshooting)을 참조하세요.
|
||||
|
||||
## 9단계 — 환경 변수에서 시크릿 참조
|
||||
|
||||
이제 다른 Secrets Manager 기반 환경 변수와 마찬가지로 자동화에서 시크릿을 참조합니다. 폼 필드와 동작은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
|
||||
WI 기반과 정적 키 기반 환경 변수의 유일한 차이는 시크릿이 **언제** 읽히는지입니다:
|
||||
|
||||
- **WI 기반:** 모든 자동화 kickoff에서 시크릿 값을 새로 읽습니다.
|
||||
- **정적 키 기반:** 배포 시점에 시크릿 값을 읽고 배포 이미지에 박힙니다.
|
||||
|
||||
## 10단계 — 로테이션 확인
|
||||
|
||||
배포가 실행 중인 상태에서 AWS의 시크릿을 로테이션합니다:
|
||||
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id crewai-test-keyword \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
|
||||
새 자동화 kickoff를 트리거합니다. kickoff의 환경은 `"rotated value"`를 볼 것입니다 — 재배포, 워커 재시작, TTL 대기 없음.
|
||||
|
||||
로그에서 확인하려면(워커에 액세스할 수 있는 경우) 다음을 찾으세요:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (aws): N secret(s) resolved
|
||||
```
|
||||
|
||||
이 줄은 모든 kickoff에 나타나며 AWS에 대한 새로운 `GetSecretValue` 호출을 의미합니다.
|
||||
|
||||
## 문제 해결
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| Test Connection이 핸드셰이크 오류로 실패함 | `sts:AssumeRoleWithWebIdentity` 호출이 거부되었습니다. 신뢰 정책의 federated principal ARN이 `oidc-provider/<your-platform-host>`(호스트로 `https://` 또는 `http://` **없음**, 후행 슬래시 없음)를 참조하는지, audience 조건이 정확히 `sts.amazonaws.com`인지, `sub` 조건이 CrewAI 조직 UUID와 일치하는지, 플랫폼의 OIDC 디스커버리 URL이 공용 인터넷을 통해 AWS에서 접근 가능한지 확인하세요. |
|
||||
| `InvalidIdentityToken: Couldn't retrieve verification key from your identity provider` | AWS STS가 CrewAI Platform 호스트에 도달하여 JWKS를 가져올 수 없습니다. 호스트가 AWS에서 인터넷에 접근 가능한지, OIDC 디스커버리 URL이 200을 반환하는지, JWKS 엔드포인트가 접근 가능한지 확인하세요. |
|
||||
| `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity` | 신뢰 정책 불일치. 3단계를 다시 확인하세요: federated principal ARN은 `oidc-provider/<your-platform-host>`(호스트로 `https://` 또는 `http://` **없음**, 후행 슬래시 없음)를 포함해야 하고, audience 조건은 정확히 `sts.amazonaws.com`이어야 하며, `sub` 조건은 `organization:<YOUR_CREWAI_ORG_UUID>`와 같아야 합니다. |
|
||||
| Secret Name 자동 완성에 `AccessDenied: secretsmanager:ListSecrets` 표시 | 역할에 `Resource: "*"`를 가진 `secretsmanager:ListSecrets`가 없습니다. 4단계의 `SecretsManagerListForUI` 문을 추가하세요. |
|
||||
| Test Connection은 통과하지만 kickoff가 시크릿을 해석하지 못함 | WI 바인딩은 정상이지만 실패한 시크릿에 리소스 범위 IAM이 없습니다. 해당 시크릿의 ARN과 KMS 키에 대한 역할의 `secretsmanager:GetSecretValue` 및 `kms:Decrypt` 권한을 감사하세요. |
|
||||
| `RegionDisabledException` / 시크릿을 찾을 수 없음 | Workload Identity Config의 리전이 시크릿이 위치한 곳과 일치하지 않습니다. 6단계를 다시 확인하세요. |
|
||||
| 다음 kickoff에서 로테이션된 값이 적용되지 않음 | 자동화의 환경 변수가 Workload Identity 기반 자격 증명을 참조하는지 확인하세요(정적 키 자격 증명이 아님). 정적 경로는 배포 이미지에 값을 박습니다. |
|
||||
|
||||
### 참고 링크
|
||||
|
||||
- AWS: [Creating OpenID Connect (OIDC) identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html)
|
||||
- AWS: [Configuring a role for OpenID Connect federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_relying-party.html)
|
||||
- AWS: [STS:AssumeRoleWithWebIdentity API reference](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- [환경 변수에서 시크릿 사용 및 권한 관리](/ko/enterprise/features/secrets-manager/usage)
|
||||
- 다중 클라우드의 경우 [GCP Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity) 및 [Azure Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity)도 참조하세요.
|
||||
294
docs/ko/enterprise/features/secrets-manager/aws.mdx
Normal file
294
docs/ko/enterprise/features/secrets-manager/aws.mdx
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: AWS Secrets Manager (정적 자격 증명)
|
||||
description: 정적 액세스 키 또는 AssumeRole을 사용하여 AWS Secrets Manager를 CrewAI Platform의 시크릿 공급자로 구성합니다
|
||||
sidebarTitle: AWS
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **정적 자격 증명**(액세스 키, 선택적으로 AssumeRole 포함)을 사용하여 AWS Secrets Manager를 CrewAI Platform 조직의 시크릿 공급자로 구성하는 방법을 안내합니다. 완료되면 CrewAI Platform이 AWS 계정에 저장된 시크릿을 읽고 런타임에 환경 변수 값으로 주입할 수 있습니다.
|
||||
|
||||
<Note>
|
||||
이 가이드는 **정적 자격 증명** 경로를 다룹니다 — 시크릿은 배포 시점에 해석되고 배포 이미지에 박힙니다. 로테이션된 값은 재배포가 필요합니다. 매 자동화 kickoff마다 업데이트되는 로테이션 인식 시크릿을 원하면(재배포 없음), [AWS Workload Identity (OIDC Federation)](/ko/enterprise/features/secrets-manager/aws-workload-identity)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
이 가이드는 AWS 측 구성과 CrewAI Platform의 자격 증명 설정을 다룹니다. 환경 변수에서 시크릿을 참조하려면 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- IAM 사용자, 고객 관리형 정책, 그리고 (선택적으로) IAM 역할을 생성할 수 있는 권한이 있는 AWS 계정.
|
||||
- 시크릿이 위치한(또는 위치할) AWS 리전(예: `us-east-1`).
|
||||
- 사용자가 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 인증 방법 선택
|
||||
|
||||
CrewAI Platform은 AWS Secrets Manager에 인증하는 두 가지 방법을 지원합니다. 시작하기 전에 하나를 선택하세요 — 아래 단계는 선택한 방법에 따라 다릅니다.
|
||||
|
||||
| 방법 | 사용 시기 | 트레이드오프 |
|
||||
|---|---|---|
|
||||
| **정적 액세스 키** | 시작 단계, 단일 계정 배포 | 가장 간단한 설정; 액세스 키를 수동으로 로테이션해야 함 |
|
||||
| **AssumeRole** | 교차 계정, 프로덕션 강화 | 단기 자격 증명; External ID 지원; 추가 IAM 역할 필요 |
|
||||
|
||||
이 가이드의 나머지 부분은 단계 3-5에서 탭을 사용하므로 선택한 경로에 맞는 단계를 따를 수 있습니다.
|
||||
|
||||
## 1단계 — IAM 사용자 생성
|
||||
|
||||
[IAM 콘솔](https://console.aws.amazon.com/iam/)을 열고 **Users**로 이동한 다음 **Create user**를 클릭합니다.
|
||||
|
||||
- 권장 이름: `crewai-secrets-reader`.
|
||||
- **Provide user access to the AWS Management Console**은 선택하지 마세요 — 이 주체는 사람이 아닌 CrewAI Platform이 프로그래밍 방식으로 사용합니다.
|
||||
- **Next**를 클릭합니다.
|
||||
|
||||
**Set permissions** 페이지에서 기본 선택을 유지합니다. 정책은 3단계에서 연결합니다.
|
||||
|
||||
**Next**를 클릭하고 검토한 다음 **Create user**를 클릭합니다.
|
||||
|
||||
자세한 내용은 AWS 문서를 참조하세요: [Create an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create user" form filled with name "crewai-secrets-reader" → /images/secrets-manager/aws/01-create-iam-user.png */}
|
||||
|
||||
## 2단계 — IAM 정책 생성
|
||||
|
||||
CrewAI Platform은 AWS Secrets Manager에 대한 읽기 전용 액세스와 KMS를 통해 시크릿을 복호화할 수 있는 권한이 필요합니다. 다음 JSON으로 고객 관리형 정책을 생성하세요.
|
||||
|
||||
IAM 콘솔에서 **Policies**로 이동한 다음 **Create policy**를 클릭합니다.
|
||||
|
||||
**JSON** 탭을 선택하고 내용을 다음으로 바꿉니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:ListSecrets",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:DescribeSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:DescribeKey",
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Next**를 클릭한 다음 **Review and create** 페이지에서:
|
||||
|
||||
- **Policy name:** `CrewAISecretsManagerRead`
|
||||
- **Description (optional):** `Read-only access to AWS Secrets Manager for CrewAI Platform`
|
||||
|
||||
**Create policy**를 클릭합니다.
|
||||
|
||||
<Tip>
|
||||
위 정책은 간소화를 위해 `Resource`에 `*`를 부여합니다. 프로덕션에서는 `Resource`를 CrewAI Platform이 액세스해야 하는 특정 시크릿의 ARN으로 좁히고, `kms:Decrypt`를 해당 시크릿을 암호화하는 특정 KMS 키 ARN으로 좁히세요. [AWS의 최소 권한 지침](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html)을 참조하세요.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create policy" → JSON tab with the policy above pasted → /images/secrets-manager/aws/02-create-policy-json-editor.png */}
|
||||
{/* SCREENSHOT: AWS IAM "Review and create policy" page with name "CrewAISecretsManagerRead" → /images/secrets-manager/aws/03-policy-review-and-create.png */}
|
||||
|
||||
## 3단계 — 정책 연결
|
||||
|
||||
<Tabs>
|
||||
<Tab title="정적 액세스 키">
|
||||
1. IAM 콘솔에서 **Users**로 이동하여 1단계에서 만든 사용자를 클릭합니다.
|
||||
2. **Permissions** 탭에서 **Add permissions** → **Attach policies directly**를 클릭합니다.
|
||||
3. `CrewAISecretsManagerRead`를 검색하고 선택한 다음 **Next**를 클릭합니다.
|
||||
4. **Add permissions**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add permissions" → "Attach policies directly" with CrewAISecretsManagerRead selected → /images/secrets-manager/aws/04a-attach-policy-to-user.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
AssumeRole의 경우 정책은 (사용자가 아닌) 별도의 IAM **역할**에 연결됩니다. 1단계의 사용자는 해당 역할에 대해 `sts:AssumeRole`을 호출할 권한만 필요합니다.
|
||||
|
||||
**역할 생성:**
|
||||
|
||||
1. IAM 콘솔에서 **Roles**로 이동하여 **Create role**을 클릭합니다.
|
||||
2. **Trusted entity type:** AWS account. **This account**를 선택합니다(또는 교차 계정 설정의 경우 **Another AWS account**를 선택하고 1단계 IAM 사용자를 호스팅하는 AWS 계정 ID를 입력합니다).
|
||||
3. (권장) **Require external ID**를 체크하고 직접 생성한 값을 입력합니다 — 이는 5단계에서 CrewAI Platform에 붙여 넣을 공유 시크릿입니다.
|
||||
4. **Next**를 클릭합니다.
|
||||
5. `CrewAISecretsManagerRead` 정책을 연결합니다.
|
||||
6. **Next**를 클릭하고 역할 이름을 `CrewAISecretsManagerRole`로 지정한 다음 **Create role**을 클릭합니다.
|
||||
|
||||
**IAM 사용자가 역할을 맡을 수 있도록 허용:**
|
||||
|
||||
1. 방금 만든 역할을 열고 **ARN**을 복사합니다.
|
||||
2. IAM 콘솔에서 **Users**로 이동하여 1단계 사용자를 클릭한 다음 **Permissions** 탭에서 **Add permissions** → **Create inline policy**를 클릭합니다.
|
||||
3. **JSON** 탭에서 다음을 붙여 넣습니다(`ROLE_ARN_FROM_ABOVE`를 교체):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "ROLE_ARN_FROM_ABOVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. 정책 이름을 `CrewAIAssumeSecretsRole`로 지정하고 **Create policy**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" trust policy step with External ID checkbox enabled → /images/secrets-manager/aws/04b-create-role-trust-policy.png */}
|
||||
{/* SCREENSHOT: Inline sts:AssumeRole policy attached to the IAM user → /images/secrets-manager/aws/04c-attach-assumerole-on-user.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 4단계 — 자격 증명 가져오기
|
||||
|
||||
<Tabs>
|
||||
<Tab title="정적 액세스 키">
|
||||
1. IAM 콘솔에서 1단계 사용자를 엽니다.
|
||||
2. **Security credentials** 탭을 클릭합니다.
|
||||
3. **Access keys** 아래에서 **Create access key**를 클릭합니다.
|
||||
4. 사용 사례로 **Application running outside AWS**(또는 **Other**)를 선택합니다. **Next**를 클릭합니다.
|
||||
5. (선택) 설명 태그를 추가합니다. **Create access key**를 클릭합니다.
|
||||
6. **Show**를 클릭하여 시크릿 액세스 키를 표시한 다음 **Access key ID**와 **Secret access key**를 모두 복사하거나 **Download .csv file**을 클릭합니다.
|
||||
|
||||
<Warning>
|
||||
시크릿 액세스 키는 한 번만 표시됩니다. 복사하지 않고 이 페이지를 닫으면 키를 삭제하고 새 키를 만들어야 합니다.
|
||||
</Warning>
|
||||
|
||||
자세한 내용은 AWS 문서를 참조하세요: [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
|
||||
|
||||
{/* SCREENSHOT: Access key use-case selector ("Application running outside AWS") → /images/secrets-manager/aws/05a-create-access-key-use-case.png */}
|
||||
{/* SCREENSHOT: "Retrieve access keys" page with Show/Download buttons → /images/secrets-manager/aws/06a-retrieve-access-keys.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
AssumeRole을 사용하더라도 CrewAI Platform은 여전히 IAM 사용자에 대한 액세스 키가 필요합니다 — `sts:AssumeRole` 호출을 수행하기 위한 호출 신원으로 해당 키를 사용합니다.
|
||||
|
||||
1. 위의 **정적 액세스 키** 탭에서 설명한 대로 사용자에 대한 액세스 키를 생성합니다.
|
||||
2. 3단계에서 만든 역할을 열고 다음을 복사합니다:
|
||||
- **Role ARN** (역할 요약에서).
|
||||
- 구성한 **External ID** (있는 경우) — 3단계에서 직접 설정했으므로 가지고 있는지 확인하세요.
|
||||
|
||||
{/* SCREENSHOT: IAM role detail page showing Role ARN → /images/secrets-manager/aws/05b-role-arn-detail.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 5단계 — CrewAI Platform에 자격 증명 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Secret Provider Credentials page with "Add Credential" button → /images/secrets-manager/usage/02-amp-credentials-empty-state.png */}
|
||||
|
||||
<Tabs>
|
||||
<Tab title="정적 액세스 키">
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `aws-prod`).
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** 시크릿이 위치한 AWS 리전(예: `us-east-1`). 읽으려는 시크릿의 리전과 일치해야 합니다.
|
||||
- **Access Key ID:** 4단계의 값.
|
||||
- **Secret Access Key:** 4단계의 값.
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다. 기본 자격 증명은 자격 증명을 명시적으로 지정하지 않고 AWS 시크릿을 참조하는 환경 변수에서 사용됩니다.
|
||||
|
||||
**Role ARN**과 **External ID**는 비워둡니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + static access keys filled in → /images/secrets-manager/usage/03a-amp-add-credential-form-aws-static.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `aws-prod-assumerole`).
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** 시크릿이 위치한 AWS 리전.
|
||||
- **Access Key ID:** 4단계의 IAM 사용자 액세스 키(STS 호출에 사용).
|
||||
- **Secret Access Key:** 4단계의 IAM 사용자 시크릿 액세스 키.
|
||||
- **Role ARN:** 4단계에서 복사한 Role ARN.
|
||||
- **External ID:** 역할의 신뢰 정책에 설정한 External ID(없으면 생략).
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + AssumeRole fields filled in → /images/secrets-manager/usage/03b-amp-add-credential-form-aws-assumerole.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
**두 모드의 런타임 동작:**
|
||||
|
||||
- **정적 액세스 키**만 사용하는 경우, CrewAI Platform은 제공된 키를 사용하여 AWS Secrets Manager를 직접 호출합니다.
|
||||
- **Role ARN**이 설정된 경우, CrewAI Platform은 먼저 제공된 액세스 키(구성된 경우 External ID 포함)로 `sts:AssumeRole`을 호출한 다음, STS가 반환한 단기 자격 증명을 사용하여 시크릿을 읽습니다.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: Credentials list showing the new AWS row, with "(default)" badge if applicable → /images/secrets-manager/usage/04-amp-credential-created.png */}
|
||||
|
||||
## 6단계 — AWS에 최소 하나의 시크릿 생성
|
||||
|
||||
AWS Secrets Manager에 시크릿이 아직 없다면, 7단계에서 연결을 확인할 수 있도록 지금 하나 만드세요.
|
||||
|
||||
[AWS Secrets Manager 콘솔](https://console.aws.amazon.com/secretsmanager/)에서 **Store a new secret**을 클릭합니다.
|
||||
|
||||
- **Secret type:** **Other type of secret**을 선택합니다.
|
||||
- **Key/value pairs** — 다음 중 하나:
|
||||
- 하나 이상의 키/값 쌍 입력(구조화된 시크릿에 권장), 또는
|
||||
- 단일 문자열 값을 위해 **Plaintext** 탭 사용.
|
||||
- **Encryption key:** 특정 KMS 키 요구 사항이 없으면 `aws/secretsmanager`(AWS 관리형 키)를 사용합니다.
|
||||
|
||||
**Next**를 클릭한 다음 입력합니다:
|
||||
|
||||
- **Secret name:** 고유한 이름(예: `crewai/openai-api-key`).
|
||||
- **Description (optional):** 시크릿 용도에 대한 간단한 메모.
|
||||
|
||||
로테이션 및 검토 단계를 통해 **Next**를 클릭한 다음 **Store**를 클릭합니다.
|
||||
|
||||
<Note>
|
||||
**JSON 키 참조 구문.** 여러 키/값 쌍(JSON 객체)이 있는 시크릿을 저장하는 경우, CrewAI Platform은 환경 변수 참조에서 `secret-name#json_key` 구문을 사용하여 특정 필드를 추출할 수 있습니다. 예를 들어, `{"username": "...", "password": "..."}`가 있는 `database-credentials`라는 시크릿은 `database-credentials#password`로 참조할 수 있습니다. 자세한 내용은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
자세한 내용은 AWS 문서를 참조하세요: [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html).
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Choose secret type" page → /images/secrets-manager/aws/07-create-secret-store-type.png */}
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Configure secret" page with name and description → /images/secrets-manager/aws/08-create-secret-name.png */}
|
||||
|
||||
## 7단계 — 연결 테스트
|
||||
|
||||
CrewAI Platform으로 돌아가 **Secret Provider Credentials** 페이지에서 방금 만든 자격 증명을 찾고 **Test Connection**을 클릭합니다.
|
||||
|
||||
성공 토스트는 CrewAI Platform이 AWS에 인증하고 계정의 시크릿을 읽을 수 있음을 확인합니다.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" → /images/secrets-manager/usage/05-amp-test-connection-success.png */}
|
||||
|
||||
테스트가 실패하면 가장 일반적인 원인을 확인하세요:
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| `secretsmanager:ListSecrets`에서 `AccessDenied` | 정책이 연결되지 않았거나 잘못된 사용자입니다. 3단계를 다시 확인하세요. |
|
||||
| `kms:Decrypt`에서 `AccessDenied` | `KMSDecrypt` 문이 누락되었거나, 시크릿이 `Resource: "*"`로 다루지 않는 고객 관리형 KMS 키를 사용합니다. |
|
||||
| `InvalidClientTokenId` / `SignatureDoesNotMatch` | 잘못된 액세스 키 ID 또는 시크릿 액세스 키입니다. 4단계와 5단계를 다시 확인하세요. |
|
||||
| `RegionDisabledException` / 시크릿을 찾을 수 없음 | 자격 증명의 **Region**이 시크릿이 실제로 위치한 곳과 일치하지 않습니다. |
|
||||
| `sts:AssumeRole`에서 `AccessDenied` (AssumeRole만 해당) | IAM 사용자에 인라인 `sts:AssumeRole` 정책이 없거나, 역할의 신뢰 정책이 이 주체를 허용하지 않거나, External ID가 일치하지 않습니다. |
|
||||
| IAM 사용자 생성 직후 테스트는 통과하지만 다음 번에는 실패함 | IAM 자격 증명이 전역적으로 전파되는 데 1-2분 정도 걸릴 수 있습니다. 다시 시도하세요. |
|
||||
|
||||
## 다음 단계
|
||||
|
||||
이제 AWS가 연결되었으므로 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)로 이동하여 다음을 수행하세요:
|
||||
|
||||
- 조직 멤버에게 Secrets Manager를 사용(또는 관리)할 수 있는 적절한 권한을 부여합니다.
|
||||
- CrewAI Platform 환경 변수에서 AWS 시크릿을 참조합니다.
|
||||
|
||||
재배포 없이 전파되는 **로테이션 인식** 시크릿을 원한다면 [AWS Workload Identity (OIDC Federation)](/ko/enterprise/features/secrets-manager/aws-workload-identity)으로 전환하세요 — 동일한 시크릿 저장소, 정적 자격 증명 없음, kickoff마다 시크릿을 가져옵니다.
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Azure Workload Identity Federation
|
||||
description: 로테이션 인식, 자격 증명 없는 시크릿 액세스를 위해 Microsoft Entra Workload Identity Federation을 통해 Azure Key Vault를 구성합니다
|
||||
sidebarTitle: Azure — Workload Identity
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **Microsoft Entra Workload Identity Federation**을 사용하여 Azure Key Vault를 시크릿 공급자로 구성합니다: CrewAI Platform이 단기 OIDC 토큰을 발급하고, Microsoft identity platform을 통해 이를 Entra 액세스 토큰과 교환하여 시크릿을 읽습니다 — 클라이언트 시크릿을 어디에도 저장하지 않습니다.
|
||||
|
||||
<Note>
|
||||
**이 경로를 선택하는 이유:** 시크릿은 자동화 실행 시점에 해석되므로, **로테이션된 값이 재배포 없이 다음 kickoff에 전파됩니다**. 정적 자격 증명만 필요하다면 더 간단한 [Azure Key Vault — 클라이언트 시크릿](/ko/enterprise/features/secrets-manager/azure) 가이드를 참조하세요.
|
||||
</Note>
|
||||
|
||||
### 런타임 동작 방식
|
||||
|
||||
1. 배포 워커가 CrewAI Platform에서 새 OIDC JWT를 요청합니다.
|
||||
2. 워커가 `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token`에서 JWT를 `client_assertion`(`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`)으로 Microsoft Entra에 제시하며, JWT의 발급자 + 주체와 일치하는 **Federated Identity Credential**을 가진 App Registration을 참조합니다.
|
||||
3. Entra가 플랫폼의 OIDC 디스커버리 문서와 JWKS에 대해 JWT를 검증한 다음, `https://vault.azure.net/.default`로 범위가 지정된 단기 액세스 토큰을 반환합니다.
|
||||
4. 워커가 Azure Key Vault를 호출하여 시크릿을 읽습니다.
|
||||
5. 가져온 값이 해당 자동화 kickoff의 환경 변수 값으로 주입됩니다.
|
||||
|
||||
OIDC 주체 토큰은 매 kickoff마다 재발급을 피하기 위해 약 1시간 동안 캐시됩니다. 시크릿 값은 OIDC 캐시 상태와 관계없이 매 kickoff마다 새로 가져오며, 이것이 이 경로를 로테이션 인식으로 만드는 요소입니다.
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- 자동화 파드 이미지에 CrewAI 런타임 버전 `1.14.5` 이상이 포함되어야 합니다.
|
||||
- 관리할 수 있는 Azure 구독과 Microsoft Entra 테넌트.
|
||||
- App Registration을 생성하고 Federated Identity Credential을 추가할 테넌트 권한.
|
||||
- 권한 부여에 **Azure RBAC**를 사용하는 Key Vault(레거시 액세스 정책 모델이 아님).
|
||||
- 사용자가 `workload_identity_configs: manage` 및 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
- **CrewAI Platform 설치가 Microsoft Entra에서 HTTPS를 통해 접근 가능해야 합니다.** Entra가 토큰 검증 중 OIDC 디스커버리 문서와 JWKS를 가져올 수 있어야 합니다. 플랫폼 관리자에게 호스트가 인터넷에서 접근 가능한지 확인하세요.
|
||||
</Note>
|
||||
|
||||
## 1단계 — CrewAI Platform OIDC 발급자 URL 찾기
|
||||
|
||||
CrewAI Platform 설치는 `https://<your-platform-host>/.well-known/openid-configuration`에서 OpenID Connect 디스커버리 문서를 게시합니다. 여기의 `issuer` 필드는 Microsoft Entra가 신뢰할 수 있는 federation 발급자로 등록할 URL입니다.
|
||||
|
||||
브라우저에서 URL을 엽니다:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
다음을 포함하는 JSON이 보일 것입니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`issuer`의 정확한 값을 기록하세요 — 3단계에서 사용합니다.
|
||||
|
||||
<Tip>
|
||||
URL이 404 또는 503을 반환하면 플랫폼 관리자에게 문의하세요. OIDC 발급자는 설치 시점에 개인 서명 키가 구성되어 있어야 합니다. `OIDC_PRIVATE_KEY` 및 `OIDC_ISSUER` 구성에 대한 내용은 플랫폼 설치 가이드를 참조하세요.
|
||||
</Tip>
|
||||
|
||||
## 2단계 — App Registration 생성
|
||||
|
||||
[Microsoft Entra 포털](https://entra.microsoft.com)에서 **App registrations**로 이동하여 **New registration**을 클릭합니다.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- **Redirect URI**는 비워둡니다.
|
||||
|
||||
**Register**를 클릭합니다. App의 개요 블레이드에서 **Application (client) ID**와 **Directory (tenant) ID**를 기록하세요 — 6단계에서 사용합니다.
|
||||
|
||||
{/* SCREENSHOT: Azure portal "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure-wi/01-register-app.png */}
|
||||
|
||||
## 3단계 — Federated Identity Credential 추가
|
||||
|
||||
Federated Identity Credential은 Microsoft Entra에 다음을 알려줍니다: *이 발급자가 발급한 JWT를 신뢰하라, 이 주체로, 이 App Registration에 대한 client assertion으로 제시될 때.*
|
||||
|
||||
App Registration에서 **Certificates & secrets** → **Federated credentials** → **Add credential**로 이동합니다.
|
||||
|
||||
- **Federated credential scenario:** `Other issuer`.
|
||||
- **Issuer:** 1단계의 CrewAI Platform 발급자 URL(예: `https://<your-platform-host>`).
|
||||
- **Subject identifier:** `organization:<YOUR_CREWAI_ORG_UUID>` — 정확히 JWT의 `sub` 클레임 값. CrewAI Platform의 조직 설정에서 조직 UUID를 찾으세요. 이는 federation을 특정 CrewAI 조직으로 범위 지정합니다 — 해당 조직의 자동화를 위해 발급된 토큰만 수락됩니다.
|
||||
- **Name:** 설명적인 레이블(예: `crewai-org-prod`).
|
||||
- **Audience:** `api://AzureADTokenExchange`. 이는 Microsoft Entra가 federated 자격 증명에 요구하는 고정 audience이며 CrewAI Platform이 JWT의 `aud` 클레임에 설정하는 값입니다.
|
||||
|
||||
**Add**를 클릭합니다.
|
||||
|
||||
<Tip>
|
||||
**조직별 격리.** 주체 식별자(`organization:<UUID>`)는 federated 자격 증명을 특정 CrewAI 조직의 토큰으로 제한합니다. 여러 CrewAI 조직이 하나의 App Registration을 공유해야 하는 경우, 조직당 하나의 Federated Identity Credential을 추가하세요(각각 조직의 UUID 사용).
|
||||
</Tip>
|
||||
|
||||
자세한 내용은 Microsoft 문서를 참조하세요: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust).
|
||||
|
||||
{/* SCREENSHOT: "Add credential" panel with scenario = "Other issuer", issuer URL, subject "organization:<uuid>", audience "api://AzureADTokenExchange" → /images/secrets-manager/azure-wi/02-add-federated-credential.png */}
|
||||
|
||||
## 4단계 — App Registration에 Key Vault 액세스 부여
|
||||
|
||||
대상 볼트에서 App Registration에 **Key Vault Secrets User**를 부여합니다 — 정적 자격 증명 경로에서 사용할 것과 동일한 역할입니다. 볼트 전체(더 간단함) 또는 시크릿별(최소 권한)을 사용하세요.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="볼트 전체 (더 간단함)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
볼트 전체 범위는 CrewAI Platform의 환경 변수 폼에 있는 **Secret Name 자동 완성**이 의존하는 `secrets/list` 권한을 부여합니다. 자동 완성이 작동하길 원한다면 이 탭을 선택하세요.
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure-wi/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="시크릿별 (최소 권한)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
시크릿별 바인딩은 CrewAI Platform의 환경 변수 폼에 있는 **Secret Name 자동 완성**을 비활성화합니다(자동 완성은 볼트 범위에서만 가능한 `secrets/list`가 필요합니다). 대신 전체 시크릿 이름을 입력하세요.
|
||||
|
||||
{/* SCREENSHOT: Per-secret IAM panel with the App Registration assigned **Key Vault Secrets User** at the secret resource scope → /images/secrets-manager/azure-wi/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="포털 (UI)">
|
||||
**볼트 전체** 할당:
|
||||
|
||||
1. Azure 포털에서 Key Vault를 엽니다.
|
||||
2. **Access control (IAM)** → **Add** → **Add role assignment**를 클릭합니다.
|
||||
3. 역할 **Key Vault Secrets User**를 선택하고 → **Next**를 클릭합니다.
|
||||
4. **Select members**를 클릭하고 App Registration `crewai-secrets-reader`를 검색한 다음 **Select**를 클릭합니다.
|
||||
5. **Review + assign**을 클릭합니다.
|
||||
|
||||
**시크릿별** 할당의 경우 동일한 흐름을 사용하지만 **Objects** → **Secrets** → 시크릿 선택 → 자체 **Access control (IAM)** 패널에서 시작합니다. 시크릿별 바인딩은 자동 완성을 비활성화합니다(위의 시크릿별 탭 참조).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 5단계 — Key Vault에 최소 하나의 시크릿 생성
|
||||
|
||||
테스트할 시크릿이 아직 없다면 Azure CLI를 통해 하나 만듭니다:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
또는 Azure 포털을 통해:
|
||||
|
||||
1. Key Vault를 열고 **Objects** → **Secrets**로 이동합니다.
|
||||
2. **Generate/Import**를 클릭합니다.
|
||||
3. **Upload options:** `Manual`. **Name:** 시크릿 이름(예: `openai-api-key`). **Secret value:** 값을 붙여 넣습니다.
|
||||
4. **Create**를 클릭합니다.
|
||||
|
||||
<Note>
|
||||
**시크릿 이름 규칙.** Azure Key Vault 시크릿 이름에는 밑줄을 포함할 수 없습니다. CrewAI Platform은 Azure를 호출할 때 밑줄을 하이픈으로 자동으로 변환하므로(예: `db_password`는 `db-password`로 전송됨), 밑줄 스타일 환경 변수 이름을 유지할 수 있습니다 — 그러나 Key Vault의 기본 시크릿은 하이픈을 사용해야 합니다.
|
||||
</Note>
|
||||
|
||||
## 6단계 — CrewAI Platform에 Workload Identity 구성 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Workload Identity**로 이동하여 **Add Workload Identity Config**를 클릭합니다.
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `azure-prod`).
|
||||
- **Cloud Provider:** `Azure`.
|
||||
- **Tenant ID:** 2단계의 Microsoft Entra **Directory (tenant) ID**.
|
||||
- **Client ID:** 2단계의 App Registration **Application (client) ID**.
|
||||
- (선택) Azure 기반 시크릿 자격 증명을 생성할 때 이것이 기본 WI 구성으로 선택되길 원한다면 **Set as default for Azure**를 체크합니다.
|
||||
|
||||
**Audience**는 `api://AzureADTokenExchange`로 고정되어 있습니다 — Microsoft Entra는 federated 자격 증명에 대해 이 정확한 audience를 요구하므로 폼에 Audience 필드가 표시되지 않습니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with Azure, tenant ID, client ID populated → /images/secrets-manager/azure-wi/05-amp-add-wi-config-azure.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing AWS, GCP, and Azure rows → /images/secrets-manager/azure-wi/06-amp-wi-list-with-azure.png */}
|
||||
|
||||
## 7단계 — WI 구성에 바인딩된 Secret Provider Credential 추가
|
||||
|
||||
**Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `azure-prod-wi`).
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** 6단계에서 만든 구성을 선택합니다.
|
||||
- **Key Vault URL:** 볼트의 DNS 호스트 이름(예: `https://my-vault.vault.azure.net`).
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다.
|
||||
|
||||
Workload Identity 아래에서는 폼이 **Key Vault URL**만 요청합니다 — 정적 자격 증명 필드(Tenant ID, Client ID, Client Secret)는 이 경로에 적용되지 않으므로 의도적으로 숨겨집니다. tenant + client는 연결된 WI 구성에서 가져옵니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
<Tip>
|
||||
**하나의 App Registration, 여러 볼트.** Key Vault URL은 WI 구성이 아닌 자격 증명에 있습니다. 따라서 하나의 App Registration(과 하나의 WI 구성)이 여러 Key Vault를 서비스할 수 있습니다 — 동일한 WI 구성에 연결된 볼트당 하나의 Secret Provider Credential을 만드세요.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure + Workload Identity + WI config dropdown + vault URL → /images/secrets-manager/azure-wi/07-amp-add-credential-azure-wi.png */}
|
||||
|
||||
## 8단계 — 연결 테스트
|
||||
|
||||
자격 증명을 저장한 후 **Test Connection**을 클릭합니다. Workload Identity 자격 증명의 경우 OIDC 핸드셰이크를 검증합니다: CrewAI Platform이 JWT를 발급하고, federated `client_assertion`으로 Microsoft Entra에 제시한 다음, Entra가 볼트 범위 액세스 토큰을 반환하는지 확인합니다. 녹색 결과는 federation 바인딩이 정상임을 의미합니다.
|
||||
|
||||
성공적인 Test Connection은 Federated Identity Credential의 발급자, 주체, audience가 모두 일치하고 App Registration이 접근 가능함을 증명합니다. 시크릿별 Key Vault RBAC가 올바르다는 것을 증명하지는 **않습니다** — 특정 시크릿에 대한 `getSecret`은 환경 변수가 kickoff에 해석될 때 별도로 수행됩니다. 핸드셰이크 실패 모드는 [문제 해결](#troubleshooting)을 참조하세요.
|
||||
|
||||
## 9단계 — 환경 변수에서 시크릿 참조
|
||||
|
||||
다른 Secrets Manager 기반 환경 변수와 마찬가지로 자동화에서 시크릿을 참조합니다. 폼 필드와 동작은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
|
||||
## 10단계 — 로테이션 확인
|
||||
|
||||
배포가 실행 중인 상태에서 Key Vault의 시크릿을 로테이션합니다:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "rotated value"
|
||||
```
|
||||
|
||||
새 자동화 kickoff를 트리거합니다. kickoff의 환경은 `"rotated value"`를 볼 것입니다 — 재배포, 워커 재시작, TTL 대기 없음.
|
||||
|
||||
워커 로그에서 확인하려면 다음을 찾으세요:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (azure): N secret(s) resolved
|
||||
```
|
||||
|
||||
이 줄은 모든 kickoff에 나타나며 Azure Key Vault에 대한 새로운 `getSecret` 호출을 의미합니다.
|
||||
|
||||
엔드 투 엔드 fingerprint 기반 검증은 [로테이션 엔드 투 엔드 검증](/ko/enterprise/features/secrets-manager/verify-rotation)을 참조하세요.
|
||||
|
||||
## 문제 해결
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| Test Connection이 핸드셰이크 오류로 실패함 | federated `client_assertion`이 Microsoft Entra에 의해 거부되었습니다. Federated Identity Credential의 **Issuer**가 플랫폼의 `issuer` 값과 정확히 일치하는지, **Subject**가 `organization:<your-org-uuid>`(JWT의 `sub` 클레임과 일치)인지, **Audience**가 `api://AzureADTokenExchange`인지, 플랫폼의 OIDC 디스커버리 URL이 공용 인터넷을 통해 Entra에서 접근 가능한지 확인하세요. |
|
||||
| `AADSTS70021: No matching federated identity record found for presented assertion` | Federated Identity Credential의 **Issuer** + **Subject** + **Audience**가 모두 JWT와 정확히 일치하지 않습니다. 3단계를 다시 확인하세요: 주체는 `organization:<your-org-uuid>`(JWT의 `sub` 클레임과 일치)여야 하고, audience는 `api://AzureADTokenExchange`여야 합니다. |
|
||||
| `AADSTS700024: Client assertion is not within its valid time range` | CrewAI Platform 호스트의 시계가 실제 시간과 크게 다릅니다. 호스트의 NTP를 확인하세요. |
|
||||
| `AADSTS50013: Assertion failed signature validation` | Microsoft Entra가 JWT의 서명을 확인할 수 없습니다. `https://<your-platform-host>/oauth2/jwks`가 공용 인터넷에서 접근 가능하고 유효한 JWKS를 제공하는지 확인하세요. |
|
||||
| Secret Name 자동 완성에 `Forbidden — does not have permission to perform action 'Microsoft.KeyVault/vaults/secrets/.../list'` 표시 | App Registration의 **Key Vault Secrets User** 역할이 단일 시크릿으로 범위 지정되어 있습니다. `list` 데이터 플레인 액션이 허용되도록 볼트 범위에서 역할을 부여하세요. 4단계를 참조하세요. |
|
||||
| Test Connection은 통과하지만 kickoff가 시크릿을 해석하지 못함 | WI 바인딩은 정상이지만 실패한 시크릿에 시크릿별 Key Vault RBAC가 없습니다. 해당 특정 시크릿에 대한 **Key Vault Secrets User**를 감사하거나(또는 역할 할당을 볼트 범위로 확장). |
|
||||
| `Forbidden — request was not authorized` (레거시 액세스 정책을 사용하는 볼트) | 볼트가 Azure RBAC로 전환되지 않았습니다. 볼트의 **Access configuration**에서 권한 모델을 **Azure role-based access control**로 설정하고 4단계의 역할을 다시 부여하세요. |
|
||||
| `azure_vault_url is required for Azure secret resolution` (워커 로그) | Secret Provider Credential에 **Key Vault URL**이 없습니다. 7단계를 다시 확인하세요. |
|
||||
| 다음 kickoff에서 로테이션된 값이 적용되지 않음 | 자동화의 환경 변수가 Workload Identity 기반 자격 증명을 참조하는지 확인하세요(정적 키 자격 증명이 아님). 정적 경로는 배포 이미지에 값을 박습니다. |
|
||||
|
||||
### 참고 링크
|
||||
|
||||
- Microsoft: [Microsoft Entra Workload Identity Federation overview](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation)
|
||||
- Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust)
|
||||
- Microsoft: [Azure Key Vault RBAC guide](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide)
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- [환경 변수에서 시크릿 사용 및 권한 관리](/ko/enterprise/features/secrets-manager/usage)
|
||||
- 다중 클라우드의 경우 AWS 동등 설정은 [AWS Workload Identity (OIDC Federation)](/ko/enterprise/features/secrets-manager/aws-workload-identity)에, GCP 동등 설정은 [GCP Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity)에 있습니다.
|
||||
|
||||
## 스크린샷 참조
|
||||
|
||||
위의 자리 표시자는 다음에 매핑됩니다:
|
||||
|
||||
- `01-register-app.png` — `crewai-secrets-reader`로 채워진 Azure 포털 "Register an application" 폼.
|
||||
- `02-add-federated-credential.png` — App Registration → Certificates & secrets → Federated credentials → Add credential, **Other issuer**, 플랫폼 발급자 URL, 주체 `organization:<uuid>`, audience `api://AzureADTokenExchange`.
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, **Key Vault Secrets User**와 App Registration이 선택됨.
|
||||
- `04-per-secret-rbac.png` — 동일한 폼이지만 단일 시크릿의 IAM 범위에서(대체 최소 권한 경로).
|
||||
- `05-amp-add-wi-config-azure.png` — Cloud Provider = Azure, Tenant ID, Client ID가 채워진 CrewAI Platform "Add Workload Identity Config" 폼.
|
||||
- `06-amp-wi-list-with-azure.png` — 생성 후 Workload Identity 목록 페이지, AWS, GCP 및 새 Azure 구성 행 표시.
|
||||
- `07-amp-add-credential-azure-wi.png` — Provider = Azure Key Vault, Auth = Workload Identity, WI 구성 선택, Key Vault URL이 채워진 "Add Secret Provider Credential" 폼.
|
||||
195
docs/ko/enterprise/features/secrets-manager/azure.mdx
Normal file
195
docs/ko/enterprise/features/secrets-manager/azure.mdx
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: Azure Key Vault
|
||||
description: Azure Key Vault를 CrewAI Platform의 시크릿 공급자로 처음부터 끝까지 구성합니다
|
||||
sidebarTitle: Azure
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **클라이언트 시크릿이 있는 Microsoft Entra App Registration**을 사용하여 Azure Key Vault를 CrewAI Platform 조직의 시크릿 공급자로 구성하는 방법을 안내합니다. 완료되면 CrewAI Platform이 Azure Key Vault에 저장된 시크릿을 읽고 런타임에 환경 변수 값으로 주입할 수 있습니다.
|
||||
|
||||
<Note>
|
||||
이 가이드는 **정적 자격 증명** 경로를 다룹니다 — 시크릿은 배포 시점에 해석되고 배포 이미지에 박힙니다. 로테이션된 값은 재배포가 필요합니다. 매 자동화 kickoff마다 업데이트되는 로테이션 인식 시크릿을 원한다면 [Azure Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
이 가이드는 Azure 측 구성과 CrewAI Platform의 자격 증명 설정을 다룹니다. 환경 변수에서 시크릿을 참조하려면 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- Microsoft Entra에서 App Registration을 생성하고 Key Vault 리소스에 역할 할당을 부여할 권한이 있는 Azure 구독.
|
||||
- 권한 부여에 **Azure RBAC**를 사용하는 Key Vault(레거시 액세스 정책 모델이 아님). 볼트가 여전히 액세스 정책을 사용하는 경우, 볼트의 **Access configuration** 블레이드에서 RBAC로 전환하세요.
|
||||
- 사용자가 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 1단계 — App Registration 생성
|
||||
|
||||
App Registration은 CrewAI Platform이 인증할 Microsoft Entra 측 ID입니다.
|
||||
|
||||
[Microsoft Entra 포털](https://entra.microsoft.com)에서 **App registrations**로 이동하여 **New registration**을 클릭합니다.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- **Redirect URI**는 비워둡니다.
|
||||
|
||||
**Register**를 클릭합니다. App의 개요 블레이드에서 **Application (client) ID**와 **Directory (tenant) ID**를 기록하세요 — 4단계에서 둘 다 CrewAI Platform에 붙여 넣습니다.
|
||||
|
||||
자세한 내용은 Microsoft 문서를 참조하세요: [Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).
|
||||
|
||||
{/* SCREENSHOT: Azure "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure/01-register-app.png */}
|
||||
|
||||
## 2단계 — 클라이언트 시크릿 생성
|
||||
|
||||
App Registration에서 **Certificates & secrets** → **Client secrets** → **New client secret**으로 이동합니다.
|
||||
|
||||
- **Description:** `crewai-platform`
|
||||
- **Expires:** 로테이션 정책과 일치하는 기간을 선택합니다(Microsoft은 이를 24개월로 제한).
|
||||
|
||||
**Add**를 클릭합니다. **Value** 열을 즉시 복사하세요 — 페이지를 떠난 후에는 다시 표시할 수 없습니다.
|
||||
|
||||
<Warning>
|
||||
클라이언트 시크릿은 장기 정적 자격 증명입니다. 값을 안전하게 저장하고(패스워드 매니저나 자체 시크릿 저장소에) 만료 전에 로테이션하세요. 정적 자격 증명을 완전히 제거하려면 대신 [Azure Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity)을 사용하세요.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: "Client secrets" tab with the new secret row and the "Value" column highlighted → /images/secrets-manager/azure/02-create-client-secret.png */}
|
||||
|
||||
## 3단계 — App Registration에 Key Vault 액세스 부여
|
||||
|
||||
CrewAI Platform은 Key Vault의 시크릿에 대한 읽기 액세스가 필요합니다. 두 가지 범위 중 하나를 사용하세요 — 간소화를 위한 **볼트 전체** 또는 최소 권한을 위한 **시크릿별**.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="볼트 전체 (더 간단함)">
|
||||
[Key Vault 콘솔](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.KeyVault%2Fvaults)에서 대상 볼트를 열고 **Access control (IAM)** → **Add** → **Add role assignment**로 이동합니다.
|
||||
|
||||
- **Role:** **Key Vault Secrets User**
|
||||
- **Assign access to:** User, group, or service principal
|
||||
- **Members:** App Registration(`crewai-secrets-reader`)을 검색하고 선택합니다.
|
||||
|
||||
**Review + assign**을 클릭합니다.
|
||||
|
||||
또는 Azure CLI를 통해:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="시크릿별 (최소 권한)">
|
||||
개별 시크릿 수준에서 역할을 부여합니다. CrewAI Platform이 액세스해야 하는 각 시크릿에 대해 반복합니다:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Access control (IAM)" panel showing role assignment scoped to one secret → /images/secrets-manager/azure/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
**Key Vault Secrets User** 역할은 시크릿 값 읽기를 허용하지만 볼트의 모든 시크릿을 나열하는 것은 허용하지 않습니다. CrewAI Platform의 시크릿 이름 자동 완성도 `list`를 호출합니다 — 해당 권한은 볼트 범위에서는 역할에 포함되지만, 시크릿별 범위에서는 **포함되지 않습니다**. 시크릿별 바인딩의 경우 자동 완성이 시크릿을 제안하지 않으므로 대신 전체 시크릿 이름을 입력하세요.
|
||||
</Tip>
|
||||
|
||||
## 4단계 — CrewAI Platform에 자격 증명 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `azure-prod`).
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Key Vault URL:** 볼트의 DNS 호스트 이름(예: `https://my-vault.vault.azure.net`).
|
||||
- **Tenant ID:** 1단계의 Microsoft Entra **Directory (tenant) ID**.
|
||||
- **Client ID:** 1단계의 App Registration **Application (client) ID**.
|
||||
- **Client Secret:** 2단계에서 복사한 **Value**.
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다. 기본 자격 증명은 자격 증명을 명시적으로 지정하지 않고 Azure 시크릿을 참조하는 환경 변수에서 사용됩니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure fields filled in → /images/secrets-manager/azure/05-amp-add-credential-form-azure.png */}
|
||||
|
||||
## 5단계 — Azure Key Vault에 최소 하나의 시크릿 생성
|
||||
|
||||
Key Vault에 시크릿이 아직 없다면, 6단계에서 연결을 확인할 수 있도록 지금 하나 만드세요.
|
||||
|
||||
Key Vault 콘솔에서 **Objects** → **Secrets** → **Generate/Import**로 이동합니다.
|
||||
|
||||
- **Upload options:** `Manual`
|
||||
- **Name:** 예: `openai-api-key`
|
||||
- **Secret value:** 시크릿 값을 붙여 넣습니다
|
||||
- 나머지는 기본값으로 둡니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
또는 Azure CLI를 통해:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
<Note>
|
||||
**시크릿 이름 규칙.** Azure Key Vault 시크릿 이름에는 밑줄을 포함할 수 없습니다. CrewAI Platform은 Azure를 호출할 때 밑줄을 하이픈으로 자동으로 변환하므로(예: `db_password`는 `db-password`로 전송됨), 밑줄 스타일 환경 변수 이름을 유지할 수 있습니다 — 그러나 Key Vault의 기본 시크릿은 하이픈을 사용해야 합니다.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**JSON 키 참조 구문.** Key Vault는 시크릿 값을 불투명한 문자열로 취급합니다. 시크릿 값이 JSON 객체인 경우, CrewAI Platform은 `secret-name#json_key` 구문(예: `database-credentials#password`)을 사용하여 단일 필드를 추출할 수 있습니다. 자세한 내용은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
자세한 내용은 Microsoft 문서를 참조하세요: [Set and retrieve a secret](https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli).
|
||||
|
||||
{/* SCREENSHOT: Azure Key Vault "Create a secret" form with name and value → /images/secrets-manager/azure/06-create-secret.png */}
|
||||
|
||||
## 6단계 — 연결 테스트
|
||||
|
||||
CrewAI Platform으로 돌아가 **Secret Provider Credentials** 페이지에서 방금 만든 자격 증명을 찾고 **Test Connection**을 클릭합니다.
|
||||
|
||||
성공 토스트는 CrewAI Platform이 Microsoft Entra에 인증하고 볼트의 시크릿을 읽을 수 있음을 확인합니다.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the Azure credential → /images/secrets-manager/azure/07-test-connection-success.png */}
|
||||
|
||||
테스트가 실패하면 가장 일반적인 원인을 확인하세요:
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| `AADSTS7000215: Invalid client secret provided` | 붙여 넣은 **Client Secret**이 잘못되었거나 만료되었습니다. 시크릿을 다시 생성하고(2단계) 자격 증명을 업데이트하세요. |
|
||||
| `AADSTS700016: Application not found in the directory` | **Tenant ID** 또는 **Client ID**가 App Registration과 일치하지 않습니다. 4단계를 다시 확인하세요. |
|
||||
| `Forbidden — caller does not have permission` | App Registration에 볼트(또는 시크릿별) **Key Vault Secrets User** 역할이 없습니다. 3단계를 다시 확인하세요. |
|
||||
| `Vault not found` / DNS 오류 | **Key Vault URL**이 잘못되었거나, 볼트에 공용 액세스를 차단하는 프라이빗 엔드포인트가 있습니다. 호스트가 `curl https://<vault-name>.vault.azure.net/secrets?api-version=7.4`에 응답하는지 확인하세요. |
|
||||
| `Forbidden — request was not authorized` (레거시 액세스 정책을 사용하는 볼트) | 볼트가 Azure RBAC로 전환되지 않았습니다. 볼트의 **Access configuration**에서 권한 모델을 **Azure role-based access control**로 설정하고 3단계의 역할을 다시 부여하세요. |
|
||||
|
||||
## 다음 단계
|
||||
|
||||
이제 Azure Key Vault가 연결되었으므로 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)로 이동하여 다음을 수행하세요:
|
||||
|
||||
- 조직 멤버에게 Secrets Manager를 사용(또는 관리)할 수 있는 적절한 권한을 부여합니다.
|
||||
- CrewAI Platform 환경 변수에서 Azure 시크릿을 참조합니다.
|
||||
|
||||
재배포 없이 전파되는 **로테이션 인식** 시크릿을 원한다면 [Azure Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity)으로 전환하세요 — 동일한 볼트, 로테이션할 클라이언트 시크릿 없음, kickoff마다 시크릿을 가져옵니다.
|
||||
|
||||
## 스크린샷 참조
|
||||
|
||||
위의 자리 표시자는 다음에 매핑됩니다:
|
||||
|
||||
- `01-register-app.png` — `crewai-secrets-reader`로 채워진 Azure 포털 "Register an application" 폼.
|
||||
- `02-create-client-secret.png` — App Registration → Certificates & secrets → Client secrets, 새로 생성된 시크릿 행이 표시됨(마스킹되기 전 Value 열 강조).
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, **Key Vault Secrets User**가 선택되고 App Registration이 멤버로 선택됨.
|
||||
- `04-per-secret-rbac.png` — 동일한 패널이지만 단일 시크릿 리소스로 범위 지정됨(대체 최소 권한 경로).
|
||||
- `05-amp-add-credential-form-azure.png` — CrewAI Platform "Add Secret Provider Credential" 폼: Provider = Azure Key Vault, 다섯 필드 모두 채워짐.
|
||||
- `06-create-secret.png` — `openai-api-key`와 붙여 넣은 값이 있는 Azure Key Vault "Create a secret" 패널.
|
||||
- `07-test-connection-success.png` — 자격 증명에서 **Test Connection**을 클릭한 후의 CrewAI Platform 성공 토스트 / 행 상태.
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: GCP Workload Identity Federation
|
||||
description: 로테이션 인식, 자격 증명 없는 시크릿 액세스를 위해 Workload Identity Federation을 통해 Google Cloud Secret Manager를 구성합니다
|
||||
sidebarTitle: GCP — Workload Identity
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **Workload Identity Federation**을 사용하여 Google Cloud Secret Manager를 시크릿 공급자로 구성합니다: CrewAI Platform이 단기 OIDC 토큰을 발급하고, Security Token Service를 통해 이를 Google Cloud 자격 증명과 교환하여 시크릿을 읽습니다 — 장기 서비스 계정 키를 어디에도 저장하지 않습니다.
|
||||
|
||||
<Note>
|
||||
**이 경로를 선택하는 이유:** 시크릿은 자동화 실행 시점에 해석되므로, **로테이션된 값이 재배포 없이 다음 kickoff에 전파됩니다**. 정적 자격 증명만 필요하다면 더 간단한 [GCP — 서비스 계정 키](/ko/enterprise/features/secrets-manager/gcp) 가이드를 참조하세요.
|
||||
</Note>
|
||||
|
||||
### 런타임 동작 방식
|
||||
|
||||
1. 배포 워커가 CrewAI Platform에서 새 OIDC JWT를 요청합니다.
|
||||
2. 워커가 아래에서 설정한 Workload Identity Pool Provider를 참조하여 [Security Token Service](https://cloud.google.com/iam/docs/reference/sts/rest)를 통해 JWT를 federated Google 자격 증명과 교환합니다.
|
||||
3. 워커가 federated 자격 증명을 직접 사용하여 시크릿을 읽기 위해 `secretmanager.googleapis.com:accessSecretVersion`을 호출합니다(federated 주체가 `roles/secretmanager.secretAccessor`를 보유함 — 4단계 참조).
|
||||
4. 가져온 값이 해당 자동화 kickoff의 환경 변수 값으로 주입됩니다.
|
||||
|
||||
OIDC 주체 토큰은 매 kickoff마다 재발급을 피하기 위해 약 1시간 동안 캐시됩니다. 시크릿 값은 OIDC 캐시 상태와 관계없이 매 kickoff마다 새로 가져오며, 이것이 이 경로를 로테이션 인식으로 만드는 요소입니다.
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- 자동화 파드 이미지에 CrewAI 런타임 버전 `1.14.5` 이상이 포함되어야 합니다.
|
||||
- **Secret Manager API**, **Security Token Service API**, **IAM Credentials API**가 활성화된 Google Cloud 프로젝트. 콘솔에서 또는 다음을 통해 활성화하세요:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com sts.googleapis.com iamcredentials.googleapis.com \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
- Workload Identity Pool, IAM 역할, 서비스 계정, (필요한 경우) 시크릿을 생성할 프로젝트 권한.
|
||||
- 사용자가 `workload_identity_configs: manage` 및 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
- **CrewAI Platform 설치가 Google Cloud에서 HTTPS를 통해 접근 가능해야 합니다.** GCP STS가 토큰 검증 중 OIDC 디스커버리 문서와 JWKS를 가져올 수 있어야 합니다. 플랫폼 관리자에게 호스트가 인터넷에서 접근 가능한지 확인하세요.
|
||||
</Note>
|
||||
|
||||
## 1단계 — CrewAI Platform OIDC 발급자 URL 찾기
|
||||
|
||||
CrewAI Platform 설치는 `https://<your-platform-host>/.well-known/openid-configuration`에서 OpenID Connect 디스커버리 문서를 게시합니다. 여기의 `issuer` 필드는 Google이 신뢰할 수 있는 OIDC 공급자로 등록할 URL입니다.
|
||||
|
||||
브라우저에서 URL을 엽니다:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
다음을 포함하는 JSON이 보일 것입니다:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
`issuer`의 정확한 값을 기록하세요 — 3단계에서 사용합니다.
|
||||
|
||||
<Tip>
|
||||
URL이 404 또는 503을 반환하면 플랫폼 관리자에게 문의하세요. OIDC 발급자는 설치 시점에 개인 서명 키가 구성되어 있어야 합니다. `OIDC_PRIVATE_KEY` 및 `OIDC_ISSUER` 구성에 대한 내용은 플랫폼 설치 가이드를 참조하세요.
|
||||
</Tip>
|
||||
|
||||
## 2단계 — Workload Identity Pool 생성
|
||||
|
||||
Workload Identity Pool은 신뢰할 수 있는 외부 ID를 위한 Google Cloud 측 컨테이너입니다. 이 풀 내부에 CrewAI Platform을 공급자로 등록합니다.
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools create crewai-pool \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--display-name="CrewAI Platform"
|
||||
```
|
||||
|
||||
또는 [Workload Identity Pools 콘솔](https://console.cloud.google.com/iam-admin/workload-identity-pools)에서 **Create Pool**을 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: GCP "Create Workload Identity Pool" form with name "crewai-pool" → /images/secrets-manager/gcp-wi/01-create-pool.png */}
|
||||
|
||||
## 3단계 — CrewAI Platform을 풀에 OIDC 공급자로 추가
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--display-name="CrewAI Platform OIDC" \
|
||||
--issuer-uri="https://<your-platform-host>" \
|
||||
--attribute-mapping="google.subject=assertion.sub,attribute.organization=assertion.organization_id" \
|
||||
--attribute-condition="assertion.organization_id != ''"
|
||||
```
|
||||
|
||||
`--attribute-mapping`은 JWT 클레임을 Google 속성으로 매핑하는 방법을 Google에 알려줍니다:
|
||||
- `google.subject`는 주체 식별자입니다 — JWT의 `sub` 클레임에 매핑하며, CrewAI Platform은 이를 `organization:<uuid>`로 설정합니다.
|
||||
- `attribute.organization`은 사용자 정의 속성입니다 — JWT의 `organization_id` 클레임에 매핑하여 나중에 IAM 바인딩에서 참조할 수 있습니다.
|
||||
|
||||
`--attribute-condition`은 `organization_id` 클레임이 없는 토큰을 거부하는 심층 방어 검사입니다.
|
||||
|
||||
**Provider 리소스 이름**을 가져옵니다(audience 및 IAM 바인딩에 필요):
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers describe crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--format="value(name)"
|
||||
```
|
||||
|
||||
출력은 다음과 같습니다:
|
||||
|
||||
```
|
||||
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider
|
||||
```
|
||||
|
||||
이것이 6단계에서 CrewAI Platform의 **Workload Identity Provider** 값입니다. CrewAI Platform은 토큰을 발급할 때 OIDC audience를 `//iam.googleapis.com/<this-resource-name>`으로 자동으로 계산합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add provider to pool" form with OIDC selected, issuer URI, audience defaults, attribute mapping → /images/secrets-manager/gcp-wi/02-add-oidc-provider.png */}
|
||||
|
||||
## 4단계 — Federated 주체에 Secret Manager 액세스 부여
|
||||
|
||||
프로젝트 범위에서 두 Secret Manager 역할을 모두 federated 주체에 바인딩합니다 — 한 역할은 환경 변수 폼의 Secret Name 자동 완성을 활성화하고, 다른 역할은 자동화 kickoff 시점에 시크릿 값을 읽을 수 있게 합니다. 기능이 처음부터 끝까지 작동하려면 두 역할 모두 필요합니다.
|
||||
|
||||
```bash
|
||||
PRINCIPAL_SET="principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/attribute.organization/<YOUR_CREWAI_ORG_UUID>"
|
||||
|
||||
# Secret Name 자동 완성에 필요 (secretmanager.secrets.list 호출)
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.viewer"
|
||||
|
||||
# kickoff 시점에 시크릿 값을 읽는 데 필요
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
`<PROJECT_NUMBER>`를 숫자 프로젝트 번호(`gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)'`)로, `<YOUR_CREWAI_ORG_UUID>`를 시크릿을 읽을 수 있도록 허용할 CrewAI Platform 조직의 UUID로 교체합니다. 조직 UUID는 플랫폼 UI의 조직 설정 페이지나 API를 통해 찾을 수 있습니다. 이는 federation을 특정 CrewAI 조직으로 범위 지정합니다 — 해당 조직의 자동화를 위해 발급된 토큰만 수락됩니다.
|
||||
|
||||
또는 Google Cloud 콘솔을 통해:
|
||||
|
||||
1. 프로젝트의 **IAM & Admin** → **IAM**을 엽니다.
|
||||
2. **GRANT ACCESS**를 클릭합니다.
|
||||
3. **New principals:** 전체 `principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID>` 문자열을 붙여 넣습니다.
|
||||
4. 역할 **Secret Manager Viewer** (`roles/secretmanager.viewer`)를 할당합니다.
|
||||
5. **SAVE**를 클릭합니다.
|
||||
6. **GRANT ACCESS**를 다시 클릭하고 **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`) 역할로 반복합니다.
|
||||
|
||||
<Tip>
|
||||
**조직별 격리.** `principalSet://...attribute.organization/<UUID>` 패턴은 특정 조직의 토큰에 대한 액세스를 제한합니다. 하나의 Google Cloud 프로젝트를 여러 CrewAI 조직이 공유하는 경우, 각 조직에 대해 올바른 UUID로 두 바인딩을 모두 반복하거나 — 격리가 필요하지 않으면 덜 제한적인 attribute condition을 사용하세요.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**시크릿별로 `secretAccessor` 범위 지정 (선택 사항).** `roles/secretmanager.secretAccessor`를 프로젝트 전체로 부여하고 싶지 않으면, 위의 두 번째 바인딩을 생략하고 대신 시크릿별로 바인딩합니다:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <SECRET_NAME> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
어느 쪽이든 `roles/secretmanager.viewer`는 프로젝트 범위로 유지하세요 — `secretmanager.secrets.list`(자동 완성이 의존)는 시크릿별로 부여할 수 없습니다.
|
||||
</Tip>
|
||||
|
||||
## 5단계 — GCP에 최소 하나의 시크릿 생성
|
||||
|
||||
테스트할 시크릿이 아직 없다면 `gcloud` CLI를 통해 하나 만듭니다:
|
||||
|
||||
```bash
|
||||
echo -n "hello from gcp" | gcloud secrets create crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
또는 [Secret Manager 콘솔](https://console.cloud.google.com/security/secret-manager)을 통해:
|
||||
|
||||
1. GCP 프로젝트에서 **Secret Manager**를 엽니다.
|
||||
2. **+ CREATE SECRET**을 클릭합니다.
|
||||
3. **Name:** `crewai-test-keyword`. **Secret value:** 값을 붙여 넣습니다.
|
||||
4. **CREATE SECRET**을 클릭합니다.
|
||||
|
||||
## 6단계 — CrewAI Platform에 Workload Identity 구성 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Workload Identity**로 이동하여 **Add Workload Identity Config**를 클릭합니다.
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `gcp-prod`).
|
||||
- **Cloud Provider:** `GCP`.
|
||||
- **Workload Identity Provider:** 3단계의 provider 리소스 이름(예: `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider`).
|
||||
- (선택) GCP 기반 시크릿 자격 증명을 생성할 때 이것이 기본 WI 구성으로 선택되길 원한다면 **Default Configuration**을 토글합니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with GCP and provider resource name → /images/secrets-manager/gcp-wi/03-amp-add-wi-config-gcp.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing both AWS and GCP rows → /images/secrets-manager/gcp-wi/04-amp-wi-list-with-gcp.png */}
|
||||
|
||||
## 7단계 — WI 구성에 바인딩된 Secret Provider Credential 추가
|
||||
|
||||
**Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `gcp-prod-wi`).
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** 6단계에서 만든 구성을 선택합니다.
|
||||
- **Project ID:** GCP 프로젝트 ID(시크릿을 소유한 동일한 프로젝트).
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다.
|
||||
|
||||
Workload Identity 아래에서는 폼이 **Project ID**만 요청합니다 — **Service Account JSON** 필드는 이 경로에 적용되지 않으므로 의도적으로 숨겨집니다. federated ID는 연결된 WI 구성에서 가져옵니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP + Workload Identity + WI config dropdown → /images/secrets-manager/gcp-wi/05-amp-add-credential-gcp-wi.png */}
|
||||
|
||||
## 8단계 — 연결 테스트
|
||||
|
||||
자격 증명을 저장한 후 **Test Connection**을 클릭합니다. Workload Identity 자격 증명의 경우 OIDC 핸드셰이크를 검증합니다: CrewAI Platform이 JWT를 발급하고, Security Token Service를 통해 federated Google 액세스 토큰과 교환합니다. 녹색 결과는 federation 바인딩이 정상임을 의미합니다.
|
||||
|
||||
성공적인 Test Connection은 Workload Identity Pool, OIDC 공급자, attribute mapping, attribute condition이 모두 올바르게 연결되었음을 증명합니다. Secret Manager IAM이 올바르다는 것을 증명하지는 **않습니다** — `secretmanager.secrets.list`와 `secretmanager.versions.access`는 Secret Name 자동 완성이 로드되거나 환경 변수가 kickoff에 해석될 때 별도로 수행됩니다. 핸드셰이크 실패 모드는 [문제 해결](#troubleshooting)을 참조하세요.
|
||||
|
||||
## 9단계 — 환경 변수에서 시크릿 참조
|
||||
|
||||
다른 Secrets Manager 기반 환경 변수와 마찬가지로 자동화에서 시크릿을 참조합니다. 폼 필드와 동작은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
|
||||
## 10단계 — 로테이션 확인
|
||||
|
||||
배포가 실행 중인 상태에서 새 버전을 추가하여 GCP의 시크릿을 로테이션합니다(Secret Manager는 기본적으로 항상 최신 활성화 버전을 읽음):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
새 자동화 kickoff를 트리거합니다. kickoff의 환경은 `"rotated value"`를 볼 것입니다 — 재배포, 워커 재시작, TTL 대기 없음.
|
||||
|
||||
워커 로그에서 확인하려면 다음을 찾으세요:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (gcp): N secret(s) resolved
|
||||
```
|
||||
|
||||
이 줄은 모든 kickoff에 나타나며 GCP에 대한 새로운 `accessSecretVersion` 호출을 의미합니다.
|
||||
|
||||
## 문제 해결
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| Test Connection이 핸드셰이크 오류로 실패함 | STS 토큰 교환이 거부되었습니다. Workload Identity Pool이 존재하는지, OIDC provider의 발급자가 플랫폼의 `issuer` 값과 일치하는지, attribute condition이 JWT의 클레임을 수락하는지 확인하세요. 플랫폼의 OIDC 디스커버리 URL이 공용 인터넷을 통해 GCP에서 접근 가능한지 확인하세요. |
|
||||
| `Could not refresh access token: invalid_target` | audience 클레임이 Workload Identity Provider의 예상 audience와 일치하지 않습니다. CrewAI Platform이 audience를 자동으로 설정합니다. 사용자 정의한 경우 `//iam.googleapis.com/<provider-resource-name>`과 일치하는지 확인하세요. |
|
||||
| `Failed to fetch JWKS from issuer` | GCP STS가 CrewAI Platform 호스트에 도달할 수 없습니다. 호스트가 인터넷에서 접근 가능하고 `/.well-known/openid-configuration`이 200을 반환하는지 확인하세요. |
|
||||
| `Attribute condition rejected token` | OIDC provider의 attribute condition(3단계)이 `organization_id`를 요구합니다. CrewAI Platform은 항상 이 클레임을 설정하므로 보통 잘못 구성된 풀/공급자를 의미합니다. provider의 attribute condition을 다시 확인하세요. |
|
||||
| Secret Name 자동 완성에 `PERMISSION_DENIED: secretmanager.secrets.list` 표시 | federated 주체에 프로젝트 범위의 `roles/secretmanager.viewer`가 없습니다. `secretmanager.secrets.list` 권한은 프로젝트 범위에서만 가능하며 시크릿별로 부여할 수 없습니다. 4단계를 참조하세요. |
|
||||
| Test Connection은 통과하지만 kickoff가 시크릿을 해석하지 못함 | WI 바인딩은 정상이지만 실패한 시크릿에 `secretmanager.versions.access`가 없습니다. `roles/secretmanager.secretAccessor`(프로젝트 범위 또는 4단계에서 그렇게 범위 지정한 경우 시크릿별)를 감사하세요. |
|
||||
| 다음 kickoff에서 로테이션된 값이 적용되지 않음 | 자동화의 환경 변수가 Workload Identity 기반 자격 증명을 참조하는지 확인하세요(정적 키 자격 증명이 아님). 정적 경로는 배포 이미지에 값을 박습니다. |
|
||||
|
||||
### 참고 링크
|
||||
|
||||
- GCP: [Workload Identity Federation overview](https://cloud.google.com/iam/docs/workload-identity-federation)
|
||||
- GCP: [Configure Workload Identity Federation with OIDC](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
|
||||
- GCP: [Secret Manager IAM roles](https://cloud.google.com/secret-manager/docs/access-control)
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- [환경 변수에서 시크릿 사용 및 권한 관리](/ko/enterprise/features/secrets-manager/usage)
|
||||
- 다중 클라우드의 경우 [AWS Workload Identity (OIDC Federation)](/ko/enterprise/features/secrets-manager/aws-workload-identity) 및 [Azure Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity)도 참조하세요.
|
||||
188
docs/ko/enterprise/features/secrets-manager/gcp.mdx
Normal file
188
docs/ko/enterprise/features/secrets-manager/gcp.mdx
Normal file
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: Google Cloud Secret Manager
|
||||
description: Google Cloud Secret Manager를 CrewAI Platform의 시크릿 공급자로 처음부터 끝까지 구성합니다
|
||||
sidebarTitle: GCP
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **서비스 계정 자격 증명**을 사용하여 Google Cloud Secret Manager를 CrewAI Platform 조직의 시크릿 공급자로 구성하는 방법을 안내합니다. 완료되면 CrewAI Platform이 Google Cloud 프로젝트에 저장된 시크릿을 읽고 런타임에 환경 변수 값으로 주입할 수 있습니다.
|
||||
|
||||
<Note>
|
||||
이 가이드는 **정적 자격 증명** 경로를 다룹니다 — 시크릿은 배포 시점에 해석되고 배포 이미지에 박힙니다. 로테이션된 값은 재배포가 필요합니다. 매 자동화 kickoff마다 업데이트되는 로테이션 인식 시크릿을 원한다면 [GCP Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
이 가이드는 GCP 측 구성과 CrewAI Platform의 자격 증명 설정을 다룹니다. 환경 변수에서 시크릿을 참조하려면 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
<Note>
|
||||
시작하기 전에 다음을 준비하세요:
|
||||
|
||||
- **Secret Manager API**가 활성화된 Google Cloud 프로젝트. [APIs & Services 콘솔](https://console.cloud.google.com/apis/library/secretmanager.googleapis.com)에서 또는 `gcloud`를 통해 활성화하세요:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com --project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
- 서비스 계정 생성, IAM 역할 부여, (필요한 경우) 시크릿 생성에 대한 프로젝트 권한.
|
||||
- 사용자가 `secret_providers: manage` 권한을 가진 CrewAI Platform 조직. [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
</Note>
|
||||
|
||||
## 1단계 — 서비스 계정 생성
|
||||
|
||||
서비스 계정은 CrewAI Platform이 인증할 GCP 측 ID입니다.
|
||||
|
||||
[IAM & Admin → Service Accounts 콘솔](https://console.cloud.google.com/iam-admin/serviceaccounts)에서 **Create Service Account**를 클릭합니다.
|
||||
|
||||
- **Service account name:** `crewai-secrets-reader`
|
||||
- **Service account ID:** 이름에서 자동으로 채워짐(예: `crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com`)
|
||||
- **Description (optional):** "Read-only access to Secret Manager for CrewAI Platform"
|
||||
|
||||
**Create and Continue**를 클릭합니다. 이 화면에서 선택적 권한 부여는 건너뜁니다 — 역할은 2단계에서 연결합니다. **Done**을 클릭합니다.
|
||||
|
||||
자세한 내용은 GCP 문서를 참조하세요: [Create service accounts](https://cloud.google.com/iam/docs/service-accounts-create).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create service account" form with name "crewai-secrets-reader" → /images/secrets-manager/gcp/01-create-service-account.png */}
|
||||
|
||||
## 2단계 — Secret Manager 액세스 부여
|
||||
|
||||
CrewAI Platform은 프로젝트의 시크릿을 나열하고 읽을 수 있는 권한이 필요합니다. 두 가지 범위 중 하나를 사용하세요 — 간소화를 위한 **프로젝트 전체** 또는 최소 권한을 위한 **시크릿별**.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="프로젝트 전체 (더 간단함)">
|
||||
[IAM 콘솔](https://console.cloud.google.com/iam-admin/iam)에서 **Grant Access**를 클릭하고:
|
||||
|
||||
- **New principals:** 1단계 서비스 계정의 이메일.
|
||||
- **Role:** **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
**Save**를 클릭합니다.
|
||||
|
||||
또는 `gcloud`를 통해:
|
||||
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: GCP IAM "Grant access" panel with the service account and Secret Manager Secret Accessor role → /images/secrets-manager/gcp/02-iam-grant-access.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="시크릿별 (최소 권한)">
|
||||
CrewAI Platform이 액세스해야 하는 특정 시크릿에만 역할을 부여합니다. 각 시크릿에 대해 반복합니다:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding YOUR_SECRET_NAME \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
또는 콘솔에서: [Secret Manager](https://console.cloud.google.com/security/secret-manager)에서 각 시크릿을 열고, 오른쪽 패널의 **Permissions**를 클릭한 다음 서비스 계정에 **Secret Manager Secret Accessor**를 부여합니다.
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Permissions" panel in Secret Manager with the service account granted accessor role → /images/secrets-manager/gcp/03-per-secret-permissions.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
`roles/secretmanager.secretAccessor` 역할은 시크릿 값에 대한 읽기 전용 액세스를 부여합니다. CrewAI Platform은 또한 환경 변수 폼의 자동 완성 경험을 위해 `secretmanager.secrets.list`를 호출합니다 — 해당 권한은 프로젝트 범위에서는 역할에 포함되지만, 시크릿별 범위에서는 **포함되지 않습니다**. 시크릿별 바인딩의 경우 자동 완성이 시크릿을 제안하지 않으므로 전체 시크릿 이름을 입력해야 합니다.
|
||||
</Tip>
|
||||
|
||||
## 3단계 — 서비스 계정 키 생성
|
||||
|
||||
[IAM & Admin → Service Accounts 콘솔](https://console.cloud.google.com/iam-admin/serviceaccounts)에서 1단계의 서비스 계정을 엽니다.
|
||||
|
||||
- **Keys** 탭을 클릭합니다.
|
||||
- **Add Key** → **Create new key**를 클릭합니다.
|
||||
- **Key type:** JSON.
|
||||
- **Create**를 클릭합니다. 브라우저가 JSON 파일을 다운로드합니다 — 안전하게 보관하세요. 다시 다운로드할 수 없습니다.
|
||||
|
||||
또는 `gcloud`를 통해:
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts keys create ./crewai-secrets-reader.json \
|
||||
--iam-account=crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
<Warning>
|
||||
서비스 계정 키는 장기 정적 자격 증명입니다. 안전하게 저장하고(패스워드 매니저나 자체 시크릿 저장소에) 정기적으로 로테이션하세요. 정적 자격 증명을 완전히 제거하려면 대신 [GCP Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity)을 사용하세요.
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: Service account "Keys" tab with the "Create new key" → JSON option → /images/secrets-manager/gcp/04-create-service-account-key.png */}
|
||||
|
||||
## 4단계 — CrewAI Platform에 자격 증명 추가
|
||||
|
||||
CrewAI Platform에서 **Settings** → **Secret Provider Credentials**로 이동하여 **Add Credential**을 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Name:** 설명적인 이름(예: `gcp-prod`).
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Project ID:** GCP 프로젝트 ID(예: `my-crewai-prod`).
|
||||
- **Service Account JSON:** 3단계에서 다운로드한 JSON 파일의 전체 내용을 붙여 넣습니다.
|
||||
- (선택) **Set as default credential for this provider**를 체크합니다. 기본 자격 증명은 자격 증명을 명시적으로 지정하지 않고 GCP 시크릿을 참조하는 환경 변수에서 사용됩니다.
|
||||
|
||||
**Create**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP fields filled in → /images/secrets-manager/gcp/05-amp-add-credential-form-gcp.png */}
|
||||
|
||||
## 5단계 — GCP에 최소 하나의 시크릿 생성
|
||||
|
||||
GCP Secret Manager에 시크릿이 아직 없다면, 6단계에서 연결을 확인할 수 있도록 지금 하나 만드세요.
|
||||
|
||||
[Secret Manager 콘솔](https://console.cloud.google.com/security/secret-manager)에서 **Create secret**을 클릭합니다.
|
||||
|
||||
- **Name:** 고유한 이름(예: `openai-api-key`).
|
||||
- **Secret value:** 원시 값을 붙여 넣거나 파일을 업로드합니다.
|
||||
- 특정 요구 사항이 없으면 로테이션, 복제 및 기타 설정을 기본값으로 둡니다.
|
||||
|
||||
**Create secret**을 클릭합니다.
|
||||
|
||||
또는 `gcloud`를 통해:
|
||||
|
||||
```bash
|
||||
echo -n "sk-your-actual-key" | gcloud secrets create openai-api-key \
|
||||
--data-file=- \
|
||||
--project=YOUR_PROJECT_ID \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
<Note>
|
||||
**JSON 키 참조 구문.** GCP Secret Manager는 시크릿 값을 불투명한 blob으로 취급합니다. 시크릿 값이 JSON 문자열인 경우, CrewAI Platform은 `secret-name#json_key` 구문(예: `database-credentials#password`)을 사용하여 단일 필드를 추출할 수 있습니다. 자세한 내용은 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
</Note>
|
||||
|
||||
자세한 내용은 GCP 문서를 참조하세요: [Create a secret](https://cloud.google.com/secret-manager/docs/create-secret-quickstart).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create secret" form with name and value → /images/secrets-manager/gcp/06-create-secret.png */}
|
||||
|
||||
## 6단계 — 연결 테스트
|
||||
|
||||
CrewAI Platform으로 돌아가 **Secret Provider Credentials** 페이지에서 방금 만든 자격 증명을 찾고 **Test Connection**을 클릭합니다.
|
||||
|
||||
성공 토스트는 CrewAI Platform이 GCP에 인증하고 프로젝트의 시크릿을 읽을 수 있음을 확인합니다.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the GCP credential → /images/secrets-manager/gcp/07-test-connection-success.png */}
|
||||
|
||||
테스트가 실패하면 가장 일반적인 원인을 확인하세요:
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| 시크릿 나열 시 `PERMISSION_DENIED` | 서비스 계정에 `roles/secretmanager.secretAccessor`가 없거나, 시크릿별로 범위를 지정했습니다(`list`가 부여되지 않음). 2단계를 다시 확인하세요. |
|
||||
| `secretmanager.secrets.access`에서 `PERMISSION_DENIED` | 위와 동일하지만 특정 시크릿에 대한 것입니다. 해당 시크릿에 서비스 계정이 accessor 역할을 갖고 있는지 확인하세요. |
|
||||
| `unauthorized_client` / `invalid_grant` | 붙여 넣은 서비스 계정 JSON이 유효하지 않거나, 만료되었거나, 삭제된 서비스 계정용입니다. 키를 다시 만들고(3단계) 다시 붙여 넣으세요. |
|
||||
| `Project ID does not match` | CrewAI Platform의 Project ID 필드가 서비스 계정/시크릿을 소유한 프로젝트와 일치하지 않습니다. 4단계를 다시 확인하세요. |
|
||||
| `API not enabled` | 프로젝트에 Secret Manager API가 활성화되어 있지 않습니다. 사전 준비 사항을 참조하세요. |
|
||||
|
||||
## 다음 단계
|
||||
|
||||
이제 GCP가 연결되었으므로 [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage)로 이동하여 다음을 수행하세요:
|
||||
|
||||
- 조직 멤버에게 Secrets Manager를 사용(또는 관리)할 수 있는 적절한 권한을 부여합니다.
|
||||
- CrewAI Platform 환경 변수에서 GCP 시크릿을 참조합니다.
|
||||
|
||||
재배포 없이 전파되는 **로테이션 인식** 시크릿을 원한다면 [GCP Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity)으로 전환하세요 — 동일한 시크릿 저장소, 정적 자격 증명 없음, kickoff마다 시크릿을 가져옵니다.
|
||||
81
docs/ko/enterprise/features/secrets-manager/overview.mdx
Normal file
81
docs/ko/enterprise/features/secrets-manager/overview.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Secrets Manager 개요
|
||||
description: 외부 시크릿 저장소를 CrewAI Platform에 연결하고 환경 변수에서 관리되는 시크릿을 참조합니다
|
||||
sidebarTitle: 개요
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
Secrets Manager 기능은 조직에서 외부 시크릿 저장소(AWS Secrets Manager, Google Cloud Secret Manager 또는 곧 지원될 Azure Key Vault)를 연결하고, 자동화(Automation) 및 Crew의 환경 변수에서 해당 시크릿을 직접 참조할 수 있게 해줍니다. 플랫폼에 평문 값을 붙여 넣는 대신, 각 공급자별로 자격 증명 한 세트만 저장하고 시크릿을 이름으로 참조합니다.
|
||||
|
||||
이를 통해 얻는 이점은 다음과 같습니다:
|
||||
|
||||
- **중앙 집중식 저장** — CrewAI Platform 설정을 편집하는 대신 공급자에서 시크릿을 관리합니다. CrewAI Platform은 시크릿 값의 평문 사본을 보관하지 않습니다.
|
||||
- **노출 감소** — 민감한 값이 CrewAI Platform 설정에 평문으로 존재하지 않습니다.
|
||||
- **클라우드 네이티브 감사 가능성** — 공급자의 감사 로그에 모든 시크릿 읽기 작업이 기록됩니다.
|
||||
|
||||
<Note>
|
||||
Secrets Manager(정적 자격 증명 및 Workload Identity 경로 모두)는 자동화 파드 이미지에 CrewAI 런타임 버전 `1.14.5` 이상이 필요합니다.
|
||||
</Note>
|
||||
|
||||
## 두 가지 경로: 정적 자격 증명 vs Workload Identity
|
||||
|
||||
CrewAI Platform을 클라우드의 시크릿 저장소에 연결하는 방법은 두 가지가 있습니다. **로테이션 동작 방식에서 큰 차이가 있으므로**, 시크릿이 얼마나 자주 로테이션되는지와 보안 정책의 엄격함에 따라 선택하세요.
|
||||
|
||||
| 항목 | 정적 자격 증명 | Workload Identity (OIDC Federation) |
|
||||
|---|---|---|
|
||||
| **인증** | 장기 액세스 키 / 서비스 계정 JSON을 CrewAI Platform에 저장 | 워커 프로세스마다 발급되는 단기 토큰; 어디에도 정적 자격 증명을 저장하지 않음 |
|
||||
| **로테이션 전파** | 배포 시점에 해석되어 **배포 컨테이너 이미지에 박힘** — 로테이션된 값을 반영하려면 재배포 필요 | **자동화 실행 시점**에 해석됨 — 로테이션된 값이 재배포 없이 다음 kickoff에 전파됨 |
|
||||
| **설정 노력** | 더 적음 — 키 붙여 넣기 / 서비스 계정 JSON 업로드 | 더 많음 — CrewAI Platform을 클라우드에 OIDC 공급자로 등록하고 신뢰 정책 구성 |
|
||||
| **적합한 용도** | 시작 단계, 드물게 로테이션되는 시크릿, 단일 계정 배포 | 프로덕션, 자주 로테이션되는 시크릿, 장기 자격 증명을 금지하는 규정 준수 환경 |
|
||||
|
||||
<Note>
|
||||
**두 경로 모두 환경 변수에서 시크릿을 참조하는 동일한 UI 흐름을 사용합니다**([Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage) 참조). 차이점은 전적으로 플랫폼이 클라우드에 어떻게 인증하고 언제 시크릿 값을 읽는지에 있습니다.
|
||||
</Note>
|
||||
|
||||
### 설정 가이드 선택
|
||||
|
||||
| 공급자 | 정적 자격 증명 | Workload Identity |
|
||||
|---|---|---|
|
||||
| AWS Secrets Manager | [AWS — 정적 키 / AssumeRole](/ko/enterprise/features/secrets-manager/aws) | [AWS — Workload Identity (OIDC)](/ko/enterprise/features/secrets-manager/aws-workload-identity) |
|
||||
| Google Cloud Secret Manager | [GCP — 서비스 계정 키](/ko/enterprise/features/secrets-manager/gcp) | [GCP — Workload Identity Federation](/ko/enterprise/features/secrets-manager/gcp-workload-identity) |
|
||||
| Azure Key Vault | [Azure — 클라이언트 시크릿](/ko/enterprise/features/secrets-manager/azure) | [Azure — Workload Identity Federation](/ko/enterprise/features/secrets-manager/azure-workload-identity) |
|
||||
|
||||
<Note>
|
||||
Secrets Manager 및 Workload Identity UI는 현재 CrewAI Platform에서 **Beta**로 표시되어 있습니다.
|
||||
</Note>
|
||||
|
||||
## 동작 방식
|
||||
|
||||
Secrets Manager 설정은 클라우드 공급자와 CrewAI Platform 양쪽 모두에서 작업하는 3단계 흐름입니다:
|
||||
|
||||
1. **관리자가 공급자 자격 증명을 구성합니다.** 이는 클라우드 측 작업이며 — 선택한 경로(정적 자격 증명 또는 Workload Identity)에 따라 작업이 다릅니다. 공급자별 가이드에서 처음부터 끝까지 다룹니다.
|
||||
2. **관리자(또는 권한이 부여된 멤버)가 환경 변수에서 시크릿을 참조합니다.** Environment Variables 페이지에서 공급자 자격 증명을 선택하고 시크릿 이름을 선택합니다. [Secrets Manager 사용하기](/ko/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables)를 참조하세요.
|
||||
3. **자동화가 런타임에 해석된 값을 받습니다.** Crew 또는 자동화가 실행될 때, CrewAI Platform이 공급자에서 시크릿을 가져와 환경 변수 값으로 주입합니다. Workload Identity의 경우 이 가져오기는 매 kickoff마다 수행됩니다(로테이션 인식). 정적 자격 증명의 경우 이 가져오기는 배포 시점에 수행되고 값이 배포 이미지에 박힙니다.
|
||||
|
||||
## 권한
|
||||
|
||||
Secrets Manager 접근을 제어하는 두 가지 CrewAI Platform 기능:
|
||||
|
||||
- `secret_providers` — 공급자 자격 증명을 보거나 관리할 수 있는 사람을 제어합니다.
|
||||
- `environment_variables` — 시크릿을 참조하는 환경 변수를 포함하여 환경 변수를 생성하고 편집할 수 있는 사람을 제어합니다.
|
||||
|
||||
세 번째 기능은 Workload Identity 설정을 제어합니다:
|
||||
|
||||
- `workload_identity_configs` — Workload Identity 구성을 보거나 관리할 수 있는 사람을 제어합니다. Workload Identity 경로를 사용하는 경우에만 필요합니다.
|
||||
|
||||
소유자(Owner)는 항상 전체 접근 권한을 가집니다. 멤버는 기본적으로 `secret_providers` 또는 `workload_identity_configs`에 대한 접근 권한을 **받지 않으며**, 사용자 정의 역할을 통해 권한을 명시적으로 부여받아야 합니다. 전체 매트릭스와 단계별 지침은 [권한 (RBAC)](/ko/enterprise/features/secrets-manager/usage#permissions-rbac)을 참조하세요.
|
||||
|
||||
## 다음 단계
|
||||
|
||||
원하는 경로를 선택하세요:
|
||||
|
||||
- **정적 자격 증명** (더 간단함, 로테이션 시 재배포 필요):
|
||||
- [AWS Secrets Manager 구성](/ko/enterprise/features/secrets-manager/aws)
|
||||
- [Google Cloud Secret Manager 구성](/ko/enterprise/features/secrets-manager/gcp)
|
||||
- [Azure Key Vault 구성](/ko/enterprise/features/secrets-manager/azure)
|
||||
- **Workload Identity** (로테이션 인식, 재배포 불필요):
|
||||
- [AWS Workload Identity 구성](/ko/enterprise/features/secrets-manager/aws-workload-identity)
|
||||
- [GCP Workload Identity Federation 구성](/ko/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- [Azure Workload Identity Federation 구성](/ko/enterprise/features/secrets-manager/azure-workload-identity)
|
||||
- 그리고: [환경 변수에서 시크릿을 사용하고 권한 관리하기](/ko/enterprise/features/secrets-manager/usage)
|
||||
136
docs/ko/enterprise/features/secrets-manager/usage.mdx
Normal file
136
docs/ko/enterprise/features/secrets-manager/usage.mdx
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Secrets Manager 사용하기
|
||||
description: CrewAI Platform에서 권한을 관리하고 환경 변수에서 관리되는 시크릿을 참조합니다
|
||||
sidebarTitle: 사용 및 권한
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 공급자 중립적입니다. 사용자(또는 다른 관리자)가 이미 최소 하나의 Secret Provider Credential을 구성했다고 가정합니다. 원하는 경로에 따라 설정 가이드를 선택하세요:
|
||||
|
||||
- 정적 자격 증명: [AWS](/ko/enterprise/features/secrets-manager/aws) · [GCP](/ko/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (로테이션 인식): [AWS](/ko/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/ko/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
|
||||
이 가이드를 사용하여 다음을 수행하세요:
|
||||
|
||||
- 조직 멤버에게 적절한 권한을 부여합니다.
|
||||
- 자동화의 환경 변수에서 시크릿을 참조합니다.
|
||||
- 런타임에서 모든 것이 올바르게 해석되는지 확인합니다.
|
||||
|
||||
## 권한 (RBAC)
|
||||
|
||||
Secrets Manager를 사용할 때 관련된 세 가지 CrewAI Platform 기능:
|
||||
|
||||
- `secret_providers` — **Secret Provider Credentials** 페이지에 대한 접근을 제어합니다.
|
||||
- `workload_identity_configs` — **Workload Identity** 페이지에 대한 접근을 제어합니다(WI 경로를 사용하는 경우에만 관련됨).
|
||||
- `environment_variables` — 환경 변수를 생성하거나 편집할 수 있는 사람을 제어합니다.
|
||||
|
||||
각 기능에는 두 가지 작업 수준이 있습니다: `read`와 `manage`. `manage`를 부여하면 자동으로 `read`를 포함합니다.
|
||||
|
||||
### 부여할 권한
|
||||
|
||||
| 목표 | `secret_providers` | `workload_identity_configs` | `environment_variables` |
|
||||
|---|---|---|---|
|
||||
| 환경 변수에서 기존 정적 자격 증명 사용 (공급자 편집 불가) | `read` | — | `manage` |
|
||||
| 정적 자격 증명 생성, 편집 또는 삭제 | `manage` | — | `manage` |
|
||||
| 환경 변수에서 기존 Workload Identity 기반 자격 증명 사용 | `read` | — | `manage` |
|
||||
| Workload Identity 구성(및 이를 참조하는 자격 증명) 생성, 편집 또는 삭제 | `manage` | `manage` | `manage` |
|
||||
|
||||
<Note>
|
||||
**소유자(Owner)**는 모든 기능에 대해 자동으로 전체 접근 권한을 가집니다. 기본 **멤버(Member)** 역할은 의도적으로 `secret_providers` 및 `workload_identity_configs`를 제외합니다 — 관리자는 사용자 정의 역할을 통해 멤버에게 명시적으로 옵트인해야 합니다.
|
||||
</Note>
|
||||
|
||||
### 할당 방법
|
||||
|
||||
1. CrewAI Platform에서 **Settings** → **Roles**로 이동합니다. 이 페이지에서 새 역할을 생성하고, 각 역할의 권한을 편집하며, 조직의 기존 멤버에게 역할을 할당할 수 있습니다.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Roles → /images/secrets-manager/usage/06-amp-settings-roles-nav.png */}
|
||||
{/* SCREENSHOT: Roles list page with "Create Role" button visible → /images/secrets-manager/usage/07-amp-roles-list.png */}
|
||||
|
||||
2. **Create Role**을 클릭하여 새 역할을 만들거나, 기존 역할을 열어 권한을 편집합니다.
|
||||
|
||||
3. 역할의 권한 편집기에서 위 표에 따라 관련 기능을 토글합니다:
|
||||
|
||||
- `secret_providers`: 이 역할이 기존 자격 증명만 사용하면 되는 경우 **read**를 선택하고, 자격 증명을 생성, 편집, 삭제할 수도 있어야 하면 **manage**를 선택합니다.
|
||||
- `environment_variables`: 역할이 시크릿을 참조하는 환경 변수를 생성할 수 있도록 **manage**를 선택합니다.
|
||||
|
||||
{/* SCREENSHOT: Role editor showing the secret_providers feature with read/manage toggles → /images/secrets-manager/usage/08-amp-role-editor-secret-providers-toggles.png */}
|
||||
{/* SCREENSHOT: Role editor showing environment_variables toggles → /images/secrets-manager/usage/09-amp-role-editor-env-vars-toggles.png */}
|
||||
|
||||
4. 역할을 저장합니다.
|
||||
|
||||
5. 동일한 Roles 페이지(또는 조직 Members 목록)에서 해당 멤버에게 역할을 할당합니다.
|
||||
|
||||
{/* SCREENSHOT: Member assignment screen where the new role is applied to a user → /images/secrets-manager/usage/10-amp-assign-role-to-member.png */}
|
||||
|
||||
## 환경 변수에서 시크릿 참조하기
|
||||
|
||||
공급자 자격 증명이 존재하고 역할에 적절한 권한이 있으면, 모든 환경 변수에서 관리되는 시크릿을 참조할 수 있습니다.
|
||||
|
||||
CrewAI Platform에서 **Environment Variables**로 이동하여 **Add Environment Variables**를 클릭합니다.
|
||||
|
||||
{/* SCREENSHOT: Environment Variables empty state with "Add" button → /images/secrets-manager/usage/11-amp-env-vars-empty.png */}
|
||||
|
||||
폼을 작성합니다:
|
||||
|
||||
- **Key** — 환경 변수의 이름입니다. 문자나 밑줄로 시작해야 하며, 문자, 숫자, 밑줄만 포함해야 합니다. 관례적으로 대문자로 작성합니다(예: `OPENAI_API_KEY`).
|
||||
|
||||
- **Value Source** — 값의 출처를 선택합니다:
|
||||
- **Direct Value** — 직접 입력하는 평문 값입니다. 공급자를 사용하고 싶지 않을 때 사용합니다.
|
||||
- **Use AWS default** (또는 공급자에 해당하는 항목) — 해당 공급자 유형에서 기본값으로 표시된 자격 증명을 사용합니다.
|
||||
- **특정 명명된 자격 증명** — 이름으로 자격 증명을 선택합니다. 동일한 공급자에 대해 여러 자격 증명(예: `aws-prod`와 `aws-staging`)이 있고 하나를 명시적으로 선택하려는 경우에 사용합니다.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the "Value Source" dropdown open, showing "AWS default" + named credentials → /images/secrets-manager/usage/12-amp-env-var-form-source-selector.png */}
|
||||
|
||||
- **Secret Name** — 공급자의 시크릿 이름입니다. 자격 증명이 선택되면 이 필드에서 자동 완성을 제공합니다: 입력을 시작하면 CrewAI Platform이 일치하는 시크릿 이름을 공급자에 쿼리합니다.
|
||||
|
||||
구조화된(JSON) 시크릿에서 단일 필드를 추출하려면 `secret-name#json_key` 구문을 사용합니다. 예를 들어, `{"username": "...", "password": "..."}` 값을 가진 시크릿 `database-credentials`가 있다면, `database-credentials#password`로 참조하여 비밀번호만 주입할 수 있습니다.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the secret name autocomplete dropdown showing live results → /images/secrets-manager/usage/13-amp-env-var-form-secret-name-autocomplete.png */}
|
||||
|
||||
<Note>
|
||||
**Azure Key Vault 참고:** Azure 시크릿 이름에는 밑줄을 포함할 수 없습니다. CrewAI Platform은 Azure를 호출할 때 `Secret Name` 필드의 밑줄을 자동으로 하이픈으로 변환합니다(예: `db_password`는 `db-password`로 전송됨).
|
||||
</Note>
|
||||
|
||||
**Create**를 클릭하여 변수를 저장합니다.
|
||||
|
||||
{/* SCREENSHOT: Env var list with the new variable showing masked value and a "secret" indicator → /images/secrets-manager/usage/14-amp-env-var-created.png */}
|
||||
|
||||
<Tip>
|
||||
기존 환경 변수를 편집할 때 **Value** 필드를 비워두면 현재 값이 유지됩니다. 이는 의도된 동작이며 — 값을 다시 입력하지 않고도 다른 필드(시크릿 이름이나 자격 증명 등)를 변경할 수 있게 해줍니다.
|
||||
</Tip>
|
||||
|
||||
## 작동 여부 검증
|
||||
|
||||
엔드 투 엔드 검증 방법:
|
||||
|
||||
1. 다른 환경 변수와 동일한 방식으로 자동화, Crew 또는 배포에서 환경 변수를 참조합니다.
|
||||
2. 자동화를 배포합니다.
|
||||
3. 실행을 트리거하고 성공적으로 완료되는지 확인합니다.
|
||||
|
||||
### 로테이션 동작은 자격 증명 경로에 따라 다릅니다
|
||||
|
||||
| 자격 증명 경로 | 시크릿이 읽히는 시점 | 로테이션 시 필요한 작업 |
|
||||
|---|---|---|
|
||||
| **정적 자격 증명** (AWS 액세스 키, GCP 서비스 계정 JSON) | **배포 시점** — 값이 배포 이미지에 박힘 | 시크릿 로테이션 후 자동화 재배포 |
|
||||
| **Workload Identity** (OIDC federation, AWS 또는 GCP) | **모든 자동화 kickoff 시점** — 값을 클라우드에서 새로 가져옴 | 없음 — 로테이션 후 다음 kickoff에서 새 값이 보임 |
|
||||
|
||||
<Note>
|
||||
**로테이션 인식 시크릿이 필요한 경우**(로테이션 시 재배포 없이), Workload Identity 경로를 사용하세요: [AWS WI](/ko/enterprise/features/secrets-manager/aws-workload-identity) 또는 [GCP WI](/ko/enterprise/features/secrets-manager/gcp-workload-identity). 트레이드오프는 초기 설정 노력이 더 많지만(CrewAI Platform을 클라우드에 OIDC 공급자로 등록), 장기적으로는 운영이 더 단순해진다는 점입니다.
|
||||
</Note>
|
||||
|
||||
배포 또는 실행이 시크릿 관련 오류로 실패하면 가장 일반적인 원인을 확인하세요:
|
||||
|
||||
| 증상 | 가능한 원인 |
|
||||
|---|---|
|
||||
| `no credential found` | 환경 변수가 공급자를 참조하지만 특정 자격 증명이 선택되지 않았고, 해당 공급자 유형에 대한 기본 자격 증명이 설정되어 있지 않습니다. 변수에서 자격 증명을 명시적으로 선택하거나, **Secret Provider Credentials** 페이지에서 자격 증명을 기본값으로 표시하세요. |
|
||||
| `secret not found` | **Secret Name**의 오타, 또는 자격 증명이 가리키는 공급자 계정/리전에 시크릿이 존재하지 않습니다. 둘 다 다시 확인하세요. |
|
||||
| 로테이션 후에도 자동화가 이전 값으로 실행됨 (정적 자격 증명 경로) | 이전 값이 배포 컨테이너 이미지에 박혀 있습니다. 로테이션된 값을 가져오려면 자동화를 재배포하세요. 이를 완전히 피하려면 자격 증명을 Workload Identity 경로로 전환하세요. |
|
||||
| 로테이션 후에도 자동화가 이전 값으로 실행됨 (Workload Identity 경로) | 환경 변수가 WI 기반 자격 증명을 참조하는지 확인하세요(정적 키가 아님). WI를 사용하면 로테이션 후 다음 kickoff에서 새 값이 보여야 합니다. 그렇지 않으면 시크릿이 실제로 클라우드에서 업데이트되었는지 확인하세요(예: `aws secretsmanager get-secret-value`). |
|
||||
| `JSON key not found` | `secret-name#json_key`를 사용할 때 기본 시크릿은 해당 키를 포함하는 유효한 JSON 객체여야 합니다. 공급자에서 직접 시크릿을 읽어 확인하세요. |
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- [Secrets Manager 개요로 돌아가기](/ko/enterprise/features/secrets-manager/overview)
|
||||
- 정적 자격 증명: [AWS](/ko/enterprise/features/secrets-manager/aws) · [GCP](/ko/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (로테이션 인식): [AWS](/ko/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/ko/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
260
docs/ko/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
260
docs/ko/enterprise/features/secrets-manager/verify-rotation.mdx
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: 로테이션 확인
|
||||
description: 클라우드 공급자에서 로테이션된 시크릿이 재배포 없이 실행 중인 배포에 전파됨을 증명하는 자체 포함된 예제 Crew입니다.
|
||||
sidebarTitle: 로테이션 확인
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
이 가이드는 **클라우드 공급자에서 로테이션된 시크릿이 바로 다음 자동화 kickoff에서 적용됨**을 검증하는 방법을 보여줍니다 — 재배포 없음, 워커 재시작 없음. Workload Identity 기반 자격 증명([AWS](/ko/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/ko/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/ko/enterprise/features/secrets-manager/azure-workload-identity))을 구성한 경우에만 관련됩니다. 정적 자격 증명 배포는 로테이션 후 재배포가 필요합니다. 여기에서는 확인할 것이 없습니다.
|
||||
|
||||
아래 레시피는 하나의 도구, 하나의 에이전트, 하나의 작업으로 구성된 작은 자체 포함된 Crew를 사용합니다. Crew 프롬프트는 시크릿 값을 절대 참조하지 않으며 — 대신 도구가 `os.environ`에서 이를 읽고 본 것의 SHA-256 fingerprint를 보고합니다. 클라우드 공급자에서 시크릿을 로테이션하고, 다시 kickoff하면 fingerprint가 변경됩니다.
|
||||
|
||||
<Note>
|
||||
원시 값이 아닌 fingerprint를 사용하는 이유? 원시 시크릿을 LLM 출력과 트레이스 로그에 넣는 것은 유출 벡터입니다. fingerprint는 실제 값을 관찰 가능한 곳에 쓰지 않고도 "값이 변경되었다"는 것을 확인하기에 충분합니다.
|
||||
</Note>
|
||||
|
||||
## 사전 준비 사항
|
||||
|
||||
이 검증을 실행하기 전에:
|
||||
|
||||
- WI 기반 Secret Provider Credential이 구성되어 있어야 합니다([AWS](/ko/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/ko/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/ko/enterprise/features/secrets-manager/azure-workload-identity)).
|
||||
- `Secret = true`, 키 `API_KEY`(또는 원하는 이름 — 아래 도구를 일치시키도록 조정)로 클라우드 공급자의 시크릿을 참조하는 배포의 환경 변수.
|
||||
- 클라우드 공급자에서 시크릿 값을 업데이트할 방법(CLI 액세스 또는 클라우드 콘솔).
|
||||
- HTTP를 통해 배포를 kickoff할 방법(curl, Postman, 또는 CrewAI Platform의 **Run** 탭).
|
||||
|
||||
## 1단계 — 검증 Crew 스캐폴딩
|
||||
|
||||
새 Crew 프로젝트를 만듭니다. CrewAI CLI가 구조를 스캐폴딩합니다:
|
||||
|
||||
```bash
|
||||
crewai create crew rotation_verifier --skip_provider
|
||||
cd rotation_verifier
|
||||
```
|
||||
|
||||
## 2단계 — Credential Echo 도구 추가
|
||||
|
||||
`src/rotation_verifier/tools/custom_tool.py`를 시크릿 기반 환경 변수를 읽고 fingerprint를 반환하는 도구로 교체합니다:
|
||||
|
||||
```python src/rotation_verifier/tools/credential_echo_tool.py
|
||||
"""Tool that verifies a runtime-injected secret without leaking the value.
|
||||
|
||||
Reads the secret-backed env var (populated by the workload-identity
|
||||
secrets manager at kickoff time) and returns a stable fingerprint. Never
|
||||
echo raw credential values into LLM output or logs in production code —
|
||||
the fingerprint alone is sufficient to confirm rotation worked.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
# Match the deployment environment variable's `key` field.
|
||||
ENV_VAR_NAME = "API_KEY"
|
||||
|
||||
|
||||
class CredentialEchoTool(BaseTool):
|
||||
name: str = "credential_echo"
|
||||
description: str = (
|
||||
"Read the API credential from the worker's environment and return a "
|
||||
"fingerprint summary. Use this exactly once when asked to verify the "
|
||||
"current credential. Takes no arguments."
|
||||
)
|
||||
|
||||
def _run(self) -> str:
|
||||
value = os.environ.get(ENV_VAR_NAME)
|
||||
if not value:
|
||||
return (
|
||||
f"ERROR: {ENV_VAR_NAME} env var is not set. The workload-"
|
||||
"identity secret fetch did not run, or the deployment is "
|
||||
"missing the secret-backed env var."
|
||||
)
|
||||
fingerprint = hashlib.sha256(value.encode()).hexdigest()[:12]
|
||||
return f"Authenticated. credential.fingerprint=sha256:{fingerprint}"
|
||||
```
|
||||
|
||||
## 3단계 — 기본 에이전트 및 작업 구성 교체
|
||||
|
||||
Crew에는 하나의 에이전트와 하나의 작업이 있습니다 — 둘 다 시크릿 값을 **절대** 언급하지 않는 설명을 가지므로, 작업 키가 로테이션 전반에 걸쳐 안정적으로 유지됩니다.
|
||||
|
||||
```yaml src/rotation_verifier/config/agents.yaml
|
||||
credential_checker:
|
||||
role: >
|
||||
Credential Verifier
|
||||
goal: >
|
||||
Confirm that the workload-identity-backed secret reached this worker
|
||||
process and report a fingerprint of the current value.
|
||||
backstory: >
|
||||
You are a no-nonsense reliability engineer responsible for verifying
|
||||
that secrets fetched at runtime via workload identity are present
|
||||
and fresh. You always use the credential_echo tool exactly once and
|
||||
report the result verbatim — you never make up values.
|
||||
```
|
||||
|
||||
```yaml src/rotation_verifier/config/tasks.yaml
|
||||
verify_credential_task:
|
||||
description: >
|
||||
Use the credential_echo tool to read the runtime-injected credential
|
||||
and produce a one-line confirmation. The current year is {current_year}
|
||||
(use it only in the timestamp; do not transform the credential output).
|
||||
expected_output: >
|
||||
A single line in the form:
|
||||
"[{current_year}] <credential_echo tool's exact output>"
|
||||
agent: credential_checker
|
||||
```
|
||||
|
||||
## 4단계 — Crew 클래스 연결
|
||||
|
||||
```python src/rotation_verifier/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
from rotation_verifier.tools.credential_echo_tool import CredentialEchoTool
|
||||
|
||||
|
||||
@CrewBase
|
||||
class RotationVerifierCrew():
|
||||
"""Single-task crew that verifies a workload-identity-backed secret
|
||||
was successfully fetched at runtime.
|
||||
|
||||
Rotate the underlying secret in the cloud provider, kickoff again, and
|
||||
the credential fingerprint in the agent's report changes — without any
|
||||
re-deploy, worker restart, or input change. The crew prompt itself
|
||||
never references the secret value.
|
||||
"""
|
||||
|
||||
agents: list[BaseAgent]
|
||||
tasks: list[Task]
|
||||
|
||||
@agent
|
||||
def credential_checker(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config["credential_checker"],
|
||||
tools=[CredentialEchoTool()],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
@task
|
||||
def verify_credential_task(self) -> Task:
|
||||
return Task(config=self.tasks_config["verify_credential_task"])
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
)
|
||||
```
|
||||
|
||||
## 5단계 — 배포 및 시크릿 환경 변수 구성
|
||||
|
||||
다른 Crew와 마찬가지로 이 Crew를 CrewAI Platform에 배포합니다. 그런 다음 배포의 **Environment Variables** 페이지에서:
|
||||
|
||||
- **Key:** `API_KEY` (도구의 `ENV_VAR_NAME`과 일치해야 함)
|
||||
- **Value Source:** [AWS WI](/ko/enterprise/features/secrets-manager/aws-workload-identity) 또는 [GCP WI](/ko/enterprise/features/secrets-manager/gcp-workload-identity)에서 설정한 WI 기반 자격 증명
|
||||
- **Secret Name:** 클라우드 공급자의 Secret Manager에 있는 시크릿 이름
|
||||
|
||||
{/* SCREENSHOT: Environment Variables form with key=API_KEY, secret-backed value source selected, secret name filled → /images/secrets-manager/verify-rotation/01-env-var-form.png */}
|
||||
|
||||
## 6단계 — 첫 번째 Kickoff 실행
|
||||
|
||||
`<DEPLOYMENT_AUTH_TOKEN>`과 `<DEPLOYMENT_HOST>`를 배포의 **Run** 탭에 있는 값으로 교체합니다.
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
kickoff가 완료되면(몇 초), 에이전트의 출력을 확인합니다. 다음과 같이 표시됩니다:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:004421b993c9
|
||||
```
|
||||
|
||||
fingerprint를 기록합니다. 그 해시는 클라우드 공급자에 현재 있는 어떤 시크릿 값과 고유하게 연결되어 있습니다.
|
||||
|
||||
## 7단계 — 클라우드 공급자에서 시크릿 로테이션
|
||||
|
||||
<Tabs>
|
||||
<Tab title="AWS">
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id <SECRET_NAME> \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="GCP">
|
||||
새 버전을 추가합니다(Secret Manager는 항상 `latest`를 읽음):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add <SECRET_NAME> \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Azure">
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name <SECRET_NAME> \
|
||||
--value "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## 8단계 — 두 번째 Kickoff 실행 및 비교
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
이제 에이전트의 출력은 **다른 fingerprint**를 보여줍니다:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:e2fc89848f72
|
||||
```
|
||||
|
||||
이는 재배포, 워커 재시작 또는 기타 운영자 작업 없이 실행 중인 배포에서 로테이션이 적용되었음을 증명합니다.
|
||||
|
||||
## 이것이 검증하는 것 — 그리고 검증하지 않는 것
|
||||
|
||||
**검증하는 것:**
|
||||
- CrewAI Platform에서 WI OIDC 토큰 발급이 작동합니다.
|
||||
- 클라우드 측 신뢰(AWS의 IAM OIDC 공급자, GCP의 Workload Identity Pool, Azure의 Federated Identity Credential)가 토큰을 수락합니다.
|
||||
- 클라우드 측 ID(IAM Role / GCP 서비스 계정 / Entra App Registration)가 시크릿을 읽을 수 있는 액세스 권한을 가집니다.
|
||||
- 시크릿 값이 kickoff 시점에 워커 프로세스의 `os.environ`에 도달합니다.
|
||||
- 후속 로테이션이 다음 kickoff에 전파됩니다.
|
||||
|
||||
**검증하지 않는 것:**
|
||||
- 실제 프로덕션 Crew가 로테이션을 우아하게 처리하는지 — 예를 들어, 시작 시 환경 변수를 한 번만 읽는 장기 실행 작업은 작업이 끝날 때까지 이전 값을 계속 사용합니다. 적절히 계획하세요: 모듈 임포트 시가 아닌 사용 시점에 시크릿을 읽으세요.
|
||||
|
||||
## 왜 프롬프트에서 직접 시크릿을 참조하지 않나요?
|
||||
|
||||
더 간단해 보이는 데모는 시크릿 값을 작업 설명에 직접 넣고(예: "`{api_key}`에 대해 조사") 프롬프트를 검사하는 것입니다. **그렇게 하지 마세요.** 두 가지 이유:
|
||||
|
||||
1. **LLM 호출 트레이스와 공급자 측 로그에 시크릿이 유출됩니다.** 트레이스 액세스가 있는 모든 사람이 읽을 수 있습니다.
|
||||
2. **모든 kickoff에서 작업 설명이 변경됩니다.** CrewAI Platform은 설명의 MD5 해시로 작업을 식별합니다. 로테이션되는 값은 kickoff마다 해시가 변경되어 배포 시간 → 런타임 작업 매핑이 깨집니다. 증상: 작업 레코드가 무한정 `pending_run`으로 표시되거나 다중 작업 Crew의 일부 작업만 등록됩니다.
|
||||
|
||||
이 가이드의 도구 기반 패턴은 두 문제를 모두 회피합니다: 프롬프트는 정적이고, 도구가 런타임에 환경 변수를 읽으며, 값의 fingerprint만 LLM에 도달합니다.
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- [Secrets Manager 개요로 돌아가기](/ko/enterprise/features/secrets-manager/overview)
|
||||
- 검증되면 검증 Crew를 삭제합니다. 실제 Crew는 동일한 패턴을 따라야 합니다: 시크릿은 도구 내부의 `os.environ`을 통해 액세스되며, 절대 프롬프트에 치환되지 않습니다.
|
||||
@@ -145,7 +145,6 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="질문에 답하기">
|
||||
crew assistant가 요구 사항을 구체화할 수 있도록 하는 추가 질문에 답변하세요.
|
||||
</Step>
|
||||
@@ -159,12 +158,10 @@ LLM 연결과 기본 설정을 구성했다면 이제 Crew Studio 사용을 시
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="승인 또는 수정">
|
||||
계획을 승인하거나 필요하다면 변경을 요청하세요.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="다운로드 또는 배포">
|
||||
사용자화를 위해 코드를 다운로드하거나 플랫폼에 직접 배포하세요.
|
||||
</Step>
|
||||
|
||||
125
docs/ko/guides/flows/inputs-id-deprecation.mdx
Normal file
125
docs/ko/guides/flows/inputs-id-deprecation.mdx
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
title: "inputs.id에서 restore_from_state_id로 마이그레이션"
|
||||
description: "더 이상 지원되지 않는 inputs.id 하이드레이션에서 지원되는 restore_from_state_id 필드로 @persist 흐름을 이동"
|
||||
icon: "arrow-right-arrow-left"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
`inputs` 내에서 `id`를 전달하여 `@persist` 흐름을 하이드레이트하는 것은 **더 이상 지원되지 않으며**
|
||||
향후 릴리스에서 제거될 예정입니다. 대체품인 `restore_from_state_id`는 CrewAI **v1.14.5 이상**에서 사용할 수 있으며,
|
||||
아래 단계는 업그레이드 후 적용됩니다.
|
||||
</Warning>
|
||||
|
||||
## 개요
|
||||
|
||||
이전 실행에서 `@persist` 흐름을 하이드레이트하는 문서화된 방법은
|
||||
해당 실행의 UUID를 `inputs.id`로 전달하는 것입니다. CrewAI는 이제
|
||||
`inputs` 페이로드를 과부하하지 않고 동일한 하이드레이션을 수행하는 전용 필드인
|
||||
`restore_from_state_id`를 제공합니다 — 그리고 하이드레이션 키를 새로운 실행의
|
||||
정체성과 결합하지 않습니다.
|
||||
|
||||
## 마이그레이션
|
||||
|
||||
현재 `inputs={"id": ...}`로 `@persist` 흐름을 시작하는 경우:
|
||||
|
||||
```python
|
||||
# 더 이상 지원되지 않음
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(inputs={"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv"})
|
||||
```
|
||||
|
||||
`restore_from_state_id`로 전환하십시오:
|
||||
|
||||
```python
|
||||
# 지원됨
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(restore_from_state_id="abcd1234-5678-90ef-ghij-klmnopqrstuv")
|
||||
```
|
||||
|
||||
두 모드는 서로 다른 계보 의미론을 가지고 있습니다:
|
||||
|
||||
- `inputs={"id": <uuid>}` (더 이상 지원되지 않음) — **재개**: 제공된
|
||||
id 아래에 기록이 작성되어 동일한 `flow_uuid` 이력이 확장됩니다.
|
||||
- `restore_from_state_id=<uuid>` — **분기**: 스냅샷에서 상태를 하이드레이트한 후
|
||||
새로운 `state.id` 아래에 기록합니다. 원본 흐름의 이력은 보존됩니다.
|
||||
|
||||
대부분의 프로덕션 시나리오에서는 — 이전 상태에서 시드된 흐름을 다시 실행하는 경우 — 분기가
|
||||
필요합니다. 전체 정신 모델은 [Flow State 마스터링](/ko/guides/flows/mastering-flow-state)을 참조하십시오.
|
||||
|
||||
CrewAI AMP REST API를 통해 흐름을 시작하는 경우, 아래 [AMP](#amp)에서
|
||||
동일한 페이로드 마이그레이션을 참조하십시오.
|
||||
|
||||
## 왜 `@persist`에 대해 `inputs.id`를 더 이상 지원하지 않습니까?
|
||||
|
||||
`inputs.id`는 현재 이전 실행에서 `@persist` 흐름을 재개하는 문서화된 방법입니다. 문제는
|
||||
동일한 UUID가 두 가지 작업을 동시에 수행한다는 것입니다:
|
||||
|
||||
1. **어떤 스냅샷에서 `@persist`가 하이드레이트되는지를 선택합니다** — 해당 UUID 아래에 저장된 상태를 로드합니다.
|
||||
2. **새 실행의 흐름 실행 ID가 됩니다** (`state.id`는 SDK에서; 일부 컨텍스트에서는 `flow_id`로 표시됨) — 이
|
||||
시작에서의 모든 `@persist` 기록도 동일한 UUID 아래에 작성됩니다.
|
||||
|
||||
이 이중 역할이 이 가이드에서 설명하는 문제의 근본 원인입니다. 제공된 UUID가 새 실행의 id이기도 하므로,
|
||||
동일한 `inputs.id`를 전달하는 두 번의 시작은 두 개의 별도 실행이 아닙니다 — 그들은 id를 공유하고,
|
||||
지속성 기록을 공유하며, (AMP에서) 실행 목록에서 행을 공유합니다. "이 스냅샷에서 하이드레이트하지만,
|
||||
이 실행을 별도로 기록하십시오"라고 말할 방법이 없습니다.
|
||||
|
||||
`restore_from_state_id`가 그 분리입니다. 이는 `@persist`에 어떤 스냅샷에서 하이드레이트할지를 알려주며,
|
||||
새 실행이 새로운 `state.id`를 받을 수 있도록 합니다. 하이드레이션 소스와 기록된 실행은 더 이상 동일한 UUID가 아닙니다 — 이는 대부분의 프로덕션 시나리오에서 실제로 원하는 것입니다.
|
||||
|
||||
## 제거 일정
|
||||
|
||||
`@persist` 하이드레이션을 위한 `inputs.id`는 CrewAI의 향후 릴리스에서 제거될 예정입니다. 즉각적인 강제 종료는 없으며 — 기존 흐름은 계속 작동합니다 — 하지만 v1.14.5 이상으로 업그레이드하면,
|
||||
새 코드에서는 `restore_from_state_id`를 사용해야 하며, 기존 흐름은 다음 편리한 기회에 마이그레이션해야 합니다.
|
||||
|
||||
## AMP
|
||||
|
||||
흐름을 CrewAI AMP에 배포하는 경우, 마이그레이션은 배포된 팀에 전송되는 시작 페이로드로 확장되며,
|
||||
`inputs.id`를 재사용하는 가시적인 증상은 배포 대시보드에 나타납니다. 아래 두 개의 하위 섹션이 이를 다룹니다.
|
||||
|
||||
### 시작 페이로드 마이그레이션
|
||||
|
||||
현재 `inputs`에 `id`를 포함하여 배포된 흐름을 시작하는 경우:
|
||||
|
||||
```bash
|
||||
# 더 이상 지원되지 않음
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{"inputs": {"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv", "topic": "AI Agent Frameworks"}}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
UUID를 최상위 `restoreFromStateId` 필드로 이동하십시오:
|
||||
|
||||
```bash
|
||||
# 지원됨
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{
|
||||
"inputs": {"topic": "AI Agent Frameworks"},
|
||||
"restoreFromStateId": "abcd1234-5678-90ef-ghij-klmnopqrstuv"
|
||||
}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
`restoreFromStateId`는 시작 페이로드에서 `inputs` 옆에 위치하며, 내부에 있지 않습니다.
|
||||
`inputs` 객체는 이제 흐름이 실제로 소비하는 값만 포함합니다.
|
||||
|
||||
### `inputs.id`가 재사용될 때 발생하는 일
|
||||
|
||||
AMP가 기존 실행과 `inputs.id`가 일치하는 흐름의 시작을 수신하면,
|
||||
새로운 기록을 생성하는 대신 기존 기록으로 해결됩니다. 배포 대시보드에서 다음을 확인할 수 있습니다:
|
||||
|
||||
- **실행 상태** — 새로운 실행의 상태가 이전 실행의 상태를 덮어씁니다. 완료된 실행은
|
||||
다시 `실행 중`으로 전환되거나, `완료`된 실행은 새로운 시작이 실패할 경우 `오류`로 전환될 수 있습니다 — 어쨌든 대시보드는 더 이상
|
||||
원래 실행을 반영하지 않습니다.
|
||||
- **추적** — OTel 추적이 시작 간에 쌓이기 때문에 동일한 실행 id를 공유합니다; 이전 실행의 추적은
|
||||
새로운 실행의 추적과 교체되거나 혼합됩니다. 단계별 재생은 더 이상 단일 실행에 해당하지 않습니다.
|
||||
- **실행 목록** — 별도의 행으로 나타나야 할 시작이 단일 항목으로 축소되어 이력을 숨깁니다.
|
||||
|
||||
`restoreFromStateId`로 마이그레이션하면 모든 시작이 자체 실행으로 유지됩니다 — 각자의 상태, 추적 및 목록의 행을 가지며 — 여전히 이전 실행에서 상태를 하이드레이트합니다.
|
||||
|
||||
<Card title="도움이 필요하신가요?" icon="headset" href="mailto:support@crewai.com">
|
||||
흐름이 어떤 모드가 필요한지 확실하지 않거나 마이그레이션 중 문제가 발생하면 지원 팀에 문의하십시오.
|
||||
</Card>
|
||||
190
docs/ko/guides/migration/upgrading-crewai.mdx
Normal file
190
docs/ko/guides/migration/upgrading-crewai.mdx
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: "CrewAI 업그레이드"
|
||||
description: "프로젝트에서 CrewAI를 업그레이드하고 버전 간 브레이킹 체인지에 적응하는 방법."
|
||||
icon: "arrow-up-circle"
|
||||
---
|
||||
|
||||
## 개요
|
||||
|
||||
CrewAI 릴리스는 정기적으로 새로운 기능을 제공합니다. 이 가이드는 CLI와 프로젝트의 가상 환경을 모두 최신 상태로 유지하기 위한 실용적인 단계를 안내합니다.
|
||||
|
||||
새로 시작한다면 [설치](/ko/installation)를 참고하세요. 다른 프레임워크에서 옮겨오는 경우라면 [LangGraph에서 마이그레이션](/ko/guides/migration/migrating-from-langgraph)을 참고하세요.
|
||||
|
||||
---
|
||||
|
||||
## 업그레이드할 수 있는 두 가지
|
||||
|
||||
CrewAI는 사용자의 머신에 두 곳에 존재하며, 각각 독립적으로 업그레이드됩니다:
|
||||
|
||||
| 무엇 | 설치 방법 | 업그레이드 방법 |
|
||||
|---|---|---|
|
||||
| **전역 `crewai` CLI** | `uv tool install crewai` | `uv tool install crewai --upgrade` |
|
||||
| **프로젝트 venv** (코드가 실행되는 곳) | `crewai install` / `uv sync` | `uv add "crewai[...]>=X.Y.Z"` 후 `crewai install` |
|
||||
|
||||
이 둘은 — 그리고 자주 — 동기화가 어긋날 수 있습니다. `crewai --version`은 CLI 버전을 알려줍니다. 프로젝트 안에서 `uv pip show crewai`를 실행하면 venv 버전을 알려줍니다. 둘이 다른 것은 정상이며, 실행 중인 코드에 중요한 것은 venv 버전입니다.
|
||||
|
||||
## 왜 `crewai install`만으로는 업그레이드되지 않는가
|
||||
|
||||
`crewai install`은 `uv sync`를 감싼 얇은 래퍼입니다. 현재 `uv.lock` 파일이 지시하는 것 그대로를 설치할 뿐이며 — 어떤 버전 제약도 올리지 **않습니다**.
|
||||
|
||||
`pyproject.toml`이 `crewai>=1.11.1`이라 적혀 있고 lock 파일이 `1.11.1`로 해소되었다면, `crewai install`을 실행해도 `1.14.4`가 사용 가능하더라도 영원히 `1.11.1`에 머무릅니다.
|
||||
|
||||
실제로 업그레이드하려면 다음을 해야 합니다:
|
||||
|
||||
1. `pyproject.toml`의 버전 제약 업데이트
|
||||
2. lock 파일 재해소
|
||||
3. venv 동기화
|
||||
|
||||
`uv add`는 이 세 가지를 한 번에 처리합니다.
|
||||
|
||||
## 프로젝트 업그레이드 방법
|
||||
|
||||
```bash
|
||||
# 제약을 올리고 lock을 다시 만드는 한 번의 명령
|
||||
uv add "crewai[tools]>=1.14.4"
|
||||
|
||||
# venv 동기화 (crewai install은 내부적으로 uv sync를 호출)
|
||||
crewai install
|
||||
|
||||
# 확인
|
||||
uv pip show crewai
|
||||
# → Version: 1.14.4
|
||||
```
|
||||
|
||||
`[tools]`를 프로젝트에서 사용하는 extras로 바꾸세요 (예: `[tools,anthropic]`). 잘 모르겠다면 `pyproject.toml`의 `dependencies` 목록을 확인하세요.
|
||||
|
||||
<Note>
|
||||
`uv add`는 `pyproject.toml`과 `uv.lock`을 **둘 다** 원자적으로 업데이트합니다. `pyproject.toml`을 수동으로 편집하는 경우, `crewai install`이 새 버전을 가져가도록 하기 전에 `uv lock --upgrade-package crewai`를 실행해 lock 파일을 다시 해소해야 합니다.
|
||||
</Note>
|
||||
|
||||
## 전역 CLI 업그레이드
|
||||
|
||||
전역 CLI는 프로젝트와 분리되어 있습니다. 다음 명령으로 업그레이드하세요:
|
||||
|
||||
```bash
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
|
||||
업그레이드 후 셸이 `PATH`에 대해 경고하면 새로고침하세요:
|
||||
|
||||
```bash
|
||||
uv tool update-shell
|
||||
```
|
||||
|
||||
이 명령은 프로젝트의 venv를 **건드리지 않습니다** — 프로젝트 내부에서는 여전히 `uv add` + `crewai install`이 필요합니다.
|
||||
|
||||
## 둘이 동기화되었는지 확인
|
||||
|
||||
```bash
|
||||
# 전역 CLI 버전
|
||||
crewai --version
|
||||
|
||||
# 프로젝트 venv 버전
|
||||
uv pip show crewai | grep Version
|
||||
```
|
||||
|
||||
둘이 일치할 필요는 없지만 — 런타임 동작에 중요한 것은 프로젝트 venv 버전입니다.
|
||||
|
||||
<Note>
|
||||
CrewAI는 `Python >=3.10, <3.14`를 요구합니다. `uv`가 더 오래된 인터프리터로 설치되어 있다면, `crewai install`을 실행하기 전에 지원되는 Python으로 프로젝트 venv를 다시 만드세요.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 브레이킹 체인지 및 마이그레이션 노트
|
||||
|
||||
대부분의 업그레이드는 작은 조정만 필요합니다. 아래 항목들은 조용히 깨지거나 헷갈리는 트레이스백을 내는 영역들입니다.
|
||||
|
||||
### Import 경로: tools와 `BaseTool`
|
||||
|
||||
tools의 정식 import 위치는 `crewai.tools`입니다. 옛 경로들이 아직 튜토리얼에 등장하지만 업데이트해야 합니다.
|
||||
|
||||
```python
|
||||
# 이전
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.agents.tools import tool
|
||||
|
||||
# 이후
|
||||
from crewai.tools import BaseTool, tool
|
||||
```
|
||||
|
||||
`@tool` 데코레이터와 `BaseTool` 서브클래스는 모두 `crewai.tools`에 있습니다. `AgentFinish` 등 내부 에이전트 심볼들은 더 이상 공개 표면이 아닙니다 — import 중이었다면 event listener나 `Task` 콜백으로 전환하세요.
|
||||
|
||||
### `Agent` 파라미터 변경
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find authoritative sources on {topic}",
|
||||
backstory="You are a careful, source-driven researcher.",
|
||||
llm="gpt-4o-mini", # 모델명 문자열 또는 LLM 객체
|
||||
verbose=True, # 정수 레벨이 아닌 bool
|
||||
max_iter=15, # 버전마다 기본값이 바뀌었음 — 명시적으로 지정
|
||||
allow_delegation=False,
|
||||
)
|
||||
```
|
||||
|
||||
- `llm`은 문자열 모델명(설정된 provider를 통해 해소)이나 세밀한 제어를 위한 `LLM` 객체를 받습니다.
|
||||
- `verbose`는 일반 `bool`입니다. 정수를 전달해도 더 이상 로그 레벨을 토글하지 않습니다.
|
||||
- `max_iter`의 기본값은 릴리스 사이에 변경되었습니다. 첫 tool 호출 후 에이전트가 조용히 반복을 멈춘다면 `max_iter`를 명시적으로 지정하세요.
|
||||
|
||||
### `Crew` 파라미터
|
||||
|
||||
```python
|
||||
from crewai import Crew, Process
|
||||
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
process=Process.sequential, # 또는 Process.hierarchical
|
||||
memory=True,
|
||||
cache=True,
|
||||
embedder={"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
)
|
||||
```
|
||||
|
||||
- `process=Process.hierarchical`은 `manager_llm=` 또는 `manager_agent=` 중 하나가 필요합니다. 둘 다 없으면 kickoff 시 검증 단계에서 오류가 발생합니다.
|
||||
- 기본이 아닌 임베딩 provider와 함께 `memory=True`를 쓰려면 `embedder` dict가 필요합니다 — 아래의 [메모리와 embedder 설정](#memory-embedder-config)을 참고하세요.
|
||||
|
||||
### `Task` 구조화된 출력
|
||||
|
||||
`output_pydantic`, `output_json`, 또는 `output_file`을 사용해 task 결과를 타입이 지정된 형태로 강제할 수 있습니다:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from crewai import Task
|
||||
|
||||
class Article(BaseModel):
|
||||
title: str
|
||||
body: str
|
||||
|
||||
write = Task(
|
||||
description="Write an article about {topic}",
|
||||
expected_output="A short article with a title and body",
|
||||
agent=writer,
|
||||
output_pydantic=Article, # 인스턴스가 아닌 클래스
|
||||
output_file="output/article.md",
|
||||
)
|
||||
```
|
||||
|
||||
`output_pydantic`은 **클래스** 자체를 받습니다. `Article(title="", body="")`을 전달하는 것은 흔한 실수이며 헷갈리는 검증 오류로 실패합니다.
|
||||
|
||||
### 메모리와 embedder 설정 {#memory-embedder-config}
|
||||
|
||||
`memory=True`이고 OpenAI의 기본 임베딩을 사용하지 않는다면, `embedder`를 반드시 전달해야 합니다:
|
||||
|
||||
```python
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
memory=True,
|
||||
embedder={
|
||||
"provider": "ollama",
|
||||
"config": {"model": "nomic-embed-text"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
해당 provider의 자격 증명(`OPENAI_API_KEY`, `OLLAMA_HOST` 등)을 `.env` 파일에 설정하세요. 메모리 저장 경로는 기본적으로 프로젝트-로컬입니다 — embedder를 바꾸면 차원이 호환되지 않으므로 프로젝트의 메모리 디렉터리를 삭제하세요.
|
||||
@@ -797,7 +797,6 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
|
||||
여러 차원에서 우수한 성능을 제공하며 실제 환경에서 광범위하게 검증된 **GPT-4.1**, **Claude 3.7 Sonnet**, **Gemini 2.0 Flash**와 같은 잘 알려진 모델부터 시작하십시오.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="특화된 요구 사항 식별">
|
||||
crew에 코드 작성, reasoning, 속도 등 특정 요구가 있는지 확인하고, 이러한
|
||||
요구에 부합하는 **Claude 4 Sonnet**(개발용) 또는 **o3**(복잡한 분석용)과 같은
|
||||
@@ -805,7 +804,6 @@ LLM 선택을 최적화하고자 하는 팀을 위해 **CrewAI AMP 플랫폼**
|
||||
더불어 **Groq**와 같은 빠른 추론 제공자를 고려할 수 있습니다.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="다중 모델 전략 구현">
|
||||
각 에이전트의 역할에 따라 다양한 모델을 사용하세요. 관리자와 복잡한 작업에는
|
||||
고성능 모델을, 일상적 운영에는 효율적인 모델을 적용합니다.
|
||||
|
||||
@@ -13,7 +13,7 @@ The Daytona sandbox tools give CrewAI agents access to isolated, ephemeral compu
|
||||
|
||||
- **`DaytonaExecTool`** — run any shell command inside a sandbox.
|
||||
- **`DaytonaPythonTool`** — execute a block of Python source code inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox; also supports `move`, `find` (content grep), `search` (filename glob), `chmod` (permissions), `replace` (bulk find-and-replace), and `exists`.
|
||||
|
||||
All three tools share the same sandbox lifecycle controls, so you can mix and match them while keeping state in a single persistent sandbox.
|
||||
|
||||
@@ -55,7 +55,7 @@ from crewai_tools import DaytonaPythonTool
|
||||
tool = DaytonaPythonTool()
|
||||
result = tool.run(code="print(sum(range(10)))")
|
||||
print(result)
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": None}
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": ExecutionArtifacts(stdout="45\n", charts=[])}
|
||||
```
|
||||
|
||||
### Multi-step shell session (persistent)
|
||||
@@ -63,17 +63,22 @@ print(result)
|
||||
```python Code
|
||||
from crewai_tools import DaytonaExecTool, DaytonaFileTool
|
||||
|
||||
# Create the persistent sandbox via the first tool, then attach the second
|
||||
# tool to it so both share state (installed packages, files, env vars).
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Install a package, then write and run a script — all in the same sandbox
|
||||
exec_tool.run(command="pip install httpx -q")
|
||||
file_tool.run(action="write", path="/workspace/fetch.py", content="import httpx; print(httpx.get('https://httpbin.org/get').status_code)")
|
||||
exec_tool.run(command="python /workspace/fetch.py")
|
||||
file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
|
||||
file_tool.run(
|
||||
action="write",
|
||||
path="workspace/script.py",
|
||||
content="import httpx; print(f'httpx loaded, version {httpx.__version__}')",
|
||||
)
|
||||
exec_tool.run(command="python workspace/script.py")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Each tool instance maintains its own persistent sandbox. To share **one** sandbox across two tools, create the first tool, grab its sandbox id via `tool._persistent_sandbox.id`, and pass it to the second tool via `sandbox_id=...`.
|
||||
By default, each tool with `persistent=True` lazily creates its **own** sandbox on first use. The pattern above shares a single sandbox across multiple tools by reading the first tool's `active_sandbox_id` after a `.run()` call and passing it to the others via `sandbox_id=...`. With `persistent=False` (the default), every `.run()` call gets a fresh sandbox that's deleted at the end of that call.
|
||||
</Note>
|
||||
|
||||
### Attach to an existing sandbox
|
||||
@@ -82,7 +87,7 @@ Each tool instance maintains its own persistent sandbox. To share **one** sandbo
|
||||
from crewai_tools import DaytonaExecTool
|
||||
|
||||
tool = DaytonaExecTool(sandbox_id="my-long-lived-sandbox")
|
||||
result = tool.run(command="ls /workspace")
|
||||
result = tool.run(command="ls workspace")
|
||||
```
|
||||
|
||||
### Custom sandbox parameters
|
||||
@@ -102,6 +107,41 @@ tool = DaytonaExecTool(
|
||||
)
|
||||
```
|
||||
|
||||
### Searching, moving, and modifying files
|
||||
|
||||
```python Code
|
||||
from crewai_tools import DaytonaFileTool
|
||||
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Find every TODO in the source tree (grep file contents recursively)
|
||||
file_tool.run(action="find", path="workspace/src", pattern="TODO:")
|
||||
|
||||
# Find all Python files (glob match on filenames)
|
||||
file_tool.run(action="search", path="workspace", pattern="*.py")
|
||||
|
||||
# Make a script executable
|
||||
file_tool.run(action="chmod", path="workspace/run.sh", mode="755")
|
||||
|
||||
# Rename or move a file
|
||||
file_tool.run(
|
||||
action="move",
|
||||
path="workspace/draft.md",
|
||||
destination="workspace/final.md",
|
||||
)
|
||||
|
||||
# Bulk find-and-replace across multiple files
|
||||
file_tool.run(
|
||||
action="replace",
|
||||
paths=["workspace/src/a.py", "workspace/src/b.py"],
|
||||
pattern="old_function",
|
||||
replacement="new_function",
|
||||
)
|
||||
|
||||
# Quick existence check before a destructive op
|
||||
file_tool.run(action="exists", path="workspace/cache.db")
|
||||
```
|
||||
|
||||
### Agent integration
|
||||
|
||||
```python Code
|
||||
@@ -121,7 +161,7 @@ coder = Agent(
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to /workspace/fib.py, and run it.",
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to workspace/fib.py, and run it.",
|
||||
expected_output="The first 10 Fibonacci numbers printed to stdout.",
|
||||
agent=coder,
|
||||
)
|
||||
@@ -168,12 +208,22 @@ All three tools accept these parameters at initialization:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`. |
|
||||
| `path` | `str` | ✓ | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | | Content to write or append. Required for `append`. |
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`, `exists`, `move`, `find`, `search`, `chmod`, `replace`. |
|
||||
| `path` | `str \| None` | ✓ for all actions except `replace` | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | ✓ for `append` | Content to write or append. |
|
||||
| `binary` | `bool` | | If `True`, `content` is base64 on write; returns base64 on read. |
|
||||
| `recursive` | `bool` | | For `delete`: remove directories recursively. |
|
||||
| `mode` | `str` | | For `mkdir`: octal permission string (default `"0755"`). |
|
||||
| `mode` | `str \| None` | | For `mkdir`: octal permissions for the new directory (defaults to `"0755"`). For `chmod`: octal permissions to apply to the target. |
|
||||
| `destination` | `str \| None` | ✓ for `move` | Destination path for `move`. |
|
||||
| `pattern` | `str \| None` | ✓ for `find`, `search`, `replace` | For `find`: substring matched against file CONTENTS. For `search`: glob matched against file NAMES (e.g. `*.py`). For `replace`: text to replace inside files. |
|
||||
| `replacement` | `str \| None` | ✓ for `replace` | Replacement text for `pattern`. |
|
||||
| `paths` | `list[str] \| None` | ✓ for `replace` | List of file paths in which to replace text. |
|
||||
| `owner` | `str \| None` | | For `chmod`: new file owner. |
|
||||
| `group` | `str \| None` | | For `chmod`: new file group. |
|
||||
|
||||
<Note>
|
||||
For `chmod`, pass at least one of `mode`, `owner`, or `group` — any field left as `None` is left unchanged on the target.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
For files larger than a few KB, create the file first with `action="write"` and empty content, then send the body via multiple `action="append"` calls of ~4 KB each to stay within tool-call payload limits.
|
||||
|
||||
@@ -4,6 +4,115 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="19 mai 2026">
|
||||
## v1.14.5
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Recursos
|
||||
- Deprecar `CrewAgentExecutor`, definir agentes Crew como `AgentExecutor`
|
||||
- Melhorar ferramentas do sandbox Daytona
|
||||
- Adicionar parâmetro de início `restore_from_state_id`
|
||||
- Adicionar destaques ao `ExaSearchTool`, renomeando de `EXASearchTool`
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir vazamento de memória em `git.py` usando `cached_property`
|
||||
- Exibir chamadas de ferramentas transmitidas quando `available_functions` está ausente
|
||||
- Garantir eventos de carregamento de `skills` para rastros
|
||||
- Corrigir caminho do endpoint de status de `/{kickoff_id}/status` para `/status/{kickoff_id}`
|
||||
- Restaurar bloco de código ausente no guia de primeiro fluxo em pt-BR
|
||||
- Impedir que `result_as_answer` retorne mensagens de bloqueio de hook ou de erro como resposta final
|
||||
- Preservar saídas de tarefas durante o descarregamento assíncrono em lote
|
||||
- Sempre restaurar `task.output_pydantic` no bloco finally
|
||||
- Lidar com entrada de `BaseModel` em `convert_to_model`
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.14.5
|
||||
- Adicionar guia de migração de atualização OSS & crew-to-flow
|
||||
- Documentar variáveis de ambiente adicionais para devtools
|
||||
- Adicionar documentação para `TavilyGetResearch`
|
||||
|
||||
### Refatoração
|
||||
- Extrair CLI para o pacote autônomo `crewai-cli`
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@NIK-TIGER-BILL, @akaKuruma, @cgoeppinger, @github-actions[bot], @greysonlalonde, @heitorado, @irfaan101, @iris-clawd, @lorenzejay, @manisrinivasan2k1, @minasami-pr, @mislavivanda, @theCyberTech, @theishangoswami, @wishhyt
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="18 mai 2026">
|
||||
## v1.14.5a7
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a7)
|
||||
|
||||
## O que Mudou
|
||||
|
||||
### Documentação
|
||||
- Atualizar changelog e versão para v1.14.5a6
|
||||
|
||||
### Mudanças Quebradoras
|
||||
- Depreciar o campo function_calling_llm
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @heitorado
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="15 mai 2026">
|
||||
## v1.14.5a6
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a6)
|
||||
|
||||
## O que mudou
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir chamadas de ferramentas transmitidas quando available_functions está ausente
|
||||
- Atualizar a dependência langsmith para a versão >=0.8.0 para resolver GHSA-3644-q5cj-c5c7
|
||||
- Resolver espaços reservados de blocos de código não traduzidos na documentação em português brasileiro
|
||||
|
||||
### Documentação
|
||||
- Adicionar documentação para TavilyGetResearch
|
||||
- Atualizar changelog e versão para v1.14.5a5
|
||||
|
||||
## Contributors
|
||||
|
||||
@greysonlalonde, @heitorado, @iris-clawd, @lorenzejay, @manisrinivasan2k1
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="13 mai 2026">
|
||||
## v1.14.5a5
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.14.5a5)
|
||||
|
||||
## O Que Mudou
|
||||
|
||||
### Recursos
|
||||
- Deprecar CrewAgentExecutor, definir agentes Crew como AgentExecutor
|
||||
- Melhorar ferramentas de sandbox Daytona
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir bloco de código ausente no guia de primeiro fluxo em pt-BR
|
||||
- Registrar falhas de pré-revisão e destilação HITL, adicionar learn_strict
|
||||
- Corrigir urllib3 para vulnerabilidades de segurança
|
||||
- Corrigir gitpython e langchain-core; ignorar CVE paramiko não corrigido
|
||||
- Atualizar todos os pacotes de workspace publicados no bloqueio/sincronização uv
|
||||
|
||||
### Documentação
|
||||
- Adicionar guia de migração de `inputs.id` para `restoreFromStateId`
|
||||
- Adicionar guia de atualização OSS e migração de crew para flow
|
||||
- Atualizar changelog e versão para v1.14.5a4
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@akaKuruma, @greysonlalonde, @iris-clawd, @lorenzejay, @mislavivanda
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="09 mai 2026">
|
||||
## v1.14.5a4
|
||||
|
||||
|
||||
@@ -24,7 +24,63 @@ Os flows permitem que você crie fluxos de trabalho estruturados e orientados po
|
||||
Vamos criar um Flow simples no qual você usará a OpenAI para gerar uma cidade aleatória em uma tarefa e, em seguida, usará essa cidade para gerar uma curiosidade em outra tarefa.
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from dotenv import load_dotenv
|
||||
from litellm import completion
|
||||
|
||||
load_dotenv()
|
||||
|
||||
class ExampleFlow(Flow):
|
||||
model = "gpt-4o-mini"
|
||||
|
||||
@start()
|
||||
def generate_city(self):
|
||||
print("Starting flow")
|
||||
# Cada estado do flow recebe automaticamente um ID único
|
||||
print(f"Flow State ID: {self.state['id']}")
|
||||
|
||||
response = completion(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Return the name of a random city in the world.",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
random_city = response["choices"][0]["message"]["content"]
|
||||
# Armazena a cidade no nosso estado
|
||||
self.state["city"] = random_city
|
||||
print(f"Random City: {random_city}")
|
||||
|
||||
return random_city
|
||||
|
||||
@listen(generate_city)
|
||||
def generate_fun_fact(self, random_city):
|
||||
response = completion(
|
||||
model=self.model,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Tell me a fun fact about {random_city}",
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
fun_fact = response["choices"][0]["message"]["content"]
|
||||
# Armazena a curiosidade no nosso estado
|
||||
self.state["fun_fact"] = fun_fact
|
||||
return fun_fact
|
||||
|
||||
|
||||
|
||||
flow = ExampleFlow()
|
||||
flow.plot()
|
||||
result = flow.kickoff()
|
||||
|
||||
print(f"Generated fun fact: {result}")
|
||||
```
|
||||
|
||||
Na ilustração acima, criamos um Flow simples que gera uma cidade aleatória usando a OpenAI e depois cria uma curiosidade sobre essa cidade. O Flow consiste em duas tarefas: `generate_city` e `generate_fun_fact`. A tarefa `generate_city` é o ponto de início do Flow, enquanto a tarefa `generate_fun_fact` fica escutando o resultado da tarefa `generate_city`.
|
||||
@@ -56,12 +112,16 @@ O decorador `@listen()` pode ser usado de várias formas:
|
||||
1. **Escutando um Método pelo Nome**: Você pode passar o nome do método ao qual deseja escutar como string. Quando esse método concluir, o método ouvinte será chamado.
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
@listen("generate_city")
|
||||
def generate_fun_fact(self, random_city):
|
||||
# Implementação
|
||||
```
|
||||
|
||||
2. **Escutando um Método Diretamente**: Você pode passar o próprio método. Quando esse método concluir, o método ouvinte será chamado.
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
@listen(generate_city)
|
||||
def generate_fun_fact(self, random_city):
|
||||
# Implementação
|
||||
```
|
||||
|
||||
### Saída de um Flow
|
||||
@@ -76,7 +136,24 @@ Veja como acessar a saída final:
|
||||
|
||||
<CodeGroup>
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class OutputExampleFlow(Flow):
|
||||
@start()
|
||||
def first_method(self):
|
||||
return "Output from first_method"
|
||||
|
||||
@listen(first_method)
|
||||
def second_method(self, first_output):
|
||||
return f"Second method received: {first_output}"
|
||||
|
||||
|
||||
flow = OutputExampleFlow()
|
||||
flow.plot("my_flow_plot")
|
||||
final_output = flow.kickoff()
|
||||
|
||||
print("---- Final Output ----")
|
||||
print(final_output)
|
||||
```
|
||||
|
||||
```text Output
|
||||
@@ -97,8 +174,34 @@ Além de recuperar a saída final, você pode acessar e atualizar o estado dentr
|
||||
Veja um exemplo de como atualizar e acessar o estado:
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ExampleState(BaseModel):
|
||||
counter: int = 0
|
||||
message: str = ""
|
||||
|
||||
class StateExampleFlow(Flow[ExampleState]):
|
||||
|
||||
@start()
|
||||
def first_method(self):
|
||||
self.state.message = "Hello from first_method"
|
||||
self.state.counter += 1
|
||||
|
||||
@listen(first_method)
|
||||
def second_method(self):
|
||||
self.state.message += " - updated by second_method"
|
||||
self.state.counter += 1
|
||||
return self.state.message
|
||||
|
||||
flow = StateExampleFlow()
|
||||
flow.plot("my_flow_plot")
|
||||
final_output = flow.kickoff()
|
||||
print(f"Final Output: {final_output}")
|
||||
print("Final State:")
|
||||
print(flow.state)
|
||||
```
|
||||
|
||||
```text Output
|
||||
@@ -128,7 +231,33 @@ Essa abordagem oferece flexibilidade, permitindo que o desenvolvedor adicione ou
|
||||
Mesmo com estados não estruturados, os flows do CrewAI geram e mantêm automaticamente um identificador único (UUID) para cada instância de estado.
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class UnstructuredExampleFlow(Flow):
|
||||
|
||||
@start()
|
||||
def first_method(self):
|
||||
# O estado inclui automaticamente um campo 'id'
|
||||
print(f"State ID: {self.state['id']}")
|
||||
self.state['counter'] = 0
|
||||
self.state['message'] = "Hello from structured flow"
|
||||
|
||||
@listen(first_method)
|
||||
def second_method(self):
|
||||
self.state['counter'] += 1
|
||||
self.state['message'] += " - updated"
|
||||
|
||||
@listen(second_method)
|
||||
def third_method(self):
|
||||
self.state['counter'] += 1
|
||||
self.state['message'] += " - updated again"
|
||||
|
||||
print(f"State after third_method: {self.state}")
|
||||
|
||||
|
||||
flow = UnstructuredExampleFlow()
|
||||
flow.plot("my_flow_plot")
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||

|
||||
@@ -148,7 +277,39 @@ Ao usar modelos como o `BaseModel` da Pydantic, os desenvolvedores podem definir
|
||||
Cada estado nos flows do CrewAI recebe automaticamente um identificador único (UUID) para ajudar no rastreamento e gerenciamento. Esse ID é gerado e mantido automaticamente pelo sistema de flows.
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class ExampleState(BaseModel):
|
||||
# Nota: o campo 'id' é adicionado automaticamente a todos os estados
|
||||
counter: int = 0
|
||||
message: str = ""
|
||||
|
||||
|
||||
class StructuredExampleFlow(Flow[ExampleState]):
|
||||
|
||||
@start()
|
||||
def first_method(self):
|
||||
# Acesse o ID gerado automaticamente, se necessário
|
||||
print(f"State ID: {self.state.id}")
|
||||
self.state.message = "Hello from structured flow"
|
||||
|
||||
@listen(first_method)
|
||||
def second_method(self):
|
||||
self.state.counter += 1
|
||||
self.state.message += " - updated"
|
||||
|
||||
@listen(second_method)
|
||||
def third_method(self):
|
||||
self.state.counter += 1
|
||||
self.state.message += " - updated again"
|
||||
|
||||
print(f"State after third_method: {self.state}")
|
||||
|
||||
|
||||
flow = StructuredExampleFlow()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||

|
||||
@@ -182,7 +343,19 @@ O decorador @persist permite a persistência automática do estado nos flows do
|
||||
Quando aplicado no nível da classe, o decorador @persist garante a persistência automática de todos os estados dos métodos do flow:
|
||||
|
||||
```python
|
||||
# (O código não é traduzido)
|
||||
@persist # Usa SQLiteFlowPersistence por padrão
|
||||
class MyFlow(Flow[MyState]):
|
||||
@start()
|
||||
def initialize_flow(self):
|
||||
# Este método terá seu estado persistido automaticamente
|
||||
self.state.counter = 1
|
||||
print("Initialized flow. State ID:", self.state.id)
|
||||
|
||||
@listen(initialize_flow)
|
||||
def next_step(self):
|
||||
# O estado (incluindo self.state.id) é recarregado automaticamente
|
||||
self.state.counter += 1
|
||||
print("Flow state is persisted. Counter:", self.state.counter)
|
||||
```
|
||||
|
||||
### Persistência no Nível de Método
|
||||
@@ -190,7 +363,14 @@ Quando aplicado no nível da classe, o decorador @persist garante a persistênci
|
||||
Para um controle mais granular, você pode aplicar @persist em métodos específicos:
|
||||
|
||||
```python
|
||||
# (O código não é traduzido)
|
||||
class AnotherFlow(Flow[dict]):
|
||||
@persist # Persiste apenas o estado deste método
|
||||
@start()
|
||||
def begin(self):
|
||||
if "runs" not in self.state:
|
||||
self.state["runs"] = 0
|
||||
self.state["runs"] += 1
|
||||
print("Method-level persisted runs:", self.state["runs"])
|
||||
```
|
||||
|
||||
### Forking de Estado Persistido
|
||||
@@ -282,8 +462,29 @@ A arquitetura de persistência enfatiza precisão técnica e opções de persona
|
||||
A função `or_` nos flows permite escutar múltiplos métodos e acionar o método ouvinte quando qualquer um dos métodos especificados gerar uma saída.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, listen, or_, start
|
||||
|
||||
class OrExampleFlow(Flow):
|
||||
|
||||
@start()
|
||||
def start_method(self):
|
||||
return "Hello from the start method"
|
||||
|
||||
@listen(start_method)
|
||||
def second_method(self):
|
||||
return "Hello from the second method"
|
||||
|
||||
@listen(or_(start_method, second_method))
|
||||
def logger(self, result):
|
||||
print(f"Logger: {result}")
|
||||
|
||||
|
||||
|
||||
flow = OrExampleFlow()
|
||||
flow.plot("my_flow_plot")
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
@@ -302,8 +503,28 @@ A função `or_` serve para escutar vários métodos e disparar o método ouvint
|
||||
A função `and_` nos flows permite escutar múltiplos métodos e acionar o método ouvinte apenas quando todos os métodos especificados emitirem uma saída.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
from crewai.flow.flow import Flow, and_, listen, start
|
||||
|
||||
class AndExampleFlow(Flow):
|
||||
|
||||
@start()
|
||||
def start_method(self):
|
||||
self.state["greeting"] = "Hello from the start method"
|
||||
|
||||
@listen(start_method)
|
||||
def second_method(self):
|
||||
self.state["joke"] = "What do computers eat? Microchips."
|
||||
|
||||
@listen(and_(start_method, second_method))
|
||||
def logger(self):
|
||||
print("---- Logger ----")
|
||||
print(self.state)
|
||||
|
||||
flow = AndExampleFlow()
|
||||
flow.plot()
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
@@ -323,8 +544,42 @@ O decorador `@router()` nos flows permite definir lógica de roteamento condicio
|
||||
Você pode especificar diferentes rotas conforme a saída do método, permitindo controlar o fluxo de execução de forma dinâmica.
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
import random
|
||||
from crewai.flow.flow import Flow, listen, router, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ExampleState(BaseModel):
|
||||
success_flag: bool = False
|
||||
|
||||
class RouterFlow(Flow[ExampleState]):
|
||||
|
||||
@start()
|
||||
def start_method(self):
|
||||
print("Starting the structured flow")
|
||||
random_boolean = random.choice([True, False])
|
||||
self.state.success_flag = random_boolean
|
||||
|
||||
@router(start_method)
|
||||
def second_method(self):
|
||||
if self.state.success_flag:
|
||||
return "success"
|
||||
else:
|
||||
return "failed"
|
||||
|
||||
@listen("success")
|
||||
def third_method(self):
|
||||
print("Third method running")
|
||||
|
||||
@listen("failed")
|
||||
def fourth_method(self):
|
||||
print("Fourth method running")
|
||||
|
||||
|
||||
flow = RouterFlow()
|
||||
flow.plot("my_flow_plot")
|
||||
flow.kickoff()
|
||||
```
|
||||
|
||||
```text Output
|
||||
@@ -401,7 +656,105 @@ Para um guia completo sobre feedback humano em flows, incluindo feedback assínc
|
||||
Os agentes podem ser integrados facilmente aos seus flows, oferecendo uma alternativa leve às crews completas quando você precisar executar tarefas simples e focadas. Veja um exemplo de como utilizar um agente em um flow para realizar uma pesquisa de mercado:
|
||||
|
||||
```python
|
||||
# (O código não é traduzido)
|
||||
import asyncio
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from crewai_tools import SerperDevTool
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from crewai.agent import Agent
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
|
||||
# Define um formato de saída estruturado
|
||||
class MarketAnalysis(BaseModel):
|
||||
key_trends: List[str] = Field(description="List of identified market trends")
|
||||
market_size: str = Field(description="Estimated market size")
|
||||
competitors: List[str] = Field(description="Major competitors in the space")
|
||||
|
||||
|
||||
# Define o estado do flow
|
||||
class MarketResearchState(BaseModel):
|
||||
product: str = ""
|
||||
analysis: MarketAnalysis | None = None
|
||||
|
||||
|
||||
# Cria uma classe de flow
|
||||
class MarketResearchFlow(Flow[MarketResearchState]):
|
||||
@start()
|
||||
def initialize_research(self) -> Dict[str, Any]:
|
||||
print(f"Starting market research for {self.state.product}")
|
||||
return {"product": self.state.product}
|
||||
|
||||
@listen(initialize_research)
|
||||
async def analyze_market(self) -> Dict[str, Any]:
|
||||
# Cria um agente para pesquisa de mercado
|
||||
analyst = Agent(
|
||||
role="Market Research Analyst",
|
||||
goal=f"Analyze the market for {self.state.product}",
|
||||
backstory="You are an experienced market analyst with expertise in "
|
||||
"identifying market trends and opportunities.",
|
||||
tools=[SerperDevTool()],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
# Define a consulta de pesquisa
|
||||
query = f"""
|
||||
Research the market for {self.state.product}. Include:
|
||||
1. Key market trends
|
||||
2. Market size
|
||||
3. Major competitors
|
||||
|
||||
Format your response according to the specified structure.
|
||||
"""
|
||||
|
||||
# Executa a análise com formato de saída estruturado
|
||||
result = await analyst.kickoff_async(query, response_format=MarketAnalysis)
|
||||
if result.pydantic:
|
||||
print("result", result.pydantic)
|
||||
else:
|
||||
print("result", result)
|
||||
|
||||
# Retorna a análise para atualizar o estado
|
||||
return {"analysis": result.pydantic}
|
||||
|
||||
@listen(analyze_market)
|
||||
def present_results(self, analysis) -> None:
|
||||
print("\nMarket Analysis Results")
|
||||
print("=====================")
|
||||
|
||||
if isinstance(analysis, dict):
|
||||
# Se recebemos um dict com a chave 'analysis', extrai o objeto de análise real
|
||||
market_analysis = analysis.get("analysis")
|
||||
else:
|
||||
market_analysis = analysis
|
||||
|
||||
if market_analysis and isinstance(market_analysis, MarketAnalysis):
|
||||
print("\nKey Market Trends:")
|
||||
for trend in market_analysis.key_trends:
|
||||
print(f"- {trend}")
|
||||
|
||||
print(f"\nMarket Size: {market_analysis.market_size}")
|
||||
|
||||
print("\nMajor Competitors:")
|
||||
for competitor in market_analysis.competitors:
|
||||
print(f"- {competitor}")
|
||||
else:
|
||||
print("No structured analysis data available.")
|
||||
print("Raw analysis:", analysis)
|
||||
|
||||
|
||||
# Exemplo de uso
|
||||
async def run_flow():
|
||||
flow = MarketResearchFlow()
|
||||
flow.plot("MarketResearchFlowPlot")
|
||||
result = await flow.kickoff_async(inputs={"product": "AI-powered chatbots"})
|
||||
return result
|
||||
|
||||
|
||||
# Executa o flow
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(run_flow())
|
||||
```
|
||||
|
||||

|
||||
@@ -463,7 +816,50 @@ No arquivo `main.py`, você cria seu flow e conecta as crews. É possível defin
|
||||
Veja um exemplo de como conectar a `poem_crew` no arquivo `main.py`:
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
#!/usr/bin/env python
|
||||
from random import randint
|
||||
|
||||
from pydantic import BaseModel
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from .crews.poem_crew.poem_crew import PoemCrew
|
||||
|
||||
class PoemState(BaseModel):
|
||||
sentence_count: int = 1
|
||||
poem: str = ""
|
||||
|
||||
class PoemFlow(Flow[PoemState]):
|
||||
|
||||
@start()
|
||||
def generate_sentence_count(self):
|
||||
print("Generating sentence count")
|
||||
self.state.sentence_count = randint(1, 5)
|
||||
|
||||
@listen(generate_sentence_count)
|
||||
def generate_poem(self):
|
||||
print("Generating poem")
|
||||
result = PoemCrew().crew().kickoff(inputs={"sentence_count": self.state.sentence_count})
|
||||
|
||||
print("Poem generated", result.raw)
|
||||
self.state.poem = result.raw
|
||||
|
||||
@listen(generate_poem)
|
||||
def save_poem(self):
|
||||
print("Saving poem")
|
||||
with open("poem.txt", "w") as f:
|
||||
f.write(self.state.poem)
|
||||
|
||||
def kickoff():
|
||||
poem_flow = PoemFlow()
|
||||
poem_flow.kickoff()
|
||||
|
||||
|
||||
def plot():
|
||||
poem_flow = PoemFlow()
|
||||
poem_flow.plot("PoemFlowPlot")
|
||||
|
||||
if __name__ == "__main__":
|
||||
kickoff()
|
||||
plot()
|
||||
```
|
||||
|
||||
Neste exemplo, a classe `PoemFlow` define um fluxo que gera a quantidade de frases, usa a `PoemCrew` para gerar um poema e, depois, salva o poema em um arquivo. O flow inicia com o método `kickoff()`, e o gráfico é gerado pelo método `plot()`.
|
||||
@@ -515,7 +911,8 @@ O CrewAI oferece duas formas práticas de gerar plots dos seus flows:
|
||||
Se estiver trabalhando diretamente com uma instância do flow, basta chamar o método `plot()` do objeto. Isso criará um arquivo HTML com o plot interativo do seu flow.
|
||||
|
||||
```python Code
|
||||
# (O código não é traduzido)
|
||||
# Considerando que você já tem uma instância do flow
|
||||
flow.plot("my_flow_plot")
|
||||
```
|
||||
|
||||
Esse comando gera um arquivo chamado `my_flow_plot.html` no diretório atual. Abra esse arquivo em um navegador para visualizar o plot interativo.
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
---
|
||||
title: AWS Workload Identity (Federação OIDC)
|
||||
description: Configure o AWS Secrets Manager via Workload Identity para acesso a segredos consciente de rotação e sem credenciais
|
||||
sidebarTitle: AWS — Workload Identity
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia configura o AWS Secrets Manager como provedor de segredos usando **Workload Identity Federation**: a CrewAI Platform emite tokens OIDC de curta duração, os troca por credenciais AWS via STS e lê seus segredos — sem que uma chave de acesso AWS de longa duração seja armazenada em lugar algum.
|
||||
|
||||
<Note>
|
||||
**Por que este caminho:** os segredos são resolvidos no momento de execução da automação, então **valores rotacionados se propagam para o próximo kickoff sem novo deploy**. Se você só precisa de credenciais estáticas e não se importa com a propagação de rotação, veja o guia mais simples [AWS — chaves estáticas / AssumeRole](/pt-BR/enterprise/features/secrets-manager/aws).
|
||||
</Note>
|
||||
|
||||
### Como funciona em runtime
|
||||
|
||||
1. O worker do deployment solicita um JWT OIDC fresco à CrewAI Platform.
|
||||
2. O worker chama `sts:AssumeRoleWithWebIdentity` no role IAM que você configurou abaixo, apresentando o JWT.
|
||||
3. O AWS STS valida o JWT contra o issuer OIDC público da CrewAI Platform (então sua instalação da plataforma deve ser acessível a partir da AWS), depois retorna credenciais AWS de curta duração.
|
||||
4. O worker usa essas credenciais para chamar `secretsmanager:GetSecretValue`.
|
||||
5. O valor obtido é injetado como valor da variável de ambiente para aquele kickoff de automação.
|
||||
|
||||
Tokens OIDC subject são cacheados por ~1 hora para evitar reemissão a cada kickoff. Valores de segredos são buscados frescos a cada kickoff independentemente do estado do cache OIDC, e é isso que torna este caminho consciente de rotação.
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- A imagem do pod de automação deve incluir o runtime da CrewAI versão `1.14.5` ou superior.
|
||||
- Uma conta AWS com permissão para criar provedores IAM OIDC, roles IAM e políticas IAM.
|
||||
- A região AWS onde seus segredos vivem (ou viverão), ex. `us-east-1`.
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem as permissões `workload_identity_configs: manage` e `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **O UUID da sua organização CrewAI.** Encontre-o na página de configurações da organização na CrewAI Platform — a trust policy no Passo 3 vincula o role IAM a esta organização específica.
|
||||
- **Sua instalação da CrewAI Platform deve ser acessível a partir da AWS via HTTPS** para que o AWS STS possa buscar o documento de discovery OIDC e o JWKS durante a validação do token. Confirme com o administrador da sua plataforma que o host é acessível pela internet (ou que a AWS tem alcance de rede até ele via VPC peering / equivalente).
|
||||
</Note>
|
||||
|
||||
## Passo 1 — Encontre a URL do Issuer OIDC da Sua CrewAI Platform
|
||||
|
||||
Sua instalação da CrewAI Platform publica um documento de discovery OpenID Connect em `https://<your-platform-host>/.well-known/openid-configuration`. O campo `issuer` nesse documento é a URL que a AWS registrará como provedor OIDC confiável.
|
||||
|
||||
Abra a URL em um navegador (substituindo `<your-platform-host>` pelo seu hostname real, ex. `app.crewai.com`):
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
Você deverá ver um JSON contendo:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Anote o valor exato de `issuer` — você o usará no Passo 3.
|
||||
|
||||
<Tip>
|
||||
Se a URL retornar 404 ou 503, contate o administrador da sua plataforma. O issuer OIDC requer uma chave de assinatura privada configurada no momento da instalação. Veja o guia de instalação da plataforma para as configurações `OIDC_PRIVATE_KEY` e `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## Passo 2 — Registrar a CrewAI Platform como um IAM OIDC Identity Provider
|
||||
|
||||
Abra o [console IAM → Identity providers](https://console.aws.amazon.com/iam/home#/identity_providers) e clique em **Add provider**.
|
||||
|
||||
- **Provider type:** OpenID Connect.
|
||||
- **Provider URL:** o valor de `issuer` do Passo 1 (ex. `https://app.crewai.com`).
|
||||
- **Audience:** `sts.amazonaws.com`
|
||||
|
||||
Clique em **Add provider**.
|
||||
|
||||
Ou via CLI:
|
||||
|
||||
```bash
|
||||
aws iam create-open-id-connect-provider \
|
||||
--url "https://<your-platform-host>" \
|
||||
--client-id-list "sts.amazonaws.com" \
|
||||
--thumbprint-list "$(echo | openssl s_client -servername <your-platform-host> -connect <your-platform-host>:443 2>/dev/null | openssl x509 -fingerprint -noout -sha1 | cut -d= -f2 | tr -d ':')"
|
||||
```
|
||||
|
||||
Copie o **OpenIDConnectProviderArn** da saída (ou o ARN do provedor no console). Você o usará no Passo 3.
|
||||
|
||||
<Note>
|
||||
A AWS não valida de fato o thumbprint para chamadas STS WebIdentity — ela sempre re-busca o JWKS no momento da validação — mas a API requer que o campo esteja presente.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Add identity provider" form filled with the Platform issuer URL and audience sts.amazonaws.com → /images/secrets-manager/aws-wi/01-add-oidc-provider.png */}
|
||||
{/* SCREENSHOT: Provider detail page showing the provider's ARN → /images/secrets-manager/aws-wi/02-oidc-provider-arn.png */}
|
||||
|
||||
## Passo 3 — Criar o Role IAM
|
||||
|
||||
Salve como `trust-policy.json`, substituindo `<YOUR_ACCOUNT_ID>`, `<your-platform-host>` (o host do issuer **sem** `https://` ou `http://`, ex. `app.crewai.com`) e `<YOUR_CREWAI_ORG_UUID>` (dos Pré-requisitos):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Federated": "arn:aws:iam::<YOUR_ACCOUNT_ID>:oidc-provider/<your-platform-host>"
|
||||
},
|
||||
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"<your-platform-host>:aud": "sts.amazonaws.com",
|
||||
"<your-platform-host>:sub": "organization:<YOUR_CREWAI_ORG_UUID>"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Crie o role:
|
||||
|
||||
```bash
|
||||
aws iam create-role \
|
||||
--role-name crewai-secrets-reader \
|
||||
--assume-role-policy-document file://trust-policy.json
|
||||
```
|
||||
|
||||
Copie o **Role Arn** da saída — esse é seu `aws_role_arn`. Você o colará na CrewAI Platform no Passo 6.
|
||||
|
||||
<Tip>
|
||||
As duas condições escopam a confiança com precisão: `aud` restringe a assunção a tokens com a audience do AWS STS, e `sub` escopa a federação a uma organização CrewAI específica — apenas tokens emitidos para as automações dessa org são aceitos. A CrewAI Platform sempre define ambas as claims nos tokens de workload identity da AWS.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" with Web Identity trust type, federated provider selector pointing at the CrewAI Platform OIDC provider → /images/secrets-manager/aws-wi/03-create-role-trust.png */}
|
||||
|
||||
## Passo 4 — Criar e anexar a política IAM para acesso ao Secrets Manager + KMS
|
||||
|
||||
Salve como `secrets-policy.json`, substituindo os placeholders pelo ID da sua conta, região, prefixo do nome do segredo e o(s) ARN(s) da chave KMS que criptografa(m) esses segredos:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerListForUI",
|
||||
"Effect": "Allow",
|
||||
"Action": "secretsmanager:ListSecrets",
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:GetSecretValue"
|
||||
],
|
||||
"Resource": "arn:aws:secretsmanager:<REGION>:<YOUR_ACCOUNT_ID>:secret:<SECRET_NAME_PREFIX>-*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "arn:aws:kms:<REGION>:<YOUR_ACCOUNT_ID>:key/<KMS_KEY_ID>"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`SecretsManagerListForUI` alimenta o **autocomplete de Secret Name** no formulário de Environment Variables e o botão **Test Connection** na credencial. `secretsmanager:ListSecrets` só aceita `Resource: "*"` — é escopado por conta na camada IAM.
|
||||
|
||||
Anexe a política ao role usando a CLI (política inline, mais simples) ou a UI do console; para ambientes que reutilizam as mesmas permissões em vários roles, use a aba **Managed policy** para uma política nomeada e reutilizável.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Política inline (CLI)">
|
||||
```bash
|
||||
aws iam put-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-name SecretsManagerRead \
|
||||
--policy-document file://secrets-policy.json
|
||||
```
|
||||
|
||||
Isso anexa a política **inline** ao role. Políticas inline são vinculadas ao role e não podem ser reutilizadas em outros roles.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Política gerenciada (CLI, reutilizável)">
|
||||
```bash
|
||||
POLICY_ARN=$(aws iam create-policy \
|
||||
--policy-name CrewAISecretsReader \
|
||||
--policy-document file://secrets-policy.json \
|
||||
--query 'Policy.Arn' --output text)
|
||||
|
||||
aws iam attach-role-policy \
|
||||
--role-name crewai-secrets-reader \
|
||||
--policy-arn "$POLICY_ARN"
|
||||
```
|
||||
|
||||
Uma política gerenciada é um recurso IAM autônomo que você pode anexar a vários roles.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Console (UI)">
|
||||
1. Abra o [console IAM → Roles](https://console.aws.amazon.com/iam/home#/roles) e selecione **crewai-secrets-reader**.
|
||||
2. Na aba **Permissions**, clique em **Add permissions** → **Create inline policy**.
|
||||
3. Mude para o editor **JSON** e cole o conteúdo de `secrets-policy.json`.
|
||||
4. Clique em **Next**, dê um nome à política (ex. `SecretsManagerRead`) e clique em **Create policy**.
|
||||
|
||||
Para criar uma política gerenciada reutilizável, use **IAM → Policies → Create policy** e depois anexe-a ao role pela aba **Permissions** do role.
|
||||
|
||||
{/* SCREENSHOT: IAM Role detail → Permissions → Create inline policy with JSON editor → /images/secrets-manager/aws-wi/03b-attach-inline-policy.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Passo 5 — Criar Pelo Menos Um Segredo na AWS
|
||||
|
||||
Se você ainda não tem um segredo para testar, crie um agora:
|
||||
|
||||
```bash
|
||||
aws secretsmanager create-secret \
|
||||
--region <REGION> \
|
||||
--name crewai-test-keyword \
|
||||
--secret-string "hello from aws"
|
||||
```
|
||||
|
||||
Ou pelo [console do AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/) → **Store a new secret**.
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Store a new secret" page with a sample value → /images/secrets-manager/aws-wi/04-create-secret.png */}
|
||||
|
||||
## Passo 6 — Adicionar uma Configuração de Workload Identity na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Workload Identity** e clique em **Add Workload Identity Config**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Workload Identity → /images/secrets-manager/aws-wi/05-amp-settings-wi-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Workload Identity page with "Add Workload Identity Config" button → /images/secrets-manager/aws-wi/06-amp-wi-empty-state.png */}
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `aws-prod`.
|
||||
- **Cloud Provider:** `AWS`.
|
||||
- **AWS Role ARN:** o **Role Arn** do Passo 3.
|
||||
- **AWS Region:** a região onde seus segredos vivem, ex. `us-east-1`.
|
||||
- (Opcional) Marque **Set as default for AWS** se você quiser que esta config WI seja a padrão selecionada ao criar uma credencial de segredo baseada em AWS.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with AWS, role ARN, and region filled in → /images/secrets-manager/aws-wi/07-amp-add-wi-config-aws.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing the new AWS row with "(default)" badge if applicable → /images/secrets-manager/aws-wi/08-amp-wi-list-with-aws.png */}
|
||||
|
||||
## Passo 7 — Adicionar uma Credencial de Provedor de Segredos Vinculada à Config WI
|
||||
|
||||
Navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `aws-prod-wi`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Authentication Method:** `Workload Identity` (em vez de chaves estáticas / AssumeRole).
|
||||
- **Workload Identity Configuration:** selecione a config que você criou no Passo 6 (ex. `aws-prod`).
|
||||
- (Opcional) Marque **Set as default credential for this provider**.
|
||||
|
||||
O formulário pedirá apenas a **AWS Region** sob Workload Identity — os campos de credencial estática (Access Key ID, Secret Access Key, Role ARN, External ID) estão intencionalmente ocultos porque não se aplicam a este caminho; o ARN do role vem da config WI vinculada.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + Workload Identity + WI config dropdown selected → /images/secrets-manager/aws-wi/09-amp-add-credential-aws-wi.png */}
|
||||
|
||||
## Passo 8 — Testar a Conexão
|
||||
|
||||
Depois de salvar a credencial, clique em **Test Connection**. Para credenciais workload-identity isso verifica o handshake OIDC: a CrewAI Platform emite um JWT, troca-o com o AWS STS via `sts:AssumeRoleWithWebIdentity` e confirma que as credenciais resultantes podem chamar `sts:GetCallerIdentity` contra o role assumido. Um resultado verde significa que o vínculo de federação está saudável.
|
||||
|
||||
Um Test Connection bem-sucedido prova que a trust policy, o registro do provedor OIDC e a condição de audience estão todos conectados corretamente. Ele **não** prova que o IAM por segredo está correto — `secretsmanager:GetSecretValue` em um ARN de segredo específico é exercitado separadamente quando uma variável de ambiente é resolvida no kickoff. Veja [Solução de Problemas](#troubleshooting) para modos de falha de handshake.
|
||||
|
||||
## Passo 9 — Referenciar o Segredo em uma Variável de Ambiente
|
||||
|
||||
Agora referencie o segredo em uma automação, exatamente como você faria para qualquer outra env var apoiada pelo Secrets Manager. Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para os campos do formulário e o comportamento.
|
||||
|
||||
A única diferença entre env vars apoiadas por WI e por chaves estáticas é **quando** o segredo é lido:
|
||||
|
||||
- **Apoiado por WI:** o valor do segredo é lido de forma fresca a cada kickoff de automação.
|
||||
- **Apoiado por chaves estáticas:** o valor do segredo é lido no momento do deploy e incorporado à imagem do deployment.
|
||||
|
||||
## Passo 10 — Verificar a Rotação
|
||||
|
||||
Após o deployment estar rodando, rotacione o segredo na AWS:
|
||||
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id crewai-test-keyword \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
|
||||
Dispare um novo kickoff de automação. O ambiente do kickoff verá `"rotated value"` — sem novo deploy, sem reinício de worker, sem esperar TTL.
|
||||
|
||||
Para confirmar nos logs (se você tiver acesso ao worker), procure por:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (aws): N secret(s) resolved
|
||||
```
|
||||
|
||||
Esta linha aparece para cada kickoff e indica uma chamada `GetSecretValue` fresca contra a AWS.
|
||||
|
||||
## Solução de Problemas
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| Test Connection falha com erro de handshake | A chamada `sts:AssumeRoleWithWebIdentity` foi rejeitada. Verifique se o ARN do principal federado da trust policy referencia `oidc-provider/<your-platform-host>` (host **sem** `https://` ou `http://`, sem barra final), se a condição de audience é exatamente `sts.amazonaws.com`, se a condição `sub` corresponde ao UUID da sua organização CrewAI e se a URL de discovery OIDC da plataforma é acessível a partir da AWS pela internet pública. |
|
||||
| `InvalidIdentityToken: Couldn't retrieve verification key from your identity provider` | O AWS STS não consegue acessar o host da sua CrewAI Platform para buscar o JWKS. Confirme que o host é acessível pela internet a partir da AWS, que a URL de discovery OIDC retorna 200 e que o endpoint JWKS é acessível. |
|
||||
| `AccessDenied: Not authorized to perform sts:AssumeRoleWithWebIdentity` | Trust policy incompatível. Reconfira o Passo 3: o ARN do principal federado deve incluir `oidc-provider/<your-platform-host>` (host **sem** `https://` ou `http://`, sem barra final), a condição de audience deve ser exatamente `sts.amazonaws.com` e a condição `sub` deve ser igual a `organization:<YOUR_CREWAI_ORG_UUID>`. |
|
||||
| Autocomplete de Secret Name mostra `AccessDenied: secretsmanager:ListSecrets` | O role está sem `secretsmanager:ListSecrets` com `Resource: "*"`. Adicione a declaração `SecretsManagerListForUI` do Passo 4. |
|
||||
| Kickoff falha ao resolver um segredo mesmo que o Test Connection passe | O vínculo WI está saudável, mas o IAM escopado por recurso está ausente no segredo que falha. Audite as permissões `secretsmanager:GetSecretValue` e `kms:Decrypt` do role para o ARN específico desse segredo e chave KMS. |
|
||||
| `RegionDisabledException` / nenhum segredo encontrado | A região na Workload Identity Config não corresponde a onde o segredo vive. Reconfira o Passo 6. |
|
||||
| Valor rotacionado não é pego no próximo kickoff | Confirme que a env var na automação está referenciando uma credencial baseada em Workload Identity (não uma credencial de chaves estáticas). O caminho estático incorpora valores à imagem do deploy. |
|
||||
|
||||
### Links de Referência
|
||||
|
||||
- AWS: [Creating OpenID Connect (OIDC) identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html)
|
||||
- AWS: [Configuring a role for OpenID Connect federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc_relying-party.html)
|
||||
- AWS: [STS:AssumeRoleWithWebIdentity API reference](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
- [Use segredos em variáveis de ambiente e gerencie permissões](/pt-BR/enterprise/features/secrets-manager/usage)
|
||||
- Para multi-cloud, veja também [GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity) e [Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
294
docs/pt-BR/enterprise/features/secrets-manager/aws.mdx
Normal file
294
docs/pt-BR/enterprise/features/secrets-manager/aws.mdx
Normal file
@@ -0,0 +1,294 @@
|
||||
---
|
||||
title: AWS Secrets Manager (Credenciais Estáticas)
|
||||
description: Configure o AWS Secrets Manager como provedor de segredos para a CrewAI Platform usando chaves de acesso estáticas ou AssumeRole
|
||||
sidebarTitle: AWS
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia o orienta na configuração do AWS Secrets Manager como provedor de segredos para sua organização na CrewAI Platform, usando **credenciais estáticas** (chaves de acesso, opcionalmente com AssumeRole). Ao final, a CrewAI Platform poderá ler segredos armazenados na sua conta AWS e injetá-los como valores de variáveis de ambiente em runtime.
|
||||
|
||||
<Note>
|
||||
Este guia cobre o caminho de **credenciais estáticas** — segredos são resolvidos no momento do deploy e incorporados à imagem do deployment. Valores rotacionados exigem um novo deploy. Se você quiser segredos conscientes de rotação que se atualizam a cada kickoff de automação (sem novo deploy), veja [AWS Workload Identity (Federação OIDC)](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Este guia cobre a configuração do lado da AWS e a configuração da credencial na CrewAI Platform. Para então referenciar um segredo a partir de uma variável de ambiente, veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- Uma conta AWS com permissão para criar usuários IAM, políticas gerenciadas pelo cliente e (opcionalmente) papéis IAM.
|
||||
- A região AWS onde seus segredos vivem (ou viverão), por exemplo `us-east-1`.
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem a permissão `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Escolha um Método de Autenticação
|
||||
|
||||
A CrewAI Platform suporta duas formas para que a plataforma se autentique no AWS Secrets Manager. Escolha uma antes de começar — os passos abaixo diferem dependendo do que você escolher.
|
||||
|
||||
| Método | Quando usar | Trade-offs |
|
||||
|---|---|---|
|
||||
| **Chaves de acesso estáticas** | Começar rápido, deployments single-account | Configuração mais simples; chaves de acesso devem ser rotacionadas manualmente |
|
||||
| **AssumeRole** | Cross-account, hardening de produção | Credenciais de curta duração; suporta External ID; requer papel IAM extra |
|
||||
|
||||
O restante deste guia usa abas nos Passos 3–5 para que você possa seguir o caminho que corresponde à sua escolha.
|
||||
|
||||
## Passo 1 — Criar um Usuário IAM
|
||||
|
||||
Abra o [console IAM](https://console.aws.amazon.com/iam/), navegue até **Users**, depois clique em **Create user**.
|
||||
|
||||
- Nome sugerido: `crewai-secrets-reader`.
|
||||
- Deixe **Provide user access to the AWS Management Console** desmarcado — este principal é usado programaticamente pela CrewAI Platform, não por humanos.
|
||||
- Clique em **Next**.
|
||||
|
||||
Na página **Set permissions**, deixe a seleção padrão. Você anexará a política no Passo 3.
|
||||
|
||||
Clique em **Next**, revise e clique em **Create user**.
|
||||
|
||||
Para detalhes completos, veja a documentação da AWS: [Create an IAM user in your AWS account](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create user" form filled with name "crewai-secrets-reader" → /images/secrets-manager/aws/01-create-iam-user.png */}
|
||||
|
||||
## Passo 2 — Criar a Política IAM
|
||||
|
||||
A CrewAI Platform precisa de acesso somente leitura ao AWS Secrets Manager e permissão para descriptografar segredos via KMS. Crie uma política gerenciada pelo cliente com o seguinte JSON.
|
||||
|
||||
No console IAM, navegue até **Policies**, depois clique em **Create policy**.
|
||||
|
||||
Escolha a aba **JSON** e substitua o conteúdo por:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "SecretsManagerRead",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"secretsmanager:ListSecrets",
|
||||
"secretsmanager:GetSecretValue",
|
||||
"secretsmanager:DescribeSecret"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Sid": "KMSDecrypt",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kms:DescribeKey",
|
||||
"kms:Decrypt"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Clique em **Next**, então na página **Review and create**:
|
||||
|
||||
- **Policy name:** `CrewAISecretsManagerRead`
|
||||
- **Description (optional):** `Read-only access to AWS Secrets Manager for CrewAI Platform`
|
||||
|
||||
Clique em **Create policy**.
|
||||
|
||||
<Tip>
|
||||
A política acima concede `*` em `Resource` para simplicidade. Em produção, restrinja o `Resource` aos ARNs dos segredos específicos que a CrewAI Platform deve acessar e restrinja `kms:Decrypt` aos ARNs das chaves KMS específicas que criptografam esses segredos. Veja a [orientação da AWS sobre menor privilégio](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create-console.html).
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: AWS IAM "Create policy" → JSON tab with the policy above pasted → /images/secrets-manager/aws/02-create-policy-json-editor.png */}
|
||||
{/* SCREENSHOT: AWS IAM "Review and create policy" page with name "CrewAISecretsManagerRead" → /images/secrets-manager/aws/03-policy-review-and-create.png */}
|
||||
|
||||
## Passo 3 — Anexar a Política
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Chaves de acesso estáticas">
|
||||
1. No console IAM, navegue até **Users** e clique no usuário que você criou no Passo 1.
|
||||
2. Na aba **Permissions**, clique em **Add permissions** → **Attach policies directly**.
|
||||
3. Procure por `CrewAISecretsManagerRead`, selecione-a e clique em **Next**.
|
||||
4. Clique em **Add permissions**.
|
||||
|
||||
{/* SCREENSHOT: "Add permissions" → "Attach policies directly" with CrewAISecretsManagerRead selected → /images/secrets-manager/aws/04a-attach-policy-to-user.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
Com AssumeRole, a política é anexada a um **role** IAM separado (não diretamente ao usuário). O usuário do Passo 1 só precisa de permissão para chamar `sts:AssumeRole` nesse role.
|
||||
|
||||
**Criar o role:**
|
||||
|
||||
1. No console IAM, navegue até **Roles** e clique em **Create role**.
|
||||
2. **Trusted entity type:** AWS account. Escolha **This account** (ou **Another AWS account** para setups cross-account, depois informe o ID da conta AWS que hospeda o usuário IAM do Passo 1).
|
||||
3. (Recomendado) Marque **Require external ID** e digite um valor que você mesmo gera — este é um segredo compartilhado que você colará na CrewAI Platform no Passo 5.
|
||||
4. Clique em **Next**.
|
||||
5. Anexe a política `CrewAISecretsManagerRead`.
|
||||
6. Clique em **Next**, nomeie o role como `CrewAISecretsManagerRole` e clique em **Create role**.
|
||||
|
||||
**Permitir que o usuário IAM assuma o role:**
|
||||
|
||||
1. Abra o role que você acabou de criar e copie seu **ARN**.
|
||||
2. No console IAM, navegue até **Users**, clique no usuário do Passo 1 e na aba **Permissions** clique em **Add permissions** → **Create inline policy**.
|
||||
3. Na aba **JSON**, cole o seguinte (substitua `ROLE_ARN_FROM_ABOVE`):
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "sts:AssumeRole",
|
||||
"Resource": "ROLE_ARN_FROM_ABOVE"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. Nomeie a política como `CrewAIAssumeSecretsRole` e clique em **Create policy**.
|
||||
|
||||
{/* SCREENSHOT: IAM "Create role" trust policy step with External ID checkbox enabled → /images/secrets-manager/aws/04b-create-role-trust-policy.png */}
|
||||
{/* SCREENSHOT: Inline sts:AssumeRole policy attached to the IAM user → /images/secrets-manager/aws/04c-attach-assumerole-on-user.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Passo 4 — Obter Credenciais
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Chaves de acesso estáticas">
|
||||
1. No console IAM, abra o usuário do Passo 1.
|
||||
2. Clique na aba **Security credentials**.
|
||||
3. Em **Access keys**, clique em **Create access key**.
|
||||
4. Selecione **Application running outside AWS** (ou **Other**) como caso de uso. Clique em **Next**.
|
||||
5. (Opcional) Adicione uma tag de descrição. Clique em **Create access key**.
|
||||
6. Clique em **Show** para revelar a secret access key, então copie tanto o **Access key ID** quanto a **Secret access key**, ou clique em **Download .csv file**.
|
||||
|
||||
<Warning>
|
||||
A secret access key é mostrada apenas uma vez. Se você fechar esta página sem copiá-la, precisará excluir a chave e criar uma nova.
|
||||
</Warning>
|
||||
|
||||
Para detalhes completos, veja a documentação da AWS: [Manage access keys for IAM users](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html).
|
||||
|
||||
{/* SCREENSHOT: Access key use-case selector ("Application running outside AWS") → /images/secrets-manager/aws/05a-create-access-key-use-case.png */}
|
||||
{/* SCREENSHOT: "Retrieve access keys" page with Show/Download buttons → /images/secrets-manager/aws/06a-retrieve-access-keys.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
Mesmo com AssumeRole, a CrewAI Platform ainda precisa de uma chave de acesso para o usuário IAM — ela usa essas chaves como identidade chamadora para realizar a chamada `sts:AssumeRole`.
|
||||
|
||||
1. Crie uma access key para o usuário exatamente como descrito na aba **Chaves de acesso estáticas** acima.
|
||||
2. Abra o role que você criou no Passo 3 e copie:
|
||||
- O **Role ARN** (do resumo do role).
|
||||
- O **External ID** que você configurou (se houver) — você definiu isso no Passo 3, então certifique-se de tê-lo à mão.
|
||||
|
||||
{/* SCREENSHOT: IAM role detail page showing Role ARN → /images/secrets-manager/aws/05b-role-arn-detail.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Passo 5 — Adicionar a Credencial na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
{/* SCREENSHOT: Empty state of Secret Provider Credentials page with "Add Credential" button → /images/secrets-manager/usage/02-amp-credentials-empty-state.png */}
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Chaves de acesso estáticas">
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `aws-prod`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** A região AWS onde seus segredos vivem, ex. `us-east-1`. Deve corresponder à região dos segredos que você quer ler.
|
||||
- **Access Key ID:** O valor do Passo 4.
|
||||
- **Secret Access Key:** O valor do Passo 4.
|
||||
- (Opcional) Marque **Set as default credential for this provider**. A credencial padrão é usada por variáveis de ambiente que referenciam segredos AWS sem especificar uma credencial explicitamente.
|
||||
|
||||
Deixe **Role ARN** e **External ID** em branco.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + static access keys filled in → /images/secrets-manager/usage/03a-amp-add-credential-form-aws-static.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="AssumeRole">
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `aws-prod-assumerole`.
|
||||
- **Provider:** `AWS Secrets Manager`.
|
||||
- **Region:** A região AWS onde seus segredos vivem.
|
||||
- **Access Key ID:** A access key do usuário IAM do Passo 4 (usada para chamar o STS).
|
||||
- **Secret Access Key:** A secret access key do usuário IAM do Passo 4.
|
||||
- **Role ARN:** O Role ARN que você copiou no Passo 4.
|
||||
- **External ID:** O External ID que você definiu na trust policy do role (omita se não houver).
|
||||
- (Opcional) Marque **Set as default credential for this provider**.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with AWS + AssumeRole fields filled in → /images/secrets-manager/usage/03b-amp-add-credential-form-aws-assumerole.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Note>
|
||||
**Como os dois modos se comportam em runtime:**
|
||||
|
||||
- Com apenas **chaves de acesso estáticas**, a CrewAI Platform chama o AWS Secrets Manager diretamente usando as chaves que você forneceu.
|
||||
- Quando um **Role ARN** está definido, a CrewAI Platform primeiro chama `sts:AssumeRole` com as chaves de acesso fornecidas (e External ID se configurado), depois usa as credenciais de curta duração retornadas pelo STS para ler seus segredos.
|
||||
</Note>
|
||||
|
||||
{/* SCREENSHOT: Credentials list showing the new AWS row, with "(default)" badge if applicable → /images/secrets-manager/usage/04-amp-credential-created.png */}
|
||||
|
||||
## Passo 6 — Criar Pelo Menos Um Segredo na AWS
|
||||
|
||||
Se você ainda não tem segredos no AWS Secrets Manager, crie um agora para que possa verificar a conexão no Passo 7.
|
||||
|
||||
No [console do AWS Secrets Manager](https://console.aws.amazon.com/secretsmanager/), clique em **Store a new secret**.
|
||||
|
||||
- **Secret type:** Escolha **Other type of secret**.
|
||||
- **Key/value pairs** — ou:
|
||||
- Informe um ou mais pares chave/valor (recomendado para segredos estruturados), ou
|
||||
- Use a aba **Plaintext** para um único valor de string.
|
||||
- **Encryption key:** Use `aws/secretsmanager` (a chave gerenciada pela AWS) a menos que você tenha um requisito específico de chave KMS.
|
||||
|
||||
Clique em **Next**, então informe:
|
||||
|
||||
- **Secret name:** Um nome único, ex. `crewai/openai-api-key`.
|
||||
- **Description (optional):** Uma nota curta sobre o propósito do segredo.
|
||||
|
||||
Clique em **Next** pelos passos de rotação e revisão, depois clique em **Store**.
|
||||
|
||||
<Note>
|
||||
**Sintaxe de referência por chave JSON.** Se você armazenar um segredo com múltiplos pares chave/valor (um objeto JSON), a CrewAI Platform pode extrair um campo específico usando a sintaxe `secret-name#json_key` em referências de variáveis de ambiente. Por exemplo, um segredo chamado `database-credentials` com `{"username": "...", "password": "..."}` pode ser referenciado como `database-credentials#password`. Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para detalhes.
|
||||
</Note>
|
||||
|
||||
Para detalhes completos, veja a documentação da AWS: [Create an AWS Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/create_secret.html).
|
||||
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Choose secret type" page → /images/secrets-manager/aws/07-create-secret-store-type.png */}
|
||||
{/* SCREENSHOT: AWS Secrets Manager "Configure secret" page with name and description → /images/secrets-manager/aws/08-create-secret-name.png */}
|
||||
|
||||
## Passo 7 — Testar a Conexão
|
||||
|
||||
De volta à CrewAI Platform, na página **Secret Provider Credentials**, encontre a credencial que você acabou de criar e clique em **Test Connection**.
|
||||
|
||||
Um toast de sucesso confirma que a CrewAI Platform consegue se autenticar na AWS e ler segredos da sua conta.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" → /images/secrets-manager/usage/05-amp-test-connection-success.png */}
|
||||
|
||||
Se o teste falhar, verifique as causas mais comuns:
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| `AccessDenied` em `secretsmanager:ListSecrets` | Política não anexada, ou usuário errado. Reconfira o Passo 3. |
|
||||
| `AccessDenied` em `kms:Decrypt` | Falta a declaração `KMSDecrypt`, ou seus segredos usam uma chave KMS gerenciada pelo cliente não coberta por `Resource: "*"`. |
|
||||
| `InvalidClientTokenId` / `SignatureDoesNotMatch` | Access key ID ou secret access key errados. Reconfira os Passos 4 e 5. |
|
||||
| `RegionDisabledException` / nenhum segredo encontrado | A **Region** da credencial não corresponde a onde seus segredos realmente vivem. |
|
||||
| `AccessDenied` em `sts:AssumeRole` (apenas AssumeRole) | Política inline `sts:AssumeRole` ausente no usuário IAM, ou a trust policy do role não permite este principal, ou o External ID não corresponde. |
|
||||
| Teste passa imediatamente após criar o usuário IAM, mas falha da próxima vez | Credenciais IAM às vezes levam um ou dois minutos para se propagar globalmente. Tente novamente. |
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
Agora que a AWS está conectada, vá para [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage) para:
|
||||
|
||||
- Conceder aos membros da organização as permissões corretas para usar (ou gerenciar) o Secrets Manager.
|
||||
- Referenciar seus segredos AWS a partir de variáveis de ambiente da CrewAI Platform.
|
||||
|
||||
Se você quiser segredos **conscientes de rotação** que se propagam sem novo deploy, mude para [AWS Workload Identity (Federação OIDC)](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) — mesmo cofre de segredos, sem credenciais estáticas, segredos buscados por kickoff.
|
||||
@@ -0,0 +1,274 @@
|
||||
---
|
||||
title: Azure Workload Identity Federation
|
||||
description: Configure o Azure Key Vault via Microsoft Entra Workload Identity Federation para acesso a segredos consciente de rotação e sem credenciais
|
||||
sidebarTitle: Azure — Workload Identity
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia configura o Azure Key Vault como provedor de segredos usando **Microsoft Entra Workload Identity Federation**: a CrewAI Platform emite tokens OIDC de curta duração, os troca por um token de acesso do Entra via Microsoft identity platform e lê seus segredos — sem nenhum client secret armazenado em lugar algum.
|
||||
|
||||
<Note>
|
||||
**Por que este caminho:** os segredos são resolvidos no momento de execução da automação, então **valores rotacionados se propagam para o próximo kickoff sem novo deploy**. Se você só precisa de credenciais estáticas, veja o guia mais simples [Azure Key Vault — client secret](/pt-BR/enterprise/features/secrets-manager/azure).
|
||||
</Note>
|
||||
|
||||
### Como funciona em runtime
|
||||
|
||||
1. O worker do deployment solicita um JWT OIDC fresco à CrewAI Platform.
|
||||
2. O worker apresenta o JWT ao Microsoft Entra em `https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token` como um `client_assertion` (`urn:ietf:params:oauth:client-assertion-type:jwt-bearer`), referenciando o App Registration cujo **Federated Identity Credential** corresponde ao issuer + subject do JWT.
|
||||
3. O Entra valida o JWT contra o documento de discovery OIDC e JWKS da sua plataforma, então retorna um token de acesso de curta duração com escopo `https://vault.azure.net/.default`.
|
||||
4. O worker chama o Azure Key Vault para ler o segredo.
|
||||
5. O valor obtido é injetado como valor da variável de ambiente para aquele kickoff de automação.
|
||||
|
||||
Tokens OIDC subject são cacheados por ~1 hora para evitar reemissão a cada kickoff. Valores de segredos são buscados frescos a cada kickoff independentemente do estado do cache OIDC, e é isso que torna este caminho consciente de rotação.
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- A imagem do pod de automação deve incluir o runtime da CrewAI versão `1.14.5` ou superior.
|
||||
- Uma subscription Azure e um tenant Microsoft Entra que você possa gerenciar.
|
||||
- Permissão no tenant para criar App Registrations e adicionar Federated Identity Credentials.
|
||||
- Um Key Vault usando **Azure RBAC** para autorização (não o modelo legado de access-policy).
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem as permissões `workload_identity_configs: manage` e `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **Sua instalação da CrewAI Platform deve ser acessível a partir do Microsoft Entra via HTTPS** para que o Entra possa buscar o documento de discovery OIDC e o JWKS durante a validação do token. Confirme com o administrador da sua plataforma que o host é acessível pela internet.
|
||||
</Note>
|
||||
|
||||
## Passo 1 — Encontre a URL do Issuer OIDC da Sua CrewAI Platform
|
||||
|
||||
Sua instalação da CrewAI Platform publica um documento de discovery OpenID Connect em `https://<your-platform-host>/.well-known/openid-configuration`. O campo `issuer` ali é a URL que o Microsoft Entra registrará como issuer de federação confiável.
|
||||
|
||||
Abra a URL em um navegador:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
Você deverá ver um JSON contendo:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Anote o valor exato de `issuer` — você o usará no Passo 3.
|
||||
|
||||
<Tip>
|
||||
Se a URL retornar 404 ou 503, contate o administrador da sua plataforma. O issuer OIDC requer uma chave de assinatura privada configurada no momento da instalação. Veja o guia de instalação da plataforma para as configurações `OIDC_PRIVATE_KEY` e `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## Passo 2 — Criar um App Registration
|
||||
|
||||
No [portal Microsoft Entra](https://entra.microsoft.com), navegue até **App registrations** e clique em **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- Deixe **Redirect URI** em branco.
|
||||
|
||||
Clique em **Register**. Anote o **Application (client) ID** e o **Directory (tenant) ID** no blade de visão geral do App — você os usará no Passo 6.
|
||||
|
||||
{/* SCREENSHOT: Azure portal "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure-wi/01-register-app.png */}
|
||||
|
||||
## Passo 3 — Adicionar um Federated Identity Credential
|
||||
|
||||
O Federated Identity Credential diz ao Microsoft Entra: *confie em JWTs emitidos por este issuer, com este subject, quando forem apresentados como client assertion para este App Registration.*
|
||||
|
||||
No App Registration, navegue até **Certificates & secrets** → **Federated credentials** → **Add credential**.
|
||||
|
||||
- **Federated credential scenario:** `Other issuer`.
|
||||
- **Issuer:** a URL do issuer da CrewAI Platform do Passo 1, ex. `https://<your-platform-host>`.
|
||||
- **Subject identifier:** `organization:<YOUR_CREWAI_ORG_UUID>` — exatamente o valor da claim `sub` do JWT. Encontre o UUID da sua org nas configurações de organização da CrewAI Platform. Isso escopa a federação a uma organização CrewAI específica — apenas tokens emitidos para as automações dessa org são aceitos.
|
||||
- **Name:** qualquer label descritivo, ex. `crewai-org-prod`.
|
||||
- **Audience:** `api://AzureADTokenExchange`. Esta é a audience fixa que o Microsoft Entra requer para federated credentials e é o que a CrewAI Platform define na claim `aud` do JWT.
|
||||
|
||||
Clique em **Add**.
|
||||
|
||||
<Tip>
|
||||
**Isolamento por organização.** O subject identifier (`organization:<UUID>`) restringe o federated credential aos tokens de uma organização CrewAI específica. Se múltiplas organizações CrewAI devem compartilhar um App Registration, adicione um Federated Identity Credential por organização (cada um com o UUID da org).
|
||||
</Tip>
|
||||
|
||||
Para detalhes completos, veja a documentação da Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust).
|
||||
|
||||
{/* SCREENSHOT: "Add credential" panel with scenario = "Other issuer", issuer URL, subject "organization:<uuid>", audience "api://AzureADTokenExchange" → /images/secrets-manager/azure-wi/02-add-federated-credential.png */}
|
||||
|
||||
## Passo 4 — Conceder ao App Registration Acesso ao Key Vault
|
||||
|
||||
Conceda ao App Registration **Key Vault Secrets User** no vault de destino — o mesmo papel que você usaria para o caminho de credenciais estáticas. Use a nível de vault (mais simples) ou por segredo (menor privilégio).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="A nível de vault (mais simples)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
O escopo a nível de vault concede a permissão `secrets/list` da qual o **autocomplete de Secret Name** no formulário de env-var da CrewAI Platform depende. Escolha esta aba se você quer que o autocomplete funcione.
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure-wi/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Por segredo (menor privilégio)">
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
Vínculos por segredo desabilitam o **autocomplete de Secret Name** no formulário de env-var da CrewAI Platform (o autocomplete requer `secrets/list`, que é escopado apenas por vault). Digite o nome completo do segredo.
|
||||
|
||||
{/* SCREENSHOT: Per-secret IAM panel with the App Registration assigned **Key Vault Secrets User** at the secret resource scope → /images/secrets-manager/azure-wi/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Portal (UI)">
|
||||
Para uma atribuição **a nível de vault**:
|
||||
|
||||
1. Abra seu Key Vault no portal Azure.
|
||||
2. Clique em **Access control (IAM)** → **Add** → **Add role assignment**.
|
||||
3. Selecione o papel **Key Vault Secrets User** → **Next**.
|
||||
4. Clique em **Select members**, procure pelo App Registration `crewai-secrets-reader`, clique em **Select**.
|
||||
5. Clique em **Review + assign**.
|
||||
|
||||
Para uma atribuição **por segredo**, use o mesmo fluxo, mas comece em **Objects** → **Secrets** → selecione o segredo → seu próprio painel **Access control (IAM)**. Vínculos por segredo desabilitam o autocomplete (veja a aba Por segredo acima).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Passo 5 — Criar Pelo Menos Um Segredo no Key Vault
|
||||
|
||||
Se você ainda não tem um segredo para testar, crie um via Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
Ou via portal Azure:
|
||||
|
||||
1. Abra seu Key Vault e navegue até **Objects** → **Secrets**.
|
||||
2. Clique em **Generate/Import**.
|
||||
3. **Upload options:** `Manual`. **Name:** o nome do segredo (ex. `openai-api-key`). **Secret value:** cole o valor.
|
||||
4. Clique em **Create**.
|
||||
|
||||
<Note>
|
||||
**Convenções de nome de segredo.** Nomes de segredos do Azure Key Vault não podem conter underscores. A CrewAI Platform converte automaticamente underscores em hífens ao chamar o Azure (ex.: `db_password` é enviado como `db-password`), então você pode manter nomes de env-var no estilo underscore — mas o segredo subjacente no Key Vault deve usar hífens.
|
||||
</Note>
|
||||
|
||||
## Passo 6 — Adicionar uma Configuração de Workload Identity na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Workload Identity** e clique em **Add Workload Identity Config**.
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `azure-prod`.
|
||||
- **Cloud Provider:** `Azure`.
|
||||
- **Tenant ID:** seu **Directory (tenant) ID** do Microsoft Entra do Passo 2.
|
||||
- **Client ID:** o **Application (client) ID** do seu App Registration do Passo 2.
|
||||
- (Opcional) Marque **Set as default for Azure** se você quiser que esta seja a config WI padrão selecionada ao criar uma credencial de segredo baseada em Azure.
|
||||
|
||||
A **Audience** é fixa em `api://AzureADTokenExchange` — o Microsoft Entra requer exatamente essa audience para federated credentials, então nenhum campo Audience é mostrado no formulário.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with Azure, tenant ID, client ID populated → /images/secrets-manager/azure-wi/05-amp-add-wi-config-azure.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing AWS, GCP, and Azure rows → /images/secrets-manager/azure-wi/06-amp-wi-list-with-azure.png */}
|
||||
|
||||
## Passo 7 — Adicionar uma Credencial de Provedor de Segredos Vinculada à Config WI
|
||||
|
||||
Navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `azure-prod-wi`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** selecione a config que você criou no Passo 6.
|
||||
- **Key Vault URL:** o hostname DNS do vault, ex. `https://my-vault.vault.azure.net`.
|
||||
- (Opcional) Marque **Set as default credential for this provider**.
|
||||
|
||||
O formulário pedirá apenas a **Key Vault URL** sob Workload Identity — os campos de credencial estática (Tenant ID, Client ID, Client Secret) estão intencionalmente ocultos porque não se aplicam a este caminho; tenant + client vêm da config WI vinculada.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
<Tip>
|
||||
**Um App Registration, muitos vaults.** A Key Vault URL fica na credencial, não na config WI. Então um App Registration (e uma config WI) pode servir múltiplos Key Vaults — basta criar uma Secret Provider Credential por vault, todas vinculadas à mesma config WI.
|
||||
</Tip>
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure + Workload Identity + WI config dropdown + vault URL → /images/secrets-manager/azure-wi/07-amp-add-credential-azure-wi.png */}
|
||||
|
||||
## Passo 8 — Testar a Conexão
|
||||
|
||||
Depois de salvar a credencial, clique em **Test Connection**. Para credenciais workload-identity isso verifica o handshake OIDC: a CrewAI Platform emite um JWT, apresenta-o ao Microsoft Entra como um `client_assertion` federado, e confirma que o Entra retorna um token de acesso escopado para o vault. Um resultado verde significa que o vínculo de federação está saudável.
|
||||
|
||||
Um Test Connection bem-sucedido prova que o issuer, subject e audience do Federated Identity Credential correspondem todos, e que o App Registration é acessível. Ele **não** prova que o RBAC por segredo do Key Vault está correto — `getSecret` contra um segredo específico é exercitado separadamente quando uma variável de ambiente é resolvida no kickoff. Veja [Solução de Problemas](#troubleshooting) para modos de falha de handshake.
|
||||
|
||||
## Passo 9 — Referenciar o Segredo em uma Variável de Ambiente
|
||||
|
||||
Referencie o segredo em uma automação, exatamente como você faria para qualquer outra env var apoiada pelo Secrets Manager. Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para os campos do formulário e o comportamento.
|
||||
|
||||
## Passo 10 — Verificar a Rotação
|
||||
|
||||
Após o deployment estar rodando, rotacione o segredo no Key Vault:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "rotated value"
|
||||
```
|
||||
|
||||
Dispare um novo kickoff de automação. O ambiente do kickoff verá `"rotated value"` — sem novo deploy, sem reinício de worker, sem espera de TTL.
|
||||
|
||||
Para confirmar nos logs do worker, procure por:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (azure): N secret(s) resolved
|
||||
```
|
||||
|
||||
Esta linha aparece para cada kickoff e indica uma chamada `getSecret` fresca contra o Azure Key Vault.
|
||||
|
||||
Para uma verificação de ponta a ponta baseada em fingerprint, veja [Verificar Rotação de Ponta a Ponta](/pt-BR/enterprise/features/secrets-manager/verify-rotation).
|
||||
|
||||
## Solução de Problemas
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| Test Connection falha com erro de handshake | O `client_assertion` federado foi rejeitado pelo Microsoft Entra. Verifique se o **Issuer** do Federated Identity Credential corresponde exatamente ao valor `issuer` da plataforma, se **Subject** é `organization:<your-org-uuid>` (correspondendo à claim `sub` do JWT), se **Audience** é `api://AzureADTokenExchange` e se a URL de discovery OIDC da plataforma é acessível a partir do Entra pela internet pública. |
|
||||
| `AADSTS70021: No matching federated identity record found for presented assertion` | O **Issuer** + **Subject** + **Audience** do Federated Identity Credential não correspondem todos exatamente ao JWT. Reconfira o Passo 3: subject deve ser `organization:<your-org-uuid>` (correspondendo à claim `sub` do JWT), audience deve ser `api://AzureADTokenExchange`. |
|
||||
| `AADSTS700024: Client assertion is not within its valid time range` | O relógio do host da CrewAI Platform está significativamente fora do tempo real. Verifique o NTP no host. |
|
||||
| `AADSTS50013: Assertion failed signature validation` | O Microsoft Entra não conseguiu verificar a assinatura do JWT. Confirme que `https://<your-platform-host>/oauth2/jwks` é acessível pela internet pública e serve um JWKS válido. |
|
||||
| Autocomplete de Secret Name mostra `Forbidden — does not have permission to perform action 'Microsoft.KeyVault/vaults/secrets/.../list'` | O papel **Key Vault Secrets User** do App Registration está escopado a um único segredo. Conceda o papel no escopo do vault para que a ação de plano de dados `list` seja permitida. Veja o Passo 4. |
|
||||
| Kickoff falha ao resolver um segredo mesmo que o Test Connection passe | O vínculo WI está saudável, mas o RBAC por segredo do Key Vault está ausente no segredo que falha. Audite **Key Vault Secrets User** naquele segredo específico (ou estenda a atribuição de papel ao escopo do vault). |
|
||||
| `Forbidden — request was not authorized` (vault usando access policies legadas) | O vault não foi alternado para Azure RBAC. Sob **Access configuration** do vault, defina o modelo de permissão para **Azure role-based access control** e reconceda o papel do Passo 4. |
|
||||
| `azure_vault_url is required for Azure secret resolution` (logs do worker) | A Secret Provider Credential está sem a **Key Vault URL**. Reconfira o Passo 7. |
|
||||
| Valor rotacionado não é pego no próximo kickoff | Confirme que a env var na automação está referenciando uma credencial baseada em Workload Identity (não uma credencial de chaves estáticas). O caminho estático incorpora valores à imagem do deploy. |
|
||||
|
||||
### Links de Referência
|
||||
|
||||
- Microsoft: [Microsoft Entra Workload Identity Federation overview](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation)
|
||||
- Microsoft: [Configure a federated identity credential on an app](https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust)
|
||||
- Microsoft: [Azure Key Vault RBAC guide](https://learn.microsoft.com/en-us/azure/key-vault/general/rbac-guide)
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
- [Use segredos em variáveis de ambiente e gerencie permissões](/pt-BR/enterprise/features/secrets-manager/usage)
|
||||
- Para multi-cloud, a configuração equivalente para AWS está em [AWS Workload Identity (Federação OIDC)](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) e a equivalente para GCP em [GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
|
||||
## Referência de Screenshots
|
||||
|
||||
Os placeholders acima mapeiam para:
|
||||
|
||||
- `01-register-app.png` — formulário "Register an application" do portal Azure preenchido com `crewai-secrets-reader`.
|
||||
- `02-add-federated-credential.png` — App Registration → Certificates & secrets → Federated credentials → Add credential, com **Other issuer**, a URL do issuer da plataforma, subject `organization:<uuid>`, audience `api://AzureADTokenExchange`.
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, com **Key Vault Secrets User** e o App Registration selecionado.
|
||||
- `04-per-secret-rbac.png` — mesmo formulário, mas no escopo IAM de um único segredo (caminho alternativo de menor privilégio).
|
||||
- `05-amp-add-wi-config-azure.png` — formulário "Add Workload Identity Config" da CrewAI Platform com Cloud Provider = Azure, Tenant ID, Client ID preenchidos.
|
||||
- `06-amp-wi-list-with-azure.png` — página de listagem do Workload Identity após criação, mostrando linhas para AWS, GCP e a nova config Azure.
|
||||
- `07-amp-add-credential-azure-wi.png` — formulário "Add Secret Provider Credential" com Provider = Azure Key Vault, Auth = Workload Identity, a config WI escolhida e Key Vault URL preenchida.
|
||||
195
docs/pt-BR/enterprise/features/secrets-manager/azure.mdx
Normal file
195
docs/pt-BR/enterprise/features/secrets-manager/azure.mdx
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
title: Azure Key Vault
|
||||
description: Configure o Azure Key Vault como provedor de segredos para a CrewAI Platform, de ponta a ponta
|
||||
sidebarTitle: Azure
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia o orienta na configuração do Azure Key Vault como provedor de segredos para sua organização na CrewAI Platform, usando um **Microsoft Entra App Registration com client secret**. Ao final, a CrewAI Platform poderá ler segredos armazenados no seu Azure Key Vault e injetá-los como valores de variáveis de ambiente em runtime.
|
||||
|
||||
<Note>
|
||||
Este guia cobre o caminho de **credenciais estáticas** — segredos são resolvidos no momento do deploy e incorporados à imagem do deployment. Valores rotacionados exigem um novo deploy. Se você quiser segredos conscientes de rotação que se atualizam a cada kickoff de automação, veja [Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Este guia cobre a configuração do lado Azure e a configuração da credencial na CrewAI Platform. Para então referenciar um segredo a partir de uma variável de ambiente, veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- Uma subscription Azure com permissão para criar App Registrations no Microsoft Entra e para conceder atribuições de papéis em recursos do Key Vault.
|
||||
- Um Key Vault usando **Azure RBAC** para autorização (não o modelo legado de access-policy). Se o seu vault ainda usa access policies, mude-o para RBAC sob o blade **Access configuration** do vault.
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem a permissão `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Passo 1 — Criar um App Registration
|
||||
|
||||
O App Registration é a identidade do lado Microsoft Entra como a qual a CrewAI Platform irá se autenticar.
|
||||
|
||||
No [portal Microsoft Entra](https://entra.microsoft.com), navegue até **App registrations** e clique em **New registration**.
|
||||
|
||||
- **Name:** `crewai-secrets-reader`
|
||||
- **Supported account types:** `Accounts in this organizational directory only (Single tenant)`.
|
||||
- Deixe **Redirect URI** em branco.
|
||||
|
||||
Clique em **Register**. Anote o **Application (client) ID** e o **Directory (tenant) ID** no blade de visão geral do App — você colará ambos na CrewAI Platform no Passo 4.
|
||||
|
||||
Para detalhes completos, veja a documentação da Microsoft: [Register an application with the Microsoft identity platform](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app).
|
||||
|
||||
{/* SCREENSHOT: Azure "Register an application" form with name "crewai-secrets-reader" → /images/secrets-manager/azure/01-register-app.png */}
|
||||
|
||||
## Passo 2 — Criar um Client Secret
|
||||
|
||||
No App Registration, navegue até **Certificates & secrets** → **Client secrets** → **New client secret**.
|
||||
|
||||
- **Description:** `crewai-platform`
|
||||
- **Expires:** escolha uma duração que corresponda à sua política de rotação (a Microsoft limita isso em 24 meses).
|
||||
|
||||
Clique em **Add**. Copie a coluna **Value** imediatamente — ela nunca pode ser re-exibida depois que você sair da página.
|
||||
|
||||
<Warning>
|
||||
Client secrets são credenciais estáticas de longa duração. Armazene o valor com segurança (em um gerenciador de senhas ou no seu próprio cofre de segredos) e rotacione-o antes da expiração. Para eliminar credenciais estáticas completamente, use [Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: "Client secrets" tab with the new secret row and the "Value" column highlighted → /images/secrets-manager/azure/02-create-client-secret.png */}
|
||||
|
||||
## Passo 3 — Conceder ao App Registration Acesso ao Key Vault
|
||||
|
||||
A CrewAI Platform precisa de acesso de leitura aos segredos no seu Key Vault. Use um de dois escopos — **a nível de vault** para simplicidade, ou **por segredo** para menor privilégio.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="A nível de vault (mais simples)">
|
||||
No [console do Key Vault](https://portal.azure.com/#view/HubsExtension/BrowseResource/resourceType/Microsoft.KeyVault%2Fvaults), abra o vault de destino, depois navegue até **Access control (IAM)** → **Add** → **Add role assignment**.
|
||||
|
||||
- **Role:** **Key Vault Secrets User**
|
||||
- **Assign access to:** User, group, or service principal
|
||||
- **Members:** procure e selecione seu App Registration (`crewai-secrets-reader`).
|
||||
|
||||
Clique em **Review + assign**.
|
||||
|
||||
Ou via Azure CLI:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault show --name <VAULT_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Key Vault "Add role assignment" panel with "Key Vault Secrets User" and the App Registration selected → /images/secrets-manager/azure/03-grant-vault-rbac.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Por segredo (menor privilégio)">
|
||||
Conceda o papel a nível de um segredo individual. Repita para cada segredo que a CrewAI Platform deve acessar:
|
||||
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee <APPLICATION_CLIENT_ID> \
|
||||
--role "Key Vault Secrets User" \
|
||||
--scope $(az keyvault secret show --vault-name <VAULT_NAME> --name <SECRET_NAME> --query id -o tsv)
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Access control (IAM)" panel showing role assignment scoped to one secret → /images/secrets-manager/azure/04-per-secret-rbac.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
O papel **Key Vault Secrets User** permite ler valores de segredos, mas não listar todos os segredos no vault. O autocomplete de nome de segredo da CrewAI Platform também chama `list` — essa permissão está incluída no papel no escopo de vault, mas **não** no escopo por segredo. Com vínculos por segredo, o autocomplete não sugerirá segredos; digite o nome completo do segredo.
|
||||
</Tip>
|
||||
|
||||
## Passo 4 — Adicionar a Credencial na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `azure-prod`.
|
||||
- **Provider:** `Azure Key Vault`.
|
||||
- **Key Vault URL:** o hostname DNS do vault, ex. `https://my-vault.vault.azure.net`.
|
||||
- **Tenant ID:** seu **Directory (tenant) ID** do Microsoft Entra do Passo 1.
|
||||
- **Client ID:** o **Application (client) ID** do seu App Registration do Passo 1.
|
||||
- **Client Secret:** o **Value** que você copiou no Passo 2.
|
||||
- (Opcional) Marque **Set as default credential for this provider**. A credencial padrão é usada por variáveis de ambiente que referenciam segredos Azure sem especificar uma credencial explicitamente.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with Azure fields filled in → /images/secrets-manager/azure/05-amp-add-credential-form-azure.png */}
|
||||
|
||||
## Passo 5 — Criar Pelo Menos Um Segredo no Azure Key Vault
|
||||
|
||||
Se você ainda não tem segredos no Key Vault, crie um agora para que possa verificar a conexão no Passo 6.
|
||||
|
||||
No console do Key Vault, navegue até **Objects** → **Secrets** → **Generate/Import**.
|
||||
|
||||
- **Upload options:** `Manual`
|
||||
- **Name:** ex. `openai-api-key`
|
||||
- **Secret value:** cole o valor do seu segredo
|
||||
- Deixe o resto nos padrões.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
Ou via Azure CLI:
|
||||
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name openai-api-key \
|
||||
--value "sk-your-actual-key"
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Convenções de nome de segredo.** Nomes de segredos do Azure Key Vault não podem conter underscores. A CrewAI Platform converte automaticamente underscores em hífens ao chamar o Azure (ex.: `db_password` é enviado como `db-password`), então você pode manter nomes de env-var no estilo underscore — mas o segredo subjacente no Key Vault deve usar hífens.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
**Sintaxe de referência por chave JSON.** O Key Vault trata valores de segredos como strings opacas. Se o valor do seu segredo for um objeto JSON, a CrewAI Platform pode extrair um único campo usando a sintaxe `secret-name#json_key` (ex. `database-credentials#password`). Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para detalhes.
|
||||
</Note>
|
||||
|
||||
Para detalhes completos, veja a documentação da Microsoft: [Set and retrieve a secret](https://learn.microsoft.com/en-us/azure/key-vault/secrets/quick-create-cli).
|
||||
|
||||
{/* SCREENSHOT: Azure Key Vault "Create a secret" form with name and value → /images/secrets-manager/azure/06-create-secret.png */}
|
||||
|
||||
## Passo 6 — Testar a Conexão
|
||||
|
||||
De volta à CrewAI Platform, na página **Secret Provider Credentials**, encontre a credencial que você acabou de criar e clique em **Test Connection**.
|
||||
|
||||
Um toast de sucesso confirma que a CrewAI Platform consegue se autenticar no Microsoft Entra e ler segredos do seu vault.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the Azure credential → /images/secrets-manager/azure/07-test-connection-success.png */}
|
||||
|
||||
Se o teste falhar, verifique as causas mais comuns:
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| `AADSTS7000215: Invalid client secret provided` | O **Client Secret** colado está errado ou expirado. Recrie o segredo (Passo 2) e atualize a credencial. |
|
||||
| `AADSTS700016: Application not found in the directory` | O **Tenant ID** ou **Client ID** não corresponde ao App Registration. Reconfira o Passo 4. |
|
||||
| `Forbidden — caller does not have permission` | O App Registration está sem o papel **Key Vault Secrets User** no vault (ou por segredo). Reconfira o Passo 3. |
|
||||
| `Vault not found` / erros de DNS | A **Key Vault URL** está errada, ou seu vault tem endpoints privados que bloqueiam acesso público. Confirme que o host responde a `curl https://<vault-name>.vault.azure.net/secrets?api-version=7.4`. |
|
||||
| `Forbidden — request was not authorized` (vault usando access policies legadas) | O vault não foi alternado para Azure RBAC. Sob **Access configuration** do vault, defina o modelo de permissão para **Azure role-based access control** e reconceda o papel do Passo 3. |
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
Agora que o Azure Key Vault está conectado, vá para [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage) para:
|
||||
|
||||
- Conceder aos membros da organização as permissões corretas para usar (ou gerenciar) o Secrets Manager.
|
||||
- Referenciar seus segredos Azure a partir de variáveis de ambiente da CrewAI Platform.
|
||||
|
||||
Se você quiser segredos **conscientes de rotação** que se propagam sem novo deploy, mude para [Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity) — mesmo vault, sem client secret para rotacionar, segredos buscados por kickoff.
|
||||
|
||||
## Referência de Screenshots
|
||||
|
||||
Os placeholders acima mapeiam para:
|
||||
|
||||
- `01-register-app.png` — formulário "Register an application" do portal Azure preenchido com `crewai-secrets-reader`.
|
||||
- `02-create-client-secret.png` — App Registration → Certificates & secrets → Client secrets, com a linha do segredo recém-criado visível (coluna Value destacada antes de ser mascarada).
|
||||
- `03-grant-vault-rbac.png` — Key Vault → Access control (IAM) → Add role assignment, com **Key Vault Secrets User** escolhido e o App Registration selecionado como membro.
|
||||
- `04-per-secret-rbac.png` — mesmo painel, mas escopado para um único recurso de segredo (caminho alternativo de menor privilégio).
|
||||
- `05-amp-add-credential-form-azure.png` — formulário "Add Secret Provider Credential" da CrewAI Platform: Provider = Azure Key Vault, todos os cinco campos preenchidos.
|
||||
- `06-create-secret.png` — painel "Create a secret" do Azure Key Vault com `openai-api-key` e um valor colado.
|
||||
- `07-test-connection-success.png` — toast de sucesso / estado da linha na CrewAI Platform após clicar em **Test Connection** na credencial.
|
||||
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: GCP Workload Identity Federation
|
||||
description: Configure o Google Cloud Secret Manager via Workload Identity Federation para acesso a segredos consciente de rotação e sem credenciais
|
||||
sidebarTitle: GCP — Workload Identity
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia configura o Google Cloud Secret Manager como provedor de segredos usando **Workload Identity Federation**: a CrewAI Platform emite tokens OIDC de curta duração, os troca por credenciais Google Cloud via Security Token Service e lê seus segredos — sem que uma chave de service account de longa duração seja armazenada em lugar algum.
|
||||
|
||||
<Note>
|
||||
**Por que este caminho:** os segredos são resolvidos no momento de execução da automação, então **valores rotacionados se propagam para o próximo kickoff sem novo deploy**. Se você só precisa de credenciais estáticas, veja o guia mais simples [GCP — chave de service account](/pt-BR/enterprise/features/secrets-manager/gcp).
|
||||
</Note>
|
||||
|
||||
### Como funciona em runtime
|
||||
|
||||
1. O worker do deployment solicita um JWT OIDC fresco à CrewAI Platform.
|
||||
2. O worker troca o JWT por uma credencial Google federada via [Security Token Service](https://cloud.google.com/iam/docs/reference/sts/rest), referenciando o Workload Identity Pool Provider que você configurou abaixo.
|
||||
3. O worker chama `secretmanager.googleapis.com:accessSecretVersion` para ler o segredo, usando a credencial federada diretamente (o principal federado detém `roles/secretmanager.secretAccessor` — veja Passo 4).
|
||||
4. O valor obtido é injetado como valor da variável de ambiente para aquele kickoff de automação.
|
||||
|
||||
Tokens OIDC subject são cacheados por ~1 hora para evitar reemissão a cada kickoff. Valores de segredos são buscados frescos a cada kickoff independentemente do estado do cache OIDC, e é isso que torna este caminho consciente de rotação.
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- A imagem do pod de automação deve incluir o runtime da CrewAI versão `1.14.5` ou superior.
|
||||
- Um projeto Google Cloud com as APIs **Secret Manager**, **Security Token Service** e **IAM Credentials** habilitadas. Habilite-as via console ou:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com sts.googleapis.com iamcredentials.googleapis.com \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
- Permissão no projeto para criar Workload Identity Pools, papéis IAM, service accounts e (se necessário) segredos.
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem as permissões `workload_identity_configs: manage` e `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
- **Sua instalação da CrewAI Platform deve ser acessível a partir do Google Cloud via HTTPS** para que o GCP STS possa buscar o documento de discovery OIDC e o JWKS durante a validação do token. Confirme com o administrador da sua plataforma que o host é acessível pela internet.
|
||||
</Note>
|
||||
|
||||
## Passo 1 — Encontre a URL do Issuer OIDC da Sua CrewAI Platform
|
||||
|
||||
Sua instalação da CrewAI Platform publica um documento de discovery OpenID Connect em `https://<your-platform-host>/.well-known/openid-configuration`. O campo `issuer` ali é a URL que o Google registrará como provedor OIDC confiável.
|
||||
|
||||
Abra a URL em um navegador:
|
||||
|
||||
```
|
||||
https://<your-platform-host>/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
Você deverá ver um JSON contendo:
|
||||
|
||||
```json
|
||||
{
|
||||
"issuer": "https://<your-platform-host>",
|
||||
"jwks_uri": "https://<your-platform-host>/oauth2/jwks",
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Anote o valor exato de `issuer` — você o usará no Passo 3.
|
||||
|
||||
<Tip>
|
||||
Se a URL retornar 404 ou 503, contate o administrador da sua plataforma. O issuer OIDC requer uma chave de assinatura privada configurada no momento da instalação. Veja o guia de instalação da plataforma para as configurações `OIDC_PRIVATE_KEY` e `OIDC_ISSUER`.
|
||||
</Tip>
|
||||
|
||||
## Passo 2 — Criar um Workload Identity Pool
|
||||
|
||||
Um Workload Identity Pool é um container do lado Google Cloud para identidades externas confiáveis. Você registrará a CrewAI Platform como um provedor dentro desse pool.
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools create crewai-pool \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--display-name="CrewAI Platform"
|
||||
```
|
||||
|
||||
Ou no [console Workload Identity Pools](https://console.cloud.google.com/iam-admin/workload-identity-pools), clique em **Create Pool**.
|
||||
|
||||
{/* SCREENSHOT: GCP "Create Workload Identity Pool" form with name "crewai-pool" → /images/secrets-manager/gcp-wi/01-create-pool.png */}
|
||||
|
||||
## Passo 3 — Adicionar a CrewAI Platform como um Provedor OIDC no Pool
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers create-oidc crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--display-name="CrewAI Platform OIDC" \
|
||||
--issuer-uri="https://<your-platform-host>" \
|
||||
--attribute-mapping="google.subject=assertion.sub,attribute.organization=assertion.organization_id" \
|
||||
--attribute-condition="assertion.organization_id != ''"
|
||||
```
|
||||
|
||||
O `--attribute-mapping` diz ao Google como mapear claims do JWT para atributos do Google:
|
||||
- `google.subject` é o identificador do principal — mapeamos para a claim `sub` do JWT, que a CrewAI Platform define como `organization:<uuid>`.
|
||||
- `attribute.organization` é um atributo customizado — mapeamos para a claim `organization_id` do JWT para que você possa referenciá-la em vínculos IAM mais tarde.
|
||||
|
||||
O `--attribute-condition` é uma verificação de defesa em profundidade que rejeita tokens sem uma claim `organization_id`.
|
||||
|
||||
Obtenha o **nome do recurso do provedor** (você precisará dele para a audience e vínculos IAM):
|
||||
|
||||
```bash
|
||||
gcloud iam workload-identity-pools providers describe crewai-provider \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--location=global \
|
||||
--workload-identity-pool=crewai-pool \
|
||||
--format="value(name)"
|
||||
```
|
||||
|
||||
A saída se parece com:
|
||||
|
||||
```
|
||||
projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider
|
||||
```
|
||||
|
||||
Este é seu valor de **Workload Identity Provider** para a CrewAI Platform no Passo 6. A CrewAI Platform automaticamente computa a audience OIDC como `//iam.googleapis.com/<this-resource-name>` ao emitir tokens.
|
||||
|
||||
{/* SCREENSHOT: "Add provider to pool" form with OIDC selected, issuer URI, audience defaults, attribute mapping → /images/secrets-manager/gcp-wi/02-add-oidc-provider.png */}
|
||||
|
||||
## Passo 4 — Conceder Acesso ao Secret Manager ao Principal Federado
|
||||
|
||||
Vincule ambos os papéis do Secret Manager no escopo do projeto ao principal federado — um papel habilita o autocomplete de Secret Name no formulário de env-var, o outro permite ler valores de segredos no kickoff da automação. Ambos são necessários para que o recurso funcione de ponta a ponta.
|
||||
|
||||
```bash
|
||||
PRINCIPAL_SET="principalSet://iam.googleapis.com/projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/attribute.organization/<YOUR_CREWAI_ORG_UUID>"
|
||||
|
||||
# Necessário para o autocomplete de Secret Name (chama secretmanager.secrets.list)
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.viewer"
|
||||
|
||||
# Necessário para ler valores de segredos no kickoff
|
||||
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
Substitua `<PROJECT_NUMBER>` pelo número numérico do projeto (`gcloud projects describe <YOUR_PROJECT_ID> --format='value(projectNumber)'`) e `<YOUR_CREWAI_ORG_UUID>` pelo UUID da organização CrewAI Platform que deve ter permissão para ler seus segredos. Você pode encontrar o UUID da org na UI da plataforma na página de configurações da organização, ou via API. Isso escopa a federação a uma organização CrewAI específica — apenas tokens emitidos para as automações dessa org são aceitos.
|
||||
|
||||
Ou via console Google Cloud:
|
||||
|
||||
1. Abra **IAM & Admin** → **IAM** do seu projeto.
|
||||
2. Clique em **GRANT ACCESS**.
|
||||
3. **New principals:** cole a string completa `principalSet://...attribute.organization/<YOUR_CREWAI_ORG_UUID>`.
|
||||
4. Atribua o papel **Secret Manager Viewer** (`roles/secretmanager.viewer`).
|
||||
5. Clique em **SAVE**.
|
||||
6. Clique em **GRANT ACCESS** novamente e repita com o papel **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
<Tip>
|
||||
**Isolamento por organização.** O padrão `principalSet://...attribute.organization/<UUID>` restringe o acesso aos tokens de uma organização específica. Se você tiver várias organizações CrewAI compartilhando um projeto Google Cloud, repita ambos os vínculos por organização com o UUID correto — ou use uma condição de atributo menos restritiva se o isolamento não for necessário.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
**Escopando `secretAccessor` por segredo (opcional).** Se você preferir não conceder `roles/secretmanager.secretAccessor` em nível de projeto, omita o segundo vínculo acima e vincule por segredo:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding <SECRET_NAME> \
|
||||
--member="$PRINCIPAL_SET" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
Mantenha `roles/secretmanager.viewer` no escopo de projeto de qualquer forma — `secretmanager.secrets.list` (do qual o autocomplete depende) não pode ser concedido por segredo.
|
||||
</Tip>
|
||||
|
||||
## Passo 5 — Criar Pelo Menos Um Segredo no GCP
|
||||
|
||||
Se você ainda não tem um segredo para testar, crie um via CLI `gcloud`:
|
||||
|
||||
```bash
|
||||
echo -n "hello from gcp" | gcloud secrets create crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID> \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
Ou via [console Secret Manager](https://console.cloud.google.com/security/secret-manager):
|
||||
|
||||
1. Abra **Secret Manager** no seu projeto GCP.
|
||||
2. Clique em **+ CREATE SECRET**.
|
||||
3. **Name:** `crewai-test-keyword`. **Secret value:** cole seu valor.
|
||||
4. Clique em **CREATE SECRET**.
|
||||
|
||||
## Passo 6 — Adicionar uma Configuração de Workload Identity na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Workload Identity** e clique em **Add Workload Identity Config**.
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `gcp-prod`.
|
||||
- **Cloud Provider:** `GCP`.
|
||||
- **Workload Identity Provider:** o nome do recurso do provedor do Passo 3, ex. `projects/<PROJECT_NUMBER>/locations/global/workloadIdentityPools/crewai-pool/providers/crewai-provider`.
|
||||
- (Opcional) Alterne **Default Configuration** se você quiser que esta seja a config WI padrão selecionada ao criar uma credencial de segredo baseada em GCP.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Workload Identity Config" form with GCP and provider resource name → /images/secrets-manager/gcp-wi/03-amp-add-wi-config-gcp.png */}
|
||||
{/* SCREENSHOT: Workload Identity list showing both AWS and GCP rows → /images/secrets-manager/gcp-wi/04-amp-wi-list-with-gcp.png */}
|
||||
|
||||
## Passo 7 — Adicionar uma Credencial de Provedor de Segredos Vinculada à Config WI
|
||||
|
||||
Navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `gcp-prod-wi`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Authentication Method:** `Workload Identity`.
|
||||
- **Workload Identity Configuration:** selecione a config que você criou no Passo 6.
|
||||
- **Project ID:** o ID do seu projeto GCP (o mesmo projeto dono dos segredos).
|
||||
- (Opcional) Marque **Set as default credential for this provider**.
|
||||
|
||||
O formulário pedirá apenas o **Project ID** sob Workload Identity — o campo **Service Account JSON** está intencionalmente oculto porque não se aplica a este caminho; a identidade federada vem da config WI vinculada.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP + Workload Identity + WI config dropdown → /images/secrets-manager/gcp-wi/05-amp-add-credential-gcp-wi.png */}
|
||||
|
||||
## Passo 8 — Testar a Conexão
|
||||
|
||||
Depois de salvar a credencial, clique em **Test Connection**. Para credenciais workload-identity isso verifica o handshake OIDC: a CrewAI Platform emite um JWT e o troca via Security Token Service por um token de acesso Google federado. Um resultado verde significa que o vínculo de federação está saudável.
|
||||
|
||||
Um Test Connection bem-sucedido prova que o Workload Identity Pool, provedor OIDC, mapeamento de atributos e condição de atributo estão todos conectados corretamente. Ele **não** prova que o IAM do Secret Manager está correto — `secretmanager.secrets.list` e `secretmanager.versions.access` são exercitados separadamente quando o autocomplete de Secret Name carrega ou quando uma variável de ambiente é resolvida no kickoff. Veja [Solução de Problemas](#troubleshooting) para modos de falha de handshake.
|
||||
|
||||
## Passo 9 — Referenciar o Segredo em uma Variável de Ambiente
|
||||
|
||||
Referencie o segredo em uma automação, exatamente como você faria para qualquer outra env var apoiada pelo Secrets Manager. Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para os campos do formulário e o comportamento.
|
||||
|
||||
## Passo 10 — Verificar a Rotação
|
||||
|
||||
Após o deployment estar rodando, rotacione o segredo no GCP adicionando uma nova versão (o Secret Manager sempre lê a versão habilitada mais recente por padrão):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add crewai-test-keyword \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
|
||||
Dispare um novo kickoff de automação. O ambiente do kickoff verá `"rotated value"` — sem novo deploy, sem reinício de worker, sem espera de TTL.
|
||||
|
||||
Para confirmar nos logs do worker, procure por:
|
||||
|
||||
```
|
||||
Workload identity config '<id>' (gcp): N secret(s) resolved
|
||||
```
|
||||
|
||||
Esta linha aparece para cada kickoff e indica uma chamada `accessSecretVersion` fresca contra o GCP.
|
||||
|
||||
## Solução de Problemas
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| Test Connection falha com erro de handshake | A troca de tokens STS foi rejeitada. Verifique se o Workload Identity Pool existe, se o issuer do provedor OIDC corresponde ao valor `issuer` da plataforma e se a condição de atributo aceita as claims do JWT. Confirme que a URL de discovery OIDC da plataforma é acessível a partir do GCP pela internet pública. |
|
||||
| `Could not refresh access token: invalid_target` | A claim de audience não corresponde à audience esperada do Workload Identity Provider. A CrewAI Platform define a audience automaticamente; se você a customizou, certifique-se de que corresponde a `//iam.googleapis.com/<provider-resource-name>`. |
|
||||
| `Failed to fetch JWKS from issuer` | O GCP STS não consegue acessar o host da sua CrewAI Platform. Confirme que o host é acessível pela internet e que `/.well-known/openid-configuration` retorna 200. |
|
||||
| `Attribute condition rejected token` | A condição de atributo do provedor OIDC (Passo 3) requer `organization_id`. A CrewAI Platform sempre define essa claim, então isso geralmente significa um pool/provedor mal configurado. Reconfira a condição de atributo do provedor. |
|
||||
| Autocomplete de Secret Name mostra `PERMISSION_DENIED: secretmanager.secrets.list` | O principal federado está sem `roles/secretmanager.viewer` no escopo de projeto. A permissão `secretmanager.secrets.list` é escopada apenas por projeto e não pode ser concedida por segredo. Veja o Passo 4. |
|
||||
| Kickoff falha ao resolver um segredo mesmo que o Test Connection passe | O vínculo WI está saudável, mas `secretmanager.versions.access` está ausente no segredo que falha. Audite `roles/secretmanager.secretAccessor` (escopado por projeto, ou por segredo se você escopou dessa forma no Passo 4). |
|
||||
| Valor rotacionado não é pego no próximo kickoff | Confirme que a env var na automação está referenciando uma credencial baseada em Workload Identity (não uma credencial de chaves estáticas). O caminho estático incorpora valores à imagem do deploy. |
|
||||
|
||||
### Links de Referência
|
||||
|
||||
- GCP: [Workload Identity Federation overview](https://cloud.google.com/iam/docs/workload-identity-federation)
|
||||
- GCP: [Configure Workload Identity Federation with OIDC](https://cloud.google.com/iam/docs/workload-identity-federation-with-other-providers)
|
||||
- GCP: [Secret Manager IAM roles](https://cloud.google.com/secret-manager/docs/access-control)
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
- [Use segredos em variáveis de ambiente e gerencie permissões](/pt-BR/enterprise/features/secrets-manager/usage)
|
||||
- Para multi-cloud, veja também [AWS Workload Identity (Federação OIDC)](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) e [Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity).
|
||||
188
docs/pt-BR/enterprise/features/secrets-manager/gcp.mdx
Normal file
188
docs/pt-BR/enterprise/features/secrets-manager/gcp.mdx
Normal file
@@ -0,0 +1,188 @@
|
||||
---
|
||||
title: Google Cloud Secret Manager
|
||||
description: Configure o Google Cloud Secret Manager como provedor de segredos para a CrewAI Platform, de ponta a ponta
|
||||
sidebarTitle: GCP
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia o orienta na configuração do Google Cloud Secret Manager como provedor de segredos para sua organização na CrewAI Platform, usando **credenciais de service account**. Ao final, a CrewAI Platform poderá ler segredos armazenados no seu projeto Google Cloud e injetá-los como valores de variáveis de ambiente em runtime.
|
||||
|
||||
<Note>
|
||||
Este guia cobre o caminho de **credenciais estáticas** — segredos são resolvidos no momento do deploy e incorporados à imagem do deployment. Valores rotacionados exigem um novo deploy. Se você quiser segredos conscientes de rotação que se atualizam a cada kickoff de automação, veja [GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Este guia cobre a configuração do lado GCP e a configuração da credencial na CrewAI Platform. Para então referenciar um segredo a partir de uma variável de ambiente, veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage).
|
||||
</Note>
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
<Note>
|
||||
Antes de começar, certifique-se de que você tem:
|
||||
|
||||
- Um projeto Google Cloud com a **Secret Manager API** habilitada. Habilite-a no [console APIs & Services](https://console.cloud.google.com/apis/library/secretmanager.googleapis.com) ou via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud services enable secretmanager.googleapis.com --project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
- Permissão no projeto para criar service accounts, conceder papéis IAM e (se necessário) criar segredos.
|
||||
- Uma organização na CrewAI Platform onde seu usuário tem a permissão `secret_providers: manage`. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac).
|
||||
</Note>
|
||||
|
||||
## Passo 1 — Criar uma Service Account
|
||||
|
||||
Uma service account é a identidade do lado GCP como a qual a CrewAI Platform irá se autenticar.
|
||||
|
||||
No [console IAM & Admin → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts), clique em **Create Service Account**.
|
||||
|
||||
- **Service account name:** `crewai-secrets-reader`
|
||||
- **Service account ID:** preenchido automaticamente a partir do nome (ex. `crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com`)
|
||||
- **Description (optional):** "Read-only access to Secret Manager for CrewAI Platform"
|
||||
|
||||
Clique em **Create and Continue**. Pule as concessões opcionais nesta tela — você anexará o papel no Passo 2. Clique em **Done**.
|
||||
|
||||
Para detalhes completos, veja a documentação GCP: [Create service accounts](https://cloud.google.com/iam/docs/service-accounts-create).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create service account" form with name "crewai-secrets-reader" → /images/secrets-manager/gcp/01-create-service-account.png */}
|
||||
|
||||
## Passo 2 — Conceder Acesso ao Secret Manager
|
||||
|
||||
A CrewAI Platform precisa de permissão para listar e ler segredos no seu projeto. Use um de dois escopos — **a nível de projeto** para simplicidade ou **por segredo** para menor privilégio.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="A nível de projeto (mais simples)">
|
||||
No [console IAM](https://console.cloud.google.com/iam-admin/iam), clique em **Grant Access** e:
|
||||
|
||||
- **New principals:** o email da service account do Passo 1.
|
||||
- **Role:** **Secret Manager Secret Accessor** (`roles/secretmanager.secretAccessor`).
|
||||
|
||||
Clique em **Save**.
|
||||
|
||||
Ou via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor"
|
||||
```
|
||||
|
||||
{/* SCREENSHOT: GCP IAM "Grant access" panel with the service account and Secret Manager Secret Accessor role → /images/secrets-manager/gcp/02-iam-grant-access.png */}
|
||||
</Tab>
|
||||
|
||||
<Tab title="Por segredo (menor privilégio)">
|
||||
Conceda o papel apenas nos segredos específicos que a CrewAI Platform deve acessar. Repita para cada segredo:
|
||||
|
||||
```bash
|
||||
gcloud secrets add-iam-policy-binding YOUR_SECRET_NAME \
|
||||
--member="serviceAccount:crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
|
||||
--role="roles/secretmanager.secretAccessor" \
|
||||
--project=YOUR_PROJECT_ID
|
||||
```
|
||||
|
||||
Ou no console: abra cada segredo no [Secret Manager](https://console.cloud.google.com/security/secret-manager), clique em **Permissions** no painel à direita e conceda **Secret Manager Secret Accessor** à service account.
|
||||
|
||||
{/* SCREENSHOT: Per-secret "Permissions" panel in Secret Manager with the service account granted accessor role → /images/secrets-manager/gcp/03-per-secret-permissions.png */}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
<Tip>
|
||||
O papel `roles/secretmanager.secretAccessor` concede acesso somente leitura aos valores de segredos. A CrewAI Platform também chama `secretmanager.secrets.list` para a experiência de autocomplete no formulário de env-var — essa permissão está incluída no papel no escopo de projeto, mas **não** no escopo por segredo. Com vínculos por segredo, o autocomplete não sugerirá segredos; você precisará digitar o nome completo do segredo.
|
||||
</Tip>
|
||||
|
||||
## Passo 3 — Criar uma Chave de Service Account
|
||||
|
||||
Abra a service account do Passo 1 no [console IAM & Admin → Service Accounts](https://console.cloud.google.com/iam-admin/serviceaccounts).
|
||||
|
||||
- Clique na aba **Keys**.
|
||||
- Clique em **Add Key** → **Create new key**.
|
||||
- **Key type:** JSON.
|
||||
- Clique em **Create**. O navegador baixa um arquivo JSON — mantenha-o seguro; não pode ser baixado novamente.
|
||||
|
||||
Ou via `gcloud`:
|
||||
|
||||
```bash
|
||||
gcloud iam service-accounts keys create ./crewai-secrets-reader.json \
|
||||
--iam-account=crewai-secrets-reader@YOUR_PROJECT_ID.iam.gserviceaccount.com
|
||||
```
|
||||
|
||||
<Warning>
|
||||
A chave de service account é uma credencial estática de longa duração. Armazene-a com segurança (em um gerenciador de senhas ou no seu próprio cofre de segredos) e rotacione-a em uma cadência regular. Para eliminar credenciais estáticas completamente, use [GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity).
|
||||
</Warning>
|
||||
|
||||
{/* SCREENSHOT: Service account "Keys" tab with the "Create new key" → JSON option → /images/secrets-manager/gcp/04-create-service-account-key.png */}
|
||||
|
||||
## Passo 4 — Adicionar a Credencial na CrewAI Platform
|
||||
|
||||
Na CrewAI Platform, navegue até **Settings** → **Secret Provider Credentials** e clique em **Add Credential**.
|
||||
|
||||
{/* SCREENSHOT: Sidebar/nav highlighting Settings → Secret Provider Credentials → /images/secrets-manager/usage/01-amp-settings-nav.png */}
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Name:** Um nome descritivo, ex. `gcp-prod`.
|
||||
- **Provider:** `Google Cloud Secret Manager`.
|
||||
- **Project ID:** O ID do seu projeto GCP (ex. `my-crewai-prod`).
|
||||
- **Service Account JSON:** Cole o conteúdo inteiro do arquivo JSON que você baixou no Passo 3.
|
||||
- (Opcional) Marque **Set as default credential for this provider**. A credencial padrão é usada por variáveis de ambiente que referenciam segredos GCP sem especificar uma credencial explicitamente.
|
||||
|
||||
Clique em **Create**.
|
||||
|
||||
{/* SCREENSHOT: "Add Secret Provider Credential" form with GCP fields filled in → /images/secrets-manager/gcp/05-amp-add-credential-form-gcp.png */}
|
||||
|
||||
## Passo 5 — Criar Pelo Menos Um Segredo no GCP
|
||||
|
||||
Se você ainda não tem segredos no GCP Secret Manager, crie um agora para que possa verificar a conexão no Passo 6.
|
||||
|
||||
No [console Secret Manager](https://console.cloud.google.com/security/secret-manager), clique em **Create secret**.
|
||||
|
||||
- **Name:** Um nome único, ex. `openai-api-key`.
|
||||
- **Secret value:** Cole um valor bruto ou faça upload de um arquivo.
|
||||
- Deixe as configurações de rotação, replicação e outras em seus padrões a menos que tenha um requisito específico.
|
||||
|
||||
Clique em **Create secret**.
|
||||
|
||||
Ou via `gcloud`:
|
||||
|
||||
```bash
|
||||
echo -n "sk-your-actual-key" | gcloud secrets create openai-api-key \
|
||||
--data-file=- \
|
||||
--project=YOUR_PROJECT_ID \
|
||||
--replication-policy=automatic
|
||||
```
|
||||
|
||||
<Note>
|
||||
**Sintaxe de referência por chave JSON.** O GCP Secret Manager trata valores de segredos como blobs opacos. Se o valor do seu segredo for uma string JSON, a CrewAI Platform pode extrair um único campo usando a sintaxe `secret-name#json_key` (ex. `database-credentials#password`). Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables) para detalhes.
|
||||
</Note>
|
||||
|
||||
Para detalhes completos, veja a documentação GCP: [Create a secret](https://cloud.google.com/secret-manager/docs/create-secret-quickstart).
|
||||
|
||||
{/* SCREENSHOT: GCP "Create secret" form with name and value → /images/secrets-manager/gcp/06-create-secret.png */}
|
||||
|
||||
## Passo 6 — Testar a Conexão
|
||||
|
||||
De volta à CrewAI Platform, na página **Secret Provider Credentials**, encontre a credencial que você acabou de criar e clique em **Test Connection**.
|
||||
|
||||
Um toast de sucesso confirma que a CrewAI Platform consegue se autenticar no GCP e ler segredos do seu projeto.
|
||||
|
||||
{/* SCREENSHOT: Success toast after clicking "Test Connection" on the GCP credential → /images/secrets-manager/gcp/07-test-connection-success.png */}
|
||||
|
||||
Se o teste falhar, verifique as causas mais comuns:
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| `PERMISSION_DENIED` ao listar segredos | A service account está sem `roles/secretmanager.secretAccessor`, ou você escopou por segredo (`list` não é concedido). Reconfira o Passo 2. |
|
||||
| `PERMISSION_DENIED` em `secretmanager.secrets.access` | Mesmo que acima, mas para um segredo específico. Confirme que a service account tem o papel accessor no segredo em questão. |
|
||||
| `unauthorized_client` / `invalid_grant` | O JSON de Service Account colado é inválido, expirado ou pertence a uma service account excluída. Recrie a chave (Passo 3) e cole novamente. |
|
||||
| `Project ID does not match` | O campo Project ID na CrewAI Platform não corresponde ao projeto dono da service account / segredos. Reconfira o Passo 4. |
|
||||
| `API not enabled` | Secret Manager API não está habilitada no projeto. Veja Pré-requisitos. |
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
Agora que o GCP está conectado, vá para [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage) para:
|
||||
|
||||
- Conceder aos membros da organização as permissões corretas para usar (ou gerenciar) o Secrets Manager.
|
||||
- Referenciar seus segredos GCP a partir de variáveis de ambiente da CrewAI Platform.
|
||||
|
||||
Se você quiser segredos **conscientes de rotação** que se propagam sem novo deploy, mude para [GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity) — mesmo cofre de segredos, sem credenciais estáticas, segredos buscados por kickoff.
|
||||
81
docs/pt-BR/enterprise/features/secrets-manager/overview.mdx
Normal file
81
docs/pt-BR/enterprise/features/secrets-manager/overview.mdx
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
title: Visão Geral do Secrets Manager
|
||||
description: Conecte cofres de segredos externos à CrewAI Platform e referencie segredos gerenciados a partir de variáveis de ambiente
|
||||
sidebarTitle: Visão Geral
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
O recurso Secrets Manager permite que sua organização conecte um cofre de segredos externo — AWS Secrets Manager, Google Cloud Secret Manager ou (em breve) Azure Key Vault — e referencie esses segredos diretamente a partir de variáveis de ambiente nas suas automações e crews. Em vez de colar valores em texto puro na plataforma, você armazena um conjunto de credenciais por provedor e se refere aos segredos pelo nome.
|
||||
|
||||
Isso oferece a você:
|
||||
|
||||
- **Armazenamento centralizado** — gerencie segredos no seu provedor em vez de editar a configuração da CrewAI Platform. A CrewAI Platform não mantém nenhuma cópia em texto puro do valor do segredo.
|
||||
- **Exposição reduzida** — valores sensíveis nunca ficam em texto puro na sua configuração da CrewAI Platform.
|
||||
- **Auditabilidade cloud-native** — o log de auditoria do seu provedor registra cada leitura de segredo.
|
||||
|
||||
<Note>
|
||||
O Secrets Manager (tanto o caminho de credenciais estáticas quanto o de Workload Identity) requer o runtime da CrewAI versão `1.14.5` ou superior na imagem do pod de automação.
|
||||
</Note>
|
||||
|
||||
## Dois Caminhos: Credenciais Estáticas vs Workload Identity
|
||||
|
||||
Existem duas formas de conectar a CrewAI Platform ao cofre de segredos da sua nuvem. **Eles diferem significativamente no comportamento de rotação**, então escolha com base em quão frequentemente seus segredos são rotacionados e quão rigorosa é a sua postura de segurança.
|
||||
|
||||
| Aspecto | Credenciais Estáticas | Workload Identity (Federação OIDC) |
|
||||
|---|---|---|
|
||||
| **Autenticação** | Chaves de acesso de longa duração / JSON de service account armazenados na CrewAI Platform | Tokens de curta duração emitidos por processo worker; nenhuma credencial estática armazenada em lugar algum |
|
||||
| **Propagação de rotação** | Resolvida no momento do deploy e **incorporada à imagem do contêiner do deployment** — valores rotacionados exigem um novo deploy | Resolvida no **momento da execução da automação** — valores rotacionados se propagam para o próximo kickoff sem novo deploy |
|
||||
| **Esforço de configuração** | Menor — cole chaves / faça upload do JSON da service account | Maior — registre a CrewAI Platform como um provedor OIDC na sua nuvem, configure políticas de confiança |
|
||||
| **Melhor para** | Começar rápido, segredos rotacionados raramente, deployments single-account | Produção, segredos rotacionados frequentemente, ambientes guiados por compliance que proíbem credenciais de longa duração |
|
||||
|
||||
<Note>
|
||||
**Ambos os caminhos usam o mesmo fluxo de UI** para referenciar segredos em variáveis de ambiente (veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage)). A diferença está inteiramente em como a plataforma se autentica na sua nuvem e em quando lê o valor do segredo.
|
||||
</Note>
|
||||
|
||||
### Escolha seu guia de configuração
|
||||
|
||||
| Provedor | Credenciais Estáticas | Workload Identity |
|
||||
|---|---|---|
|
||||
| AWS Secrets Manager | [AWS — chaves estáticas / AssumeRole](/pt-BR/enterprise/features/secrets-manager/aws) | [AWS — Workload Identity (OIDC)](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) |
|
||||
| Google Cloud Secret Manager | [GCP — chave de service account](/pt-BR/enterprise/features/secrets-manager/gcp) | [GCP — Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity) |
|
||||
| Azure Key Vault | [Azure — client secret](/pt-BR/enterprise/features/secrets-manager/azure) | [Azure — Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity) |
|
||||
|
||||
<Note>
|
||||
As interfaces de Secrets Manager e Workload Identity estão atualmente marcadas como **Beta** na CrewAI Platform.
|
||||
</Note>
|
||||
|
||||
## Como Tudo se Encaixa
|
||||
|
||||
Configurar o Secrets Manager é um fluxo de três passos que envolve tanto o seu provedor de nuvem quanto a CrewAI Platform:
|
||||
|
||||
1. **Um admin configura uma credencial de provedor.** Este é o trabalho do lado da nuvem — e o trabalho difere dependendo de qual caminho (credenciais estáticas ou Workload Identity) você escolher. Os guias específicos por provedor cobrem isso de ponta a ponta.
|
||||
2. **Um admin (ou um membro autorizado) referencia um segredo em uma variável de ambiente.** Na página de Variáveis de Ambiente, o usuário escolhe uma credencial de provedor e seleciona o nome do segredo. Veja [Usando o Secrets Manager](/pt-BR/enterprise/features/secrets-manager/usage#referencing-secrets-in-environment-variables).
|
||||
3. **A automação recebe o valor resolvido em runtime.** Quando um crew ou automação executa, a CrewAI Platform busca o segredo do seu provedor e o injeta como o valor da variável de ambiente. Com Workload Identity, essa busca acontece a cada kickoff (consciente de rotação). Com credenciais estáticas, essa busca acontece no momento do deploy e o valor é incorporado à imagem do deployment.
|
||||
|
||||
## Permissões
|
||||
|
||||
Duas features da CrewAI Platform controlam o acesso ao Secrets Manager:
|
||||
|
||||
- `secret_providers` — controla quem pode visualizar ou gerenciar credenciais de provedor.
|
||||
- `environment_variables` — controla quem pode criar e editar variáveis de ambiente (incluindo aquelas que referenciam segredos).
|
||||
|
||||
Uma terceira feature controla a configuração do Workload Identity:
|
||||
|
||||
- `workload_identity_configs` — controla quem pode visualizar ou gerenciar configurações de Workload Identity. Necessário apenas se você estiver usando o caminho Workload Identity.
|
||||
|
||||
Owners sempre têm acesso total. Members **não** recebem acesso a `secret_providers` ou `workload_identity_configs` por padrão e precisam receber permissão por meio de um papel customizado. Veja [Permissões (RBAC)](/pt-BR/enterprise/features/secrets-manager/usage#permissions-rbac) para a matriz completa e instruções passo a passo.
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
Escolha seu caminho:
|
||||
|
||||
- **Credenciais estáticas** (mais simples, requer novo deploy na rotação):
|
||||
- [Configurar AWS Secrets Manager](/pt-BR/enterprise/features/secrets-manager/aws)
|
||||
- [Configurar Google Cloud Secret Manager](/pt-BR/enterprise/features/secrets-manager/gcp)
|
||||
- [Configurar Azure Key Vault](/pt-BR/enterprise/features/secrets-manager/azure)
|
||||
- **Workload Identity** (consciente de rotação, sem novo deploy):
|
||||
- [Configurar AWS Workload Identity](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity)
|
||||
- [Configurar GCP Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- [Configurar Azure Workload Identity Federation](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity)
|
||||
- Em seguida: [Use segredos em variáveis de ambiente e gerencie permissões](/pt-BR/enterprise/features/secrets-manager/usage)
|
||||
136
docs/pt-BR/enterprise/features/secrets-manager/usage.mdx
Normal file
136
docs/pt-BR/enterprise/features/secrets-manager/usage.mdx
Normal file
@@ -0,0 +1,136 @@
|
||||
---
|
||||
title: Usando o Secrets Manager
|
||||
description: Gerencie permissões e referencie segredos gerenciados a partir de variáveis de ambiente na CrewAI Platform
|
||||
sidebarTitle: Uso e Permissões
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia é agnóstico em relação ao provedor. Ele assume que você (ou outro admin) já configurou pelo menos uma Credencial de Provedor de Segredos. Escolha seu guia de configuração com base no caminho que deseja:
|
||||
|
||||
- Credenciais estáticas: [AWS](/pt-BR/enterprise/features/secrets-manager/aws) · [GCP](/pt-BR/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (consciente de rotação): [AWS](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
|
||||
Use este guia para:
|
||||
|
||||
- Conceder as permissões corretas aos membros da organização.
|
||||
- Referenciar segredos a partir de variáveis de ambiente nas suas automações.
|
||||
- Verificar se tudo é resolvido corretamente em runtime.
|
||||
|
||||
## Permissões (RBAC)
|
||||
|
||||
Três features da CrewAI Platform são relevantes ao trabalhar com o Secrets Manager:
|
||||
|
||||
- `secret_providers` — controla o acesso à página **Secret Provider Credentials**.
|
||||
- `workload_identity_configs` — controla o acesso à página **Workload Identity** (relevante apenas se você usar o caminho WI).
|
||||
- `environment_variables` — controla quem pode criar ou editar variáveis de ambiente.
|
||||
|
||||
Cada feature tem dois níveis de ação: `read` e `manage`. Conceder `manage` implica automaticamente em `read`.
|
||||
|
||||
### O que Conceder
|
||||
|
||||
| Objetivo | `secret_providers` | `workload_identity_configs` | `environment_variables` |
|
||||
|---|---|---|---|
|
||||
| Usar credenciais estáticas existentes em variáveis de ambiente (sem edição de provedor) | `read` | — | `manage` |
|
||||
| Criar, editar ou excluir credenciais estáticas | `manage` | — | `manage` |
|
||||
| Usar credenciais existentes baseadas em Workload Identity em env vars | `read` | — | `manage` |
|
||||
| Criar, editar ou excluir configs de Workload Identity (e credenciais que as referenciam) | `manage` | `manage` | `manage` |
|
||||
|
||||
<Note>
|
||||
**Owners** automaticamente têm acesso total a todas as features. O papel padrão **Member** intencionalmente exclui `secret_providers` e `workload_identity_configs` — admins devem incluir explicitamente os membros por meio de um papel customizado.
|
||||
</Note>
|
||||
|
||||
### Como Atribuir
|
||||
|
||||
1. Na CrewAI Platform, navegue até **Settings** → **Roles**. Nesta página você pode criar novos papéis, editar as permissões de cada papel e atribuir papéis aos membros existentes da organização.
|
||||
|
||||
{/* SCREENSHOT: Sidebar highlighting Settings → Roles → /images/secrets-manager/usage/06-amp-settings-roles-nav.png */}
|
||||
{/* SCREENSHOT: Roles list page with "Create Role" button visible → /images/secrets-manager/usage/07-amp-roles-list.png */}
|
||||
|
||||
2. Clique em **Create Role** para criar um novo papel ou abra um papel existente para editar suas permissões.
|
||||
|
||||
3. No editor de permissões do papel, alterne as features relevantes conforme a tabela acima:
|
||||
|
||||
- `secret_providers`: escolha **read** se este papel só precisa usar credenciais existentes, ou **manage** se também deve ser capaz de criar, editar e excluir credenciais.
|
||||
- `environment_variables`: escolha **manage** para que o papel possa criar variáveis de ambiente que referenciam segredos.
|
||||
|
||||
{/* SCREENSHOT: Role editor showing the secret_providers feature with read/manage toggles → /images/secrets-manager/usage/08-amp-role-editor-secret-providers-toggles.png */}
|
||||
{/* SCREENSHOT: Role editor showing environment_variables toggles → /images/secrets-manager/usage/09-amp-role-editor-env-vars-toggles.png */}
|
||||
|
||||
4. Salve o papel.
|
||||
|
||||
5. Atribua o papel aos membros relevantes pela mesma página de Roles (ou pela lista de Membros da organização).
|
||||
|
||||
{/* SCREENSHOT: Member assignment screen where the new role is applied to a user → /images/secrets-manager/usage/10-amp-assign-role-to-member.png */}
|
||||
|
||||
## Referenciando Segredos em Variáveis de Ambiente
|
||||
|
||||
Uma vez que uma credencial de provedor exista e seu papel tenha as permissões corretas, você pode referenciar segredos gerenciados a partir de qualquer variável de ambiente.
|
||||
|
||||
Na CrewAI Platform, navegue até **Environment Variables** e clique em **Add Environment Variables**.
|
||||
|
||||
{/* SCREENSHOT: Environment Variables empty state with "Add" button → /images/secrets-manager/usage/11-amp-env-vars-empty.png */}
|
||||
|
||||
Preencha o formulário:
|
||||
|
||||
- **Key** — o nome da variável de ambiente. Deve começar com uma letra ou underscore e conter apenas letras, números e underscores. Convencionalmente em maiúsculas, ex. `OPENAI_API_KEY`.
|
||||
|
||||
- **Value Source** — escolha de onde vem o valor:
|
||||
- **Direct Value** — um valor em texto puro que você digita. Use isto quando não quiser envolver um provedor.
|
||||
- **Use AWS default** (ou o equivalente para o seu provedor) — usa a credencial atualmente marcada como padrão para aquele tipo de provedor.
|
||||
- **A specific named credential** — selecione a credencial pelo nome. Use isto se você tiver múltiplas credenciais para o mesmo provedor (por exemplo, `aws-prod` e `aws-staging`) e quiser escolher uma explicitamente.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the "Value Source" dropdown open, showing "AWS default" + named credentials → /images/secrets-manager/usage/12-amp-env-var-form-source-selector.png */}
|
||||
|
||||
- **Secret Name** — o nome do segredo no seu provedor. Uma vez que uma credencial é selecionada, este campo oferece autocomplete: comece a digitar e a CrewAI Platform consulta seu provedor por nomes de segredos correspondentes.
|
||||
|
||||
Use a sintaxe `secret-name#json_key` para extrair um único campo de um segredo estruturado (JSON). Por exemplo, dado um segredo `database-credentials` com valor `{"username": "...", "password": "..."}`, referencie `database-credentials#password` para injetar apenas a senha.
|
||||
|
||||
{/* SCREENSHOT: Env var form with the secret name autocomplete dropdown showing live results → /images/secrets-manager/usage/13-amp-env-var-form-secret-name-autocomplete.png */}
|
||||
|
||||
<Note>
|
||||
**Nota sobre Azure Key Vault:** Nomes de segredos do Azure não podem conter underscores. A CrewAI Platform converte automaticamente underscores no seu campo `Secret Name` para hífens ao chamar o Azure (ex.: `db_password` é enviado como `db-password`).
|
||||
</Note>
|
||||
|
||||
Clique em **Create** para salvar a variável.
|
||||
|
||||
{/* SCREENSHOT: Env var list with the new variable showing masked value and a "secret" indicator → /images/secrets-manager/usage/14-amp-env-var-created.png */}
|
||||
|
||||
<Tip>
|
||||
Ao editar uma variável de ambiente existente, deixar o campo **Value** em branco preserva o valor atual. Isto é intencional — permite que você altere outros campos (como o nome do segredo ou a credencial) sem precisar digitar o valor novamente.
|
||||
</Tip>
|
||||
|
||||
## Verificando se Funciona
|
||||
|
||||
Para verificar de ponta a ponta:
|
||||
|
||||
1. Referencie a variável de ambiente em uma automação, crew ou deployment exatamente como você faria com qualquer outra variável de ambiente.
|
||||
2. Faça o deploy da automação.
|
||||
3. Dispare uma execução e confirme que ela é concluída com sucesso.
|
||||
|
||||
### O comportamento de rotação depende do caminho da credencial
|
||||
|
||||
| Caminho da credencial | Quando o segredo é lido | O que a rotação requer |
|
||||
|---|---|---|
|
||||
| **Credenciais estáticas** (chaves de acesso AWS, JSON de service account GCP) | No **momento do deploy** — o valor é incorporado à imagem do deployment | Fazer novo deploy da automação após rotacionar o segredo |
|
||||
| **Workload Identity** (federação OIDC, AWS ou GCP) | A **cada kickoff de automação** — o valor é buscado de forma fresca da sua nuvem | Nada — o próximo kickoff após a rotação verá o novo valor |
|
||||
|
||||
<Note>
|
||||
**Se você precisa de segredos conscientes de rotação** (sem novo deploy na rotação), use o caminho Workload Identity: [AWS WI](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) ou [GCP WI](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity). O trade-off é mais esforço de configuração inicial (registrar a CrewAI Platform como provedor OIDC na sua nuvem), mas operações mais simples no longo prazo.
|
||||
</Note>
|
||||
|
||||
Se o deploy ou a execução falhar com um erro relacionado ao seu segredo, verifique as causas mais comuns:
|
||||
|
||||
| Sintoma | Causa provável |
|
||||
|---|---|
|
||||
| `no credential found` | A variável de ambiente referencia um provedor, mas nenhuma credencial específica foi selecionada e não há credencial padrão definida para aquele tipo de provedor. Selecione uma credencial explicitamente na variável, ou marque uma credencial como padrão na página **Secret Provider Credentials**. |
|
||||
| `secret not found` | Erro de digitação no **Secret Name**, ou o segredo não existe na conta/região do provedor para a qual a credencial aponta. Reconfira ambos. |
|
||||
| Automação executa com o valor antigo após rotação (caminho de credenciais estáticas) | O valor anterior está incorporado à imagem do contêiner do deployment. Faça novo deploy da automação para pegar o valor rotacionado. Para evitar isso completamente, mude a credencial para o caminho Workload Identity. |
|
||||
| Automação executa com o valor antigo após rotação (caminho Workload Identity) | Confirme que a env var referencia uma credencial baseada em WI (não uma de chaves estáticas). Com WI, o próximo kickoff após a rotação deve ver o novo valor. Se não vir, verifique se o segredo foi realmente atualizado na sua nuvem (ex.: `aws secretsmanager get-secret-value`). |
|
||||
| `JSON key not found` | Ao usar `secret-name#json_key`, o segredo subjacente deve ser um objeto JSON válido contendo essa chave. Verifique lendo o segredo diretamente no seu provedor. |
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
- [Voltar à visão geral do Secrets Manager](/pt-BR/enterprise/features/secrets-manager/overview)
|
||||
- Credenciais estáticas: [AWS](/pt-BR/enterprise/features/secrets-manager/aws) · [GCP](/pt-BR/enterprise/features/secrets-manager/gcp)
|
||||
- Workload Identity (consciente de rotação): [AWS](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) · [GCP](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
@@ -0,0 +1,260 @@
|
||||
---
|
||||
title: Verificar Rotação
|
||||
description: Um crew de exemplo autocontido que prova que a rotação de segredos se propaga para deployments em execução sem novo deploy.
|
||||
sidebarTitle: Verificar Rotação
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Este guia mostra como verificar que **um segredo rotacionado no seu provedor de nuvem é pego no próprio kickoff de automação seguinte** — sem novo deploy, sem reinício de worker. É relevante apenas quando você configurou uma credencial baseada em Workload Identity ([AWS](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity)). Deployments com credenciais estáticas exigem um novo deploy após a rotação; nada a verificar aqui.
|
||||
|
||||
A receita abaixo usa um crew minúsculo e autocontido com uma ferramenta, um agente, uma tarefa. O prompt do crew nunca referencia o valor do segredo — em vez disso, uma ferramenta o lê de `os.environ` e reporta um fingerprint SHA-256 do que ela vê. Rotacione o segredo no seu provedor de nuvem, dispare novamente e o fingerprint muda.
|
||||
|
||||
<Note>
|
||||
Por que um fingerprint, não o valor bruto? Colocar segredos brutos na saída do LLM e em trace logs é um vetor de vazamento. O fingerprint é suficiente para confirmar "o valor mudou" sem escrever o valor real em nenhum lugar observável.
|
||||
</Note>
|
||||
|
||||
## Pré-requisitos
|
||||
|
||||
Antes de executar esta verificação:
|
||||
|
||||
- Uma Secret Provider Credential baseada em WI está configurada ([AWS](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity), [GCP](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity), [Azure](/pt-BR/enterprise/features/secrets-manager/azure-workload-identity)).
|
||||
- Uma variável de ambiente no seu deployment com `Secret = true`, chave `API_KEY` (ou qualquer nome que preferir — ajuste a ferramenta abaixo para corresponder), referenciando um segredo no seu provedor de nuvem.
|
||||
- Uma forma de atualizar o valor do segredo no seu provedor de nuvem (acesso à CLI ou ao console da nuvem).
|
||||
- Uma forma de disparar o deployment via HTTP (curl, Postman ou a aba **Run** na CrewAI Platform).
|
||||
|
||||
## Passo 1 — Estruturar um Crew de Verificação
|
||||
|
||||
Crie um novo projeto de crew. O CrewAI CLI estrutura tudo:
|
||||
|
||||
```bash
|
||||
crewai create crew rotation_verifier --skip_provider
|
||||
cd rotation_verifier
|
||||
```
|
||||
|
||||
## Passo 2 — Adicionar a Ferramenta de Echo de Credencial
|
||||
|
||||
Substitua `src/rotation_verifier/tools/custom_tool.py` por uma ferramenta que lê a env var apoiada pelo segredo e retorna um fingerprint:
|
||||
|
||||
```python src/rotation_verifier/tools/credential_echo_tool.py
|
||||
"""Tool that verifies a runtime-injected secret without leaking the value.
|
||||
|
||||
Reads the secret-backed env var (populated by the workload-identity
|
||||
secrets manager at kickoff time) and returns a stable fingerprint. Never
|
||||
echo raw credential values into LLM output or logs in production code —
|
||||
the fingerprint alone is sufficient to confirm rotation worked.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
|
||||
from crewai.tools import BaseTool
|
||||
|
||||
|
||||
# Match the deployment environment variable's `key` field.
|
||||
ENV_VAR_NAME = "API_KEY"
|
||||
|
||||
|
||||
class CredentialEchoTool(BaseTool):
|
||||
name: str = "credential_echo"
|
||||
description: str = (
|
||||
"Read the API credential from the worker's environment and return a "
|
||||
"fingerprint summary. Use this exactly once when asked to verify the "
|
||||
"current credential. Takes no arguments."
|
||||
)
|
||||
|
||||
def _run(self) -> str:
|
||||
value = os.environ.get(ENV_VAR_NAME)
|
||||
if not value:
|
||||
return (
|
||||
f"ERROR: {ENV_VAR_NAME} env var is not set. The workload-"
|
||||
"identity secret fetch did not run, or the deployment is "
|
||||
"missing the secret-backed env var."
|
||||
)
|
||||
fingerprint = hashlib.sha256(value.encode()).hexdigest()[:12]
|
||||
return f"Authenticated. credential.fingerprint=sha256:{fingerprint}"
|
||||
```
|
||||
|
||||
## Passo 3 — Substituir as Configs Padrão de Agente e Task
|
||||
|
||||
O crew tem um agente e uma tarefa — ambos com descrições que **nunca** mencionam o valor do segredo, para que as chaves de tarefa permaneçam estáveis entre rotações.
|
||||
|
||||
```yaml src/rotation_verifier/config/agents.yaml
|
||||
credential_checker:
|
||||
role: >
|
||||
Credential Verifier
|
||||
goal: >
|
||||
Confirm that the workload-identity-backed secret reached this worker
|
||||
process and report a fingerprint of the current value.
|
||||
backstory: >
|
||||
You are a no-nonsense reliability engineer responsible for verifying
|
||||
that secrets fetched at runtime via workload identity are present
|
||||
and fresh. You always use the credential_echo tool exactly once and
|
||||
report the result verbatim — you never make up values.
|
||||
```
|
||||
|
||||
```yaml src/rotation_verifier/config/tasks.yaml
|
||||
verify_credential_task:
|
||||
description: >
|
||||
Use the credential_echo tool to read the runtime-injected credential
|
||||
and produce a one-line confirmation. The current year is {current_year}
|
||||
(use it only in the timestamp; do not transform the credential output).
|
||||
expected_output: >
|
||||
A single line in the form:
|
||||
"[{current_year}] <credential_echo tool's exact output>"
|
||||
agent: credential_checker
|
||||
```
|
||||
|
||||
## Passo 4 — Conectar a Classe do Crew
|
||||
|
||||
```python src/rotation_verifier/crew.py
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from crewai.project import CrewBase, agent, crew, task
|
||||
from crewai.agents.agent_builder.base_agent import BaseAgent
|
||||
|
||||
from rotation_verifier.tools.credential_echo_tool import CredentialEchoTool
|
||||
|
||||
|
||||
@CrewBase
|
||||
class RotationVerifierCrew():
|
||||
"""Single-task crew that verifies a workload-identity-backed secret
|
||||
was successfully fetched at runtime.
|
||||
|
||||
Rotate the underlying secret in the cloud provider, kickoff again, and
|
||||
the credential fingerprint in the agent's report changes — without any
|
||||
re-deploy, worker restart, or input change. The crew prompt itself
|
||||
never references the secret value.
|
||||
"""
|
||||
|
||||
agents: list[BaseAgent]
|
||||
tasks: list[Task]
|
||||
|
||||
@agent
|
||||
def credential_checker(self) -> Agent:
|
||||
return Agent(
|
||||
config=self.agents_config["credential_checker"],
|
||||
tools=[CredentialEchoTool()],
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
@task
|
||||
def verify_credential_task(self) -> Task:
|
||||
return Task(config=self.tasks_config["verify_credential_task"])
|
||||
|
||||
@crew
|
||||
def crew(self) -> Crew:
|
||||
return Crew(
|
||||
agents=self.agents,
|
||||
tasks=self.tasks,
|
||||
process=Process.sequential,
|
||||
verbose=True,
|
||||
)
|
||||
```
|
||||
|
||||
## Passo 5 — Fazer Deploy e Configurar a Env Var do Segredo
|
||||
|
||||
Faça deploy deste crew na CrewAI Platform exatamente como você faria com qualquer outro crew. Em seguida, na página **Environment Variables** do deployment:
|
||||
|
||||
- **Key:** `API_KEY` (deve corresponder a `ENV_VAR_NAME` na ferramenta)
|
||||
- **Value Source:** a credencial baseada em WI que você configurou em [AWS WI](/pt-BR/enterprise/features/secrets-manager/aws-workload-identity) ou [GCP WI](/pt-BR/enterprise/features/secrets-manager/gcp-workload-identity)
|
||||
- **Secret Name:** o nome do segredo no Secret Manager do seu provedor de nuvem
|
||||
|
||||
{/* SCREENSHOT: Environment Variables form with key=API_KEY, secret-backed value source selected, secret name filled → /images/secrets-manager/verify-rotation/01-env-var-form.png */}
|
||||
|
||||
## Passo 6 — Executar o Primeiro Kickoff
|
||||
|
||||
Substitua `<DEPLOYMENT_AUTH_TOKEN>` e `<DEPLOYMENT_HOST>` por valores da aba **Run** do seu deployment.
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
Quando o kickoff for concluído (alguns segundos), verifique a saída do agente. Você verá:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:004421b993c9
|
||||
```
|
||||
|
||||
Anote o fingerprint. Esse hash está unicamente vinculado a qualquer valor de segredo que esteja atualmente no seu provedor de nuvem.
|
||||
|
||||
## Passo 7 — Rotacionar o Segredo no Seu Provedor de Nuvem
|
||||
|
||||
<Tabs>
|
||||
<Tab title="AWS">
|
||||
```bash
|
||||
aws secretsmanager update-secret \
|
||||
--region <REGION> \
|
||||
--secret-id <SECRET_NAME> \
|
||||
--secret-string "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="GCP">
|
||||
Adicione uma nova versão (o Secret Manager sempre lê `latest`):
|
||||
|
||||
```bash
|
||||
echo -n "rotated value" | gcloud secrets versions add <SECRET_NAME> \
|
||||
--data-file=- \
|
||||
--project=<YOUR_PROJECT_ID>
|
||||
```
|
||||
</Tab>
|
||||
|
||||
<Tab title="Azure">
|
||||
```bash
|
||||
az keyvault secret set \
|
||||
--vault-name <VAULT_NAME> \
|
||||
--name <SECRET_NAME> \
|
||||
--value "rotated value"
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Passo 8 — Executar um Segundo Kickoff e Comparar
|
||||
|
||||
```bash
|
||||
curl -m 60 \
|
||||
-H "Authorization: Bearer <DEPLOYMENT_AUTH_TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST https://<DEPLOYMENT_HOST>/kickoff \
|
||||
-d '{"inputs":{"current_year":"2026"}}'
|
||||
```
|
||||
|
||||
A saída do agente agora mostra um **fingerprint diferente**:
|
||||
|
||||
```
|
||||
[2026] Authenticated. credential.fingerprint=sha256:e2fc89848f72
|
||||
```
|
||||
|
||||
Isso prova que a rotação foi pega pelo deployment em execução sem novo deploy, reinício de worker ou outra ação do operador.
|
||||
|
||||
## O que Isso Verifica — e o que Não Verifica
|
||||
|
||||
**Verifica:**
|
||||
- A emissão de tokens OIDC do WI a partir da CrewAI Platform funciona.
|
||||
- A confiança do lado da nuvem (provedor IAM OIDC para AWS, Workload Identity Pool para GCP, Federated Identity Credential para Azure) aceita o token.
|
||||
- A identidade do lado da nuvem (IAM Role / GCP service account / Entra App Registration) tem acesso para ler o segredo.
|
||||
- O valor do segredo chega ao `os.environ` do processo worker no momento do kickoff.
|
||||
- Rotações subsequentes se propagam para o próximo kickoff.
|
||||
|
||||
**Não verifica:**
|
||||
- Que seus crews de produção reais lidam com a rotação graciosamente — ex., tarefas de longa duração que leem a env var uma vez na inicialização continuarão usando o valor antigo até que a tarefa termine. Planeje conforme: leia segredos no ponto de uso, não no momento do import do módulo.
|
||||
|
||||
## Por que Não Referenciar o Segredo Diretamente no Prompt?
|
||||
|
||||
Uma demonstração de aparência mais simples colocaria o valor do segredo diretamente em uma descrição de tarefa (ex.: "Research about `{api_key}`") e inspecionaria o prompt. **Não faça isso.** Duas razões:
|
||||
|
||||
1. **Isso vaza o segredo para traces de chamadas LLM e logs do lado do provedor.** Qualquer um com acesso a traces pode lê-lo.
|
||||
2. **Isso muda a descrição da tarefa a cada kickoff.** A CrewAI Platform identifica tarefas por um hash MD5 da descrição; um valor rotativo significa que o hash muda por kickoff, o que quebra o mapeamento deploy-time → runtime das tarefas. Sintoma: os registros de tarefa aparecem como `pending_run` indefinidamente, ou apenas algumas das tarefas de um crew multi-task se registram.
|
||||
|
||||
O padrão baseado em ferramenta neste guia contorna ambas as questões: o prompt é estático, a ferramenta lê a env var em runtime e apenas um fingerprint do valor chega ao LLM.
|
||||
|
||||
## Próximos Passos
|
||||
|
||||
- [Voltar à visão geral do Secrets Manager](/pt-BR/enterprise/features/secrets-manager/overview)
|
||||
- Uma vez verificado, descarte o crew de verificação. Crews reais devem seguir o mesmo padrão: segredos acessados via `os.environ` dentro de uma ferramenta, nunca substituídos em prompts.
|
||||
@@ -146,7 +146,6 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Responder Perguntas">
|
||||
Responda às perguntas de esclarecimento do Crew Assistant para refinar seus
|
||||
requisitos.
|
||||
@@ -161,12 +160,10 @@ Veja um fluxo de trabalho típico para criação de um crew com o Crew Studio:
|
||||
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Aprovar ou Modificar">
|
||||
Aprove o plano ou solicite alterações, se necessário.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Baixar ou Fazer Deploy">
|
||||
Baixe o código para personalização ou faça o deploy diretamente na plataforma.
|
||||
</Step>
|
||||
|
||||
@@ -266,7 +266,165 @@ Nosso flow irá:
|
||||
Vamos criar nosso flow no arquivo `main.py`:
|
||||
|
||||
```python
|
||||
# [CÓDIGO NÃO TRADUZIDO, MANTER COMO ESTÁ]
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import os
|
||||
from typing import List, Dict
|
||||
from pydantic import BaseModel, Field
|
||||
from crewai import LLM
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from guide_creator_flow.crews.content_crew.content_crew import ContentCrew
|
||||
|
||||
# Definir nossos modelos para dados estruturados
|
||||
class Section(BaseModel):
|
||||
title: str = Field(description="Title of the section")
|
||||
description: str = Field(description="Brief description of what the section should cover")
|
||||
|
||||
class GuideOutline(BaseModel):
|
||||
title: str = Field(description="Title of the guide")
|
||||
introduction: str = Field(description="Introduction to the topic")
|
||||
target_audience: str = Field(description="Description of the target audience")
|
||||
sections: List[Section] = Field(description="List of sections in the guide")
|
||||
conclusion: str = Field(description="Conclusion or summary of the guide")
|
||||
|
||||
# Definir o estado do nosso flow
|
||||
class GuideCreatorState(BaseModel):
|
||||
topic: str = ""
|
||||
audience_level: str = ""
|
||||
guide_outline: GuideOutline = None
|
||||
sections_content: Dict[str, str] = {}
|
||||
|
||||
class GuideCreatorFlow(Flow[GuideCreatorState]):
|
||||
"""Flow para criar um guia abrangente sobre qualquer tópico"""
|
||||
|
||||
@start()
|
||||
def get_user_input(self):
|
||||
"""Obter entrada do usuário sobre o tópico e público do guia"""
|
||||
print("\n=== Create Your Comprehensive Guide ===\n")
|
||||
|
||||
# Obter entrada do usuário
|
||||
self.state.topic = input("What topic would you like to create a guide for? ")
|
||||
|
||||
# Obter nível do público com validação
|
||||
while True:
|
||||
audience = input("Who is your target audience? (beginner/intermediate/advanced) ").lower()
|
||||
if audience in ["beginner", "intermediate", "advanced"]:
|
||||
self.state.audience_level = audience
|
||||
break
|
||||
print("Please enter 'beginner', 'intermediate', or 'advanced'")
|
||||
|
||||
print(f"\nCreating a guide on {self.state.topic} for {self.state.audience_level} audience...\n")
|
||||
return self.state
|
||||
|
||||
@listen(get_user_input)
|
||||
def create_guide_outline(self, state):
|
||||
"""Criar um esboço estruturado para o guia usando uma chamada direta ao LLM"""
|
||||
print("Creating guide outline...")
|
||||
|
||||
# Inicializar o LLM
|
||||
llm = LLM(model="openai/gpt-4o-mini", response_format=GuideOutline)
|
||||
|
||||
# Criar as mensagens para o esboço
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a helpful assistant designed to output JSON."},
|
||||
{"role": "user", "content": f"""
|
||||
Create a detailed outline for a comprehensive guide on "{state.topic}" for {state.audience_level} level learners.
|
||||
|
||||
The outline should include:
|
||||
1. A compelling title for the guide
|
||||
2. An introduction to the topic
|
||||
3. 4-6 main sections that cover the most important aspects of the topic
|
||||
4. A conclusion or summary
|
||||
|
||||
For each section, provide a clear title and a brief description of what it should cover.
|
||||
"""}
|
||||
]
|
||||
|
||||
# Fazer a chamada ao LLM com formato de resposta JSON
|
||||
response = llm.call(messages=messages)
|
||||
|
||||
# Analisar a resposta JSON
|
||||
outline_dict = json.loads(response)
|
||||
self.state.guide_outline = GuideOutline(**outline_dict)
|
||||
|
||||
# Garantir que o diretório de saída exista antes de salvar
|
||||
os.makedirs("output", exist_ok=True)
|
||||
|
||||
# Salvar o esboço em um arquivo
|
||||
with open("output/guide_outline.json", "w") as f:
|
||||
json.dump(outline_dict, f, indent=2)
|
||||
|
||||
print(f"Guide outline created with {len(self.state.guide_outline.sections)} sections")
|
||||
return self.state.guide_outline
|
||||
|
||||
@listen(create_guide_outline)
|
||||
def write_and_compile_guide(self, outline):
|
||||
"""Escrever todas as seções e compilar o guia"""
|
||||
print("Writing guide sections and compiling...")
|
||||
completed_sections = []
|
||||
|
||||
# Processar seções uma por uma para manter o fluxo de contexto
|
||||
for section in outline.sections:
|
||||
print(f"Processing section: {section.title}")
|
||||
|
||||
# Construir contexto a partir das seções anteriores
|
||||
previous_sections_text = ""
|
||||
if completed_sections:
|
||||
previous_sections_text = "# Previously Written Sections\n\n"
|
||||
for title in completed_sections:
|
||||
previous_sections_text += f"## {title}\n\n"
|
||||
previous_sections_text += self.state.sections_content.get(title, "") + "\n\n"
|
||||
else:
|
||||
previous_sections_text = "No previous sections written yet."
|
||||
|
||||
# Executar a crew de conteúdo para esta seção
|
||||
result = ContentCrew().crew().kickoff(inputs={
|
||||
"section_title": section.title,
|
||||
"section_description": section.description,
|
||||
"audience_level": self.state.audience_level,
|
||||
"previous_sections": previous_sections_text,
|
||||
"draft_content": ""
|
||||
})
|
||||
|
||||
# Armazenar o conteúdo
|
||||
self.state.sections_content[section.title] = result.raw
|
||||
completed_sections.append(section.title)
|
||||
print(f"Section completed: {section.title}")
|
||||
|
||||
# Compilar o guia final
|
||||
guide_content = f"# {outline.title}\n\n"
|
||||
guide_content += f"## Introduction\n\n{outline.introduction}\n\n"
|
||||
|
||||
# Adicionar cada seção em ordem
|
||||
for section in outline.sections:
|
||||
section_content = self.state.sections_content.get(section.title, "")
|
||||
guide_content += f"\n\n{section_content}\n\n"
|
||||
|
||||
# Adicionar conclusão
|
||||
guide_content += f"## Conclusion\n\n{outline.conclusion}\n\n"
|
||||
|
||||
# Salvar o guia
|
||||
with open("output/complete_guide.md", "w") as f:
|
||||
f.write(guide_content)
|
||||
|
||||
print("\nComplete guide compiled and saved to output/complete_guide.md")
|
||||
return "Guide creation completed successfully"
|
||||
|
||||
def kickoff():
|
||||
"""Executar o flow criador de guias"""
|
||||
GuideCreatorFlow().kickoff()
|
||||
print("\n=== Flow Complete ===")
|
||||
print("Your comprehensive guide is ready in the output directory.")
|
||||
print("Open output/complete_guide.md to view it.")
|
||||
|
||||
def plot():
|
||||
"""Gerar uma visualização do flow"""
|
||||
flow = GuideCreatorFlow()
|
||||
flow.plot("guide_creator_flow")
|
||||
print("Flow visualization saved to guide_creator_flow.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
kickoff()
|
||||
```
|
||||
|
||||
Vamos analisar o que está acontecendo neste flow:
|
||||
|
||||
142
docs/pt-BR/guides/flows/inputs-id-deprecation.mdx
Normal file
142
docs/pt-BR/guides/flows/inputs-id-deprecation.mdx
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: "Migrando de inputs.id para restore_from_state_id"
|
||||
description: "Mover fluxos @persist da hidratação obsoleta inputs.id para o campo suportado restore_from_state_id"
|
||||
icon: "arrow-right-arrow-left"
|
||||
---
|
||||
|
||||
<Warning>
|
||||
Passar `id` dentro de `inputs` para hidratar um fluxo `@persist` é **obsoleto** e
|
||||
programado para remoção em uma versão futura. A substituição, `restore_from_state_id`,
|
||||
está disponível no CrewAI **v1.14.5 e posterior** — os passos abaixo se aplicam uma vez que você
|
||||
faça a atualização.
|
||||
</Warning>
|
||||
|
||||
## Visão Geral
|
||||
|
||||
A maneira documentada de hidratar um fluxo `@persist` de uma execução anterior é passar
|
||||
o UUID dessa execução como `inputs.id`. O CrewAI agora expõe um campo dedicado,
|
||||
`restore_from_state_id`, que realiza a mesma hidratação sem sobrecarregar a
|
||||
carga útil de `inputs` — e sem acoplar a chave de hidratação à identidade da nova execução.
|
||||
|
||||
## Migração
|
||||
|
||||
Se você atualmente inicia um fluxo `@persist` com `inputs={"id": ...}`:
|
||||
|
||||
```python
|
||||
# Obsoleto
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(inputs={"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv"})
|
||||
```
|
||||
|
||||
Mude para `restore_from_state_id`:
|
||||
|
||||
```python
|
||||
# Suportado
|
||||
flow = CounterFlow()
|
||||
flow.kickoff(restore_from_state_id="abcd1234-5678-90ef-ghij-klmnopqrstuv")
|
||||
```
|
||||
|
||||
Os dois modos têm semânticas de linhagem diferentes:
|
||||
|
||||
- `inputs={"id": <uuid>}` (obsoleto) — **retomar**: as gravações são feitas sob o id fornecido,
|
||||
estendendo a mesma história de `flow_uuid`.
|
||||
- `restore_from_state_id=<uuid>` — **dividir**: hidrata o estado a partir de um snapshot, então
|
||||
grava sob um novo `state.id`. A história do fluxo de origem é preservada.
|
||||
|
||||
Para a maioria dos cenários de produção — reexecutar um fluxo hidratado de um estado anterior — criar um fork
|
||||
é o que você deseja. Veja [Dominando o Estado do Fluxo](/pt-BR/guides/flows/mastering-flow-state)
|
||||
para o modelo mental completo.
|
||||
|
||||
Se você iniciar seu fluxo pela API REST do CrewAI AMP, veja [AMP](#amp) abaixo para a
|
||||
migração equivalente da carga útil.
|
||||
|
||||
## Por que estamos descontinuando `inputs.id` para `@persist`?
|
||||
|
||||
`inputs.id` é atualmente a maneira documentada de retomar um fluxo `@persist` de uma
|
||||
execução anterior. O problema é que o mesmo UUID faz duas funções ao mesmo tempo:
|
||||
|
||||
1. **Seleciona qual snapshot o `@persist` usa para hidratar** — carrega o estado salvo
|
||||
sob aquele UUID.
|
||||
2. **Torna-se o ID de Execução do Fluxo da nova execução** (`state.id` no SDK;
|
||||
apresentado como `flow_id` em alguns contextos) — cada gravação `@persist` a partir desta
|
||||
inicialização também cai sob aquele mesmo UUID.
|
||||
|
||||
Esse papel duplo é a causa raiz dos problemas que este guia descreve. Como o
|
||||
UUID fornecido também é o id da nova execução, duas inicializações que passam o mesmo
|
||||
`inputs.id` não são duas execuções distintas — elas compartilham um id, compartilham um registro
|
||||
de persistência e (no AMP) compartilham uma linha na lista de execuções. Não há como dizer
|
||||
"hidratar a partir deste snapshot, mas registrar esta execução separadamente" sem dividir as
|
||||
duas responsabilidades.
|
||||
|
||||
`restore_from_state_id` é essa divisão. Ele informa ao `@persist` de qual snapshot hidratar,
|
||||
enquanto deixa a nova execução livre para receber um novo `state.id`. A
|
||||
fonte de hidratação e a execução registrada não são mais o mesmo UUID — que é o que
|
||||
a maioria dos cenários de produção realmente deseja.
|
||||
|
||||
## Cronograma de remoção
|
||||
|
||||
`inputs.id` para hidratação `@persist` está programado para remoção em uma versão futura do
|
||||
CrewAI. Não há um corte imediato — fluxos existentes continuam a funcionar — mas
|
||||
uma vez que você atualize para v1.14.5 ou posterior, novo código deve usar `restore_from_state_id`, e
|
||||
fluxos existentes devem migrar na próxima oportunidade conveniente.
|
||||
|
||||
## AMP
|
||||
|
||||
Se você implantar seu fluxo no CrewAI AMP, a migração se estende à carga útil de inicialização
|
||||
enviada para sua Crew implantada, e os sintomas visíveis de reutilização de `inputs.id` aparecem
|
||||
no painel de controle de implantação. As duas subseções abaixo cobrem ambos.
|
||||
|
||||
### Migrando a carga útil de inicialização
|
||||
|
||||
Se você atualmente inicia um fluxo implantado incorporando `id` em `inputs`:
|
||||
|
||||
```bash
|
||||
# Obsoleto
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{"inputs": {"id": "abcd1234-5678-90ef-ghij-klmnopqrstuv", "topic": "AI Agent Frameworks"}}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
Mova o UUID para o campo `restoreFromStateId` de nível superior:
|
||||
|
||||
```bash
|
||||
# Suportado
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_CREW_TOKEN" \
|
||||
-d '{
|
||||
"inputs": {"topic": "AI Agent Frameworks"},
|
||||
"restoreFromStateId": "abcd1234-5678-90ef-ghij-klmnopqrstuv"
|
||||
}' \
|
||||
https://your-crew-url.crewai.com/kickoff
|
||||
```
|
||||
|
||||
`restoreFromStateId` fica ao lado de `inputs` na carga útil de inicialização, não dentro dela. O
|
||||
objeto `inputs` agora carrega apenas valores que seu fluxo realmente consome.
|
||||
|
||||
### O que acontece quando `inputs.id` é reutilizado
|
||||
|
||||
Quando o AMP recebe um kickoff para um fluxo cujo `inputs.id` corresponde a uma execução
|
||||
existente, ele resolve para o registro existente em vez de criar um novo. A partir
|
||||
do painel de controle de implantação, você verá:
|
||||
|
||||
- **Status da execução** — o status da nova execução sobrescreve o status da execução anterior. Uma
|
||||
execução finalizada pode voltar para `running`, ou uma execução `completed` pode mudar para
|
||||
`error` se a nova inicialização falhar — de qualquer forma, o painel não reflete mais
|
||||
a execução original.
|
||||
- **Rastros** — Os OTel traces se acumulam entre as inicializações porque compartilham o mesmo
|
||||
id de execução; os traces da execução anterior são substituídos ou misturados
|
||||
com os da nova execução. Uma reprodução passo a passo não corresponde mais a uma única execução.
|
||||
- **Lista de execuções** — kickoffs que deveriam aparecer como linhas separadas colapsam em
|
||||
uma única entrada, ocultando o histórico.
|
||||
|
||||
Migrar para `restoreFromStateId` mantém cada kickoff como sua própria execução — com
|
||||
seu próprio status, traces e entrada na lista — enquanto ainda hidrata o estado de uma
|
||||
execução anterior.
|
||||
|
||||
<Card title="Precisa de Ajuda?" icon="headset" href="mailto:support@crewai.com">
|
||||
Entre em contato com nossa equipe de suporte se você não tiver certeza de qual modo seu fluxo precisa ou se encontrar problemas
|
||||
durante a migração.
|
||||
</Card>
|
||||
@@ -63,7 +63,60 @@ Com estado não estruturado:
|
||||
Veja um exemplo simples de gerenciamento de estado não estruturado:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class UnstructuredStateFlow(Flow):
|
||||
@start()
|
||||
def initialize_data(self):
|
||||
print("Initializing flow data")
|
||||
# Adiciona pares chave-valor ao estado
|
||||
self.state["user_name"] = "Alex"
|
||||
self.state["preferences"] = {
|
||||
"theme": "dark",
|
||||
"language": "English"
|
||||
}
|
||||
self.state["items"] = []
|
||||
|
||||
# O estado do flow recebe automaticamente um ID único
|
||||
print(f"Flow ID: {self.state['id']}")
|
||||
|
||||
return "Initialized"
|
||||
|
||||
@listen(initialize_data)
|
||||
def process_data(self, previous_result):
|
||||
print(f"Previous step returned: {previous_result}")
|
||||
|
||||
# Acessa e modifica o estado
|
||||
user = self.state["user_name"]
|
||||
print(f"Processing data for {user}")
|
||||
|
||||
# Adiciona itens a uma lista no estado
|
||||
self.state["items"].append("item1")
|
||||
self.state["items"].append("item2")
|
||||
|
||||
# Adiciona um novo par chave-valor
|
||||
self.state["processed"] = True
|
||||
|
||||
return "Processed"
|
||||
|
||||
@listen(process_data)
|
||||
def generate_summary(self, previous_result):
|
||||
# Acessa múltiplos valores do estado
|
||||
user = self.state["user_name"]
|
||||
theme = self.state["preferences"]["theme"]
|
||||
items = self.state["items"]
|
||||
processed = self.state.get("processed", False)
|
||||
|
||||
summary = f"User {user} has {len(items)} items with {theme} theme. "
|
||||
summary += "Data is processed." if processed else "Data is not processed."
|
||||
|
||||
return summary
|
||||
|
||||
# Executa o flow
|
||||
flow = UnstructuredStateFlow()
|
||||
result = flow.kickoff()
|
||||
print(f"Final result: {result}")
|
||||
print(f"Final state: {flow.state}")
|
||||
```
|
||||
|
||||
### Quando Usar Estado Não Estruturado
|
||||
@@ -94,7 +147,63 @@ Ao utilizar estado estruturado:
|
||||
Veja como implementar o gerenciamento de estado estruturado:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
# Define o modelo de estado
|
||||
class UserPreferences(BaseModel):
|
||||
theme: str = "light"
|
||||
language: str = "English"
|
||||
|
||||
class AppState(BaseModel):
|
||||
user_name: str = ""
|
||||
preferences: UserPreferences = UserPreferences()
|
||||
items: List[str] = []
|
||||
processed: bool = False
|
||||
completion_percentage: float = 0.0
|
||||
|
||||
# Cria um flow com estado tipado
|
||||
class StructuredStateFlow(Flow[AppState]):
|
||||
@start()
|
||||
def initialize_data(self):
|
||||
print("Initializing flow data")
|
||||
# Define valores do estado (com checagem de tipo)
|
||||
self.state.user_name = "Taylor"
|
||||
self.state.preferences.theme = "dark"
|
||||
|
||||
# O campo ID está disponível automaticamente
|
||||
print(f"Flow ID: {self.state.id}")
|
||||
|
||||
return "Initialized"
|
||||
|
||||
@listen(initialize_data)
|
||||
def process_data(self, previous_result):
|
||||
print(f"Processing data for {self.state.user_name}")
|
||||
|
||||
# Modifica o estado (com checagem de tipo)
|
||||
self.state.items.append("item1")
|
||||
self.state.items.append("item2")
|
||||
self.state.processed = True
|
||||
self.state.completion_percentage = 50.0
|
||||
|
||||
return "Processed"
|
||||
|
||||
@listen(process_data)
|
||||
def generate_summary(self, previous_result):
|
||||
# Acessa o estado (com autocompletar)
|
||||
summary = f"User {self.state.user_name} has {len(self.state.items)} items "
|
||||
summary += f"with {self.state.preferences.theme} theme. "
|
||||
summary += "Data is processed." if self.state.processed else "Data is not processed."
|
||||
summary += f" Completion: {self.state.completion_percentage}%"
|
||||
|
||||
return summary
|
||||
|
||||
# Executa o flow
|
||||
flow = StructuredStateFlow()
|
||||
result = flow.kickoff()
|
||||
print(f"Final result: {result}")
|
||||
print(f"Final state: {flow.state}")
|
||||
```
|
||||
|
||||
### Benefícios do Estado Estruturado
|
||||
@@ -138,7 +247,29 @@ Independente de você usar estado estruturado ou não estruturado, é possível
|
||||
Métodos do flow podem retornar valores que serão passados como argumento para métodos listeners:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
class DataPassingFlow(Flow):
|
||||
@start()
|
||||
def generate_data(self):
|
||||
# Este valor de retorno será passado para os métodos listeners
|
||||
return "Generated data"
|
||||
|
||||
@listen(generate_data)
|
||||
def process_data(self, data_from_previous_step):
|
||||
print(f"Received: {data_from_previous_step}")
|
||||
# Você pode modificar os dados e repassá-los adiante
|
||||
processed_data = f"{data_from_previous_step} - processed"
|
||||
# Também atualiza o estado
|
||||
self.state["last_processed"] = processed_data
|
||||
return processed_data
|
||||
|
||||
@listen(process_data)
|
||||
def finalize_data(self, processed_data):
|
||||
print(f"Received processed data: {processed_data}")
|
||||
# Acessa tanto os dados passados quanto o estado
|
||||
last_processed = self.state.get("last_processed", "")
|
||||
return f"Final: {processed_data} (from state: {last_processed})"
|
||||
```
|
||||
|
||||
Esse padrão permite combinar passagem de dados direta com atualizações de estado para obter máxima flexibilidade.
|
||||
@@ -156,7 +287,36 @@ O decorador `@persist()` automatiza a persistência de estado, salvando o estado
|
||||
Ao aplicar em nível de classe, `@persist()` salva o estado após cada execução de método:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from crewai.flow.persistence import persist
|
||||
from pydantic import BaseModel
|
||||
|
||||
class CounterState(BaseModel):
|
||||
value: int = 0
|
||||
|
||||
@persist() # Aplica à classe inteira do flow
|
||||
class PersistentCounterFlow(Flow[CounterState]):
|
||||
@start()
|
||||
def increment(self):
|
||||
self.state.value += 1
|
||||
print(f"Incremented to {self.state.value}")
|
||||
return self.state.value
|
||||
|
||||
@listen(increment)
|
||||
def double(self, value):
|
||||
self.state.value = value * 2
|
||||
print(f"Doubled to {self.state.value}")
|
||||
return self.state.value
|
||||
|
||||
# Primeira execução
|
||||
flow1 = PersistentCounterFlow()
|
||||
result1 = flow1.kickoff()
|
||||
print(f"First run result: {result1}")
|
||||
|
||||
# Segunda execução - passa o ID para carregar o estado persistido
|
||||
flow2 = PersistentCounterFlow()
|
||||
result2 = flow2.kickoff(inputs={"id": flow1.state.id})
|
||||
print(f"Second run result: {result2}") # Será maior devido ao estado persistido
|
||||
```
|
||||
|
||||
#### Persistência em Nível de Método
|
||||
@@ -164,7 +324,26 @@ Ao aplicar em nível de classe, `@persist()` salva o estado após cada execuçã
|
||||
Para mais controle, você pode aplicar `@persist()` em métodos específicos:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from crewai.flow.persistence import persist
|
||||
|
||||
class SelectivePersistFlow(Flow):
|
||||
@start()
|
||||
def first_step(self):
|
||||
self.state["count"] = 1
|
||||
return "First step"
|
||||
|
||||
@persist() # Persiste apenas após este método
|
||||
@listen(first_step)
|
||||
def important_step(self, prev_result):
|
||||
self.state["count"] += 1
|
||||
self.state["important_data"] = "This will be persisted"
|
||||
return "Important step completed"
|
||||
|
||||
@listen(important_step)
|
||||
def final_step(self, prev_result):
|
||||
self.state["count"] += 1
|
||||
return f"Complete with count {self.state['count']}"
|
||||
```
|
||||
|
||||
#### Forking de Estado Persistido
|
||||
@@ -216,7 +395,45 @@ Notas sobre o comportamento:
|
||||
Você pode usar o estado para implementar lógicas condicionais complexas em seus flows:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, router, start
|
||||
from pydantic import BaseModel
|
||||
|
||||
class PaymentState(BaseModel):
|
||||
amount: float = 0.0
|
||||
is_approved: bool = False
|
||||
retry_count: int = 0
|
||||
|
||||
class PaymentFlow(Flow[PaymentState]):
|
||||
@start()
|
||||
def process_payment(self):
|
||||
# Simula o processamento do pagamento
|
||||
self.state.amount = 100.0
|
||||
self.state.is_approved = self.state.amount < 1000
|
||||
return "Payment processed"
|
||||
|
||||
@router(process_payment)
|
||||
def check_approval(self, previous_result):
|
||||
if self.state.is_approved:
|
||||
return "approved"
|
||||
elif self.state.retry_count < 3:
|
||||
return "retry"
|
||||
else:
|
||||
return "rejected"
|
||||
|
||||
@listen("approved")
|
||||
def handle_approval(self):
|
||||
return f"Payment of ${self.state.amount} approved!"
|
||||
|
||||
@listen("retry")
|
||||
def handle_retry(self):
|
||||
self.state.retry_count += 1
|
||||
print(f"Retrying payment (attempt {self.state.retry_count})...")
|
||||
# Aqui poderia ser implementada a lógica de retry
|
||||
return "Retry initiated"
|
||||
|
||||
@listen("rejected")
|
||||
def handle_rejection(self):
|
||||
return f"Payment of ${self.state.amount} rejected after {self.state.retry_count} retries."
|
||||
```
|
||||
|
||||
### Manipulações Complexas de Estado
|
||||
@@ -224,7 +441,60 @@ Você pode usar o estado para implementar lógicas condicionais complexas em seu
|
||||
Para transformar estados complexos, você pode criar métodos dedicados:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Dict
|
||||
|
||||
class UserData(BaseModel):
|
||||
name: str
|
||||
active: bool = True
|
||||
login_count: int = 0
|
||||
|
||||
class ComplexState(BaseModel):
|
||||
users: Dict[str, UserData] = {}
|
||||
active_user_count: int = 0
|
||||
|
||||
class TransformationFlow(Flow[ComplexState]):
|
||||
@start()
|
||||
def initialize(self):
|
||||
# Adiciona alguns usuários
|
||||
self.add_user("alice", "Alice")
|
||||
self.add_user("bob", "Bob")
|
||||
self.add_user("charlie", "Charlie")
|
||||
return "Initialized"
|
||||
|
||||
@listen(initialize)
|
||||
def process_users(self, _):
|
||||
# Incrementa contagens de login
|
||||
for user_id in self.state.users:
|
||||
self.increment_login(user_id)
|
||||
|
||||
# Desativa um usuário
|
||||
self.deactivate_user("bob")
|
||||
|
||||
# Atualiza a contagem de ativos
|
||||
self.update_active_count()
|
||||
|
||||
return f"Processed {len(self.state.users)} users"
|
||||
|
||||
# Métodos auxiliares para transformações de estado
|
||||
def add_user(self, user_id: str, name: str):
|
||||
self.state.users[user_id] = UserData(name=name)
|
||||
self.update_active_count()
|
||||
|
||||
def increment_login(self, user_id: str):
|
||||
if user_id in self.state.users:
|
||||
self.state.users[user_id].login_count += 1
|
||||
|
||||
def deactivate_user(self, user_id: str):
|
||||
if user_id in self.state.users:
|
||||
self.state.users[user_id].active = False
|
||||
self.update_active_count()
|
||||
|
||||
def update_active_count(self):
|
||||
self.state.active_user_count = sum(
|
||||
1 for user in self.state.users.values() if user.active
|
||||
)
|
||||
```
|
||||
|
||||
Esse padrão de criar métodos auxiliares mantém seus métodos de flow limpos, enquanto permite manipulações complexas de estado.
|
||||
@@ -238,7 +508,71 @@ Um dos padrões mais poderosos na CrewAI é combinar o gerenciamento de estado d
|
||||
Você pode usar o estado do flow para parametrizar crews:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
from crewai import Agent, Crew, Process, Task
|
||||
from pydantic import BaseModel
|
||||
|
||||
class ResearchState(BaseModel):
|
||||
topic: str = ""
|
||||
depth: str = "medium"
|
||||
results: str = ""
|
||||
|
||||
class ResearchFlow(Flow[ResearchState]):
|
||||
@start()
|
||||
def get_parameters(self):
|
||||
# Em uma aplicação real, isso pode vir da entrada do usuário
|
||||
self.state.topic = "Artificial Intelligence Ethics"
|
||||
self.state.depth = "deep"
|
||||
return "Parameters set"
|
||||
|
||||
@listen(get_parameters)
|
||||
def execute_research(self, _):
|
||||
# Cria os agentes
|
||||
researcher = Agent(
|
||||
role="Research Specialist",
|
||||
goal=f"Research {self.state.topic} in {self.state.depth} detail",
|
||||
backstory="You are an expert researcher with a talent for finding accurate information."
|
||||
)
|
||||
|
||||
writer = Agent(
|
||||
role="Content Writer",
|
||||
goal="Transform research into clear, engaging content",
|
||||
backstory="You excel at communicating complex ideas clearly and concisely."
|
||||
)
|
||||
|
||||
# Cria as tarefas
|
||||
research_task = Task(
|
||||
description=f"Research {self.state.topic} with {self.state.depth} analysis",
|
||||
expected_output="Comprehensive research notes in markdown format",
|
||||
agent=researcher
|
||||
)
|
||||
|
||||
writing_task = Task(
|
||||
description=f"Create a summary on {self.state.topic} based on the research",
|
||||
expected_output="Well-written article in markdown format",
|
||||
agent=writer,
|
||||
context=[research_task]
|
||||
)
|
||||
|
||||
# Cria e executa a crew
|
||||
research_crew = Crew(
|
||||
agents=[researcher, writer],
|
||||
tasks=[research_task, writing_task],
|
||||
process=Process.sequential,
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Executa a crew e armazena o resultado no estado
|
||||
result = research_crew.kickoff()
|
||||
self.state.results = result.raw
|
||||
|
||||
return "Research completed"
|
||||
|
||||
@listen(execute_research)
|
||||
def summarize_results(self, _):
|
||||
# Acessa os resultados armazenados
|
||||
result_length = len(self.state.results)
|
||||
return f"Research on {self.state.topic} completed with {result_length} characters of results."
|
||||
```
|
||||
|
||||
### Manipulando Saídas de Crews no Estado
|
||||
@@ -246,7 +580,21 @@ Você pode usar o estado do flow para parametrizar crews:
|
||||
Quando um crew finaliza, é possível processar sua saída e armazená-la no estado do flow:
|
||||
|
||||
```python
|
||||
# código não traduzido
|
||||
@listen(execute_crew)
|
||||
def process_crew_results(self, _):
|
||||
# Faz parsing dos resultados brutos (assumindo saída em JSON)
|
||||
import json
|
||||
try:
|
||||
results_dict = json.loads(self.state.raw_results)
|
||||
self.state.processed_results = {
|
||||
"title": results_dict.get("title", ""),
|
||||
"main_points": results_dict.get("main_points", []),
|
||||
"conclusion": results_dict.get("conclusion", "")
|
||||
}
|
||||
return "Results processed successfully"
|
||||
except json.JSONDecodeError:
|
||||
self.state.error = "Failed to parse crew results as JSON"
|
||||
return "Error processing results"
|
||||
```
|
||||
|
||||
## Boas Práticas para Gerenciamento de Estado
|
||||
@@ -256,7 +604,19 @@ Quando um crew finaliza, é possível processar sua saída e armazená-la no est
|
||||
Projete seu estado para conter somente o necessário:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
# Abrangente demais
|
||||
class BloatedState(BaseModel):
|
||||
user_data: Dict = {}
|
||||
system_settings: Dict = {}
|
||||
temporary_calculations: List = []
|
||||
debug_info: Dict = {}
|
||||
# ...muitos outros campos
|
||||
|
||||
# Melhor: estado focado
|
||||
class FocusedState(BaseModel):
|
||||
user_id: str
|
||||
preferences: Dict[str, str]
|
||||
completion_status: Dict[str, bool]
|
||||
```
|
||||
|
||||
### 2. Use Estado Estruturado em Flows Complexos
|
||||
@@ -264,7 +624,23 @@ Projete seu estado para conter somente o necessário:
|
||||
À medida que seus flows evoluem em complexidade, o estado estruturado se torna cada vez mais valioso:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
# Flow simples pode usar estado não estruturado
|
||||
class SimpleGreetingFlow(Flow):
|
||||
@start()
|
||||
def greet(self):
|
||||
self.state["name"] = "World"
|
||||
return f"Hello, {self.state['name']}!"
|
||||
|
||||
# Flow complexo se beneficia de estado estruturado
|
||||
class UserRegistrationState(BaseModel):
|
||||
username: str
|
||||
email: str
|
||||
verification_status: bool = False
|
||||
registration_date: datetime = Field(default_factory=datetime.now)
|
||||
last_login: Optional[datetime] = None
|
||||
|
||||
class RegistrationFlow(Flow[UserRegistrationState]):
|
||||
# Métodos com acesso ao estado fortemente tipado
|
||||
```
|
||||
|
||||
### 3. Documente Transições de Estado
|
||||
@@ -272,7 +648,18 @@ Projete seu estado para conter somente o necessário:
|
||||
Para flows complexos, documente como o estado muda ao longo da execução:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
@start()
|
||||
def initialize_order(self):
|
||||
"""
|
||||
Initialize order state with empty values.
|
||||
|
||||
State before: {}
|
||||
State after: {order_id: str, items: [], status: 'new'}
|
||||
"""
|
||||
self.state.order_id = str(uuid.uuid4())
|
||||
self.state.items = []
|
||||
self.state.status = "new"
|
||||
return "Order initialized"
|
||||
```
|
||||
|
||||
### 4. Trate Erros de Estado de Forma Elegante
|
||||
@@ -280,7 +667,18 @@ Para flows complexos, documente como o estado muda ao longo da execução:
|
||||
Implemente tratamento de erros ao acessar o estado:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
@listen(previous_step)
|
||||
def process_data(self, _):
|
||||
try:
|
||||
# Tenta acessar um valor que pode não existir
|
||||
user_preference = self.state.preferences.get("theme", "default")
|
||||
except (AttributeError, KeyError):
|
||||
# Trata o erro de forma elegante
|
||||
self.state.errors = self.state.get("errors", [])
|
||||
self.state.errors.append("Failed to access preferences")
|
||||
user_preference = "default"
|
||||
|
||||
return f"Used preference: {user_preference}"
|
||||
```
|
||||
|
||||
### 5. Use o Estado Para Acompanhar o Progresso
|
||||
@@ -288,7 +686,30 @@ Implemente tratamento de erros ao acessar o estado:
|
||||
Aproveite o estado para monitorar o progresso em flows de longa duração:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
class ProgressTrackingFlow(Flow):
|
||||
@start()
|
||||
def initialize(self):
|
||||
self.state["total_steps"] = 3
|
||||
self.state["current_step"] = 0
|
||||
self.state["progress"] = 0.0
|
||||
self.update_progress()
|
||||
return "Initialized"
|
||||
|
||||
def update_progress(self):
|
||||
"""Helper method to calculate and update progress"""
|
||||
if self.state.get("total_steps", 0) > 0:
|
||||
self.state["progress"] = (self.state.get("current_step", 0) /
|
||||
self.state["total_steps"]) * 100
|
||||
print(f"Progress: {self.state['progress']:.1f}%")
|
||||
|
||||
@listen(initialize)
|
||||
def step_one(self, _):
|
||||
# Realiza o trabalho...
|
||||
self.state["current_step"] = 1
|
||||
self.update_progress()
|
||||
return "Step 1 complete"
|
||||
|
||||
# Etapas adicionais...
|
||||
```
|
||||
|
||||
### 6. Prefira Operações Imutáveis Quando Possível
|
||||
@@ -296,7 +717,22 @@ Aproveite o estado para monitorar o progresso em flows de longa duração:
|
||||
Especialmente com estado estruturado, prefira operações imutáveis para maior clareza:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
# Em vez de modificar listas no local:
|
||||
self.state.items.append(new_item) # Operação mutável
|
||||
|
||||
# Considere criar um novo estado:
|
||||
from pydantic import BaseModel
|
||||
from typing import List
|
||||
|
||||
class ItemState(BaseModel):
|
||||
items: List[str] = []
|
||||
|
||||
class ImmutableFlow(Flow[ItemState]):
|
||||
@start()
|
||||
def add_item(self):
|
||||
# Cria uma nova lista com o item adicionado
|
||||
self.state.items = [*self.state.items, "new item"]
|
||||
return "Item added"
|
||||
```
|
||||
|
||||
## Depurando o Estado do Flow
|
||||
@@ -306,7 +742,24 @@ Especialmente com estado estruturado, prefira operações imutáveis para maior
|
||||
Ao desenvolver, adicione logs para acompanhar mudanças no estado:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class LoggingFlow(Flow):
|
||||
def log_state(self, step_name):
|
||||
logging.info(f"State after {step_name}: {self.state}")
|
||||
|
||||
@start()
|
||||
def initialize(self):
|
||||
self.state["counter"] = 0
|
||||
self.log_state("initialize")
|
||||
return "Initialized"
|
||||
|
||||
@listen(initialize)
|
||||
def increment(self, _):
|
||||
self.state["counter"] += 1
|
||||
self.log_state("increment")
|
||||
return f"Incremented to {self.state['counter']}"
|
||||
```
|
||||
|
||||
### Visualizando o Estado
|
||||
@@ -314,7 +767,30 @@ Ao desenvolver, adicione logs para acompanhar mudanças no estado:
|
||||
Você pode adicionar métodos para visualizar seu estado durante o debug:
|
||||
|
||||
```python
|
||||
# Exemplo não traduzido
|
||||
def visualize_state(self):
|
||||
"""Create a simple visualization of the current state"""
|
||||
import json
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
|
||||
console = Console()
|
||||
|
||||
if hasattr(self.state, "model_dump"):
|
||||
# Pydantic v2
|
||||
state_dict = self.state.model_dump()
|
||||
elif hasattr(self.state, "dict"):
|
||||
# Pydantic v1
|
||||
state_dict = self.state.dict()
|
||||
else:
|
||||
# Estado não estruturado
|
||||
state_dict = dict(self.state)
|
||||
|
||||
# Remove o id para uma saída mais limpa
|
||||
if "id" in state_dict:
|
||||
state_dict.pop("id")
|
||||
|
||||
state_json = json.dumps(state_dict, indent=2, default=str)
|
||||
console.print(Panel(state_json, title="Current Flow State"))
|
||||
```
|
||||
|
||||
## Conclusão
|
||||
|
||||
190
docs/pt-BR/guides/migration/upgrading-crewai.mdx
Normal file
190
docs/pt-BR/guides/migration/upgrading-crewai.mdx
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
title: "Atualizando o CrewAI"
|
||||
description: "Como atualizar o CrewAI no seu projeto e adaptar-se a breaking changes entre versões."
|
||||
icon: "arrow-up-circle"
|
||||
---
|
||||
|
||||
## Visão Geral
|
||||
|
||||
Os lançamentos do CrewAI trazem novos recursos regularmente. Este guia mostra os passos práticos para manter sua instalação atualizada — tanto a CLI quanto o ambiente virtual do seu projeto.
|
||||
|
||||
Se você está começando do zero, veja [Instalação](/pt-BR/installation). Se está vindo de outro framework, veja [Migrando do LangGraph](/pt-BR/guides/migration/migrating-from-langgraph).
|
||||
|
||||
---
|
||||
|
||||
## As Duas Coisas Que Você Pode Querer Atualizar
|
||||
|
||||
O CrewAI vive em dois lugares na sua máquina, e cada um se atualiza de forma independente:
|
||||
|
||||
| O quê | Como é instalado | Como atualizar |
|
||||
|---|---|---|
|
||||
| A **CLI global `crewai`** | `uv tool install crewai` | `uv tool install crewai --upgrade` |
|
||||
| O **venv do projeto** (onde seu código roda) | `crewai install` / `uv sync` | `uv add "crewai[...]>=X.Y.Z"` e depois `crewai install` |
|
||||
|
||||
Esses dois podem — e frequentemente ficam — fora de sincronia. Rodar `crewai --version` mostra a versão da CLI. Rodar `uv pip show crewai` dentro do seu projeto mostra a versão do venv. Se forem diferentes, isso é normal; o que importa para o código em execução é a versão do venv.
|
||||
|
||||
## Por Que `crewai install` Sozinho Não Atualiza
|
||||
|
||||
`crewai install` é um wrapper fino em torno de `uv sync`. Ele instala exatamente o que o arquivo `uv.lock` atual diz — ele **não** muda nenhuma restrição de versão.
|
||||
|
||||
Se seu `pyproject.toml` diz `crewai>=1.11.1` e o lock file resolveu para `1.11.1`, executar `crewai install` vai te manter em `1.11.1` para sempre, mesmo que `1.14.4` esteja disponível.
|
||||
|
||||
Para realmente atualizar, você precisa:
|
||||
|
||||
1. Atualizar a restrição de versão em `pyproject.toml`
|
||||
2. Re-resolver o lock file
|
||||
3. Sincronizar o venv
|
||||
|
||||
`uv add` faz os três de uma vez só.
|
||||
|
||||
## Como Atualizar Seu Projeto
|
||||
|
||||
```bash
|
||||
# Aumenta a restrição e re-resolve o lock em um único comando
|
||||
uv add "crewai[tools]>=1.14.4"
|
||||
|
||||
# Sincroniza o venv (crewai install chama uv sync por baixo dos panos)
|
||||
crewai install
|
||||
|
||||
# Verifica
|
||||
uv pip show crewai
|
||||
# → Version: 1.14.4
|
||||
```
|
||||
|
||||
Substitua `[tools]` por quaisquer extras que seu projeto utilize (ex.: `[tools,anthropic]`). Verifique a lista de `dependencies` do seu `pyproject.toml` se estiver em dúvida.
|
||||
|
||||
<Note>
|
||||
`uv add` atualiza tanto `pyproject.toml` **quanto** `uv.lock` atomicamente. Se você editar `pyproject.toml` manualmente, ainda precisa rodar `uv lock --upgrade-package crewai` para re-resolver o lock file antes que `crewai install` pegue a nova versão.
|
||||
</Note>
|
||||
|
||||
## Atualizando a CLI Global
|
||||
|
||||
A CLI global é separada do seu projeto. Atualize com:
|
||||
|
||||
```bash
|
||||
uv tool install crewai --upgrade
|
||||
```
|
||||
|
||||
Se seu shell avisar sobre o `PATH` após a atualização, recarregue-o:
|
||||
|
||||
```bash
|
||||
uv tool update-shell
|
||||
```
|
||||
|
||||
Isso **não** mexe no venv do seu projeto — você ainda precisa de `uv add` + `crewai install` dentro do projeto.
|
||||
|
||||
## Verifique Se Ambos Estão em Sincronia
|
||||
|
||||
```bash
|
||||
# Versão da CLI global
|
||||
crewai --version
|
||||
|
||||
# Versão do venv do projeto
|
||||
uv pip show crewai | grep Version
|
||||
```
|
||||
|
||||
Eles não precisam coincidir — mas a versão do venv do projeto é o que importa para o comportamento em runtime.
|
||||
|
||||
<Note>
|
||||
CrewAI requer `Python >=3.10, <3.14`. Se o `uv` foi instalado contra um interpretador mais antigo, recrie o venv do projeto com uma versão suportada do Python antes de rodar `crewai install`.
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes e Notas de Migração
|
||||
|
||||
A maioria das atualizações requer apenas pequenos ajustes. As áreas abaixo são as que quebram silenciosamente ou com tracebacks confusos.
|
||||
|
||||
### Caminhos de import: tools e `BaseTool`
|
||||
|
||||
O caminho canônico para tools é `crewai.tools`. Caminhos antigos ainda aparecem em tutoriais, mas devem ser atualizados.
|
||||
|
||||
```python
|
||||
# Antes
|
||||
from crewai_tools import BaseTool
|
||||
from crewai.agents.tools import tool
|
||||
|
||||
# Depois
|
||||
from crewai.tools import BaseTool, tool
|
||||
```
|
||||
|
||||
O decorador `@tool` e a subclasse `BaseTool` ambos vivem em `crewai.tools`. `AgentFinish` e outros símbolos internos do agente não fazem mais parte da superfície pública — se você os estava importando, mude para event listeners ou callbacks de `Task`.
|
||||
|
||||
### Mudanças de parâmetros em `Agent`
|
||||
|
||||
```python
|
||||
from crewai import Agent
|
||||
|
||||
agent = Agent(
|
||||
role="Researcher",
|
||||
goal="Find authoritative sources on {topic}",
|
||||
backstory="You are a careful, source-driven researcher.",
|
||||
llm="gpt-4o-mini", # nome do modelo como string OU um objeto LLM
|
||||
verbose=True, # bool, não um nível inteiro
|
||||
max_iter=15, # default mudou entre versões — defina explicitamente
|
||||
allow_delegation=False,
|
||||
)
|
||||
```
|
||||
|
||||
- `llm` aceita tanto um nome de modelo como string (resolvido pelo provedor configurado) quanto um objeto `LLM` para controle granular.
|
||||
- `verbose` é um `bool` puro. Passar um inteiro não alterna mais níveis de log.
|
||||
- Os defaults de `max_iter` mudaram entre releases. Se seu agente para silenciosamente de iterar após a primeira chamada de tool, defina `max_iter` explicitamente.
|
||||
|
||||
### Parâmetros de `Crew`
|
||||
|
||||
```python
|
||||
from crewai import Crew, Process
|
||||
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
process=Process.sequential, # ou Process.hierarchical
|
||||
memory=True,
|
||||
cache=True,
|
||||
embedder={"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
)
|
||||
```
|
||||
|
||||
- `process=Process.hierarchical` requer ou `manager_llm=` ou `manager_agent=`. Sem um deles, o kickoff lança erro na validação.
|
||||
- `memory=True` com um provedor de embedding não-default precisa de um dicionário `embedder` — veja [Configuração de memória e embedder](#memory-embedder-config) abaixo.
|
||||
|
||||
### Saída estruturada de `Task`
|
||||
|
||||
Use `output_pydantic`, `output_json` ou `output_file` para forçar o resultado de uma task em um formato tipado:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from crewai import Task
|
||||
|
||||
class Article(BaseModel):
|
||||
title: str
|
||||
body: str
|
||||
|
||||
write = Task(
|
||||
description="Write an article about {topic}",
|
||||
expected_output="A short article with a title and body",
|
||||
agent=writer,
|
||||
output_pydantic=Article, # a classe, NÃO uma instância
|
||||
output_file="output/article.md",
|
||||
)
|
||||
```
|
||||
|
||||
`output_pydantic` recebe a **classe** em si. Passar `Article(title="", body="")` é um erro comum e falha com um erro de validação confuso.
|
||||
|
||||
### Configuração de memória e embedder {#memory-embedder-config}
|
||||
|
||||
Se `memory=True` e você não está usando os embeddings padrão da OpenAI, é preciso passar um `embedder`:
|
||||
|
||||
```python
|
||||
crew = Crew(
|
||||
agents=[...],
|
||||
tasks=[...],
|
||||
memory=True,
|
||||
embedder={
|
||||
"provider": "ollama",
|
||||
"config": {"model": "nomic-embed-text"},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
Defina as credenciais do provedor relevante (`OPENAI_API_KEY`, `OLLAMA_HOST`, etc.) no seu arquivo `.env`. Os caminhos de armazenamento de memória são locais ao projeto por default — apague o diretório de memória do projeto se trocar de embedder, já que dimensões diferentes não se misturam.
|
||||
@@ -797,7 +797,6 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
|
||||
Inicie com opções consagradas como **GPT-4.1**, **Claude 3.7 Sonnet** ou **Gemini 2.0 Flash**, que oferecem bom desempenho e ampla validação.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Identifique Demandas Especializadas">
|
||||
Descubra se sua crew possui requisitos específicos (código, raciocínio,
|
||||
velocidade) que justifiquem modelos como **Claude 4 Sonnet** para
|
||||
@@ -805,7 +804,6 @@ As tabelas abaixo mostram uma amostra dos modelos de maior destaque em cada cate
|
||||
velocidade, considere Groq aliado à seleção do modelo.
|
||||
</Step>
|
||||
|
||||
{" "}
|
||||
<Step title="Implemente Estratégia Multi-Modelo">
|
||||
Use modelos diferentes para agentes distintos conforme o papel. Modelos de
|
||||
alta capacidade para managers e tarefas complexas, eficientes para rotinas.
|
||||
|
||||
@@ -13,7 +13,7 @@ The Daytona sandbox tools give CrewAI agents access to isolated, ephemeral compu
|
||||
|
||||
- **`DaytonaExecTool`** — run any shell command inside a sandbox.
|
||||
- **`DaytonaPythonTool`** — execute a block of Python source code inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox.
|
||||
- **`DaytonaFileTool`** — read, write, append, list, delete, and inspect files inside a sandbox; also supports `move`, `find` (content grep), `search` (filename glob), `chmod` (permissions), `replace` (bulk find-and-replace), and `exists`.
|
||||
|
||||
All three tools share the same sandbox lifecycle controls, so you can mix and match them while keeping state in a single persistent sandbox.
|
||||
|
||||
@@ -55,7 +55,7 @@ from crewai_tools import DaytonaPythonTool
|
||||
tool = DaytonaPythonTool()
|
||||
result = tool.run(code="print(sum(range(10)))")
|
||||
print(result)
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": None}
|
||||
# {"exit_code": 0, "result": "45\n", "artifacts": ExecutionArtifacts(stdout="45\n", charts=[])}
|
||||
```
|
||||
|
||||
### Multi-step shell session (persistent)
|
||||
@@ -63,17 +63,22 @@ print(result)
|
||||
```python Code
|
||||
from crewai_tools import DaytonaExecTool, DaytonaFileTool
|
||||
|
||||
# Create the persistent sandbox via the first tool, then attach the second
|
||||
# tool to it so both share state (installed packages, files, env vars).
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Install a package, then write and run a script — all in the same sandbox
|
||||
exec_tool.run(command="pip install httpx -q")
|
||||
file_tool.run(action="write", path="/workspace/fetch.py", content="import httpx; print(httpx.get('https://httpbin.org/get').status_code)")
|
||||
exec_tool.run(command="python /workspace/fetch.py")
|
||||
file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
|
||||
file_tool.run(
|
||||
action="write",
|
||||
path="workspace/script.py",
|
||||
content="import httpx; print(f'httpx loaded, version {httpx.__version__}')",
|
||||
)
|
||||
exec_tool.run(command="python workspace/script.py")
|
||||
```
|
||||
|
||||
<Note>
|
||||
Each tool instance maintains its own persistent sandbox. To share **one** sandbox across two tools, create the first tool, grab its sandbox id via `tool._persistent_sandbox.id`, and pass it to the second tool via `sandbox_id=...`.
|
||||
By default, each tool with `persistent=True` lazily creates its **own** sandbox on first use. The pattern above shares a single sandbox across multiple tools by reading the first tool's `active_sandbox_id` after a `.run()` call and passing it to the others via `sandbox_id=...`. With `persistent=False` (the default), every `.run()` call gets a fresh sandbox that's deleted at the end of that call.
|
||||
</Note>
|
||||
|
||||
### Attach to an existing sandbox
|
||||
@@ -82,7 +87,7 @@ Each tool instance maintains its own persistent sandbox. To share **one** sandbo
|
||||
from crewai_tools import DaytonaExecTool
|
||||
|
||||
tool = DaytonaExecTool(sandbox_id="my-long-lived-sandbox")
|
||||
result = tool.run(command="ls /workspace")
|
||||
result = tool.run(command="ls workspace")
|
||||
```
|
||||
|
||||
### Custom sandbox parameters
|
||||
@@ -102,6 +107,41 @@ tool = DaytonaExecTool(
|
||||
)
|
||||
```
|
||||
|
||||
### Searching, moving, and modifying files
|
||||
|
||||
```python Code
|
||||
from crewai_tools import DaytonaFileTool
|
||||
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Find every TODO in the source tree (grep file contents recursively)
|
||||
file_tool.run(action="find", path="workspace/src", pattern="TODO:")
|
||||
|
||||
# Find all Python files (glob match on filenames)
|
||||
file_tool.run(action="search", path="workspace", pattern="*.py")
|
||||
|
||||
# Make a script executable
|
||||
file_tool.run(action="chmod", path="workspace/run.sh", mode="755")
|
||||
|
||||
# Rename or move a file
|
||||
file_tool.run(
|
||||
action="move",
|
||||
path="workspace/draft.md",
|
||||
destination="workspace/final.md",
|
||||
)
|
||||
|
||||
# Bulk find-and-replace across multiple files
|
||||
file_tool.run(
|
||||
action="replace",
|
||||
paths=["workspace/src/a.py", "workspace/src/b.py"],
|
||||
pattern="old_function",
|
||||
replacement="new_function",
|
||||
)
|
||||
|
||||
# Quick existence check before a destructive op
|
||||
file_tool.run(action="exists", path="workspace/cache.db")
|
||||
```
|
||||
|
||||
### Agent integration
|
||||
|
||||
```python Code
|
||||
@@ -121,7 +161,7 @@ coder = Agent(
|
||||
)
|
||||
|
||||
task = Task(
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to /workspace/fib.py, and run it.",
|
||||
description="Write a Python script that prints the first 10 Fibonacci numbers, save it to workspace/fib.py, and run it.",
|
||||
expected_output="The first 10 Fibonacci numbers printed to stdout.",
|
||||
agent=coder,
|
||||
)
|
||||
@@ -168,12 +208,22 @@ All three tools accept these parameters at initialization:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`. |
|
||||
| `path` | `str` | ✓ | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | | Content to write or append. Required for `append`. |
|
||||
| `action` | `str` | ✓ | One of: `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`, `exists`, `move`, `find`, `search`, `chmod`, `replace`. |
|
||||
| `path` | `str \| None` | ✓ for all actions except `replace` | Absolute path inside the sandbox. |
|
||||
| `content` | `str \| None` | ✓ for `append` | Content to write or append. |
|
||||
| `binary` | `bool` | | If `True`, `content` is base64 on write; returns base64 on read. |
|
||||
| `recursive` | `bool` | | For `delete`: remove directories recursively. |
|
||||
| `mode` | `str` | | For `mkdir`: octal permission string (default `"0755"`). |
|
||||
| `mode` | `str \| None` | | For `mkdir`: octal permissions for the new directory (defaults to `"0755"`). For `chmod`: octal permissions to apply to the target. |
|
||||
| `destination` | `str \| None` | ✓ for `move` | Destination path for `move`. |
|
||||
| `pattern` | `str \| None` | ✓ for `find`, `search`, `replace` | For `find`: substring matched against file CONTENTS. For `search`: glob matched against file NAMES (e.g. `*.py`). For `replace`: text to replace inside files. |
|
||||
| `replacement` | `str \| None` | ✓ for `replace` | Replacement text for `pattern`. |
|
||||
| `paths` | `list[str] \| None` | ✓ for `replace` | List of file paths in which to replace text. |
|
||||
| `owner` | `str \| None` | | For `chmod`: new file owner. |
|
||||
| `group` | `str \| None` | | For `chmod`: new file group. |
|
||||
|
||||
<Note>
|
||||
For `chmod`, pass at least one of `mode`, `owner`, or `group` — any field left as `None` is left unchanged on the target.
|
||||
</Note>
|
||||
|
||||
<Tip>
|
||||
For files larger than a few KB, create the file first with `action="write"` and empty content, then send the body via multiple `action="append"` calls of ~4 KB each to stay within tool-call payload limits.
|
||||
|
||||
@@ -8,7 +8,7 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"crewai-core==1.14.5a4",
|
||||
"crewai-core==1.14.5",
|
||||
"click~=8.1.7",
|
||||
"pydantic>=2.11.9,<2.13",
|
||||
"pydantic-settings~=2.10.1",
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.14.5a4"
|
||||
__version__ = "1.14.5"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from typing import Any
|
||||
|
||||
from crewai_core.plus_api import CreateCrewPayload
|
||||
from rich.console import Console
|
||||
|
||||
from crewai_cli import git
|
||||
@@ -161,7 +162,7 @@ class DeployCommand(BaseCommand, PlusAPIMixin):
|
||||
self,
|
||||
env_vars: dict[str, str],
|
||||
remote_repo_url: str,
|
||||
) -> dict[str, Any]:
|
||||
) -> CreateCrewPayload:
|
||||
"""
|
||||
Create the payload for crew creation.
|
||||
|
||||
@@ -172,6 +173,8 @@ class DeployCommand(BaseCommand, PlusAPIMixin):
|
||||
Returns:
|
||||
Dict[str, Any]: The payload for crew creation.
|
||||
"""
|
||||
if not self.project_name:
|
||||
raise ValueError("project_name is required to create a deployment payload")
|
||||
return {
|
||||
"deploy": {
|
||||
"name": self.project_name,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from functools import lru_cache
|
||||
from functools import cached_property
|
||||
import subprocess
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ class Repository:
|
||||
if not self.is_git_installed():
|
||||
raise ValueError("Git is not installed or not found in your PATH.")
|
||||
|
||||
if not self.is_git_repo():
|
||||
if not self.is_git_repo:
|
||||
raise ValueError(f"{self.path} is not a Git repository.")
|
||||
|
||||
self.fetch()
|
||||
@@ -40,13 +40,9 @@ class Repository:
|
||||
encoding="utf-8",
|
||||
).strip()
|
||||
|
||||
@lru_cache(maxsize=None) # noqa: B019
|
||||
@cached_property # noqa: B019
|
||||
def is_git_repo(self) -> bool:
|
||||
"""Check if the current directory is a git repository.
|
||||
|
||||
Notes:
|
||||
- TODO: This method is cached to avoid redundant checks, but using lru_cache on methods can lead to memory leaks
|
||||
"""
|
||||
"""Check if the current directory is a git repository."""
|
||||
try:
|
||||
subprocess.check_output(
|
||||
["git", "rev-parse", "--is-inside-work-tree"], # noqa: S607
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "1.14.5a4"
|
||||
__version__ = "1.14.5"
|
||||
|
||||
@@ -3,36 +3,161 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any
|
||||
from typing import Any, Final, Literal, TypedDict, cast
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import httpx
|
||||
from typing_extensions import NotRequired
|
||||
|
||||
from crewai_core.constants import DEFAULT_CREWAI_ENTERPRISE_URL
|
||||
from crewai_core.settings import Settings
|
||||
from crewai_core.version import get_crewai_version
|
||||
|
||||
|
||||
HttpMethod = Literal["GET", "POST", "PATCH", "DELETE"]
|
||||
|
||||
|
||||
class AvailableExport(TypedDict):
|
||||
name: str
|
||||
|
||||
|
||||
class EnvVarEntry(TypedDict):
|
||||
name: str
|
||||
description: str
|
||||
required: bool
|
||||
default: str | None
|
||||
|
||||
|
||||
class ToolMetadata(TypedDict):
|
||||
name: str
|
||||
module: str
|
||||
humanized_name: str
|
||||
description: str
|
||||
run_params_schema: dict[str, Any]
|
||||
init_params_schema: dict[str, Any]
|
||||
env_vars: list[EnvVarEntry]
|
||||
|
||||
|
||||
class ToolsMetadataPayload(TypedDict):
|
||||
package: str
|
||||
tools: list[ToolMetadata] | None
|
||||
|
||||
|
||||
class PublishToolPayload(TypedDict):
|
||||
handle: str
|
||||
public: bool
|
||||
version: str
|
||||
file: str
|
||||
description: str | None
|
||||
available_exports: list[AvailableExport] | None
|
||||
tools_metadata: ToolsMetadataPayload | None
|
||||
|
||||
|
||||
class CrewDeploymentSpec(TypedDict):
|
||||
name: str
|
||||
repo_clone_url: str
|
||||
env: dict[str, str]
|
||||
|
||||
|
||||
class CreateCrewPayload(TypedDict):
|
||||
deploy: CrewDeploymentSpec
|
||||
|
||||
|
||||
class _WithUserIdentifier(TypedDict):
|
||||
user_identifier: NotRequired[str]
|
||||
|
||||
|
||||
class LoginPayload(_WithUserIdentifier):
|
||||
pass
|
||||
|
||||
|
||||
class TraceExecutionContext(TypedDict):
|
||||
crew_fingerprint: str | None
|
||||
crew_name: str | None
|
||||
flow_name: str | None
|
||||
crewai_version: str
|
||||
privacy_level: str
|
||||
|
||||
|
||||
class TraceExecutionMetadata(TypedDict):
|
||||
expected_duration_estimate: int
|
||||
agent_count: int
|
||||
task_count: int
|
||||
flow_method_count: int
|
||||
execution_started_at: str
|
||||
|
||||
|
||||
class TraceBatchInitPayload(_WithUserIdentifier):
|
||||
trace_id: str
|
||||
execution_type: str
|
||||
execution_context: TraceExecutionContext
|
||||
execution_metadata: TraceExecutionMetadata
|
||||
ephemeral_trace_id: NotRequired[str]
|
||||
|
||||
|
||||
class TraceBatchMetadata(TypedDict):
|
||||
events_count: int
|
||||
batch_sequence: int
|
||||
is_final_batch: bool
|
||||
|
||||
|
||||
class TraceEventsPayload(TypedDict):
|
||||
events: list[dict[str, Any]]
|
||||
batch_metadata: TraceBatchMetadata
|
||||
|
||||
|
||||
class TraceFinalizePayload(TypedDict):
|
||||
status: Literal["completed"]
|
||||
duration_ms: float | None
|
||||
final_event_count: int
|
||||
|
||||
|
||||
class TraceFailedPayload(TypedDict):
|
||||
status: Literal["failed"]
|
||||
failure_reason: str
|
||||
|
||||
|
||||
Headers = TypedDict(
|
||||
"Headers",
|
||||
{
|
||||
"Content-Type": str,
|
||||
"User-Agent": str,
|
||||
"X-Crewai-Version": str,
|
||||
"Authorization": NotRequired[str],
|
||||
"X-Crewai-Organization-Id": NotRequired[str],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class RequestKwargs(TypedDict):
|
||||
headers: dict[str, str]
|
||||
json: NotRequired[Any]
|
||||
params: NotRequired[dict[str, str]]
|
||||
timeout: NotRequired[float]
|
||||
|
||||
|
||||
class PlusAPI:
|
||||
"""Client for working with the CrewAI+ API."""
|
||||
|
||||
TOOLS_RESOURCE = "/crewai_plus/api/v1/tools"
|
||||
ORGANIZATIONS_RESOURCE = "/crewai_plus/api/v1/me/organizations"
|
||||
CREWS_RESOURCE = "/crewai_plus/api/v1/crews"
|
||||
AGENTS_RESOURCE = "/crewai_plus/api/v1/agents"
|
||||
TRACING_RESOURCE = "/crewai_plus/api/v1/tracing"
|
||||
EPHEMERAL_TRACING_RESOURCE = "/crewai_plus/api/v1/tracing/ephemeral"
|
||||
INTEGRATIONS_RESOURCE = "/crewai_plus/api/v1/integrations"
|
||||
TOOLS_RESOURCE: Final = "/crewai_plus/api/v1/tools"
|
||||
ORGANIZATIONS_RESOURCE: Final = "/crewai_plus/api/v1/me/organizations"
|
||||
CREWS_RESOURCE: Final = "/crewai_plus/api/v1/crews"
|
||||
AGENTS_RESOURCE: Final = "/crewai_plus/api/v1/agents"
|
||||
TRACING_RESOURCE: Final = "/crewai_plus/api/v1/tracing"
|
||||
EPHEMERAL_TRACING_RESOURCE: Final = "/crewai_plus/api/v1/tracing/ephemeral"
|
||||
INTEGRATIONS_RESOURCE: Final = "/crewai_plus/api/v1/integrations"
|
||||
|
||||
def __init__(self, api_key: str | None = None) -> None:
|
||||
version = get_crewai_version()
|
||||
self.api_key = api_key
|
||||
self.headers = {
|
||||
self.headers: Headers = {
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": f"CrewAI-CLI/{get_crewai_version()}",
|
||||
"X-Crewai-Version": get_crewai_version(),
|
||||
"User-Agent": f"CrewAI-CLI/{version}",
|
||||
"X-Crewai-Version": version,
|
||||
}
|
||||
if api_key:
|
||||
self.headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
settings = Settings()
|
||||
if settings.org_uuid:
|
||||
self.headers["X-Crewai-Organization-Id"] = settings.org_uuid
|
||||
@@ -44,17 +169,30 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def _make_request(
|
||||
self, method: str, endpoint: str, **kwargs: Any
|
||||
self,
|
||||
method: HttpMethod,
|
||||
endpoint: str,
|
||||
*,
|
||||
json: Any = None,
|
||||
params: dict[str, str] | None = None,
|
||||
timeout: float | None = None,
|
||||
verify: bool = True,
|
||||
) -> httpx.Response:
|
||||
url = urljoin(self.base_url, endpoint)
|
||||
verify = kwargs.pop("verify", True)
|
||||
request_kwargs: RequestKwargs = {"headers": cast(dict[str, str], self.headers)}
|
||||
if json is not None:
|
||||
request_kwargs["json"] = json
|
||||
if params is not None:
|
||||
request_kwargs["params"] = params
|
||||
if timeout is not None:
|
||||
request_kwargs["timeout"] = timeout
|
||||
with httpx.Client(trust_env=False, verify=verify) as client:
|
||||
return client.request(method, url, headers=self.headers, **kwargs)
|
||||
return client.request(method, url, **request_kwargs)
|
||||
|
||||
def login_to_tool_repository(
|
||||
self, user_identifier: str | None = None
|
||||
) -> httpx.Response:
|
||||
payload = {}
|
||||
payload: LoginPayload = {}
|
||||
if user_identifier:
|
||||
payload["user_identifier"] = user_identifier
|
||||
return self._make_request("POST", f"{self.TOOLS_RESOURCE}/login", json=payload)
|
||||
@@ -65,7 +203,7 @@ class PlusAPI:
|
||||
async def get_agent(self, handle: str) -> httpx.Response:
|
||||
url = urljoin(self.base_url, f"{self.AGENTS_RESOURCE}/{handle}")
|
||||
async with httpx.AsyncClient() as client:
|
||||
return await client.get(url, headers=self.headers)
|
||||
return await client.get(url, headers=cast(dict[str, str], self.headers))
|
||||
|
||||
def publish_tool(
|
||||
self,
|
||||
@@ -74,10 +212,10 @@ class PlusAPI:
|
||||
version: str,
|
||||
description: str | None,
|
||||
encoded_file: str,
|
||||
available_exports: list[dict[str, Any]] | None = None,
|
||||
tools_metadata: list[dict[str, Any]] | None = None,
|
||||
available_exports: list[AvailableExport] | None = None,
|
||||
tools_metadata: list[ToolMetadata] | None = None,
|
||||
) -> httpx.Response:
|
||||
params = {
|
||||
params: PublishToolPayload = {
|
||||
"handle": handle,
|
||||
"public": is_public,
|
||||
"version": version,
|
||||
@@ -129,13 +267,13 @@ class PlusAPI:
|
||||
def list_crews(self) -> httpx.Response:
|
||||
return self._make_request("GET", self.CREWS_RESOURCE)
|
||||
|
||||
def create_crew(self, payload: dict[str, Any]) -> httpx.Response:
|
||||
def create_crew(self, payload: CreateCrewPayload) -> httpx.Response:
|
||||
return self._make_request("POST", self.CREWS_RESOURCE, json=payload)
|
||||
|
||||
def get_organizations(self) -> httpx.Response:
|
||||
return self._make_request("GET", self.ORGANIZATIONS_RESOURCE)
|
||||
|
||||
def initialize_trace_batch(self, payload: dict[str, Any]) -> httpx.Response:
|
||||
def initialize_trace_batch(self, payload: TraceBatchInitPayload) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"POST",
|
||||
f"{self.TRACING_RESOURCE}/batches",
|
||||
@@ -144,7 +282,7 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def initialize_ephemeral_trace_batch(
|
||||
self, payload: dict[str, Any]
|
||||
self, payload: TraceBatchInitPayload
|
||||
) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"POST",
|
||||
@@ -153,7 +291,7 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def send_trace_events(
|
||||
self, trace_batch_id: str, payload: dict[str, Any]
|
||||
self, trace_batch_id: str, payload: TraceEventsPayload
|
||||
) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"POST",
|
||||
@@ -163,7 +301,7 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def send_ephemeral_trace_events(
|
||||
self, trace_batch_id: str, payload: dict[str, Any]
|
||||
self, trace_batch_id: str, payload: TraceEventsPayload
|
||||
) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"POST",
|
||||
@@ -173,7 +311,7 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def finalize_trace_batch(
|
||||
self, trace_batch_id: str, payload: dict[str, Any]
|
||||
self, trace_batch_id: str, payload: TraceFinalizePayload
|
||||
) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"PATCH",
|
||||
@@ -183,7 +321,7 @@ class PlusAPI:
|
||||
)
|
||||
|
||||
def finalize_ephemeral_trace_batch(
|
||||
self, trace_batch_id: str, payload: dict[str, Any]
|
||||
self, trace_batch_id: str, payload: TraceFinalizePayload
|
||||
) -> httpx.Response:
|
||||
return self._make_request(
|
||||
"PATCH",
|
||||
@@ -195,20 +333,28 @@ class PlusAPI:
|
||||
def mark_trace_batch_as_failed(
|
||||
self, trace_batch_id: str, error_message: str
|
||||
) -> httpx.Response:
|
||||
payload: TraceFailedPayload = {
|
||||
"status": "failed",
|
||||
"failure_reason": error_message,
|
||||
}
|
||||
return self._make_request(
|
||||
"PATCH",
|
||||
f"{self.TRACING_RESOURCE}/batches/{trace_batch_id}",
|
||||
json={"status": "failed", "failure_reason": error_message},
|
||||
json=payload,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
def mark_ephemeral_trace_batch_as_failed(
|
||||
self, trace_batch_id: str, error_message: str
|
||||
) -> httpx.Response:
|
||||
payload: TraceFailedPayload = {
|
||||
"status": "failed",
|
||||
"failure_reason": error_message,
|
||||
}
|
||||
return self._make_request(
|
||||
"PATCH",
|
||||
f"{self.EPHEMERAL_TRACING_RESOURCE}/batches/{trace_batch_id}",
|
||||
json={"status": "failed", "failure_reason": error_message},
|
||||
json=payload,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
|
||||
@@ -152,4 +152,4 @@ __all__ = [
|
||||
"wrap_file_source",
|
||||
]
|
||||
|
||||
__version__ = "1.14.5a4"
|
||||
__version__ = "1.14.5"
|
||||
|
||||
@@ -10,7 +10,7 @@ requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"pytube~=15.0.0",
|
||||
"requests>=2.33.0,<3",
|
||||
"crewai==1.14.5a4",
|
||||
"crewai==1.14.5",
|
||||
"tiktoken>=0.8.0,<0.13",
|
||||
"beautifulsoup4~=4.13.4",
|
||||
"python-docx~=1.2.0",
|
||||
@@ -107,7 +107,7 @@ stagehand = [
|
||||
"stagehand>=0.4.1",
|
||||
]
|
||||
github = [
|
||||
"gitpython>=3.1.47,<4",
|
||||
"gitpython>=3.1.50,<4",
|
||||
"PyGithub==1.59.1",
|
||||
]
|
||||
rag = [
|
||||
|
||||
@@ -330,4 +330,4 @@ __all__ = [
|
||||
"ZapierActionTools",
|
||||
]
|
||||
|
||||
__version__ = "1.14.5a4"
|
||||
__version__ = "1.14.5"
|
||||
|
||||
@@ -55,10 +55,11 @@ from crewai_tools import DaytonaExecTool, DaytonaFileTool
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
file_tool = DaytonaFileTool(persistent=True)
|
||||
|
||||
# Agent writes a script, then runs it — both share the same sandbox instance
|
||||
# because they each keep their own persistent sandbox. If you need the *same*
|
||||
# sandbox across two tools, create one tool, grab the sandbox id via
|
||||
# `tool._persistent_sandbox.id`, and pass it to the other via `sandbox_id=...`.
|
||||
# Agent writes a script, then runs it — but each tool keeps its OWN persistent
|
||||
# sandbox. To share the *same* sandbox across two tools, create and use the
|
||||
# first tool, then read its `active_sandbox_id` and pass it to the second:
|
||||
# exec_tool.run(command="pip install httpx")
|
||||
# file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
```
|
||||
|
||||
### Attach to an existing sandbox
|
||||
@@ -99,9 +100,14 @@ tool = DaytonaExecTool(
|
||||
- `timeout: int | None` — seconds.
|
||||
|
||||
### `DaytonaFileTool`
|
||||
- `action: "read" | "write" | "list" | "delete" | "mkdir" | "info"`
|
||||
- `path: str` — absolute path inside the sandbox.
|
||||
- `content: str | None` — required for `write`.
|
||||
- `action`: one of `read`, `write`, `append`, `list`, `delete`, `mkdir`, `info`, `exists`, `move`, `find`, `search`, `chmod`, `replace`.
|
||||
- `path: str | None` — absolute path inside the sandbox. Required for all actions except `replace`.
|
||||
- `content: str | None` — required for `append`; optional for `write`.
|
||||
- `binary: bool` — if `True`, `content` is base64 on write / returned as base64 on read.
|
||||
- `recursive: bool` — for `delete`, removes directories recursively.
|
||||
- `mode: str` — for `mkdir`, octal permission string (default `"0755"`).
|
||||
- `mode: str | None` — for `mkdir` (defaults to `"0755"`) or for `chmod` (e.g. `"755"`).
|
||||
- `destination: str | None` — required for `move`.
|
||||
- `pattern: str | None` — required for `find` (content grep), `search` (filename glob), and `replace`.
|
||||
- `replacement: str | None` — required for `replace`.
|
||||
- `paths: list[str] | None` — required for `replace`; list of files to operate on.
|
||||
- `owner: str | None` / `group: str | None` — for `chmod`. Pass at least one of `mode`, `owner`, or `group`.
|
||||
|
||||
@@ -196,3 +196,27 @@ class DaytonaBaseTool(BaseTool):
|
||||
"the sandbox may need manual deletion.",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
@property
|
||||
def active_sandbox_id(self) -> str | None:
|
||||
"""The id of the sandbox this tool is currently bound to, if any.
|
||||
|
||||
Returns:
|
||||
- the explicitly attached `sandbox_id`, if set at construction;
|
||||
- the id of the lazily-created persistent sandbox, once a call has
|
||||
triggered creation;
|
||||
- None for ephemeral mode (where no sandbox lives between calls).
|
||||
|
||||
Use this to share one sandbox across multiple tool instances:
|
||||
|
||||
exec_tool = DaytonaExecTool(persistent=True)
|
||||
exec_tool.run(command="pip install httpx")
|
||||
file_tool = DaytonaFileTool(sandbox_id=exec_tool.active_sandbox_id)
|
||||
"""
|
||||
if self.sandbox_id:
|
||||
return self.sandbox_id
|
||||
with self._lock:
|
||||
sandbox = self._persistent_sandbox
|
||||
if sandbox is None:
|
||||
return None
|
||||
return getattr(sandbox, "id", None)
|
||||
|
||||
@@ -4,7 +4,9 @@ import base64
|
||||
from builtins import type as type_
|
||||
import logging
|
||||
import posixpath
|
||||
import shlex
|
||||
from typing import Any, Literal
|
||||
import uuid
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
@@ -14,22 +16,110 @@ from crewai_tools.tools.daytona_sandbox_tool.daytona_base_tool import DaytonaBas
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
FileAction = Literal["read", "write", "append", "list", "delete", "mkdir", "info"]
|
||||
FileAction = Literal[
|
||||
"read",
|
||||
"write",
|
||||
"append",
|
||||
"list",
|
||||
"delete",
|
||||
"mkdir",
|
||||
"info",
|
||||
"exists",
|
||||
"move",
|
||||
"find",
|
||||
"search",
|
||||
"chmod",
|
||||
"replace",
|
||||
]
|
||||
|
||||
|
||||
def _daytona_file_schema_extra(schema: dict[str, Any]) -> None:
|
||||
schema["allOf"] = [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"enum": [
|
||||
"read",
|
||||
"write",
|
||||
"append",
|
||||
"list",
|
||||
"delete",
|
||||
"mkdir",
|
||||
"info",
|
||||
"exists",
|
||||
"move",
|
||||
"find",
|
||||
"search",
|
||||
"chmod",
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {"required": ["path"]},
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"action": {"const": "append"}}},
|
||||
"then": {"required": ["content"]},
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"action": {"const": "move"}}},
|
||||
"then": {"required": ["destination"]},
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"action": {"enum": ["find", "search"]}}},
|
||||
"then": {"required": ["pattern"]},
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"action": {"const": "replace"}}},
|
||||
"then": {"required": ["paths", "pattern", "replacement"]},
|
||||
},
|
||||
{
|
||||
"if": {"properties": {"action": {"const": "chmod"}}},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{"required": ["mode"]},
|
||||
{"required": ["owner"]},
|
||||
{"required": ["group"]},
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
class DaytonaFileToolSchema(BaseModel):
|
||||
model_config = {"json_schema_extra": _daytona_file_schema_extra}
|
||||
|
||||
action: FileAction = Field(
|
||||
...,
|
||||
description=(
|
||||
"The filesystem action to perform: 'read' (returns file contents), "
|
||||
"'write' (create or replace a file with content), 'append' (append "
|
||||
"content to an existing file — use this for writing large files in "
|
||||
"chunks to avoid hitting tool-call size limits), 'list' (lists a "
|
||||
"directory), 'delete' (removes a file/dir), 'mkdir' (creates a "
|
||||
"directory), 'info' (returns file metadata)."
|
||||
"The filesystem action to perform: "
|
||||
"'read' (returns file contents); "
|
||||
"'write' (create or replace a file with content); "
|
||||
"'append' (append content to an existing file — use this for "
|
||||
"writing large files in chunks to avoid hitting tool-call size "
|
||||
"limits); "
|
||||
"'list' (lists a directory); "
|
||||
"'delete' (removes a file/dir); "
|
||||
"'mkdir' (creates a directory); "
|
||||
"'info' (returns file metadata); "
|
||||
"'exists' (returns whether a path exists); "
|
||||
"'move' (rename or relocate a file/dir; requires 'destination'); "
|
||||
"'find' (grep file CONTENTS recursively; requires 'pattern'); "
|
||||
"'search' (find files by NAME pattern; requires 'pattern'); "
|
||||
"'chmod' (change permissions/owner/group; pass at least one of "
|
||||
"'mode', 'owner', 'group'); "
|
||||
"'replace' (find-and-replace text across files; requires "
|
||||
"'paths', 'pattern', and 'replacement')."
|
||||
),
|
||||
)
|
||||
path: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Absolute path inside the sandbox. Required for all actions "
|
||||
"except 'replace' (which uses 'paths' instead)."
|
||||
),
|
||||
)
|
||||
path: str = Field(..., description="Absolute path inside the sandbox.")
|
||||
content: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
@@ -50,18 +140,78 @@ class DaytonaFileToolSchema(BaseModel):
|
||||
default=False,
|
||||
description="For action='delete': remove directories recursively.",
|
||||
)
|
||||
mode: str = Field(
|
||||
default="0755",
|
||||
description="For action='mkdir': octal permission string (default 0755).",
|
||||
mode: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"Octal permission string. For 'mkdir' it sets the new directory "
|
||||
"permissions (defaults to '0755' if omitted). For 'chmod' it sets "
|
||||
"the target's mode (e.g. '755' to make a script executable). "
|
||||
"Ignored for other actions."
|
||||
),
|
||||
)
|
||||
destination: str | None = Field(
|
||||
default=None,
|
||||
description="For action='move': absolute destination path.",
|
||||
)
|
||||
pattern: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"For 'find': substring matched against file CONTENTS. "
|
||||
"For 'search': glob-style pattern matched against file NAMES "
|
||||
"(e.g. '*.py'). "
|
||||
"For 'replace': text to replace inside files."
|
||||
),
|
||||
)
|
||||
replacement: str | None = Field(
|
||||
default=None,
|
||||
description="For action='replace': replacement text for 'pattern'.",
|
||||
)
|
||||
paths: list[str] | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"For action='replace': list of absolute file paths in which to "
|
||||
"replace 'pattern' with 'replacement'."
|
||||
),
|
||||
)
|
||||
owner: str | None = Field(
|
||||
default=None,
|
||||
description="For action='chmod': new file owner (user name).",
|
||||
)
|
||||
group: str | None = Field(
|
||||
default=None,
|
||||
description="For action='chmod': new file group.",
|
||||
)
|
||||
|
||||
@model_validator(mode="after")
|
||||
def _validate_action_args(self) -> DaytonaFileToolSchema:
|
||||
if self.action != "replace" and not self.path:
|
||||
raise ValueError(f"action={self.action!r} requires 'path'.")
|
||||
if self.action == "append" and self.content is None:
|
||||
raise ValueError(
|
||||
"action='append' requires 'content'. Pass the chunk to append "
|
||||
"in the 'content' field."
|
||||
)
|
||||
if self.action == "move" and not self.destination:
|
||||
raise ValueError("action='move' requires 'destination'.")
|
||||
if self.action == "find" and not self.pattern:
|
||||
raise ValueError(
|
||||
"action='find' requires 'pattern' (text to search for inside files)."
|
||||
)
|
||||
if self.action == "search" and not self.pattern:
|
||||
raise ValueError("action='search' requires 'pattern' (glob, e.g. '*.py').")
|
||||
if self.action == "chmod" and not (self.mode or self.owner or self.group):
|
||||
raise ValueError(
|
||||
"action='chmod' requires at least one of 'mode', 'owner', or 'group'."
|
||||
)
|
||||
if self.action == "replace":
|
||||
if not self.paths:
|
||||
raise ValueError(
|
||||
"action='replace' requires 'paths' (list of file paths)."
|
||||
)
|
||||
if not self.pattern:
|
||||
raise ValueError("action='replace' requires 'pattern'.")
|
||||
if self.replacement is None:
|
||||
raise ValueError("action='replace' requires 'replacement'.")
|
||||
return self
|
||||
|
||||
|
||||
@@ -75,9 +225,10 @@ class DaytonaFileTool(DaytonaBaseTool):
|
||||
|
||||
name: str = "Daytona Sandbox Files"
|
||||
description: str = (
|
||||
"Perform filesystem operations inside a Daytona sandbox: read a file, "
|
||||
"write content to a path, append content to an existing file, list a "
|
||||
"directory, delete a path, make a directory, or fetch file metadata. "
|
||||
"Perform filesystem operations inside a Daytona sandbox: read, "
|
||||
"write, append, list, delete, mkdir, info, exists, move, find "
|
||||
"(content grep), search (filename glob), chmod (permissions/owner/"
|
||||
"group), and replace (bulk find-and-replace across files). "
|
||||
"For files larger than a few KB, create the file with action='write' "
|
||||
"and empty content, then send the body via multiple 'append' calls of "
|
||||
"~4KB each to stay within tool-call payload limits."
|
||||
@@ -87,30 +238,79 @@ class DaytonaFileTool(DaytonaBaseTool):
|
||||
def _run(
|
||||
self,
|
||||
action: FileAction,
|
||||
path: str,
|
||||
path: str | None = None,
|
||||
content: str | None = None,
|
||||
binary: bool = False,
|
||||
recursive: bool = False,
|
||||
mode: str = "0755",
|
||||
mode: str | None = None,
|
||||
destination: str | None = None,
|
||||
pattern: str | None = None,
|
||||
replacement: str | None = None,
|
||||
paths: list[str] | None = None,
|
||||
owner: str | None = None,
|
||||
group: str | None = None,
|
||||
) -> Any:
|
||||
sandbox, should_delete = self._acquire_sandbox()
|
||||
try:
|
||||
if action == "read":
|
||||
if path is None:
|
||||
raise ValueError("action='read' requires 'path'")
|
||||
return self._read(sandbox, path, binary=binary)
|
||||
if action == "write":
|
||||
if path is None:
|
||||
raise ValueError("action='write' requires 'path'")
|
||||
return self._write(sandbox, path, content or "", binary=binary)
|
||||
if action == "append":
|
||||
if path is None:
|
||||
raise ValueError("action='append' requires 'path'")
|
||||
return self._append(sandbox, path, content or "", binary=binary)
|
||||
if action == "list":
|
||||
if path is None:
|
||||
raise ValueError("action='list' requires 'path'")
|
||||
return self._list(sandbox, path)
|
||||
if action == "delete":
|
||||
if path is None:
|
||||
raise ValueError("action='delete' requires 'path'")
|
||||
sandbox.fs.delete_file(path, recursive=recursive)
|
||||
return {"status": "deleted", "path": path}
|
||||
if action == "mkdir":
|
||||
sandbox.fs.create_folder(path, mode)
|
||||
return {"status": "created", "path": path, "mode": mode}
|
||||
if path is None:
|
||||
raise ValueError("action='mkdir' requires 'path'")
|
||||
mkdir_mode = mode or "0755"
|
||||
sandbox.fs.create_folder(path, mkdir_mode)
|
||||
return {"status": "created", "path": path, "mode": mkdir_mode}
|
||||
if action == "info":
|
||||
if path is None:
|
||||
raise ValueError("action='info' requires 'path'")
|
||||
return self._info(sandbox, path)
|
||||
if action == "exists":
|
||||
if path is None:
|
||||
raise ValueError("action='exists' requires 'path'")
|
||||
return self._exists(sandbox, path)
|
||||
if action == "move":
|
||||
if path is None or destination is None:
|
||||
raise ValueError("action='move' requires 'path' and 'destination'")
|
||||
sandbox.fs.move_files(path, destination)
|
||||
return {"status": "moved", "from": path, "to": destination}
|
||||
if action == "find":
|
||||
if path is None or pattern is None:
|
||||
raise ValueError("action='find' requires 'path' and 'pattern'")
|
||||
return self._find(sandbox, path, pattern)
|
||||
if action == "search":
|
||||
if path is None or pattern is None:
|
||||
raise ValueError("action='search' requires 'path' and 'pattern'")
|
||||
return self._search(sandbox, path, pattern)
|
||||
if action == "chmod":
|
||||
if path is None:
|
||||
raise ValueError("action='chmod' requires 'path'")
|
||||
return self._chmod(sandbox, path, mode=mode, owner=owner, group=group)
|
||||
if action == "replace":
|
||||
if paths is None or pattern is None or replacement is None:
|
||||
raise ValueError(
|
||||
"action='replace' requires 'paths', 'pattern', and "
|
||||
"'replacement'"
|
||||
)
|
||||
return self._replace(sandbox, paths, pattern, replacement)
|
||||
raise ValueError(f"Unknown action: {action}")
|
||||
finally:
|
||||
self._release_sandbox(sandbox, should_delete)
|
||||
@@ -146,17 +346,46 @@ class DaytonaFileTool(DaytonaBaseTool):
|
||||
) -> dict[str, Any]:
|
||||
chunk = base64.b64decode(content) if binary else content.encode("utf-8")
|
||||
self._ensure_parent_dir(sandbox, path)
|
||||
|
||||
# Server-side `cat >>` keeps this O(chunk_size) per call. The naive
|
||||
# download-concat-reupload alternative is O(N^2) in total transfer.
|
||||
# /tmp/ is on the sandbox's ephemeral filesystem, not the host.
|
||||
temp_path = f"/tmp/.crewai-append-{uuid.uuid4().hex}" # noqa: S108
|
||||
sandbox.fs.upload_file(chunk, temp_path)
|
||||
|
||||
quoted_temp = shlex.quote(temp_path)
|
||||
quoted_target = shlex.quote(path)
|
||||
response = sandbox.process.exec(
|
||||
f"cat {quoted_temp} >> {quoted_target}; "
|
||||
f"rc=$?; rm -f {quoted_temp}; exit $rc"
|
||||
)
|
||||
|
||||
exit_code = getattr(response, "exit_code", 0)
|
||||
if exit_code != 0:
|
||||
try:
|
||||
sandbox.fs.delete_file(temp_path)
|
||||
except Exception:
|
||||
logger.debug(
|
||||
"Best-effort temp-file cleanup failed after append "
|
||||
"error; the file may need manual deletion.",
|
||||
exc_info=True,
|
||||
)
|
||||
raise RuntimeError(
|
||||
f"append failed: exit_code={exit_code}, "
|
||||
f"output={getattr(response, 'result', '')!r}"
|
||||
)
|
||||
|
||||
try:
|
||||
existing: bytes = sandbox.fs.download_file(path)
|
||||
info = sandbox.fs.get_file_info(path)
|
||||
total_bytes = getattr(info, "size", None)
|
||||
except Exception:
|
||||
existing = b""
|
||||
payload = existing + chunk
|
||||
sandbox.fs.upload_file(payload, path)
|
||||
total_bytes = None
|
||||
|
||||
return {
|
||||
"status": "appended",
|
||||
"path": path,
|
||||
"appended_bytes": len(chunk),
|
||||
"total_bytes": len(payload),
|
||||
"total_bytes": total_bytes,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -190,6 +419,77 @@ class DaytonaFileTool(DaytonaBaseTool):
|
||||
def _info(self, sandbox: Any, path: str) -> dict[str, Any]:
|
||||
return self._file_info_to_dict(sandbox.fs.get_file_info(path))
|
||||
|
||||
def _exists(self, sandbox: Any, path: str) -> dict[str, Any]:
|
||||
try:
|
||||
info = sandbox.fs.get_file_info(path)
|
||||
except Exception:
|
||||
return {"path": path, "exists": False}
|
||||
return {
|
||||
"path": path,
|
||||
"exists": True,
|
||||
"is_dir": getattr(info, "is_dir", False),
|
||||
}
|
||||
|
||||
def _find(self, sandbox: Any, path: str, pattern: str) -> dict[str, Any]:
|
||||
matches = sandbox.fs.find_files(path, pattern)
|
||||
return {
|
||||
"path": path,
|
||||
"pattern": pattern,
|
||||
"matches": [
|
||||
{
|
||||
"file": getattr(m, "file", None),
|
||||
"line": getattr(m, "line", None),
|
||||
"content": getattr(m, "content", None),
|
||||
}
|
||||
for m in matches
|
||||
],
|
||||
}
|
||||
|
||||
def _search(self, sandbox: Any, path: str, pattern: str) -> dict[str, Any]:
|
||||
response = sandbox.fs.search_files(path, pattern)
|
||||
files = getattr(response, "files", None) or []
|
||||
return {"path": path, "pattern": pattern, "files": list(files)}
|
||||
|
||||
def _chmod(
|
||||
self,
|
||||
sandbox: Any,
|
||||
path: str,
|
||||
*,
|
||||
mode: str | None,
|
||||
owner: str | None,
|
||||
group: str | None,
|
||||
) -> dict[str, Any]:
|
||||
kwargs: dict[str, str] = {}
|
||||
if mode is not None:
|
||||
kwargs["mode"] = mode
|
||||
if owner is not None:
|
||||
kwargs["owner"] = owner
|
||||
if group is not None:
|
||||
kwargs["group"] = group
|
||||
sandbox.fs.set_file_permissions(path, **kwargs)
|
||||
return {"status": "permissions_set", "path": path, **kwargs}
|
||||
|
||||
def _replace(
|
||||
self,
|
||||
sandbox: Any,
|
||||
paths: list[str],
|
||||
pattern: str,
|
||||
replacement: str,
|
||||
) -> dict[str, Any]:
|
||||
results = sandbox.fs.replace_in_files(paths, pattern, replacement)
|
||||
return {
|
||||
"pattern": pattern,
|
||||
"replacement": replacement,
|
||||
"results": [
|
||||
{
|
||||
"file": getattr(r, "file", None),
|
||||
"success": getattr(r, "success", None),
|
||||
"error": getattr(r, "error", None),
|
||||
}
|
||||
for r in (results or [])
|
||||
],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _file_info_to_dict(info: Any) -> dict[str, Any]:
|
||||
fields = (
|
||||
|
||||
@@ -7184,7 +7184,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Perform filesystem operations inside a Daytona sandbox: read a file, write content to a path, append content to an existing file, list a directory, delete a path, make a directory, or fetch file metadata. For files larger than a few KB, create the file with action='write' and empty content, then send the body via multiple 'append' calls of ~4KB each to stay within tool-call payload limits.",
|
||||
"description": "Perform filesystem operations inside a Daytona sandbox: read, write, append, list, delete, mkdir, info, exists, move, find (content grep), search (filename glob), chmod (permissions/owner/group), and replace (bulk find-and-replace across files). For files larger than a few KB, create the file with action='write' and empty content, then send the body via multiple 'append' calls of ~4KB each to stay within tool-call payload limits.",
|
||||
"env_vars": [
|
||||
{
|
||||
"default": null,
|
||||
@@ -7334,9 +7334,127 @@
|
||||
"daytona"
|
||||
],
|
||||
"run_params_schema": {
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"enum": [
|
||||
"read",
|
||||
"write",
|
||||
"append",
|
||||
"list",
|
||||
"delete",
|
||||
"mkdir",
|
||||
"info",
|
||||
"exists",
|
||||
"move",
|
||||
"find",
|
||||
"search",
|
||||
"chmod"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"const": "append"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"content"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"const": "move"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"destination"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"enum": [
|
||||
"find",
|
||||
"search"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"pattern"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"const": "replace"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"paths",
|
||||
"pattern",
|
||||
"replacement"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"action": {
|
||||
"const": "chmod"
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"anyOf": [
|
||||
{
|
||||
"required": [
|
||||
"mode"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"owner"
|
||||
]
|
||||
},
|
||||
{
|
||||
"required": [
|
||||
"group"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"action": {
|
||||
"description": "The filesystem action to perform: 'read' (returns file contents), 'write' (create or replace a file with content), 'append' (append content to an existing file \u2014 use this for writing large files in chunks to avoid hitting tool-call size limits), 'list' (lists a directory), 'delete' (removes a file/dir), 'mkdir' (creates a directory), 'info' (returns file metadata).",
|
||||
"description": "The filesystem action to perform: 'read' (returns file contents); 'write' (create or replace a file with content); 'append' (append content to an existing file \u2014 use this for writing large files in chunks to avoid hitting tool-call size limits); 'list' (lists a directory); 'delete' (removes a file/dir); 'mkdir' (creates a directory); 'info' (returns file metadata); 'exists' (returns whether a path exists); 'move' (rename or relocate a file/dir; requires 'destination'); 'find' (grep file CONTENTS recursively; requires 'pattern'); 'search' (find files by NAME pattern; requires 'pattern'); 'chmod' (change permissions/owner/group; pass at least one of 'mode', 'owner', 'group'); 'replace' (find-and-replace text across files; requires 'paths', 'pattern', and 'replacement').",
|
||||
"enum": [
|
||||
"read",
|
||||
"write",
|
||||
@@ -7344,7 +7462,13 @@
|
||||
"list",
|
||||
"delete",
|
||||
"mkdir",
|
||||
"info"
|
||||
"info",
|
||||
"exists",
|
||||
"move",
|
||||
"find",
|
||||
"search",
|
||||
"chmod",
|
||||
"replace"
|
||||
],
|
||||
"title": "Action",
|
||||
"type": "string"
|
||||
@@ -7368,27 +7492,122 @@
|
||||
"description": "Content to write or append. If omitted for 'write', an empty file is created. For files larger than a few KB, prefer one 'write' with empty content followed by multiple 'append' calls of ~4KB each to stay within tool-call payload limits.",
|
||||
"title": "Content"
|
||||
},
|
||||
"destination": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For action='move': absolute destination path.",
|
||||
"title": "Destination"
|
||||
},
|
||||
"group": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For action='chmod': new file group.",
|
||||
"title": "Group"
|
||||
},
|
||||
"mode": {
|
||||
"default": "0755",
|
||||
"description": "For action='mkdir': octal permission string (default 0755).",
|
||||
"title": "Mode",
|
||||
"type": "string"
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Octal permission string. For 'mkdir' it sets the new directory permissions (defaults to '0755' if omitted). For 'chmod' it sets the target's mode (e.g. '755' to make a script executable). Ignored for other actions.",
|
||||
"title": "Mode"
|
||||
},
|
||||
"owner": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For action='chmod': new file owner (user name).",
|
||||
"title": "Owner"
|
||||
},
|
||||
"path": {
|
||||
"description": "Absolute path inside the sandbox.",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "Absolute path inside the sandbox. Required for all actions except 'replace' (which uses 'paths' instead).",
|
||||
"title": "Path"
|
||||
},
|
||||
"paths": {
|
||||
"anyOf": [
|
||||
{
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For action='replace': list of absolute file paths in which to replace 'pattern' with 'replacement'.",
|
||||
"title": "Paths"
|
||||
},
|
||||
"pattern": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For 'find': substring matched against file CONTENTS. For 'search': glob-style pattern matched against file NAMES (e.g. '*.py'). For 'replace': text to replace inside files.",
|
||||
"title": "Pattern"
|
||||
},
|
||||
"recursive": {
|
||||
"default": false,
|
||||
"description": "For action='delete': remove directories recursively.",
|
||||
"title": "Recursive",
|
||||
"type": "boolean"
|
||||
},
|
||||
"replacement": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "For action='replace': replacement text for 'pattern'.",
|
||||
"title": "Replacement"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"action",
|
||||
"path"
|
||||
"action"
|
||||
],
|
||||
"title": "DaytonaFileToolSchema",
|
||||
"type": "object"
|
||||
|
||||
@@ -8,8 +8,8 @@ authors = [
|
||||
]
|
||||
requires-python = ">=3.10, <3.14"
|
||||
dependencies = [
|
||||
"crewai-core==1.14.5a4",
|
||||
"crewai-cli==1.14.5a4",
|
||||
"crewai-core==1.14.5",
|
||||
"crewai-cli==1.14.5",
|
||||
# Core Dependencies
|
||||
"pydantic>=2.11.9,<2.13",
|
||||
"openai>=2.30.0,<3",
|
||||
@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
|
||||
|
||||
[project.optional-dependencies]
|
||||
tools = [
|
||||
"crewai-tools==1.14.5a4",
|
||||
"crewai-tools==1.14.5",
|
||||
]
|
||||
embeddings = [
|
||||
"tiktoken>=0.8.0,<0.13"
|
||||
@@ -105,7 +105,7 @@ a2a = [
|
||||
"aiocache[redis,memcached]~=0.12.3",
|
||||
]
|
||||
file-processing = [
|
||||
"crewai-files==1.14.5a4",
|
||||
"crewai-files",
|
||||
]
|
||||
qdrant-edge = [
|
||||
"qdrant-edge-py>=0.6.0",
|
||||
|
||||
@@ -48,7 +48,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
|
||||
|
||||
_suppress_pydantic_deprecation_warnings()
|
||||
|
||||
__version__ = "1.14.5a4"
|
||||
__version__ = "1.14.5"
|
||||
|
||||
_LAZY_IMPORTS: dict[str, tuple[str, str]] = {
|
||||
"Memory": ("crewai.memory.unified_memory", "Memory"),
|
||||
|
||||
@@ -7,6 +7,7 @@ from collections.abc import Callable, Coroutine, Sequence
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
from datetime import datetime
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -35,13 +36,11 @@ from typing_extensions import Self, TypeIs
|
||||
from crewai.agent.planning_config import PlanningConfig
|
||||
from crewai.agent.utils import (
|
||||
ahandle_knowledge_retrieval,
|
||||
append_skill_context,
|
||||
apply_training_data,
|
||||
build_task_prompt_with_schema,
|
||||
format_task_with_context,
|
||||
get_knowledge_config,
|
||||
handle_knowledge_retrieval,
|
||||
handle_reasoning,
|
||||
prepare_tools,
|
||||
process_tool_results,
|
||||
save_last_messages,
|
||||
@@ -150,7 +149,17 @@ def _validate_executor_class(value: Any) -> Any:
|
||||
cls = _EXECUTOR_CLASS_MAP.get(value)
|
||||
if cls is None:
|
||||
raise ValueError(f"Unknown executor class: {value}")
|
||||
return cls
|
||||
value = cls
|
||||
import warnings
|
||||
|
||||
if value is CrewAgentExecutor:
|
||||
warnings.warn(
|
||||
"CrewAgentExecutor is deprecated and will be removed in a future release. "
|
||||
"Agents inside Crews now use AgentExecutor by default. "
|
||||
"Switch to crewai.experimental.AgentExecutor.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
@@ -211,7 +220,11 @@ class Agent(BaseAgent):
|
||||
str | BaseLLM | None,
|
||||
BeforeValidator(_validate_llm_ref),
|
||||
PlainSerializer(_serialize_llm_ref, return_type=dict | None, when_used="json"),
|
||||
] = Field(description="Language model that will run the agent.", default=None)
|
||||
] = Field(
|
||||
description="Language model that will run the agent.",
|
||||
default=None,
|
||||
deprecated="function_calling_llm is deprecated and will be removed in a future release.",
|
||||
)
|
||||
system_template: str | None = Field(
|
||||
default=None, description="System format for the agent."
|
||||
)
|
||||
@@ -325,8 +338,8 @@ class Agent(BaseAgent):
|
||||
BeforeValidator(_validate_executor_class),
|
||||
PlainSerializer(_serialize_executor_class, return_type=str, when_used="json"),
|
||||
] = Field(
|
||||
default=CrewAgentExecutor,
|
||||
description="Class to use for the agent executor. Defaults to CrewAgentExecutor, can optionally use AgentExecutor.",
|
||||
default=AgentExecutor,
|
||||
description="Class to use for the agent executor. Defaults to AgentExecutor, can optionally use CrewAgentExecutor.",
|
||||
)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@@ -512,8 +525,6 @@ class Agent(BaseAgent):
|
||||
The task prompt after memory retrieval, ready for knowledge lookup.
|
||||
"""
|
||||
get_env_context()
|
||||
if self.executor_class is not AgentExecutor:
|
||||
handle_reasoning(self, task)
|
||||
|
||||
self._inject_date_to_task(task)
|
||||
|
||||
@@ -541,7 +552,6 @@ class Agent(BaseAgent):
|
||||
Returns:
|
||||
The fully prepared task prompt.
|
||||
"""
|
||||
task_prompt = append_skill_context(self, task_prompt)
|
||||
prepare_tools(self, tools, task)
|
||||
|
||||
return apply_training_data(self, task_prompt)
|
||||
@@ -843,18 +853,22 @@ class Agent(BaseAgent):
|
||||
if not self.agent_executor:
|
||||
raise RuntimeError("Agent executor is not initialized.")
|
||||
|
||||
result = cast(
|
||||
dict[str, Any],
|
||||
self.agent_executor.invoke(
|
||||
{
|
||||
"input": task_prompt,
|
||||
"tool_names": self.agent_executor.tools_names,
|
||||
"tools": self.agent_executor.tools_description,
|
||||
"ask_for_human_input": task.human_input,
|
||||
}
|
||||
),
|
||||
invoke_result = self.agent_executor.invoke(
|
||||
{
|
||||
"input": task_prompt,
|
||||
"tool_names": self.agent_executor.tools_names,
|
||||
"tools": self.agent_executor.tools_description,
|
||||
"ask_for_human_input": task.human_input,
|
||||
}
|
||||
)
|
||||
return result["output"]
|
||||
if inspect.isawaitable(invoke_result):
|
||||
invoke_result.close()
|
||||
raise RuntimeError(
|
||||
"Agent execution was invoked synchronously from within a running "
|
||||
"event loop. Use `agent.kickoff_async()` / `crew.kickoff_async()` "
|
||||
"(or `await agent.aexecute_task(...)`) when calling from async code."
|
||||
)
|
||||
return invoke_result["output"]
|
||||
|
||||
async def aexecute_task(
|
||||
self,
|
||||
@@ -1474,8 +1488,6 @@ class Agent(BaseAgent):
|
||||
),
|
||||
)
|
||||
|
||||
formatted_messages = append_skill_context(self, formatted_messages)
|
||||
|
||||
inputs: dict[str, Any] = {
|
||||
"input": formatted_messages,
|
||||
"tool_names": get_tool_names(parsed_tools),
|
||||
|
||||
@@ -213,30 +213,6 @@ def _combine_knowledge_context(agent: Agent) -> str:
|
||||
return agent_ctx + separator + crew_ctx
|
||||
|
||||
|
||||
def append_skill_context(agent: Agent, task_prompt: str) -> str:
|
||||
"""Append activated skill context sections to the task prompt.
|
||||
|
||||
Args:
|
||||
agent: The agent with optional skills.
|
||||
task_prompt: The current task prompt.
|
||||
|
||||
Returns:
|
||||
The task prompt with skill context appended.
|
||||
"""
|
||||
if not agent.skills:
|
||||
return task_prompt
|
||||
|
||||
from crewai.skills.loader import format_skill_context
|
||||
from crewai.skills.models import Skill
|
||||
|
||||
skill_sections = [
|
||||
format_skill_context(s) for s in agent.skills if isinstance(s, Skill)
|
||||
]
|
||||
if skill_sections:
|
||||
task_prompt += "\n\n" + "\n\n".join(skill_sections)
|
||||
return task_prompt
|
||||
|
||||
|
||||
def apply_training_data(agent: Agent, task_prompt: str) -> str:
|
||||
"""Apply training data to the task prompt.
|
||||
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from crewai.agents.cache.cache_handler import CacheHandler
|
||||
from crewai.agents.parser import AgentAction, AgentFinish, OutputParserError, parse
|
||||
from crewai.agents.tools_handler import ToolsHandler
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AgentAction",
|
||||
"AgentFinish",
|
||||
"CacheHandler",
|
||||
"CrewAgentExecutor",
|
||||
"OutputParserError",
|
||||
"ToolsHandler",
|
||||
"parse",
|
||||
]
|
||||
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if name == "CrewAgentExecutor":
|
||||
from crewai.agents.crew_agent_executor import CrewAgentExecutor
|
||||
|
||||
return CrewAgentExecutor
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user