diff --git a/README-role.md b/README-role.md
index 75248359e01bc622f7f2ed832949f1fb43fe2b03..fc915c2b6d9b301adc5dff666f2b774849e09b08 100644
--- a/README-role.md
+++ b/README-role.md
@@ -245,6 +245,7 @@ Variable | Description | Required
 -------- | ----------- | --------
 `ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
 `ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
+`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no
 `name` \| `cn` | The list of role name strings. | yes
 `description` | A description for the role. | no
 `rename` \| `new_name` | Rename the role object. | no
diff --git a/tests/role/env_cleanup.yml b/tests/role/env_cleanup.yml
index 24064a1c7d7c2073ba19146d4ac4891768f44f88..0b459651d3a81ebc490f5382c755d3eb9d96ffbc 100644
--- a/tests/role/env_cleanup.yml
+++ b/tests/role/env_cleanup.yml
@@ -2,6 +2,7 @@
 - name: Ensure test user is absent.
   ipauser:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - user01
     - user02
@@ -11,6 +12,7 @@
 - name: Ensure test group is absent.
   ipagroup:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - group01
     - group02
@@ -19,6 +21,7 @@
 - name: Ensure test hostgroup is absent.
   ipahostgroup:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - hostgroup01
     - hostgroup02
@@ -27,6 +30,7 @@
 - name: Ensure test host is absent.
   ipahost:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - "{{ host1_fqdn }}"
     - "{{ host2_fqdn }}"
@@ -35,6 +39,7 @@
 - name: Ensure test service is absent.
   ipaservice:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - "service01/{{ host1_fqdn }}"
     - "service02/{{ host2_fqdn }}"
@@ -43,6 +48,7 @@
 - name: Ensure test roles are absent.
   iparole:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name:
     - renamerole
     - testrole
diff --git a/tests/role/env_setup.yml b/tests/role/env_setup.yml
index 32bd32a912eda2e9d86ae92d4b0caffa9b339823..eb72c82be49c03a491075353eb066c9cc279ceeb 100644
--- a/tests/role/env_setup.yml
+++ b/tests/role/env_setup.yml
@@ -5,6 +5,7 @@
 - name: Ensure test user is present.
   ipauser:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     users:
     - name: user01
       first: First
@@ -19,6 +20,7 @@
 - name: Ensure test group is present.
   ipagroup:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name: "{{ item }}"
   with_items:
   - group01
@@ -27,6 +29,7 @@
 - name: Ensure test host is present.
   ipahost:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name: "{{ item }}"
     force: yes
   with_items:
@@ -36,6 +39,7 @@
 - name: Ensure test hostgroup is present.
   ipahostgroup:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name: "{{ item[0] }}"
     host:
       - "{{ item[1] }}"
@@ -46,6 +50,7 @@
 - name: Ensure test service is present.
   ipaservice:
     ipaadmin_password: SomeADMINpassword
+    ipaapi_context: "{{ ipa_context | default(omit) }}"
     name: "{{ item }}"
     force: yes
   with_items:
diff --git a/tests/role/test_role.yml b/tests/role/test_role.yml
index 0c4661e669eb20fdc3bf7fc390f1c4e8822d43c4..6f9ba90d2bbf7f5c2d49f4a6977e413d70a4709b 100644
--- a/tests/role/test_role.yml
+++ b/tests/role/test_role.yml
@@ -1,6 +1,6 @@
 ---
 - name: Test role module
-  hosts: ipaserver
+  hosts: "{{ ipa_test_host | default('ipaserver') }}"
   become: yes
   gather_facts: yes
 
@@ -15,6 +15,7 @@
   - name: Ensure role is present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: renamerole
       description: A role in IPA.
     register: result
@@ -23,6 +24,7 @@
   - name: Ensure role is present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: renamerole
       description: A role in IPA.
     register: result
@@ -31,6 +33,7 @@
   - name: Rename role.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: renamerole
       rename: testrole
     register: result
@@ -41,6 +44,7 @@
   - name: Rename role, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: renamerole
       rename: testrole
     register: result
@@ -49,6 +53,7 @@
   - name: Ensure role has member has privileges.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -60,6 +65,7 @@
   - name: Ensure role has member has privileges, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -71,6 +77,7 @@
   - name: Ensure role has less privileges.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - Host Administrators
@@ -82,6 +89,7 @@
   - name: Ensure role has less privileges, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - Host Administrators
@@ -93,6 +101,7 @@
   - name: Ensure role has member has privileges restored.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -104,6 +113,7 @@
   - name: Ensure role has member has privileges restored, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -115,6 +125,7 @@
   - name: Ensure role member privileges are absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -127,6 +138,7 @@
   - name: Ensure role member privileges are absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege:
       - DNS Servers
@@ -139,6 +151,7 @@
   - name: Ensure invalid privileged is not assigned to role.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       privilege: Invalid Privilege
       action: member
@@ -148,6 +161,7 @@
   - name: Ensure role has member user present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -158,6 +172,7 @@
   - name: Ensure role has member user present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -168,6 +183,7 @@
   - name: Ensure role has member user absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -179,6 +195,7 @@
   - name: Ensure role has member user absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -190,6 +207,7 @@
   - name: Ensure role has member group present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       group:
       - group01
@@ -200,6 +218,7 @@
   - name: Ensure role has member group present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       group:
       - group01
@@ -210,6 +229,7 @@
   - name: Ensure role has member group absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       group:
       - group01
@@ -221,6 +241,7 @@
   - name: Ensure role has member group absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       group:
       - group01
@@ -232,6 +253,7 @@
   - name: Ensure role has member host present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       host:
       - "{{ host1_fqdn }}"
@@ -242,6 +264,7 @@
   - name: Ensure role has member host present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       host:
       - "{{ host1_fqdn }}"
@@ -252,6 +275,7 @@
   - name: Ensure role has member host absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       host:
       - "{{ host1_fqdn }}"
@@ -263,6 +287,7 @@
   - name: Ensure role has member host absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       host:
       - "{{ host1_fqdn }}"
@@ -274,6 +299,7 @@
   - name: Ensure role has member hostgroup present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       hostgroup:
       - hostgroup01
@@ -284,6 +310,7 @@
   - name: Ensure role has member hostgroup present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       hostgroup:
       - hostgroup01
@@ -294,6 +321,7 @@
   - name: Ensure role has member hostgroup absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       hostgroup:
       - hostgroup01
@@ -305,6 +333,7 @@
   - name: Ensure role has member hostgroup absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       hostgroup:
       - hostgroup01
@@ -316,6 +345,7 @@
   - name: Ensure role is absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       state: absent
     register: result
@@ -324,6 +354,7 @@
   - name: Ensure role is absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       state: absent
     register: result
@@ -332,6 +363,7 @@
   - name: Ensure role with members is present.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -352,6 +384,7 @@
   - name: Ensure role with members is present, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       user:
       - user01
@@ -372,6 +405,7 @@
   - name: Ensure role is absent.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       state: absent
     register: result
@@ -380,6 +414,7 @@
   - name: Ensure role is absent, again.
     iparole:
       ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testrole
       state: absent
     register: result
diff --git a/tests/role/test_role_client_context.yml b/tests/role/test_role_client_context.yml
new file mode 100644
index 0000000000000000000000000000000000000000..594d3ec1e4af0078f1324873bdac8738066dd9d3
--- /dev/null
+++ b/tests/role/test_role_client_context.yml
@@ -0,0 +1,37 @@
+---
+- name: Test role
+  hosts: ipaclients, ipaserver
+  become: no
+  gather_facts: no
+
+  tasks:
+  - name: Include FreeIPA facts.
+    include_tasks: ../env_freeipa_facts.yml
+
+  # Test will only be executed if host is not a server.
+  - name: Execute with server context in the client.
+    iparole:
+      ipaadmin_password: SomeADMINpassword
+      ipaapi_context: server
+      name: ThisShouldNotWork
+    register: result
+    failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*"))
+    when: ipa_host_is_client
+
+# Import basic module tests, and execute with ipa_context set to 'client'.
+# If ipaclients is set, it will be executed using the client, if not,
+# ipaserver will be used.
+#
+# With this setup, tests can be executed against an IPA client, against
+# an IPA server using "client" context, and ensure that tests are executed
+# in upstream CI.
+
+- name: Test role using client context, in client host.
+  import_playbook: test_role.yml
+  when: groups['ipaclients']
+  vars:
+    ipa_test_host: ipaclients
+
+- name: Test role using client context, in server host.
+  import_playbook: test_role.yml
+  when: groups['ipaclients'] is not defined or not groups['ipaclients']