diff --git a/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts b/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts
index 12921d059ce2a..9083cdecf3d3b 100644
--- a/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts
+++ b/x-pack/platform/plugins/shared/fleet/common/services/package_to_package_policy.ts
@@ -112,10 +112,6 @@ export const varsReducer = (
configObject: PackagePolicyConfigRecord,
registryVar: RegistryVarsEntry
): PackagePolicyConfigRecord => {
- // section_header vars are decorative only and hold no value
- if (registryVar.type === 'section_header') {
- return configObject;
- }
const configEntry: PackagePolicyConfigRecordEntry = {
value: !registryVar.default && registryVar.multi ? [] : registryVar.default,
};
diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts
index 5e442b415601e..4ebd19e09c82b 100644
--- a/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts
+++ b/x-pack/platform/plugins/shared/fleet/common/types/models/epm.ts
@@ -272,6 +272,7 @@ export enum RegistryPolicyTemplateKeys {
dynamic_signal_types = 'dynamic_signal_types',
var_groups = 'var_groups',
deprecated = 'deprecated',
+ sections = 'sections',
}
interface BaseTemplate {
[RegistryPolicyTemplateKeys.name]: string;
@@ -300,6 +301,7 @@ export interface RegistryPolicyInputOnlyTemplate extends BaseTemplate {
[RegistryPolicyTemplateKeys.required_vars]?: RegistryRequiredVars;
[RegistryPolicyTemplateKeys.vars]?: RegistryVarsEntry[];
[RegistryPolicyTemplateKeys.var_groups]?: RegistryVarGroup[];
+ [RegistryPolicyTemplateKeys.sections]?: RegistrySection[];
[RegistryPolicyTemplateKeys.dynamic_signal_types]?: boolean;
}
@@ -325,6 +327,7 @@ export enum RegistryInputKeys {
migrate_from = 'migrate_from',
dynamic_signal_types = 'dynamic_signal_types',
show_divider = 'show_divider',
+ sections = 'sections',
}
export type RegistryInputGroup = 'logs' | 'metrics';
@@ -350,6 +353,7 @@ export interface RegistryInput {
[RegistryInputKeys.dynamic_signal_types]?: boolean;
/** When false, suppresses the automatic horizontal divider rendered after the input-level config section. Defaults to true. */
[RegistryInputKeys.show_divider]?: boolean;
+ [RegistryInputKeys.sections]?: RegistrySection[];
}
export enum RegistryStreamKeys {
@@ -365,6 +369,7 @@ export enum RegistryStreamKeys {
var_groups = 'var_groups',
deprecated = 'deprecated',
migrate_from = 'migrate_from',
+ sections = 'sections',
}
export interface RegistryStream {
@@ -380,6 +385,7 @@ export interface RegistryStream {
[RegistryStreamKeys.var_groups]?: RegistryVarGroup[];
[RegistryStreamKeys.deprecated]?: DeprecationInfo;
[RegistryStreamKeys.migrate_from]?: string;
+ [RegistryStreamKeys.sections]?: RegistrySection[];
}
export type RegistryStreamWithDataStream = RegistryStream & { data_stream: RegistryDataStream };
@@ -554,8 +560,13 @@ export type RegistryVarType =
| 'string'
| 'textarea'
| 'duration'
- | 'url'
- | 'section_header';
+ | 'url';
+
+export interface RegistrySection {
+ name: string;
+ title: string;
+ description?: string;
+}
export enum RegistryVarsEntryKeys {
name = 'name',
title = 'title',
@@ -574,6 +585,7 @@ export enum RegistryVarsEntryKeys {
max_duration = 'max_duration',
url_allowed_schemes = 'url_allowed_schemes',
deprecated = 'deprecated',
+ section = 'section',
}
// EPR types this as `[]map[string]interface{}`
@@ -601,6 +613,7 @@ export interface RegistryVarsEntry {
[RegistryVarsEntryKeys.max_duration]?: string;
[RegistryVarsEntryKeys.url_allowed_schemes]?: string[];
[RegistryVarsEntryKeys.deprecated]?: DeprecationInfo;
+ [RegistryVarsEntryKeys.section]?: string;
}
// Deprecated as part of the removing public references to saved object schemas
diff --git a/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts b/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts
index 39c34505d5466..894555058da85 100644
--- a/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts
+++ b/x-pack/platform/plugins/shared/fleet/common/types/models/package_spec.ts
@@ -9,6 +9,7 @@ import type {
DeprecationInfo,
RegistryElasticsearch,
RegistryPolicyTemplate,
+ RegistrySection,
RegistryVarsEntry,
} from './epm';
@@ -63,6 +64,7 @@ export interface PackageSpecManifest {
policy_templates?: RegistryPolicyTemplate[];
vars?: RegistryVarsEntry[];
var_groups?: RegistryVarGroup[];
+ sections?: RegistrySection[];
owner: { github?: string; type?: 'elastic' | 'partner' | 'community' };
elasticsearch?: Pick<
RegistryElasticsearch,
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.test.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.test.tsx
index 4bc70f1cdd50a..6c7dc68982b0b 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.test.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.test.tsx
@@ -86,6 +86,106 @@ describe('PackagePolicyInputConfig', () => {
expect(utils.queryByText('Deprecated Var')).not.toBeInTheDocument();
});
+ it('should render section titles for vars with a matching section attribute', () => {
+ const renderer = createFleetTestRendererMock();
+ const mockOnChange = jest.fn();
+
+ const utils = renderer.render(
+
+ );
+
+ expect(utils.queryByText('Ungrouped Var')).toBeInTheDocument();
+ expect(utils.queryByText('Authentication')).toBeInTheDocument();
+ expect(utils.queryByText('Auth settings')).toBeInTheDocument();
+ expect(utils.queryByText('Auth Var')).toBeInTheDocument();
+ });
+
+ it('should render vars without a section attribute as ungrouped even when sections are defined', () => {
+ const renderer = createFleetTestRendererMock();
+ const mockOnChange = jest.fn();
+
+ const utils = renderer.render(
+
+ );
+
+ expect(utils.queryByText('Ungrouped Var')).toBeInTheDocument();
+ // Section header should not render if no vars reference it
+ expect(utils.queryByText('Authentication')).not.toBeInTheDocument();
+ });
+
+ it('should not render section titles when no sections prop is provided', () => {
+ const renderer = createFleetTestRendererMock();
+ const mockOnChange = jest.fn();
+
+ const utils = renderer.render(
+
+ );
+
+ expect(utils.queryByText('Auth Var')).toBeInTheDocument();
+ expect(utils.queryByText('Authentication')).not.toBeInTheDocument();
+ });
+
it('should show deprecated vars on edit page (isEditPage=true)', () => {
const renderer = createFleetTestRendererMock();
const mockOnChange = jest.fn();
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx
index 4363ec92d88da..716d605f87991 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_config.tsx
@@ -12,6 +12,7 @@ import {
EuiFlexGroup,
EuiFlexItem,
EuiText,
+ EuiTitle,
EuiSpacer,
EuiButtonEmpty,
useIsWithinMinBreakpoint,
@@ -21,6 +22,7 @@ import {
import type {
NewPackagePolicyInput,
NewPackagePolicyInputStream,
+ RegistrySection,
RegistryVarGroup,
RegistryVarsEntry,
} from '../../../../../../types';
@@ -33,6 +35,59 @@ import { useAgentless } from '../../../single_page_layout/hooks/setup_technology
import { PackagePolicyInputVarField } from './package_policy_input_var_field';
import { VarGroupSelector } from './var_group_selector';
+const renderVarsWithSections = (
+ vars: RegistryVarsEntry[],
+ renderVarItem: (varDef: RegistryVarsEntry) => React.ReactNode,
+ sectionDefs?: RegistrySection[]
+): React.ReactNode => {
+ if (!sectionDefs?.length) {
+ return <>{vars.map((varDef) => renderVarItem(varDef))}>;
+ }
+
+ const sectionSet = new Set(sectionDefs.map((s) => s.name));
+ const varsBySectionName = new Map();
+ const varsWithoutSection: RegistryVarsEntry[] = [];
+
+ for (const varDef of vars) {
+ const sectionName = varDef.section;
+ if (sectionName && sectionSet.has(sectionName)) {
+ if (!varsBySectionName.has(sectionName)) {
+ varsBySectionName.set(sectionName, []);
+ }
+ varsBySectionName.get(sectionName)!.push(varDef);
+ } else {
+ varsWithoutSection.push(varDef);
+ }
+ }
+
+ return (
+ <>
+ {varsWithoutSection.map((varDef) => renderVarItem(varDef))}
+ {sectionDefs.map((section) => {
+ const sectionVars = varsBySectionName.get(section.name);
+ if (!sectionVars?.length) return null;
+ return (
+
+
+
+
+ {section.title}
+
+ {section.description && (
+
+ {section.description}
+
+ )}
+
+ {sectionVars.map((varDef) => renderVarItem(varDef))}
+
+
+ );
+ })}
+ >
+ );
+};
+
export interface StreamAdvancedVarsConfig {
vars: RegistryVarsEntry[];
packagePolicyInputStream: NewPackagePolicyInputStream;
@@ -53,6 +108,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{
onVarGroupSelectionChange?: (groupName: string, optionName: string) => void;
showDescriptionColumn?: boolean;
streamAdvancedVars?: StreamAdvancedVarsConfig;
+ sections?: RegistrySection[];
}> = memo(
({
hasInputStreams,
@@ -67,6 +123,7 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{
onVarGroupSelectionChange,
showDescriptionColumn = true,
streamAdvancedVars,
+ sections,
}) => {
// Showing advanced options toggle state
const [isShowingAdvanced, setIsShowingAdvanced] = useState(false);
@@ -198,36 +255,38 @@ export const PackagePolicyInputConfig: React.FunctionComponent<{
) : null}
- {preGroupRequiredVars.map((varDef) => {
- const { name: varName, type: varType } = varDef;
-
- const value = packagePolicyInput.vars?.[varName]?.value;
- const frozen = packagePolicyInput.vars?.[varName]?.frozen;
-
- return (
-
- {
- updatePackagePolicyInput({
- vars: {
- ...packagePolicyInput.vars,
- [varName]: {
- type: varType,
- value: newValue,
+ {renderVarsWithSections(
+ preGroupRequiredVars,
+ (varDef) => {
+ const { name: varName, type: varType } = varDef;
+ const value = packagePolicyInput.vars?.[varName]?.value;
+ const frozen = packagePolicyInput.vars?.[varName]?.frozen;
+ return (
+
+ {
+ updatePackagePolicyInput({
+ vars: {
+ ...packagePolicyInput.vars,
+ [varName]: {
+ type: varType,
+ value: newValue,
+ },
},
- },
- });
- }}
- errors={inputValidationResults.vars?.[varName]}
- forceShowErrors={forceShowErrors}
- isEditPage={isEditPage}
- />
-
- );
- })}
+ });
+ }}
+ errors={inputValidationResults.vars?.[varName]}
+ forceShowErrors={forceShowErrors}
+ isEditPage={isEditPage}
+ />
+
+ );
+ },
+ sections
+ )}
{varGroups?.map((varGroup) => (
))}
- {postGroupRequiredVars.map((varDef) => {
- const { name: varName, type: varType } = varDef;
-
- const value = packagePolicyInput.vars?.[varName]?.value;
- const frozen = packagePolicyInput.vars?.[varName]?.frozen;
-
- return (
-
- {
- updatePackagePolicyInput({
- vars: {
- ...packagePolicyInput.vars,
- [varName]: {
- type: varType,
- value: newValue,
+ {renderVarsWithSections(
+ postGroupRequiredVars,
+ (varDef) => {
+ const { name: varName, type: varType } = varDef;
+ const value = packagePolicyInput.vars?.[varName]?.value;
+ const frozen = packagePolicyInput.vars?.[varName]?.frozen;
+ return (
+
+ {
+ updatePackagePolicyInput({
+ vars: {
+ ...packagePolicyInput.vars,
+ [varName]: {
+ type: varType,
+ value: newValue,
+ },
},
- },
- });
- }}
- errors={inputValidationResults.vars?.[varName]}
- forceShowErrors={forceShowErrors}
- isEditPage={isEditPage}
- />
-
- );
- })}
+ });
+ }}
+ errors={inputValidationResults.vars?.[varName]}
+ forceShowErrors={forceShowErrors}
+ isEditPage={isEditPage}
+ />
+
+ );
+ },
+ sections
+ )}
{allAdvancedVars.length ? (
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx
index 72092f3f1122c..59c2ec345cd4f 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_panel.tsx
@@ -524,6 +524,7 @@ export const PackagePolicyInputPanel: React.FunctionComponent<{
onVarGroupSelectionChange={handleInputVarGroupSelectionChange}
showDescriptionColumn={!isSingleInputAndStreams}
streamAdvancedVars={consolidatedStreamAdvancedVars}
+ sections={packageInput.sections}
/>
{hasInputStreams &&
!shouldConsolidateAdvancedSections &&
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx
index 5f6aa973b4bab..811d67b32515a 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/components/steps/components/package_policy_input_var_field.tsx
@@ -13,7 +13,6 @@ import {
EuiSwitch,
EuiFieldText,
EuiText,
- EuiTitle,
EuiFieldPassword,
EuiCodeBlock,
EuiTextArea,
@@ -113,21 +112,6 @@ export const PackagePolicyInputVarField: React.FunctionComponent
-
- {title || name}
-
- {description && (
-
-
-
- )}
- >
- );
- }
-
if (name === DATASET_VAR_NAME && packageType === 'input') {
return (
([]);
const toggleIsManaged = (newIsManaged: boolean) => {
@@ -85,6 +92,17 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
const packageInfo = useMemo(() => packageInfoData?.item, [packageInfoData]);
+ // Skip the splash for agentless-only or agentless-default integrations — the
+ // "Install Elastic Agent" first step is not applicable to those flows.
+ const isAgentlessByDefault = useMemo(
+ () =>
+ isAgentlessEnabled &&
+ !!packageInfo &&
+ (isOnlyAgentlessIntegration(packageInfo, integration) ||
+ isAgentlessSetupDefault(isAgentlessDefault, packageInfo, integration)),
+ [isAgentlessEnabled, isAgentlessDefault, packageInfo, integration]
+ );
+
const integrationInfo = useMemo(() => {
if (!integration) return;
return packageInfo?.policy_templates?.find(
@@ -107,7 +125,7 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
...(prerelease ? { prerelease: 'true' } : {}),
});
- if (onSplash || !packageInfo) {
+ if ((onSplash && !isAgentlessByDefault) || !packageInfo) {
return (
{
- // section_header vars are decorative and always render with required vars, never under "Advanced options"
- if (varDef.type === 'section_header') {
- return false;
- }
-
// If var is in a selected var_group option, treat as non-advanced (override show_user: false)
if (
varGroups &&
diff --git a/x-pack/platform/plugins/shared/fleet/public/types/index.ts b/x-pack/platform/plugins/shared/fleet/public/types/index.ts
index a3b11d049dd9e..59585834b47c2 100644
--- a/x-pack/platform/plugins/shared/fleet/public/types/index.ts
+++ b/x-pack/platform/plugins/shared/fleet/public/types/index.ts
@@ -102,6 +102,7 @@ export type {
CategorySummaryList,
PackageInfo,
PackageMetadata,
+ RegistrySection,
RegistryVarsEntry,
RegistryInput,
RegistryStream,