diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py
index a6fa9cb129fb6a1bef3a8599d39579850c4402f8..5046144ad6e89a81d6cdaa10d9d512e47631d838 100644
--- a/plugins/modules/ipaservice.py
+++ b/plugins/modules/ipaservice.py
@@ -45,6 +45,127 @@ options:
     elements: str
     required: true
     aliases: ["service"]
+  services:
+    description: The list of service dicts.
+    type: list
+    elements: dict
+    suboptions:
+      name:
+        description: The service to manage
+        type: str
+        required: true
+        aliases: ["service"]
+      certificate:
+        description: Base-64 encoded service certificate.
+        required: false
+        type: list
+        elements: str
+        aliases: ["usercertificate"]
+      pac_type:
+        description: Supported PAC type.
+        required: false
+        choices: ["MS-PAC", "PAD", "NONE", ""]
+        type: list
+        elements: str
+        aliases: ["pac_type", "ipakrbauthzdata"]
+      auth_ind:
+        description: Defines an allow list for Authentication Indicators.
+        type: list
+        elements: str
+        required: false
+        choices: ["otp", "radius", "pkinit", "hardened", ""]
+        aliases: ["krbprincipalauthind"]
+      skip_host_check:
+        description: Skip checking if host object exists.
+        required: False
+        type: bool
+      force:
+        description: Force principal name even if host is not in DNS.
+        required: False
+        type: bool
+      requires_pre_auth:
+        description: Pre-authentication is required for the service.
+        required: false
+        type: bool
+        aliases: ["ipakrbrequirespreauth"]
+      ok_as_delegate:
+        description: Client credentials may be delegated to the service.
+        required: false
+        type: bool
+        aliases: ["ipakrbokasdelegate"]
+      ok_to_auth_as_delegate:
+        description: Allow service to authenticate on behalf of a client.
+        required: false
+        type: bool
+        aliases: ["ipakrboktoauthasdelegate"]
+      principal:
+        description: List of principal aliases for the service.
+        required: false
+        type: list
+        elements: str
+        aliases: ["krbprincipalname"]
+      smb:
+        description: Add a SMB service.
+        required: false
+        type: bool
+      netbiosname:
+        description: NETBIOS name for the SMB service.
+        required: false
+        type: str
+      host:
+        description: Host that can manage the service.
+        required: false
+        type: list
+        elements: str
+        aliases: ["managedby_host"]
+      allow_create_keytab_user:
+        description: Users allowed to create a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_write_keys_user"]
+      allow_create_keytab_group:
+        description: Groups allowed to create a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_write_keys_group"]
+      allow_create_keytab_host:
+        description: Hosts allowed to create a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_write_keys_host"]
+      allow_create_keytab_hostgroup:
+        description: Host group allowed to create a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_write_keys_hostgroup"]
+      allow_retrieve_keytab_user:
+        description: User allowed to retrieve a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_read_keys_user"]
+      allow_retrieve_keytab_group:
+        description: Groups allowed to retrieve a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_read_keys_group"]
+      allow_retrieve_keytab_host:
+        description: Hosts allowed to retrieve a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_read_keys_host"]
+      allow_retrieve_keytab_hostgroup:
+        description: Host groups allowed to retrieve a keytab of this host.
+        required: false
+        type: list
+        elements: str
+        aliases: ["ipaallowedtoperform_read_keys_hostgroup"]
   certificate:
     description: Base-64 encoded service certificate.
     required: false
