From 0435b4fe2675e1f8f4bad4acff7a01e8e1936cf3 Mon Sep 17 00:00:00 2001 From: Sean Rathier Date: Wed, 22 Jul 2026 10:33:07 -0400 Subject: [PATCH] Migrate agentless CSPM setup to managed_integrations API Replace the deprecated two-step flow (POST agent_policies + POST package_policies with supports_agentless) with a single POST to /api/fleet/managed_integrations. Add delete_managed_integration for cleanup, detected in purge_integrations by agnt_policy_id == pkg_policy_id. Co-Authored-By: Claude Sonnet 4.6 --- tests/fleet_api/managed_integration_api.py | 70 +++++++++++++++++++ .../install_agentless_integrations.py | 30 ++------ .../integrations_setup/purge_integrations.py | 11 ++- 3 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 tests/fleet_api/managed_integration_api.py diff --git a/tests/fleet_api/managed_integration_api.py b/tests/fleet_api/managed_integration_api.py new file mode 100644 index 0000000000..2484d785ae --- /dev/null +++ b/tests/fleet_api/managed_integration_api.py @@ -0,0 +1,70 @@ +""" +This module contains API calls related to the managed integrations API. +(/api/fleet/managed_integrations — replaces the deprecated agentless_policies endpoint) +""" + +from fleet_api.base_call_api import APICallException, perform_api_call +from loguru import logger +from munch import Munch, munchify + +_FIELDS_NOT_ACCEPTED = frozenset({"policy_id", "policy_ids", "supports_agentless", "output_id"}) + + +def create_managed_integration(cfg: Munch, json_policy: dict) -> str: + """Create a managed (agentless) integration via the unified managed_integrations endpoint. + + Args: + cfg (Munch): Config object containing authentication data. + json_policy (dict): Simplified package policy body. Fields policy_id, policy_ids, + supports_agentless, and output_id are stripped automatically. + + Returns: + str: The ID of the created managed integration. + + Raises: + APICallException: If the API call fails or returns a non-200 status code. + """ + url = f"{cfg.kibana_url}/api/fleet/managed_integrations" + body = {k: v for k, v in json_policy.items() if k not in _FIELDS_NOT_ACCEPTED} + + try: + response = perform_api_call( + method="POST", + url=url, + auth=cfg.auth, + params={"json": body}, + ) + managed_id = munchify(response).item.id + logger.info(f"Managed integration '{managed_id}' created successfully") + return managed_id + except APICallException as api_ex: + logger.error( + f"API call failed, status code {api_ex.status_code}. Response: {api_ex.response_text}", + ) + raise api_ex + + +def delete_managed_integration(cfg: Munch, policy_id: str): + """Delete a managed integration. + + Args: + cfg (Munch): Config object containing authentication data. + policy_id (str): The ID of the managed integration to delete. + + Raises: + APICallException: If the API call fails or returns a non-200 status code. + """ + url = f"{cfg.kibana_url}/api/fleet/managed_integrations/{policy_id}" + + try: + perform_api_call( + method="DELETE", + url=url, + auth=cfg.auth, + ) + logger.info(f"Managed integration '{policy_id}' deleted successfully") + except APICallException as api_ex: + logger.error( + f"API call failed, status code {api_ex.status_code}. Response: {api_ex.response_text}", + ) + raise api_ex diff --git a/tests/integrations_setup/install_agentless_integrations.py b/tests/integrations_setup/install_agentless_integrations.py index e0e8dd6c68..28d6eed567 100644 --- a/tests/integrations_setup/install_agentless_integrations.py +++ b/tests/integrations_setup/install_agentless_integrations.py @@ -8,11 +8,9 @@ """ import json -import os import configuration_fleet as cnfg -from fleet_api.agent_policy_api import create_agent_policy -from fleet_api.package_policy_api import create_cspm_integration +from fleet_api.managed_integration_api import create_managed_integration from loguru import logger from package_policy import generate_policy_template, generate_random_name, load_data from state_file_manager import HostType, PolicyState, state_manager @@ -82,7 +80,6 @@ def generate_gcp_integration_data(): generate_azure_integration_data(), generate_gcp_integration_data(), ] - serverless_mode = os.getenv("SERVERLESS_MODE", "false").lower() == "true" cspm_template = generate_policy_template( cfg=cnfg.elk_config, @@ -90,35 +87,22 @@ def generate_gcp_integration_data(): ) for integration_data in integrations: INTEGRATION_NAME = integration_data["name"] - AGENTLESS_INPUT = { - "name": f"Agentless policy for {INTEGRATION_NAME}", - "supports_agentless": True, - "fleet_server_host_id": "default-fleet-server" if serverless_mode else "fleet-default-fleet-server-host", - } logger.info(f"Starting installation of agentless-agent {INTEGRATION_NAME} integration.") - agent_data, package_data = load_data( + _, package_data = load_data( cfg=cnfg.elk_config, - agent_input=AGENTLESS_INPUT, + agent_input={"name": INTEGRATION_NAME}, package_input=integration_data, stream_name="cloud_security_posture.findings", ) - logger.info("Create agentless-agent policy") - agent_policy_id = create_agent_policy(cfg=cnfg.elk_config, json_policy=agent_data) - - logger.info(f"Create agentless-agent {INTEGRATION_NAME} integration") - package_policy_id = create_cspm_integration( - cfg=cnfg.elk_config, - pkg_policy=package_data, - agent_policy_id=agent_policy_id, - cspm_data={}, - ) + logger.info(f"Create managed integration for {INTEGRATION_NAME}") + managed_id = create_managed_integration(cfg=cnfg.elk_config, json_policy=package_data) state_manager.add_policy( PolicyState( - agent_policy_id, - package_policy_id, + managed_id, + managed_id, 1, [], HostType.KUBERNETES.value, diff --git a/tests/integrations_setup/purge_integrations.py b/tests/integrations_setup/purge_integrations.py index 1c0b273b0d..922ff74bb8 100755 --- a/tests/integrations_setup/purge_integrations.py +++ b/tests/integrations_setup/purge_integrations.py @@ -20,6 +20,7 @@ get_agents, unenroll_agents_from_policy, ) +from fleet_api.managed_integration_api import delete_managed_integration from fleet_api.package_policy_api import delete_package_policy from loguru import logger from state_file_manager import state_manager @@ -29,12 +30,18 @@ def purge_integrations(): """ Purge integrations and policies based on stored IDs in the state_data.json file. """ - # Check if the state_data.json file exists - agents = get_agents(cfg=cnfg.elk_config) # Delete policies based on the stored IDs for policy in state_manager.get_policies(): logger.info("Deleting policy", policy.pkg_policy_id, policy.agnt_policy_id) + + # When agnt_policy_id == pkg_policy_id the entry was created via the managed_integrations + # API (a single combined resource). Deleting the managed integration cleans up both the + # agent policy and package policy automatically. + if policy.agnt_policy_id == policy.pkg_policy_id: + delete_managed_integration(cfg=cnfg.elk_config, policy_id=policy.agnt_policy_id) + continue + delete_package_policy(cfg=cnfg.elk_config, policy_ids=[policy.pkg_policy_id]) agents_list = [item.agent.id for item in agents if item.policy_id == policy.agnt_policy_id]