diff --git a/plugins/modules/aci_interface_policy_port_channel_member.py b/plugins/modules/aci_interface_policy_port_channel_member.py
new file mode 100644
index 000000000..44209b342
--- /dev/null
+++ b/plugins/modules/aci_interface_policy_port_channel_member.py
@@ -0,0 +1,285 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Akini Ross (@akinross)
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+ANSIBLE_METADATA = {
+ "metadata_version": "1.1",
+ "status": ["preview"],
+ "supported_by": "community",
+}
+
+DOCUMENTATION = r"""
+---
+module: aci_interface_policy_port_channel_member
+short_description: Manage Port Channel Member interface policies (lacp:IfPol)
+description:
+- Manage Port Channel Member interface policy configuration on Cisco ACI fabrics.
+options:
+ name:
+ description:
+ - The name of the Port Channel Member interface policy.
+ type: str
+ aliases: [ port_channel_member_interface_policy ]
+ description:
+ description:
+ - The description of the Port Channel Member interface policy.
+ type: str
+ priority:
+ description:
+ - The priority of the Port Channel Member interface policy.
+ - The APIC defaults to C(32768) when not provided.
+ - Accepted values range between C(1) and C(65535).
+ type: int
+ transmit_rate:
+ description:
+ - The transmit rate of the Port Channel Member interface policy.
+ - The APIC defaults to C(normal) when not provided.
+ type: str
+ choices: [ normal, fast ]
+ name_alias:
+ description:
+ - The alias for the current object.
+ - This relates to the nameAlias field in ACI.
+ type: str
+ state:
+ description:
+ - Use C(present) or C(absent) for adding or removing.
+ - Use C(query) for listing an object or multiple objects.
+ type: str
+ choices: [ absent, present, query ]
+ default: present
+extends_documentation_fragment:
+- cisco.aci.aci
+- cisco.aci.annotation
+- cisco.aci.owner
+
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(lacp:IfPol).
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Akini Ross (@akinross)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: ansible_port_channel_member_policy
+ description: Ansible Port Channel Member interface policy
+ priority: 32700
+ transmit_rate: fast
+ state: present
+ delegate_to: localhost
+
+- name: Query a Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: ansible_port_channel_member_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Port Channel Member interface policies
+ cisco.aci.aci_interface_policy_port_channel_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Remove a Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ name: my_ntp_policy
+ state: absent
+ delegate_to: localhost
+"""
+
+RETURN = r"""
+ current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+ raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+ sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+ previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+ proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+ filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+ method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+ response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+ status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+ url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+ """
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ name=dict(type="str", aliases=["port_channel_member_interface_policy"]),
+ description=dict(type="str"),
+ priority=dict(type="int"),
+ transmit_rate=dict(type="str", choices=["normal", "fast"]),
+ name_alias=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["name"]],
+ ["state", "present", ["name"]],
+ ],
+ )
+
+ name = module.params.get("name")
+ description = module.params.get("description")
+ priority = module.params.get("priority")
+ transmit_rate = module.params.get("transmit_rate")
+ name_alias = module.params.get("name_alias")
+ state = module.params.get("state")
+
+ aci = ACIModule(module)
+ aci.construct_url(
+ root_class=dict(
+ aci_class="lacpIfPol",
+ aci_rn="infra/lacpifp-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="lacpIfPol",
+ class_config=dict(
+ name=name,
+ descr=description,
+ prio=priority,
+ txRate=transmit_rate,
+ nameAlias=name_alias,
+ ),
+ )
+
+ aci.get_diff(aci_class="lacpIfPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/integration/targets/aci_interface_policy_port_channel_member/aliases b/tests/integration/targets/aci_interface_policy_port_channel_member/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_interface_policy_port_channel_member/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_interface_policy_port_channel_member/tasks/main.yml b/tests/integration/targets/aci_interface_policy_port_channel_member/tasks/main.yml
new file mode 100644
index 000000000..5036d1836
--- /dev/null
+++ b/tests/integration/targets/aci_interface_policy_port_channel_member/tasks/main.yml
@@ -0,0 +1,225 @@
+# Test code for the ACI modules
+# Copyright: (c) 2025, Akini Ross (@akinross)
+
+# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+- name: Test that we have an ACI APIC host, ACI username and ACI password
+ ansible.builtin.fail:
+ msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.'
+ when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined
+
+# SET VARS
+- name: Set vars
+ ansible.builtin.set_fact:
+ aci_info: &aci_info
+ host: '{{ aci_hostname }}'
+ username: '{{ aci_username }}'
+ password: '{{ aci_password }}'
+ validate_certs: '{{ aci_validate_certs | default(false) }}'
+ use_ssl: '{{ aci_use_ssl | default(true) }}'
+ use_proxy: '{{ aci_use_proxy | default(true) }}'
+ output_level: '{{ aci_output_level | default("debug") }}'
+
+- name: Query system information
+ cisco.aci.aci_system:
+ <<: *aci_info
+ id: 1
+ state: query
+ register: version
+
+- name: Verify Cloud and Non-Cloud Sites in use.
+ ansible.builtin.include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml
+
+- name: Execute tasks only for ACI v5+ and non-cloud sites
+ when:
+ - version.current.0.topSystem.attributes.version is version('5', '>=')
+ - query_cloud.current == [] # This condition will execute only non-cloud sites
+ block: # block specifies execution of tasks within, based on conditions
+
+ # CLEAN ENVIRONMENT
+
+ - name: Clean existing Port Channel Member interface policies
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *aci_info
+ name: "{{ item }}"
+ state: absent
+ loop:
+ - ansible_port_channel_member_interface_policy_1
+ - ansible_port_channel_member_interface_policy_2
+
+ # CREATE
+
+ - name: Create Port Channel Member interface policy (check_mode)
+ cisco.aci.aci_interface_policy_port_channel_member: &create_port_channel_member_interface_policy
+ <<: *aci_info
+ name: ansible_port_channel_member_interface_policy_1
+ check_mode: true
+ register: cm_create_port_channel_member_interface_policy
+
+ - name: Create Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *create_port_channel_member_interface_policy
+ register: nm_create_port_channel_member_interface_policy
+
+ - name: Create Port Channel Member interface policy again
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *create_port_channel_member_interface_policy
+ register: nm_create_port_channel_member_interface_policy_again
+
+ - name: Assert Port Channel Member interface policy creation
+ ansible.builtin.assert:
+ that:
+ - cm_create_port_channel_member_interface_policy is changed
+ - cm_create_port_channel_member_interface_policy.previous == []
+ - cm_create_port_channel_member_interface_policy.current == []
+ - cm_create_port_channel_member_interface_policy.proposed.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_create_port_channel_member_interface_policy is changed
+ - nm_create_port_channel_member_interface_policy.previous == []
+ - nm_create_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_create_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.descr == ''
+ - nm_create_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.prio == '32768'
+ - nm_create_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.txRate == 'normal'
+ - nm_create_port_channel_member_interface_policy_again is not changed
+ - nm_create_port_channel_member_interface_policy_again.previous == nm_create_port_channel_member_interface_policy_again.current
+ - nm_create_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_create_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.descr == ''
+ - nm_create_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.prio == '32768'
+ - nm_create_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.txRate == 'normal'
+
+ # UPDATE
+
+ - name: Update Port Channel Member interface policy (check_mode)
+ cisco.aci.aci_interface_policy_port_channel_member: &update_port_channel_member_interface_policy
+ <<: *aci_info
+ name: ansible_port_channel_member_interface_policy_1
+ description: description_changed
+ priority: 32700
+ transmit_rate: fast
+ check_mode: true
+ register: cm_update_port_channel_member_interface_policy
+
+ - name: Update Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *update_port_channel_member_interface_policy
+ register: nm_update_port_channel_member_interface_policy
+
+ - name: Update Port Channel Member interface policy again
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *update_port_channel_member_interface_policy
+ register: nm_update_port_channel_member_interface_policy_again
+
+ - name: Assert Port Channel Member interface policy update
+ ansible.builtin.assert:
+ that:
+ - cm_update_port_channel_member_interface_policy is changed
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.descr == ''
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.prio == '32768'
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.txRate == 'normal'
+ - cm_update_port_channel_member_interface_policy.proposed.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_update_port_channel_member_interface_policy.proposed.lacpIfPol.attributes.descr == 'description_changed'
+ - cm_update_port_channel_member_interface_policy.proposed.lacpIfPol.attributes.prio == '32700'
+ - cm_update_port_channel_member_interface_policy.proposed.lacpIfPol.attributes.txRate == 'fast'
+ - cm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.descr == ''
+ - cm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.prio == '32768'
+ - cm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.txRate == 'normal'
+ - nm_update_port_channel_member_interface_policy is changed
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.descr == ''
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.prio == '32768'
+ - cm_update_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.txRate == 'normal'
+ - nm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.descr == 'description_changed'
+ - nm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.prio == '32700'
+ - nm_update_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.txRate == 'fast'
+ - nm_update_port_channel_member_interface_policy_again is not changed
+ - nm_update_port_channel_member_interface_policy_again.previous == nm_update_port_channel_member_interface_policy_again.current
+ - nm_update_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_update_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.descr == 'description_changed'
+ - nm_update_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.prio == '32700'
+ - nm_update_port_channel_member_interface_policy_again.current.0.lacpIfPol.attributes.txRate == 'fast'
+
+ # QUERY
+
+ - name: Create another Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *create_port_channel_member_interface_policy
+ name: ansible_port_channel_member_interface_policy_2
+
+ - name: Query Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *create_port_channel_member_interface_policy
+ state: query
+ register: query_one
+
+ - name: Query all Port Channel Member interface policies
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *aci_info
+ state: query
+ register: query_all
+
+ - name: Assert Port Channel Member interface policy query
+ ansible.builtin.assert:
+ that:
+ - query_one is not changed
+ - query_one.current | length == 1
+ - query_one.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - query_one.current.0.lacpIfPol.attributes.descr == 'description_changed'
+ - query_one.current.0.lacpIfPol.attributes.prio == '32700'
+ - query_one.current.0.lacpIfPol.attributes.txRate == 'fast'
+ - query_all is not changed
+ - query_all.current | length >= 2
+ - "'ansible_port_channel_member_interface_policy_1' in query_all.current | map(attribute='lacpIfPol.attributes.name') | list"
+ - "'ansible_port_channel_member_interface_policy_2' in query_all.current | map(attribute='lacpIfPol.attributes.name') | list"
+
+ # DELETE
+
+ - name: Delete Port Channel Member interface policy (check_mode)
+ cisco.aci.aci_interface_policy_port_channel_member: &delete_port_channel_member_interface_policy
+ <<: *update_port_channel_member_interface_policy
+ state: absent
+ register: cm_delete_port_channel_member_interface_policy
+ check_mode: true
+
+ - name: Delete Port Channel Member interface policy
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *delete_port_channel_member_interface_policy
+ register: nm_delete_port_channel_member_interface_policy
+
+ - name: Delete Port Channel Member interface policy again
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *delete_port_channel_member_interface_policy
+ register: nm_delete_port_channel_member_interface_policy_again
+
+ - name: Assert Port Channel Member interface policy deletion
+ ansible.builtin.assert:
+ that:
+ - cm_delete_port_channel_member_interface_policy is changed
+ - cm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.descr == 'description_changed'
+ - cm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.prio == '32700'
+ - cm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.txRate == 'fast'
+ - cm_delete_port_channel_member_interface_policy.proposed == {}
+ - cm_delete_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - cm_delete_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.descr == 'description_changed'
+ - cm_delete_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.prio == '32700'
+ - cm_delete_port_channel_member_interface_policy.current.0.lacpIfPol.attributes.txRate == 'fast'
+ - nm_delete_port_channel_member_interface_policy is changed
+ - nm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.name == 'ansible_port_channel_member_interface_policy_1'
+ - nm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.descr == 'description_changed'
+ - nm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.prio == '32700'
+ - nm_delete_port_channel_member_interface_policy.previous.0.lacpIfPol.attributes.txRate == 'fast'
+ - nm_delete_port_channel_member_interface_policy.current == []
+ - nm_delete_port_channel_member_interface_policy_again is not changed
+ - nm_delete_port_channel_member_interface_policy_again.previous == []
+ - nm_delete_port_channel_member_interface_policy_again.current == []
+
+ # CLEAN ENVIRONMENT
+
+ - name: Clean remaining Port Channel Member interface policies
+ cisco.aci.aci_interface_policy_port_channel_member:
+ <<: *aci_info
+ name: ansible_port_channel_member_interface_policy_2
+ state: absent
+