@@ -239,6 +360,15 @@ EXAMPLES = """
       - host1.example.com
       - host2.example.com
       action: member
+
+  # Ensure multiple services are present.
+  - ipaservice:
+      ipaadmin_password: SomeADMINpassword
+      services:
+      - name: HTTP/www.example.com
+        host:
+        - host1.example.com
+      - name: HTTP/www.service.com
 """
 
 RETURN = """
@@ -248,6 +378,9 @@ from ansible.module_utils.ansible_freeipa_module import \
     IPAAnsibleModule, compare_args_ipa, encode_certificate, \
     gen_add_del_lists, gen_add_list, gen_intersection_list, ipalib_errors, \
     api_get_realm, to_text
+from ansible.module_utils import six
+if six.PY3:
+    unicode = str
 
 
 def find_service(module, name):
@@ -321,8 +454,9 @@ def check_parameters(module, state, action, names):
          'allow_retrieve_keytab_hostgroup']
 
     if state == 'present':
-        if len(names) != 1:
-            module.fail_json(msg="Only one service can be added at a time.")
+        if names is not None and len(names) != 1:
+            module.fail_json(msg="Only one service can be added at a time "
+                                 "using 'name'.")
 
         if action == 'service':
             invalid = ['delete_continue']
@@ -338,9 +472,6 @@ def check_parameters(module, state, action, names):
             invalid.append('delete_continue')
 
     elif state == 'absent':
-        if len(names) < 1:
-            module.fail_json(msg="No name given.")
-
         if action == "service":
             invalid.extend(invalid_not_member)
         else:
@@ -360,67 +491,85 @@ def check_parameters(module, state, action, names):
 
 
 def init_ansible_module():
+    service_spec = dict(
+        # service attributesstr
+        certificate=dict(type="list", elements="str",
+                         aliases=['usercertificate'],
+                         default=None, required=False),
+        principal=dict(type="list", elements="str",
+                       aliases=["krbprincipalname"], default=None),
+        smb=dict(type="bool", required=False),
+        netbiosname=dict(type="str", required=False),
+        pac_type=dict(type="list", elements="str",
+                      aliases=["ipakrbauthzdata"],
+                      choices=["MS-PAC", "PAD", "NONE", ""]),
+        auth_ind=dict(type="list", elements="str",
+                      aliases=["krbprincipalauthind"],
+                      choices=["otp", "radius", "pkinit", "hardened", ""]),
+        skip_host_check=dict(type="bool"),
+        force=dict(type="bool"),
+        requires_pre_auth=dict(
+            type="bool", aliases=["ipakrbrequirespreauth"]),
+        ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"]),
+        ok_to_auth_as_delegate=dict(type="bool",
+                                    aliases=["ipakrboktoauthasdelegate"]),
+        host=dict(type="list", elements="str", aliases=["managedby_host"],
+                  required=False),
+        allow_create_keytab_user=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_write_keys_user']),
+        allow_retrieve_keytab_user=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_read_keys_user']),
+        allow_create_keytab_group=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_write_keys_group']),
+        allow_retrieve_keytab_group=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_read_keys_group']),
+        allow_create_keytab_host=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_write_keys_host']),
+        allow_retrieve_keytab_host=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_read_keys_host']),
+        allow_create_keytab_hostgroup=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_write_keys_hostgroup']),
+        allow_retrieve_keytab_hostgroup=dict(
+            type="list", elements="str", required=False, no_log=False,
+            aliases=['ipaallowedtoperform_read_keys_hostgroup']),
+        delete_continue=dict(type="bool", required=False,
+                             aliases=['continue']),
+    )
     ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
             name=dict(type="list", elements="str", aliases=["service"],
-                      required=True),
-            # service attributesstr
-            certificate=dict(type="list", elements="str",
-                             aliases=['usercertificate'],
-                             default=None, required=False),
-            principal=dict(type="list", elements="str",
-                           aliases=["krbprincipalname"], default=None),
-            smb=dict(type="bool", required=False),
-            netbiosname=dict(type="str", required=False),
-            pac_type=dict(type="list", elements="str",
-                          aliases=["ipakrbauthzdata"],
-                          choices=["MS-PAC", "PAD", "NONE", ""]),
-            auth_ind=dict(type="list", elements="str",
-                          aliases=["krbprincipalauthind"],
-                          choices=["otp", "radius", "pkinit", "hardened", ""]),
-            skip_host_check=dict(type="bool"),
-            force=dict(type="bool"),
-            requires_pre_auth=dict(
-                type="bool", aliases=["ipakrbrequirespreauth"]),
-            ok_as_delegate=dict(type="bool", aliases=["ipakrbokasdelegate"]),
-            ok_to_auth_as_delegate=dict(type="bool",
-                                        aliases=["ipakrboktoauthasdelegate"]),
-            host=dict(type="list", elements="str", aliases=["managedby_host"],
-                      required=False),
-            allow_create_keytab_user=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_write_keys_user']),
-            allow_retrieve_keytab_user=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_read_keys_user']),
-            allow_create_keytab_group=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_write_keys_group']),
-            allow_retrieve_keytab_group=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_read_keys_group']),
-            allow_create_keytab_host=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_write_keys_host']),
-            allow_retrieve_keytab_host=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_read_keys_host']),
-            allow_create_keytab_hostgroup=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_write_keys_hostgroup']),
-            allow_retrieve_keytab_hostgroup=dict(
-                type="list", elements="str", required=False, no_log=False,
-                aliases=['ipaallowedtoperform_read_keys_hostgroup']),
-            delete_continue=dict(type="bool", required=False,
-                                 aliases=['continue']),
+                      default=None, required=False),
+            services=dict(type="list",
+                          default=None,
+                          options=dict(
+                              # Here name is a simple string
+                              name=dict(type="str", required=True,
+                                        aliases=["service"]),
+                              # Add service specific parameters
+                              **service_spec
+                          ),
+                          elements='dict',
+                          required=False),
             # action
             action=dict(type="str", default="service",
                         choices=["member", "service"]),
             # state
             state=dict(type="str", default="present",
                        choices=["present", "absent", "disabled"]),
+
+            # Add service specific parameters for simple use case
+            **service_spec
         ),
+        mutually_exclusive=[["name", "services"]],
+        required_one_of=[["name", "services"]],
         supports_check_mode=True,
     )
 
@@ -436,6 +585,7 @@ def main():
 
     # general
     names = ansible_module.params_get("name")
+    services = ansible_module.params_get("services")
 
     # service attributes
     principal = ansible_module.params_get("principal")
@@ -462,8 +612,16 @@ def main():
     state = ansible_module.params_get("state")
 
     # check parameters
+    if (names is None or len(names) < 1) and \
+       (services is None or len(services) < 1):
+        ansible_module.fail_json(msg="At least one name or services is "
+                                     "required")
     check_parameters(ansible_module, state, action, names)
 
+    # Use services if names is None
+    if services is not None:
+        names = services
+
     # Init
 
     changed = False
@@ -480,8 +638,39 @@ def main():
 
         commands = []
         keytab_members = ["user", "group", "host", "hostgroup"]
+        service_set = set()
 
-        for name in names:
+        for service in names:
+            if isinstance(service, dict):
+                name = service.get("name")
+                if name in service_set:
+                    ansible_module.fail_json(
+                        msg="service '%s' is used more than once" % name)
+                service_set.add(name)
+                principal = service.get("principal")
+                certificate = service.get("certificate")
+                pac_type = service.get("pac_type")
+                auth_ind = service.get("auth_ind")
+                skip_host_check = service.get("skip_host_check")
+                if skip_host_check and not has_skip_host_check:
+                    ansible_module.fail_json(
+                        msg="Skipping host check is not supported by your IPA "
+                            "version")
+                force = service.get("force")
+                requires_pre_auth = service.get("requires_pre_auth")
+                ok_as_delegate = service.get("ok_as_delegate")
+                ok_to_auth_as_delegate = service.get("ok_to_auth_as_delegate")
+                smb = service.get("smb")
+                netbiosname = service.get("netbiosname")
+                host = service.get("host")
+
+                delete_continue = service.get("delete_continue")
+
+            elif isinstance(service, (str, unicode)):
+                name = service
+            else:
+                ansible_module.fail_json(msg="Service '%s' is not valid" %
+                                         repr(service))
             res_find = find_service(ansible_module, name)
             res_principals = []
 
diff --git a/tests/service/create_services_json.yml b/tests/service/create_services_json.yml
new file mode 100644
index 0000000000000000000000000000000000000000..197648b5fd6c2168ede761a384a0ca22faf66177
--- /dev/null
+++ b/tests/service/create_services_json.yml
@@ -0,0 +1,22 @@
+---
+- name: Create services.json
+  hosts: localhost
+
+  tasks:
+  - name: Check if services.json exists
+    ansible.builtin.stat:
+      path: services.json
+    register: register_stat_services
+
+  - name: Create services.json
+    ansible.builtin.command: /bin/bash services.sh 500
+    when: not register_stat_services.stat.exists
+
+  - name: Check if hosts.json exists
+    ansible.builtin.stat:
+      path: hosts.json
+    register: register_stat_hosts
+
+  - name: Create hosts.json
+    ansible.builtin.command: /bin/bash hosts.sh 500
+    when: not register_stat_hosts.stat.exists
diff --git a/tests/service/hosts.sh b/tests/service/hosts.sh
new file mode 100644
index 0000000000000000000000000000000000000000..2a64855c5158bb83b4c14e0659b448a393bc9ef6
--- /dev/null
+++ b/tests/service/hosts.sh
@@ -0,0 +1,24 @@
+#!/bin/bash -eu
+
+NUM=${1-1000}
+FILE="hosts.json"
+
+echo "{" > "$FILE"
+
+echo "  \"host_list\": [" >> "$FILE"
+
+for i in $(seq 1 "$NUM"); do
+    {
+        echo "    {"
+        echo "      \"name\": \"www.example$i.com\""
+    } >> "$FILE"
+    if [ "$i" -lt "$NUM" ]; then
+       echo "    }," >> "$FILE"
+    else
+       echo "    }" >> "$FILE"
+    fi
+done
+
+echo "  ]" >> "$FILE"
+
+echo "}" >> "$FILE"
diff --git a/tests/service/services.sh b/tests/service/services.sh
new file mode 100644
index 0000000000000000000000000000000000000000..79f3b38acef44c3658c8a1ca5f0ea77141f7b261
--- /dev/null
+++ b/tests/service/services.sh
@@ -0,0 +1,25 @@
+#!/bin/bash -eu
+
+NUM=${1-1000}
+FILE="services.json"
+
+echo "{" > "$FILE"
+
+echo "  \"service_list\": [" >> "$FILE"
+
+for i in $(seq 1 "$NUM"); do
+    {
+        echo "    {"
+        echo "      \"name\": \"HTTP/www.example$i.com\","
+        echo "      \"principal\": \"host/test.example$i.com\""
+    } >> "$FILE"
+    if [ "$i" -lt "$NUM" ]; then
+       echo "    }," >> "$FILE"
+    else
+       echo "    }" >> "$FILE"
+    fi
+done
+
+echo "  ]" >> "$FILE"
+
+echo "}" >> "$FILE"
diff --git a/tests/service/services_absent.sh b/tests/service/services_absent.sh
new file mode 100644
index 0000000000000000000000000000000000000000..80d0b796fabfdcea1e9d21f1321c35119bac9ed7
--- /dev/null
+++ b/tests/service/services_absent.sh
@@ -0,0 +1,22 @@
+#!/bin/bash -eu
+
+NUM=1000
+FILE="services_absent.json"
+
+echo "{" > "$FILE"
+
+echo "  \"services\": [" >> "$FILE"
+
+for i in $(seq 1 "$NUM"); do
+    echo "    {" >> "$FILE"
+    echo "      \"name\": \"HTTP/www.example$i.com\"," >> "$FILE"
+    if [ "$i" -lt "$NUM" ]; then
+       echo "    }," >> "$FILE"
+    else
+       echo "    }" >> "$FILE"
+    fi
+done
+
+echo "  ]" >> "$FILE"
+
+echo "}" >> "$FILE"
diff --git a/tests/service/test_services_absent.yml b/tests/service/test_services_absent.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a212ee24f17e6afd2c36a553e867d63a0521095e
--- /dev/null
+++ b/tests/service/test_services_absent.yml
@@ -0,0 +1,32 @@
+---
+- name: Include create_services_json.yml
+  ansible.builtin.import_playbook: create_services_json.yml
+
+- name: Test services absent
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  - name: Include services.json
+    ansible.builtin.include_vars:
+      file: services.json  # noqa 505
+
+  - name: Create dict with service names
+    ansible.builtin.set_fact:
+      services_names: "{{ services_names | default([]) + [{'name': item.name}] }}"
+    loop: "{{ service_list }}"
+
+  - name: Services absent len:{{ service_list | length }}
+    ipaservice:
+      ipaadmin_password: SomeADMINpassword
+      services: "{{ services_names }}"
+      state: absent
+
+- name: Remove services.json
+  hosts: localhost
+  tasks:
+  - name: Remove services.json
+    ansible.builtin.file:
+      state: absent
+      path: services.json
diff --git a/tests/service/test_services_present.yml b/tests/service/test_services_present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b8491fb717b0aaa63c2fa1e43156bf3a4fe3d73f
--- /dev/null
+++ b/tests/service/test_services_present.yml
@@ -0,0 +1,39 @@
+---
+- name: Include create_services_json.yml
+  ansible.builtin.import_playbook: create_services_json.yml
+
+- name: Test services present
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  - name: Include services.json
+    ansible.builtin.include_vars:
+      file: services.json  # noqa 505
+
+  - name: Include hosts.json
+    ansible.builtin.include_vars:
+      file: hosts.json  # noqa 505
+
+  - name: Hosts present len:{{ host_list | length }}
+    ipahost:
+      ipaadmin_password: SomeADMINpassword
+      hosts: "{{ host_list }}"
+
+  - name: Services present len:{{ service_list | length }}
+    ipaservice:
+      ipaadmin_password: SomeADMINpassword
+      services: "{{ service_list }}"
+
+- name: Remove services.json
+  hosts: localhost
+  tasks:
+  - name: Remove services.json
+    ansible.builtin.file:
+      state: absent
+      path: services.json
+  - name: Remove hosts.json
+    ansible.builtin.file:
+      state: absent
+      path: hosts.json
diff --git a/tests/service/test_services_present_slice.yml b/tests/service/test_services_present_slice.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d9e7055d3e6e36c30b091338d52794db10a97b36
--- /dev/null
+++ b/tests/service/test_services_present_slice.yml
@@ -0,0 +1,46 @@
+---
+- name: Include create_services_json.yml
+  ansible.builtin.import_playbook: create_services_json.yml
+
+- name: Test services present slice
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  vars:
+    slice_size: 500
+  tasks:
+  - name: Include services.json
+    ansible.builtin.include_vars:
+      file: services.json  # noqa 505
+  - name: Include hosts.json
+    ansible.builtin.include_vars:
+      file: hosts.json  # noqa 505
+  - name: Size of services slice.
+    ansible.builtin.debug:
+      msg: "{{ service_list | length }}"
+  - name: Size of hosts slice.
+    ansible.builtin.debug:
+      msg: "{{ host_list | length }}"
+  - name: Hosts present
+    ipahost:
+      ipaadmin_password: SomeADMINpassword
+      hosts: "{{ host_list[item : item + slice_size] }}"
+    loop: "{{ range(0, service_list | length, slice_size) | list }}"
+  - name: Services present
+    ipaservice:
+      ipaadmin_password: SomeADMINpassword
+      services: "{{ service_list[item : item + slice_size] }}"
+    loop: "{{ range(0, service_list | length, slice_size) | list }}"
+
+- name: Remove services.json
+  hosts: localhost
+  tasks:
+  - name: Remove services.json
+    ansible.builtin.file:
+      state: absent
+      path: services.json
+  - name: Remove hosts.json
+    ansible.builtin.file:
+      state: absent
+      path: hosts.json
diff --git a/tests/service/test_services_without_skip_host_check.yml b/tests/service/test_services_without_skip_host_check.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0cb71281314e53caa836aeda2078590b1b571ef0
--- /dev/null
+++ b/tests/service/test_services_without_skip_host_check.yml
@@ -0,0 +1,100 @@
+---
+- name: Test services without using option skip_host_check
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  # setup
+  - name: Test services without using option skip_host_check
+    block:
+    - name: Setup test environment
+      ansible.builtin.include_tasks: env_setup.yml
+
+    - name: Services are present
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        services:
+        - name: "HTTP/{{ svc_fqdn }}"
+          principal:
+            - host/test.example.com
+        - name: "mysvc/{{ host1_fqdn }}"
+          pac_type: NONE
+          ok_as_delegate: yes
+          ok_to_auth_as_delegate: yes
+        - name: "HTTP/{{ host1_fqdn }}"
+          allow_create_keytab_user:
+            - user01
+            - user02
+          allow_create_keytab_group:
+            - group01
+            - group02
+          allow_create_keytab_host:
+            - "{{ host1_fqdn }}"
+            - "{{ host2_fqdn }}"
+          allow_create_keytab_hostgroup:
+            - hostgroup01
+            - hostgroup02
+        - name: "mysvc/{{ host2_fqdn }}"
+          auth_ind: otp,radius
+
+      register: result
+      failed_when: not result.changed or result.failed
+
+    - name: Services are present again
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        services:
+        - name: "HTTP/{{ svc_fqdn }}"
+        - name: "mysvc/{{ host1_fqdn }}"
+        - name: "HTTP/{{ host1_fqdn }}"
+        - name: "mysvc/{{ host2_fqdn }}"
+      register: result
+      failed_when: result.changed or result.failed
+
+    # failed_when: not result.failed has been added as this test needs to
+    # fail because two services with the same name should be added in the same
+    # task.
+    - name: Duplicate names in services failure test
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        services:
+        - name: "HTTP/{{ svc_fqdn }}"
+        - name: "mysvc/{{ host1_fqdn }}"
+        - name: "HTTP/{{ nohost_fqdn }}"
+        - name: "HTTP/{{ svc_fqdn }}"
+      register: result
+      failed_when: result.changed or not result.failed or "is used more than once" not in result.msg
+
+    - name: Services/name and name 'service' present
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        name: "HTTP/{{ svc_fqdn }}"
+        services:
+        - name: "HTTP/{{ svc_fqdn }}"
+      register: result
+      failed_when: result.changed or not result.failed or "parameters are mutually exclusive" not in result.msg
+
+    - name: Services/name and name are absent
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+      register: result
+      failed_when: result.changed or not result.failed or "one of the following is required" not in result.msg
+
+    - name: Name is absent
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        name:
+      register: result
+      failed_when: result.changed or not result.failed or "At least one name or services is required" not in result.msg
+
+    - name: Only one service can be added at a time using name.
+      ipaservice:
+        ipaadmin_password: SomeADMINpassword
+        name: example.com,example1.com
+      register: result
+      failed_when: result.changed or not result.failed or "Only one service can be added at a time using 'name'." not in result.msg
+
+    always:
+    # cleanup
+    - name: Cleanup test environment
+      ansible.builtin.include_tasks: env_cleanup.yml