+
### Enter the Management Account Role ARN
-Paste the **Role ARN** of the management account role you created in [Step 1](#step-1-create-the-management-account-role) into the **Management Account Role ARN** field.
+Paste the **Role ARN** of the management account role — created by the single stack above, or beforehand in [Step 1](#step-1-create-the-management-account-role) — into the **Management Account Role ARN** field.
The ARN follows this format:
```
@@ -317,7 +334,7 @@ For example: `arn:aws:iam::123456789012:role/ProwlerScan`
### Confirm and Discover
-1. Check the box: **"The Stack and StackSet have been successfully deployed in AWS"**.
+1. Check the box: **"The Stack has been successfully deployed in AWS"**.
2. Click **Authenticate**.
Here's what happens behind the scenes:
diff --git a/ui/changelog.d/aws-org-one-step-stackset-deploy.changed.md b/ui/changelog.d/aws-org-one-step-stackset-deploy.changed.md
new file mode 100644
index 0000000000..caffe6c120
--- /dev/null
+++ b/ui/changelog.d/aws-org-one-step-stackset-deploy.changed.md
@@ -0,0 +1 @@
+AWS Organizations onboarding now deploys the management account role and the member-account StackSet from a single CloudFormation stack, replacing the manual StackSet console step
diff --git a/ui/changelog.d/s3-integration-bucket-account-id.fixed.md b/ui/changelog.d/s3-integration-bucket-account-id.fixed.md
new file mode 100644
index 0000000000..5b2d6310be
--- /dev/null
+++ b/ui/changelog.d/s3-integration-bucket-account-id.fixed.md
@@ -0,0 +1 @@
+The AWS S3 integration CloudFormation quick-create link now sets the bucket owner account ID, preventing a stack validation error when S3 integration is enabled
diff --git a/ui/components/integrations/s3/s3-integration-form.tsx b/ui/components/integrations/s3/s3-integration-form.tsx
index 670e3c9aae..d7843f996f 100644
--- a/ui/components/integrations/s3/s3-integration-form.tsx
+++ b/ui/components/integrations/s3/s3-integration-form.tsx
@@ -75,6 +75,7 @@ export const S3IntegrationForm = ({
defaultValues: {
integration_type: "amazon_s3" as const,
bucket_name: integration?.attributes.configuration.bucket_name || "",
+ bucket_account_id: "",
output_directory:
integration?.attributes.configuration.output_directory || "output",
providers:
@@ -95,6 +96,26 @@ export const S3IntegrationForm = ({
const isLoading = form.formState.isSubmitting;
+ // Derives the AWS Account ID that owns the S3 bucket from the selected
+ // provider(s). For AWS providers the uid is the 12-digit account id. This is
+ // the common case (bucket lives in a scanned account); cross-account buckets
+ // can still be overridden via the "Bucket owner account ID" field.
+ const deriveBucketAccountId = (): string => {
+ const selectedIds = form.getValues("providers") || [];
+ for (const id of selectedIds) {
+ const provider = providers.find((p) => p.id === id);
+ const uid = provider?.attributes.uid;
+ if (
+ provider?.attributes.provider === "aws" &&
+ uid &&
+ /^\d{12}$/.test(uid)
+ ) {
+ return uid;
+ }
+ }
+ return "";
+ };
+
const handleNext = async (e: React.FormEvent) => {
e.preventDefault();
@@ -103,10 +124,17 @@ export const S3IntegrationForm = ({
return;
}
- // Validate current step fields for creation flow
+ // Validate current step fields for creation flow. bucket_account_id is
+ // validated here, while its input is visible, so a malformed value surfaces
+ // its error instead of silently blocking the step 1 submit.
const stepFields =
currentStep === 0
- ? (["bucket_name", "output_directory", "providers"] as const)
+ ? ([
+ "bucket_name",
+ "output_directory",
+ "providers",
+ "bucket_account_id",
+ ] as const)
: // Step 1: No required fields since role_arn and external_id are optional
[];
@@ -259,12 +287,15 @@ export const S3IntegrationForm = ({
// If editing credentials, show only credentials form
if (isEditingCredentials || currentStep === 1) {
const bucketName = form.getValues("bucket_name") || "";
+ const bucketAccountId =
+ form.getValues("bucket_account_id") || deriveBucketAccountId();
const externalId =
form.getValues("external_id") || session?.tenantId || "";
const templateLinks = getAWSCredentialsTemplateLinks(
externalId,
bucketName,
"amazon_s3",
+ bucketAccountId,
);
return (
@@ -346,6 +377,24 @@ export const S3IntegrationForm = ({
variant="bordered"
isRequired
/>
+
+ + AWS account ID that owns the bucket. Leave empty to use the + selected account, or set it if the bucket lives in a different + account. +
+- 1) Deploy the ProwlerScan role in your{" "} - management account using a CloudFormation - Stack. + 1) Choose the AWS Organizational Unit (or root) + to deploy to. Prowler creates the role in your management + account and rolls it out to every member account under this + target. +
+
+ Find this in the AWS Organizations console. Use your{" "}
+ root ID (starts with r-) to deploy
+ to the whole organization, or an OU ID (starts
+ with ou-) to target a specific unit.
+
+ 2) Create the CloudFormation Stack in your{" "} + management account. It deploys the ProwlerScan + role and a service-managed StackSet that rolls the role out to + your member accounts in one step.
-- 2) Deploy the ProwlerScan role to{" "} - member accounts using a CloudFormation - StackSet. -
-- Open the StackSets console, select{" "} - Service-managed permissions, and paste the - template URL below. Set the ExternalId{" "} - parameter to the value shown above. -
-+ Enter a valid Organizational Unit or Root ID above to enable + deployment. +
+ )}- 3) Paste the management account Role ARN and confirm both - deployments are complete. + 3) Paste the management account Role ARN and confirm the + deployment is complete.