diff --git a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_auth_type_selector.tsx b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_auth_type_selector.tsx
index e652d1d0cf076..161c6bad3dc77 100644
--- a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_auth_type_selector.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_auth_type_selector.tsx
@@ -48,16 +48,21 @@ const OPTIONS = [
interface AwsAuthTypeSelectorProps {
selectedAuthType: AwsAuthType;
+ showIdentityFederation?: boolean;
onChange: (authType: AwsAuthType) => void;
}
export const AwsAuthTypeSelector: React.FC = ({
selectedAuthType,
+ showIdentityFederation = true,
onChange,
}) => {
+ const options = showIdentityFederation
+ ? OPTIONS
+ : OPTIONS.filter((o) => o.value !== 'identity_federation');
return (
onChange(e.target.value as AwsAuthType)}
aria-label={i18n.translate('xpack.fleet.awsConnectSetup.authType.selectorAriaLabel', {
diff --git a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_identity_federation_setup.tsx b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_identity_federation_setup.tsx
index 30af47e241872..95c9ddafa3cf8 100644
--- a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_identity_federation_setup.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/aws_identity_federation_setup.tsx
@@ -15,6 +15,7 @@ import {
EuiFlexItem,
EuiFormRow,
EuiLink,
+ EuiSkeletonText,
EuiSpacer,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@@ -57,7 +58,7 @@ export const AwsIdentityFederationSetup: React.FC {
- const { data: cloudConnectors = [] } = useGetCloudConnectors({
+ const { data: cloudConnectors = [], isLoading: isLoadingConnectors } = useGetCloudConnectors({
cloudProvider: 'aws',
accountType,
packageName,
@@ -123,12 +124,16 @@ export const AwsIdentityFederationSetup: React.FC {
+ if (isLoadingConnectors) {
+ return ;
+ }
+
+ const handleTabClick = (tab: { id: string }) => {
setSelectedTabId(tab.id);
if (tab.id === TABS.NEW_CONNECTION) {
setSelectedConnectorId(undefined);
}
- }, []);
+ };
const tabs: CloudConnectorTab[] = [
{
diff --git a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/index.tsx b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/index.tsx
index 87e4be52dac53..ee74d6371fd4b 100644
--- a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/index.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_connect_setup/index.tsx
@@ -30,6 +30,7 @@ export interface AwsConnectSetupProps {
initialConnectorId?: string;
initialStaticKeys?: Partial;
initialTemporaryKeys?: Partial;
+ showIdentityFederation?: boolean;
onNext?: () => void;
onConnectorIdChange?: (connectorId: string | undefined) => void;
onStaticKeysChange?: (keys: AwsStaticKeyCredentials | undefined) => void;
@@ -53,6 +54,7 @@ export const AwsConnectSetup: React.FC = ({
initialConnectorId,
initialStaticKeys,
initialTemporaryKeys,
+ showIdentityFederation = true,
onNext,
onConnectorIdChange,
onStaticKeysChange,
@@ -63,7 +65,9 @@ export const AwsConnectSetup: React.FC = ({
? 'temporary_keys'
: initialStaticKeys
? 'static_keys'
- : 'identity_federation'
+ : showIdentityFederation
+ ? 'identity_federation'
+ : 'static_keys'
);
const [isFormReady, setIsFormReady] = useState(false);
@@ -91,7 +95,11 @@ export const AwsConnectSetup: React.FC = ({
-
+
{authType === 'identity_federation' && (
{
});
it('has only valid delivery method values', () => {
- entry.deliveryMethods.forEach((method) => {
+ entry.deliveryMethods.forEach(({ method }) => {
expect(VALID_DELIVERY_METHODS).toContain(method);
});
});
+ it('has exactly one preferred delivery method', () => {
+ const preferred = entry.deliveryMethods.filter((dm) => dm.preferred === true);
+ expect(preferred).toHaveLength(1);
+ });
+
it('has a non-empty packageName', () => {
expect(entry.packageName).toBeTruthy();
});
diff --git a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_cloud_connector/aws_services_matrix.ts b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/aws_service_matrix.ts
similarity index 81%
rename from x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_cloud_connector/aws_services_matrix.ts
rename to x-pack/platform/plugins/shared/ingest_hub/public/onboarding/aws_service_matrix.ts
index 0ad12ac064419..07e0a14c3c779 100644
--- a/x-pack/platform/plugins/shared/fleet/public/components/cloud_connector/aws_cloud_connector/aws_services_matrix.ts
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/aws_service_matrix.ts
@@ -31,13 +31,20 @@ export type ServiceCategory =
| 'Serverless & Compute'
| 'Storage';
+export interface DeliveryMethodEntry {
+ method: DeliveryMethod;
+ /** When true, this is the mechanism used by default in the onboarding deployment step.
+ * Exactly one entry per service should be preferred. */
+ preferred?: boolean;
+}
+
export interface AwsServiceMatrixEntry {
/** Data stream identifier, matching packages//data_stream/ */
id: string;
name: string;
category: ServiceCategory;
signalType: SignalType;
- deliveryMethods: DeliveryMethod[];
+ deliveryMethods: DeliveryMethodEntry[];
/** Authentication types available per delivery method. Populated once IF rollout status is confirmed. */
authTypes?: AuthType[];
/** Whether OIDC-based IAM role assumption is supported. Populated once Security team confirms per-service status. */
@@ -61,7 +68,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS API Gateway',
category: 'Serverless & Compute',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -73,7 +80,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS API Gateway',
category: 'Serverless & Compute',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -85,7 +92,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Lambda',
category: 'Serverless & Compute',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -97,7 +104,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Lambda',
category: 'Serverless & Compute',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws-cloudwatch'],
requiredConfig: ['log_group_arn', 'region_name'],
packageName: 'aws',
@@ -111,7 +118,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS EC2',
category: 'Infrastructure',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -123,7 +130,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS EC2',
category: 'Infrastructure',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -135,7 +142,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS ECS',
category: 'Infrastructure',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -147,7 +154,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS EMR',
category: 'Infrastructure',
signalType: 'logs',
- deliveryMethods: ['agentless', 'cloud_forwarder'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }, { method: 'cloud_forwarder' }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -159,7 +166,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS EMR',
category: 'Infrastructure',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -173,7 +180,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Health',
category: 'Monitoring',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -185,7 +192,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS CloudWatch Logs',
category: 'Monitoring',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws-cloudwatch'],
requiredConfig: ['log_group_arn', 'region_name'],
packageName: 'aws',
@@ -197,7 +204,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS CloudWatch Metrics',
category: 'Monitoring',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions', 'metrics'],
packageName: 'aws',
@@ -211,7 +218,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Billing',
category: 'Cost & Billing',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: [],
packageName: 'aws',
@@ -223,7 +230,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Usage',
category: 'Cost & Billing',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -237,7 +244,11 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS CloudTrail',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless', 'cloud_forwarder', 'firehose'],
+ deliveryMethods: [
+ { method: 'agentless', preferred: true },
+ { method: 'cloud_forwarder' },
+ { method: 'firehose' },
+ ],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -249,7 +260,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Config',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['cel'],
requiredConfig: ['aws_region'],
packageName: 'aws',
@@ -261,19 +272,20 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS GuardDuty',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws-s3', 'httpjson'],
requiredConfig: ['aws_region', 'detector_id', 'bucket_arn', 'region'],
packageName: 'aws',
defaultEnabled: true,
showInUI: true,
+ identityFederationSupported: true,
},
{
id: 'inspector',
name: 'AWS Inspector',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['httpjson'],
requiredConfig: ['aws_region'],
packageName: 'aws',
@@ -285,7 +297,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Network Firewall',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -297,7 +309,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Network Firewall',
category: 'Security',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -309,7 +321,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Security Hub',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['httpjson'],
requiredConfig: ['aws_region'],
packageName: 'aws',
@@ -321,7 +333,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Security Hub (Full Posture / CSPM)',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['httpjson'],
requiredConfig: ['aws_region'],
packageName: 'aws',
@@ -333,7 +345,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Security Hub (Insights)',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['httpjson'],
requiredConfig: ['aws_region'],
packageName: 'aws',
@@ -345,7 +357,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS WAF',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -359,7 +371,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS CloudFront',
category: 'Networking / CDN',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-s3'],
requiredConfig: ['bucket_arn', 'region'],
packageName: 'aws',
@@ -371,7 +383,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS ELB',
category: 'Networking',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -383,7 +395,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS ELB',
category: 'Networking',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -395,7 +407,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS NAT Gateway',
category: 'Networking',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -407,7 +419,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Route 53 Public DNS',
category: 'Networking',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-cloudwatch'],
requiredConfig: ['log_group_arn', 'region_name'],
packageName: 'aws',
@@ -419,7 +431,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Route 53 Resolver',
category: 'Networking',
signalType: 'logs',
- deliveryMethods: ['agentless', 'cloud_forwarder'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }, { method: 'cloud_forwarder' }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -431,7 +443,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Transit Gateway',
category: 'Networking',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -443,7 +455,11 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS VPC Flow',
category: 'Security / Networking',
signalType: 'logs',
- deliveryMethods: ['agentless', 'cloud_forwarder', 'firehose'],
+ deliveryMethods: [
+ { method: 'agentless', preferred: true },
+ { method: 'cloud_forwarder' },
+ { method: 'firehose' },
+ ],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws',
@@ -455,7 +471,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS VPN',
category: 'Networking',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -469,7 +485,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS EBS',
category: 'Storage',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -481,7 +497,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS S3 (Storage metrics)',
category: 'Storage',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -493,7 +509,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS S3 (Request metrics)',
category: 'Storage',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -505,7 +521,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS S3 (Access logs)',
category: 'Storage',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder', 'firehose'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }, { method: 'firehose' }],
inputs: ['aws-s3'],
requiredConfig: ['bucket_arn', 'region'],
packageName: 'aws',
@@ -517,7 +533,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS S3 Storage Lens',
category: 'Storage',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -531,7 +547,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS DynamoDB',
category: 'Databases',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -543,7 +559,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS RDS',
category: 'Databases',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -555,7 +571,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Redshift',
category: 'Databases',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -569,7 +585,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS MSK (Kafka)',
category: 'Messaging',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -581,7 +597,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Kinesis',
category: 'Messaging',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -593,7 +609,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS SNS',
category: 'Messaging',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -605,7 +621,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS SQS',
category: 'Messaging',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws',
@@ -619,7 +635,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Bedrock (Guardrails)',
category: 'AI / ML',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws_bedrock',
@@ -631,7 +647,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Bedrock (Invocation)',
category: 'AI / ML',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws_bedrock',
@@ -643,7 +659,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Bedrock (Runtime)',
category: 'AI / ML',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['aws/metrics'],
requiredConfig: ['regions'],
packageName: 'aws_bedrock',
@@ -656,7 +672,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Bedrock AgentCore',
category: 'AI / ML',
signalType: 'logs',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'aws_bedrock_agentcore',
@@ -670,7 +686,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Fargate',
category: 'Infrastructure',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: ['awsfargate/metrics'],
requiredConfig: ['regions'],
packageName: 'awsfargate',
@@ -685,7 +701,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS MQ',
category: 'Messaging',
signalType: 'metrics',
- deliveryMethods: ['agentless'],
+ deliveryMethods: [{ method: 'agentless', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'aws_mq',
@@ -699,7 +715,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS CloudTrail (OTel)',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'aws_cloudtrail_otel',
@@ -712,7 +728,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS VPC Flow (OTel)',
category: 'Security / Networking',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'aws_vpcflow_otel',
@@ -725,7 +741,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS WAF (OTel)',
category: 'Security',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'aws_waf_otel',
@@ -740,7 +756,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Logs (Generic)',
category: 'Monitoring',
signalType: 'logs',
- deliveryMethods: ['cloud_forwarder'],
+ deliveryMethods: [{ method: 'cloud_forwarder', preferred: true }],
inputs: ['aws-s3', 'aws-cloudwatch'],
requiredConfig: ['bucket_arn', 'log_group_arn', 'region', 'region_name'],
packageName: 'aws_logs',
@@ -754,7 +770,7 @@ export const AWS_SERVICES_MATRIX: AwsServiceMatrixEntry[] = [
name: 'AWS Firehose (Receiver)',
category: 'Monitoring',
signalType: 'logs',
- deliveryMethods: ['firehose'],
+ deliveryMethods: [{ method: 'firehose', preferred: true }],
inputs: [],
requiredConfig: [],
packageName: 'awsfirehose',
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/index.ts b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/index.ts
index 49ae14e67423c..e7beeba79fd70 100644
--- a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/index.ts
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/index.ts
@@ -6,4 +6,3 @@
*/
export { registerOnboardingApp } from './register_onboarding_app';
-export { renderOnboardingApp } from './onboarding_app';
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/onboarding_flow_context.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/onboarding_flow_context.tsx
index 5c1473c5835df..94bf7d05c4201 100644
--- a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/onboarding_flow_context.tsx
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/onboarding_flow_context.tsx
@@ -9,6 +9,8 @@ import React, { createContext, useContext, useCallback, useState } from 'react';
import useSessionStorage from 'react-use/lib/useSessionStorage';
import type { AwsStaticKeyCredentials, AwsTemporaryKeyCredentials } from '@kbn/fleet-plugin/public';
+import { AWS_SERVICES_MATRIX } from './aws_service_matrix';
+
export interface ConnectStepState {
connectorId?: string;
staticKeys?: AwsStaticKeyCredentials;
@@ -22,11 +24,25 @@ interface PersistedConnectStep {
accessKeyId?: string;
}
+export interface ServicesStepState {
+ selectedServiceIds: string[];
+}
+
+interface PersistedServicesStep {
+ selectedServiceIds: string[];
+}
+
+const DEFAULT_SELECTED_IDS = AWS_SERVICES_MATRIX.filter((s) => s.showInUI && s.defaultEnabled).map(
+ (s) => s.id
+);
+
interface OnboardingFlowState {
connectStep: ConnectStepState;
setConnectorId: (id: string | undefined) => void;
setStaticKeys: (keys: AwsStaticKeyCredentials | undefined) => void;
setTemporaryKeys: (keys: AwsTemporaryKeyCredentials | undefined) => void;
+ servicesStep: ServicesStepState;
+ setSelectedServiceIds: (ids: string[]) => void;
}
const OnboardingFlowContext = createContext(undefined);
@@ -37,6 +53,11 @@ export function OnboardingFlowProvider({ children }: { children: React.ReactNode
{}
);
+ const [persistedServices, setPersistedServices] = useSessionStorage(
+ 'onboarding.aws.servicesStep',
+ { selectedServiceIds: DEFAULT_SELECTED_IDS }
+ );
+
// Sensitive fields (secret_access_key, session_token) live in memory only.
// access_key_id is restored from session storage; passwords start empty on page refresh.
const [staticKeys, setStaticKeysState] = useState(() =>
@@ -92,15 +113,33 @@ export function OnboardingFlowProvider({ children }: { children: React.ReactNode
[setPersistedConnectStep]
);
+ const setSelectedServiceIds = useCallback(
+ (ids: string[]) => {
+ setPersistedServices({ ...persistedServices, selectedServiceIds: ids });
+ },
+ [persistedServices, setPersistedServices]
+ );
+
const connectStep: ConnectStepState = {
connectorId: persistedConnectStep?.connectorId,
staticKeys,
temporaryKeys,
};
+ const servicesStep: ServicesStepState = {
+ selectedServiceIds: persistedServices?.selectedServiceIds ?? DEFAULT_SELECTED_IDS,
+ };
+
return (
{children}
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/connect_step.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/connect_step.tsx
index 95f9719437ebc..dedd8a75c5b54 100644
--- a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/connect_step.tsx
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/connect_step.tsx
@@ -5,22 +5,34 @@
* 2.0.
*/
-import React, { Suspense } from 'react';
+import React, { Suspense, useMemo } from 'react';
import { EuiLoadingSpinner } from '@elastic/eui';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { CoreStart } from '@kbn/core/public';
import type { CloudStart } from '@kbn/cloud-plugin/public';
import type { CloudSetupForCloudConnector } from '@kbn/fleet-plugin/public';
import { LazyAwsConnectSetup } from '@kbn/fleet-plugin/public';
+import { AWS_SERVICES_MATRIX } from '../aws_service_matrix';
import { useOnboardingFlow } from '../onboarding_flow_context';
+const SERVICE_MAP = new Map(AWS_SERVICES_MATRIX.map((s) => [s.id, s]));
+
interface ConnectStepProps {
onNext: () => void;
}
export function ConnectStep({ onNext }: ConnectStepProps) {
const { services } = useKibana();
- const { connectStep, setConnectorId, setStaticKeys, setTemporaryKeys } = useOnboardingFlow();
+ const { connectStep, setConnectorId, setStaticKeys, setTemporaryKeys, servicesStep } =
+ useOnboardingFlow();
+ const { selectedServiceIds } = servicesStep;
+
+ const showIdentityFederation = useMemo(() => {
+ if (selectedServiceIds.length === 0) return true;
+ return selectedServiceIds.every(
+ (id) => SERVICE_MAP.get(id)?.identityFederationSupported === true
+ );
+ }, [selectedServiceIds]);
return (
@@ -30,6 +42,7 @@ export function ConnectStep({ onNext }: ConnectStepProps) {
initialConnectorId={connectStep.connectorId}
initialStaticKeys={connectStep.staticKeys}
initialTemporaryKeys={connectStep.temporaryKeys}
+ showIdentityFederation={showIdentityFederation}
onNext={onNext}
onConnectorIdChange={setConnectorId}
onStaticKeysChange={setStaticKeys}
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step.tsx
deleted file mode 100644
index 93275dbea1d89..0000000000000
--- a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React from 'react';
-import { EuiEmptyPrompt } from '@elastic/eui';
-
-export function ServicesStep() {
- return (
-
Services}
- body={Services step content will go here.
}
- />
- );
-}
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/delivery_method_badge.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/delivery_method_badge.tsx
new file mode 100644
index 0000000000000..09e981c9353e2
--- /dev/null
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/delivery_method_badge.tsx
@@ -0,0 +1,42 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiBadge, EuiToolTip } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+
+import type { DeliveryMethod } from '../../aws_service_matrix';
+
+const LABELS: Record = {
+ agentless: i18n.translate('xpack.ingestHub.servicesStep.deliveryMethod.agentless', {
+ defaultMessage: 'Agentless',
+ }),
+ firehose: i18n.translate('xpack.ingestHub.servicesStep.deliveryMethod.firehose', {
+ defaultMessage: 'Firehose',
+ }),
+ cloud_forwarder: i18n.translate('xpack.ingestHub.servicesStep.deliveryMethod.cloudForwarder', {
+ defaultMessage: 'ECF',
+ }),
+};
+
+const TOOLTIPS: Partial> = {
+ cloud_forwarder: i18n.translate(
+ 'xpack.ingestHub.servicesStep.deliveryMethod.cloudForwarderTooltip',
+ { defaultMessage: 'EDOT Cloud Forwarder' }
+ ),
+};
+
+interface DeliveryMethodBadgeProps {
+ method: DeliveryMethod;
+ preferred?: boolean;
+}
+
+export const DeliveryMethodBadge: React.FC = ({ method, preferred }) => {
+ const tooltip = TOOLTIPS[method];
+ const badge = {LABELS[method]};
+ return tooltip ? {badge} : badge;
+};
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/index.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/index.tsx
new file mode 100644
index 0000000000000..54bdf803c058a
--- /dev/null
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/index.tsx
@@ -0,0 +1,203 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useCallback, useMemo, useState } from 'react';
+import {
+ EuiButton,
+ EuiButtonEmpty,
+ EuiButtonGroup,
+ EuiFieldSearch,
+ EuiFlexGroup,
+ EuiFlexGrid,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiText,
+} from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n-react';
+
+import { AWS_SERVICES_MATRIX } from '../../aws_service_matrix';
+import type { SignalType } from '../../aws_service_matrix';
+import { useOnboardingFlow } from '../../onboarding_flow_context';
+import { ServiceRow } from './service_row';
+
+interface ServicesStepProps {
+ onNext: () => void;
+}
+
+type SignalFilter = SignalType | 'all';
+
+const SIGNAL_FILTER_OPTIONS = [
+ {
+ id: 'all' as SignalFilter,
+ label: i18n.translate('xpack.ingestHub.servicesStep.filter.all', { defaultMessage: 'All' }),
+ },
+ {
+ id: 'logs' as SignalFilter,
+ label: i18n.translate('xpack.ingestHub.servicesStep.filter.logs', { defaultMessage: 'Logs' }),
+ },
+ {
+ id: 'metrics' as SignalFilter,
+ label: i18n.translate('xpack.ingestHub.servicesStep.filter.metrics', {
+ defaultMessage: 'Metrics',
+ }),
+ },
+];
+
+export function ServicesStep({ onNext }: ServicesStepProps) {
+ const { servicesStep, setSelectedServiceIds } = useOnboardingFlow();
+ const { selectedServiceIds } = servicesStep;
+
+ const [signalFilter, setSignalFilter] = useState('all');
+ const [searchQuery, setSearchQuery] = useState('');
+
+ const filteredServices = useMemo(() => {
+ const q = searchQuery.trim().toLowerCase();
+ return AWS_SERVICES_MATRIX.filter(
+ (s) =>
+ s.showInUI &&
+ (signalFilter === 'all' || s.signalType === signalFilter) &&
+ (q === '' || s.name.toLowerCase().includes(q))
+ );
+ }, [signalFilter, searchQuery]);
+
+ const selectedSet = useMemo(() => new Set(selectedServiceIds), [selectedServiceIds]);
+
+ const isReady = useMemo(() => {
+ return selectedServiceIds.length > 0;
+ }, [selectedServiceIds]);
+
+ const handleToggle = useCallback(
+ (serviceId: string, checked: boolean) => {
+ const next = checked
+ ? [...new Set([...selectedServiceIds, serviceId])]
+ : selectedServiceIds.filter((id) => id !== serviceId);
+ setSelectedServiceIds(next);
+ },
+ [selectedServiceIds, setSelectedServiceIds]
+ );
+
+ const handleSelectAll = useCallback(() => {
+ const filteredIdSet = new Set(filteredServices.map((s) => s.id));
+ const existing = selectedServiceIds.filter((id) => !filteredIdSet.has(id));
+ setSelectedServiceIds([...existing, ...filteredIdSet]);
+ }, [filteredServices, selectedServiceIds, setSelectedServiceIds]);
+
+ const handleDeselectAll = useCallback(() => {
+ const filteredIds = new Set(filteredServices.map((s) => s.id));
+ setSelectedServiceIds(selectedServiceIds.filter((id) => !filteredIds.has(id)));
+ }, [filteredServices, selectedServiceIds, setSelectedServiceIds]);
+
+ const handleNext = useCallback(() => {
+ if (!isReady) {
+ return;
+ }
+ onNext();
+ }, [isReady, onNext]);
+
+ return (
+
+
+
+ setSearchQuery(e.target.value)}
+ placeholder={i18n.translate('xpack.ingestHub.servicesStep.searchPlaceholder', {
+ defaultMessage: 'Search services',
+ })}
+ data-test-subj="servicesStep-searchBox"
+ />
+
+
+ setSignalFilter(id as SignalFilter)}
+ buttonSize="compressed"
+ data-test-subj="servicesStep-signalFilter"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {filteredServices.map((service) => (
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/service_row.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/service_row.tsx
new file mode 100644
index 0000000000000..81c6907041360
--- /dev/null
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/service_row.tsx
@@ -0,0 +1,54 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiCheckableCard, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui';
+
+import type { AwsServiceMatrixEntry } from '../../aws_service_matrix';
+import { SignalTypeBadge } from './signal_type_badge';
+import { DeliveryMethodBadge } from './delivery_method_badge';
+
+interface ServiceRowProps {
+ service: AwsServiceMatrixEntry;
+ isSelected: boolean;
+ onToggle: (id: string, checked: boolean) => void;
+}
+
+export const ServiceRow: React.FC = ({ service, isSelected, onToggle }) => {
+ return (
+
+
+
+
+ {service.name}
+
+
+
+
+
+
+
+ {service.deliveryMethods.map((entry) => (
+
+
+
+ ))}
+
+
+
+ }
+ checkableType="checkbox"
+ checked={isSelected}
+ onChange={(e) => onToggle(service.id, e.target.checked)}
+ data-test-subj={`servicesStep-toggle-${service.id}`}
+ />
+
+ );
+};
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/signal_type_badge.tsx b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/signal_type_badge.tsx
new file mode 100644
index 0000000000000..69d250f04d5e9
--- /dev/null
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/step_components/services_step/signal_type_badge.tsx
@@ -0,0 +1,34 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React from 'react';
+import { EuiBadge } from '@elastic/eui';
+import { i18n } from '@kbn/i18n';
+
+import type { SignalType } from '../../aws_service_matrix';
+
+const LABELS: Record = {
+ logs: i18n.translate('xpack.ingestHub.servicesStep.signalType.logs', {
+ defaultMessage: 'Logs',
+ }),
+ metrics: i18n.translate('xpack.ingestHub.servicesStep.signalType.metrics', {
+ defaultMessage: 'Metrics',
+ }),
+};
+
+const COLORS: Record = {
+ logs: 'hollow',
+ metrics: 'hollow',
+};
+
+interface SignalTypeBadgeProps {
+ signalType: SignalType;
+}
+
+export const SignalTypeBadge: React.FC = ({ signalType }) => (
+ {LABELS[signalType]}
+);
diff --git a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/steps.ts b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/steps.ts
index e2afcac2c3014..b0f2d12988062 100644
--- a/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/steps.ts
+++ b/x-pack/platform/plugins/shared/ingest_hub/public/onboarding/steps.ts
@@ -12,18 +12,18 @@ export interface OnboardingStepConfig {
}
export const ONBOARDING_STEPS: OnboardingStepConfig[] = [
- {
- id: 'connect',
- title: i18n.translate('xpack.ingestHub.onboarding.steps.connect.title', {
- defaultMessage: 'Connect',
- }),
- },
{
id: 'services',
title: i18n.translate('xpack.ingestHub.onboarding.steps.services.title', {
defaultMessage: 'Services',
}),
},
+ {
+ id: 'connect',
+ title: i18n.translate('xpack.ingestHub.onboarding.steps.connect.title', {
+ defaultMessage: 'Connect',
+ }),
+ },
{
id: 'name-and-scope',
title: i18n.translate('xpack.ingestHub.onboarding.steps.nameAndScope.title', {
diff --git a/x-pack/platform/plugins/shared/ingest_hub/test/scout/ui/tests/onboarding_services_step.spec.ts b/x-pack/platform/plugins/shared/ingest_hub/test/scout/ui/tests/onboarding_services_step.spec.ts
new file mode 100644
index 0000000000000..7e0c077b393cb
--- /dev/null
+++ b/x-pack/platform/plugins/shared/ingest_hub/test/scout/ui/tests/onboarding_services_step.spec.ts
@@ -0,0 +1,110 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import { tags } from '@kbn/scout';
+import { expect } from '@kbn/scout/ui';
+import { test } from '../fixtures';
+
+// cloudwatch_logs and cloudwatch_metrics have defaultEnabled: false; all others are enabled by default.
+const TOTAL_SERVICES = 14;
+const DEFAULT_SELECTED_COUNT = 12;
+
+test.describe('Onboarding services step', { tag: tags.stateful.classic }, () => {
+ test.beforeAll(async ({ apiServices }) => {
+ await apiServices.core.settings({
+ 'feature_flags.overrides': {
+ 'ingestHub.onboardingEnabled': 'true',
+ },
+ });
+ });
+
+ test.afterAll(async ({ apiServices }) => {
+ await apiServices.core.settings({
+ 'feature_flags.overrides': {
+ 'ingestHub.onboardingEnabled': 'false',
+ },
+ });
+ });
+
+ test('renders all services with matrix-driven defaults', async ({ browserAuth, page }) => {
+ await browserAuth.loginAsAdmin();
+ await page.gotoApp('onboarding/aws#services');
+ await expect(page.testSubj.locator('onboardingStep-services')).toBeVisible();
+
+ const rows = page.locator('[data-test-subj^="servicesStep-serviceRow-"]');
+ await expect(rows).toHaveCount(TOTAL_SERVICES);
+
+ // cloudwatch_logs and cloudwatch_metrics are off by default
+ await expect(page.testSubj.locator('servicesStep-toggle-cloudwatch_logs')).not.toBeChecked();
+ await expect(page.testSubj.locator('servicesStep-toggle-cloudwatch_metrics')).not.toBeChecked();
+
+ // a defaultEnabled service is checked
+ await expect(page.testSubj.locator('servicesStep-toggle-guardduty')).toBeChecked();
+
+ // selected count text reflects defaults
+ await expect(page.getByText(`${DEFAULT_SELECTED_COUNT} services selected`)).toBeVisible();
+ });
+
+ test('deselect and reselect a service', async ({ browserAuth, page }) => {
+ await browserAuth.loginAsAdmin();
+ await page.gotoApp('onboarding/aws#services');
+ await expect(page.testSubj.locator('onboardingStep-services')).toBeVisible();
+
+ // deselect guardduty
+ await page.testSubj.locator('servicesStep-toggle-guardduty').click();
+ await expect(page.testSubj.locator('servicesStep-toggle-guardduty')).not.toBeChecked();
+ await expect(page.getByText(`${DEFAULT_SELECTED_COUNT - 1} services selected`)).toBeVisible();
+
+ // reselect it
+ await page.testSubj.locator('servicesStep-toggle-guardduty').click();
+ await expect(page.testSubj.locator('servicesStep-toggle-guardduty')).toBeChecked();
+ await expect(page.getByText(`${DEFAULT_SELECTED_COUNT} services selected`)).toBeVisible();
+ });
+
+ test('Next is disabled when no services are selected', async ({ browserAuth, page }) => {
+ await browserAuth.loginAsAdmin();
+ await page.gotoApp('onboarding/aws#services');
+ await expect(page.testSubj.locator('onboardingStep-services')).toBeVisible();
+
+ // Next is enabled while services are selected (defaults have selections)
+ await expect(page.testSubj.locator('servicesStep-nextButton')).toBeEnabled();
+
+ // deselect all services via "Deselect all"
+ await page.testSubj.locator('servicesStep-deselectAllButton').click();
+ await expect(page.getByText('0 services selected')).toBeVisible();
+
+ // Next must be disabled with nothing selected
+ await expect(page.testSubj.locator('servicesStep-nextButton')).toBeDisabled();
+
+ // re-selecting any service re-enables Next
+ await page.testSubj.locator(`servicesStep-toggle-cloudtrail`).click();
+ await expect(page.testSubj.locator('servicesStep-nextButton')).toBeEnabled();
+ });
+
+ test('signal-type filter shows only matching services', async ({ browserAuth, page }) => {
+ await browserAuth.loginAsAdmin();
+ await page.gotoApp('onboarding/aws#services');
+ await expect(page.testSubj.locator('onboardingStep-services')).toBeVisible();
+
+ // switch to Logs filter
+ await page.testSubj.locator('servicesStep-signalFilter').getByText('Logs').click();
+
+ const rows = page.locator('[data-test-subj^="servicesStep-serviceRow-"]');
+ // only log-signal services are shown; metrics rows are hidden
+ const count = await rows.count();
+ expect(count).toBeLessThan(TOTAL_SERVICES);
+
+ // a metrics-only row must not be visible
+ await expect(page.testSubj.locator('servicesStep-serviceRow-dynamodb')).toBeHidden();
+
+ // switch to Metrics
+ await page.testSubj.locator('servicesStep-signalFilter').getByText('Metrics').click();
+ await expect(page.testSubj.locator('servicesStep-serviceRow-dynamodb')).toBeVisible();
+ // a logs-only row must not be visible
+ await expect(page.testSubj.locator('servicesStep-serviceRow-guardduty')).toBeHidden();
+ });
+});
diff --git a/x-pack/platform/plugins/shared/ingest_hub/tsconfig.json b/x-pack/platform/plugins/shared/ingest_hub/tsconfig.json
index b18ddd1f2b5ac..0ed5c8e4fe9ae 100644
--- a/x-pack/platform/plugins/shared/ingest_hub/tsconfig.json
+++ b/x-pack/platform/plugins/shared/ingest_hub/tsconfig.json
@@ -22,6 +22,7 @@
"@kbn/scout",
"@kbn/kibana-react-plugin",
"@kbn/react-query",
- "@kbn/fleet-plugin"
+ "@kbn/fleet-plugin",
+ "@kbn/i18n-react"
]
}