diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml
index bcf300025f..fac8adad78 100644
--- a/.github/BOTMETA.yml
+++ b/.github/BOTMETA.yml
@@ -830,6 +830,8 @@ files:
     maintainers: ahussey-redhat
   $modules/kibana_plugin.py:
     maintainers: barryib
+  $modules/krb_ticket.py:
+    maintainers: abakanovskii
   $modules/launchd.py:
     maintainers: martinm82
   $modules/layman.py:
diff --git a/plugins/modules/krb_ticket.py b/plugins/modules/krb_ticket.py
new file mode 100644
index 0000000000..8894a64ef6
--- /dev/null
+++ b/plugins/modules/krb_ticket.py
@@ -0,0 +1,378 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright (c) 2024 Alexander Bakanovskii <skottttt228@gmail.com>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+DOCUMENTATION = r'''
+---
+module: krb_ticket
+short_description: Kerberos utils for managing tickets
+version_added: 10.0.0
+description:
+  - Manage Kerberos tickets with C(kinit), C(klist) and C(kdestroy) base utilities.
+  - See U(https://web.mit.edu/kerberos/krb5-1.12/doc/user/user_commands/index.html) for reference.
+author: "Alexander Bakanovskii (@abakanovskii)"
+attributes:
+  check_mode:
+    support: full
+  diff_mode:
+    support: none
+options:
+  password:
+    description:
+      - Principal password.
+      - It is required to specify O(password) or O(keytab_path).
+    type: str
+  principal:
+    description:
+      - The principal name.
+      - If not set, the user running this module will be used.
+    type: str
+  state:
+    description:
+      - The state of the Kerberos ticket.
+      - V(present) is equivalent of C(kinit) command.
+      - V(absent) is equivalent of C(kdestroy) command.
+    type: str
+    default: present
+    choices: ["present", "absent"]
+  kdestroy_all:
+    description:
+      - When O(state=absent) destroys all credential caches in collection.
+      - Equivalent of running C(kdestroy -A).
+    type: bool
+  cache_name:
+    description:
+      - Use O(cache_name) as the ticket cache name and location.
+      - If this option is not used, the default cache name and location are used.
+      - The default credentials cache may vary between systems.
+      - If not set the the value of E(KRB5CCNAME) environment variable will be used instead, its value is used to name the default ticket cache.
+    type: str
+  lifetime:
+    description:
+      - Requests a ticket with the lifetime, if the O(lifetime) is not specified, the default ticket lifetime is used.
+      - Specifying a ticket lifetime longer than the maximum ticket lifetime (configured by each site) will not override the configured maximum ticket lifetime.
+      - "The value for O(lifetime) must be followed by one of the following suffixes: V(s) - seconds, V(m) - minutes, V(h) - hours, V(d) - days."
+      - You cannot mix units; a value of V(3h30m) will result in an error.
+      - See U(https://web.mit.edu/kerberos/krb5-1.12/doc/basic/date_format.html) for reference.
+    type: str
+  start_time:
+    description:
+      - Requests a postdated ticket.
+      - Postdated tickets are issued with the invalid flag set, and need to be resubmitted to the KDC for validation before use.
+      - O(start_time) specifies the duration of the delay before the ticket can become valid.
+      - You can use absolute time formats, for example V(July 27, 2012 at 20:30) you would neet to set O(start_time=20120727203000).
+      - You can also use time duration format similar to O(lifetime) or O(renewable).
+      - See U(https://web.mit.edu/kerberos/krb5-1.12/doc/basic/date_format.html) for reference.
+    type: str
+  renewable:
+    description:
+      - Requests renewable tickets, with a total lifetime equal to O(renewable).
+      - "The value for O(renewable) must be followed by one of the following delimiters: V(s) - seconds, V(m) - minutes, V(h) - hours, V(d) - days."
+      - You cannot mix units; a value of V(3h30m) will result in an error.
+      - See U(https://web.mit.edu/kerberos/krb5-1.12/doc/basic/date_format.html) for reference.
+    type: str
+  forwardable:
+    description:
+      - Request forwardable or non-forwardable tickets.
+    type: bool
+  proxiable:
+    description:
+      - Request proxiable or non-proxiable tickets.
+    type: bool
+  address_restricted:
+    description:
+      - Request tickets restricted to the host's local address or non-restricted.
+    type: bool
+  anonymous:
+    description:
+      - Requests anonymous processing.
+    type: bool
+  canonicalization:
+    description:
+      - Requests canonicalization of the principal name, and allows the KDC to reply with a different client principal from the one requested.
+    type: bool
+  enterprise:
+    description:
+      - Treats the principal name as an enterprise name (implies the O(canonicalization) option).
+    type: bool
+  renewal:
+    description:
+      - Requests renewal of the ticket-granting ticket.
+      - Note that an expired ticket cannot be renewed, even if the ticket is still within its renewable life.
+    type: bool
+  validate:
+    description:
+      - Requests that the ticket-granting ticket in the cache (with the invalid flag set) be passed to the KDC for validation.
+      - If the ticket is within its requested time range, the cache is replaced with the validated ticket.
+    type: bool
+  keytab:
+    description:
+      - Requests a ticket, obtained from a key in the local host's keytab.
+      - If O(keytab_path) is not specified will try to use default client keytab path (C(-i) option).
+    type: bool
+  keytab_path:
+    description:
+      - Use when O(keytab=true) to specify path to a keytab file.
+      - It is required to specify O(password) or O(keytab_path).
+    type: path
+requirements:
+  - krb5-user and krb5-config packages
+extends_documentation_fragment:
+  - community.general.attributes
+'''
+
+EXAMPLES = r'''
+- name: Get Kerberos ticket using default principal
+  community.general.krb_ticket:
+    password: some_password
+
+- name: Get Kerberos ticket using keytab
+  community.general.krb_ticket:
+    keytab: true
+    keytab_path: /etc/ipa/file.keytab
+
+- name: Get Kerberos ticket with a lifetime of 7 days
+  community.general.krb_ticket:
+    password: some_password
+    lifetime: 7d
+
+- name: Get Kerberos ticket with a starting time of July 2, 2024, 1:35:30 p.m.
+  community.general.krb_ticket:
+    password: some_password
+    start_time: "240702133530"
+
+- name: Get Kerberos ticket using principal name
+  community.general.krb_ticket:
+    password: some_password
+    principal: admin
+
+- name: Get Kerberos ticket using principal with realm
+  community.general.krb_ticket:
+    password: some_password
+    principal: admin@IPA.TEST
+
+- name: Check for existence by ticket cache
+  community.general.krb_ticket:
+    cache_name: KEYRING:persistent:0:0
+
+- name: Make sure default ticket is destroyed
+  community.general.krb_ticket:
+    state: absent
+
+- name: Make sure specific ticket destroyed by principal
+  community.general.krb_ticket:
+    state: absent
+    principal: admin@IPA.TEST
+
+- name: Make sure specific ticket destroyed by cache_name
+  community.general.krb_ticket:
+    state: absent
+    cache_name: KEYRING:persistent:0:0
+
+- name: Make sure all tickets are destroyed
+  community.general.krb_ticket:
+    state: absent
+    kdestroy_all: true
+'''
+
+from ansible.module_utils.basic import AnsibleModule, env_fallback
+from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt
+
+
+class IPAKeytab(object):
+    def __init__(self, module, **kwargs):
+        self.module = module
+        self.password = kwargs['password']
+        self.principal = kwargs['principal']
+        self.state = kwargs['state']
+        self.kdestroy_all = kwargs['kdestroy_all']
+        self.cache_name = kwargs['cache_name']
+        self.start_time = kwargs['start_time']
+        self.renewable = kwargs['renewable']
+        self.forwardable = kwargs['forwardable']
+        self.proxiable = kwargs['proxiable']
+        self.address_restricted = kwargs['address_restricted']
+        self.canonicalization = kwargs['canonicalization']
+        self.enterprise = kwargs['enterprise']
+        self.renewal = kwargs['renewal']
+        self.validate = kwargs['validate']
+        self.keytab = kwargs['keytab']
+        self.keytab_path = kwargs['keytab_path']
+
+        self.kinit = CmdRunner(
+            module,
+            command='kinit',
+            arg_formats=dict(
+                lifetime=cmd_runner_fmt.as_opt_val('-l'),
+                start_time=cmd_runner_fmt.as_opt_val('-s'),
+                renewable=cmd_runner_fmt.as_opt_val('-r'),
+                forwardable=cmd_runner_fmt.as_bool('-f', '-F', ignore_none=True),
+                proxiable=cmd_runner_fmt.as_bool('-p', '-P', ignore_none=True),
+                address_restricted=cmd_runner_fmt.as_bool('-a', '-A', ignore_none=True),
+                anonymous=cmd_runner_fmt.as_bool('-n'),
+                canonicalization=cmd_runner_fmt.as_bool('-C'),
+                enterprise=cmd_runner_fmt.as_bool('-E'),
+                renewal=cmd_runner_fmt.as_bool('-R'),
+                validate=cmd_runner_fmt.as_bool('-v'),
+                keytab=cmd_runner_fmt.as_bool('-k'),
+                keytab_path=cmd_runner_fmt.as_func(lambda v: ['-t', v] if v else ['-i']),
+                cache_name=cmd_runner_fmt.as_opt_val('-c'),
+                principal=cmd_runner_fmt.as_list(),
+            )
+        )
+
+        self.kdestroy = CmdRunner(
+            module,
+            command='kdestroy',
+            arg_formats=dict(
+                kdestroy_all=cmd_runner_fmt.as_bool('-A'),
+                cache_name=cmd_runner_fmt.as_opt_val('-c'),
+                principal=cmd_runner_fmt.as_opt_val('-p'),
+            )
+        )
+
+        self.klist = CmdRunner(
+            module,
+            command='klist',
+            arg_formats=dict(
+                show_list=cmd_runner_fmt.as_bool('-l'),
+            )
+        )
+
+    def exec_kinit(self):
+        params = dict(self.module.params)
+        with self.kinit(
+            "lifetime start_time renewable forwardable proxiable address_restricted anonymous "
+            "canonicalization enterprise renewal validate keytab keytab_path cache_name principal",
+            check_rc=True,
+            data=self.password,
+        ) as ctx:
+            rc, out, err = ctx.run(**params)
+        return out
+
+    def exec_kdestroy(self):
+        params = dict(self.module.params)
+        with self.kdestroy(
+            "kdestroy_all cache_name principal",
+            check_rc=True
+        ) as ctx:
+            rc, out, err = ctx.run(**params)
+        return out
+
+    def exec_klist(self, show_list):
+        # Use chech_rc = False because
+        # If no tickets present, klist command will always return rc = 1
+        params = dict(show_list=show_list)
+        with self.klist(
+            "show_list",
+            check_rc=False
+        ) as ctx:
+            rc, out, err = ctx.run(**params)
+        return rc, out, err
+
+    def check_ticket_present(self):
+        ticket_present = True
+        show_list = False
+
+        if not self.principal and not self.cache_name:
+            rc, out, err = self.exec_klist(show_list)
+            if rc != 0:
+                ticket_present = False
+        else:
+            show_list = True
+            rc, out, err = self.exec_klist(show_list)
+            if self.principal and self.principal not in str(out):
+                ticket_present = False
+            if self.cache_name and self.cache_name not in str(out):
+                ticket_present = False
+
+        return ticket_present
+
+
+def main():
+    arg_spec = dict(
+        principal=dict(type='str'),
+        password=dict(type='str', no_log=True),
+        state=dict(default='present', choices=['present', 'absent']),
+        kdestroy_all=dict(type='bool'),
+        cache_name=dict(type='str', fallback=(env_fallback, ['KRB5CCNAME'])),
+        lifetime=dict(type='str'),
+        start_time=dict(type='str'),
+        renewable=dict(type='str'),
+        forwardable=dict(type='bool'),
+        proxiable=dict(type='bool'),
+        address_restricted=dict(type='bool'),
+        anonymous=dict(type='bool'),
+        canonicalization=dict(type='bool'),
+        enterprise=dict(type='bool'),
+        renewal=dict(type='bool'),
+        validate=dict(type='bool'),
+        keytab=dict(type='bool'),
+        keytab_path=dict(type='path'),
+    )
+    module = AnsibleModule(
+        argument_spec=arg_spec,
+        supports_check_mode=True,
+        required_by={
+            'keytab_path': 'keytab'
+        },
+        required_if=[
+            ('state', 'present', ('password', 'keytab_path'), True),
+        ],
+    )
+
+    state = module.params['state']
+    kdestroy_all = module.params['kdestroy_all']
+
+    keytab = IPAKeytab(module,
+                       state=state,
+                       kdestroy_all=kdestroy_all,
+                       principal=module.params['principal'],
+                       password=module.params['password'],
+                       cache_name=module.params['cache_name'],
+                       lifetime=module.params['lifetime'],
+                       start_time=module.params['start_time'],
+                       renewable=module.params['renewable'],
+                       forwardable=module.params['forwardable'],
+                       proxiable=module.params['proxiable'],
+                       address_restricted=module.params['address_restricted'],
+                       anonymous=module.params['anonymous'],
+                       canonicalization=module.params['canonicalization'],
+                       enterprise=module.params['enterprise'],
+                       renewal=module.params['renewal'],
+                       validate=module.params['validate'],
+                       keytab=module.params['keytab'],
+                       keytab_path=module.params['keytab_path'],
+                       )
+
+    if module.params['keytab_path'] is not None and module.params['keytab'] is not True:
+        module.fail_json(msg="If keytab_path is specified then keytab parameter must be True")
+
+    changed = False
+    if state == 'present':
+        if not keytab.check_ticket_present():
+            changed = True
+            if not module.check_mode:
+                keytab.exec_kinit()
+
+    if state == 'absent':
+        if kdestroy_all:
+            changed = True
+            if not module.check_mode:
+                keytab.exec_kdestroy()
+        elif keytab.check_ticket_present():
+            changed = True
+            if not module.check_mode:
+                keytab.exec_kdestroy()
+
+    module.exit_json(changed=changed)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/tests/unit/plugins/modules/test_krb_ticket.py b/tests/unit/plugins/modules/test_krb_ticket.py
new file mode 100644
index 0000000000..8c17e2e43b
--- /dev/null
+++ b/tests/unit/plugins/modules/test_krb_ticket.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+
+from ansible_collections.community.general.plugins.modules import krb_ticket
+from .helper import Helper, RunCommandMock  # pylint: disable=unused-import
+
+
+Helper.from_module(krb_ticket, __name__)
diff --git a/tests/unit/plugins/modules/test_krb_ticket.yaml b/tests/unit/plugins/modules/test_krb_ticket.yaml
new file mode 100644
index 0000000000..9882bf137d
--- /dev/null
+++ b/tests/unit/plugins/modules/test_krb_ticket.yaml
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) Alexei Znamensky (russoz@gmail.com)
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+---
+- id: test_kinit_default
+  input:
+    state: present
+    password: cool_password
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/klist]
+      environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
+      rc: 1
+      out: ""
+      err: ""
+    - command: [/testbin/kinit]
+      environ: &env-data {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true, data: cool_password}
+      rc: 0
+      out: ""
+      err: ""
+- id: test_kinit_principal
+  input:
+    state: present
+    password: cool_password
+    principal: admin@IPA.TEST
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/klist, -l]
+      environ: *env-def
+      rc: 0
+      out: ""
+      err: ""
+    - command: [/testbin/kinit, admin@IPA.TEST]
+      environ: *env-data
+      rc: 0
+      out: ""
+      err: ""
+- id: test_kdestroy_default
+  input:
+    state: absent
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/klist]
+      environ: *env-def
+      rc: 0
+      out: ""
+      err: ""
+    - command: [/testbin/kdestroy]
+      environ: &env-norc {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
+      rc: 0
+      out: ""
+      err: ""
+- id: test_kdestroy_principal
+  input:
+    state: absent
+    principal: admin@IPA.TEST
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/klist, -l]
+      environ: *env-def
+      rc: 0
+      out: "admin@IPA.TEST"
+      err: ""
+    - command: [/testbin/kdestroy, -p, admin@IPA.TEST]
+      environ: *env-norc
+      rc: 0
+      out: ""
+      err: ""
+- id: test_kdestroy_cache_name
+  input:
+    state: absent
+    cache_name: KEYRING:persistent:0:0
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/klist, -l]
+      environ: *env-def
+      rc: 0
+      out: "KEYRING:persistent:0:0"
+      err: ""
+    - command: [/testbin/kdestroy, -c, KEYRING:persistent:0:0]
+      environ: *env-norc
+      rc: 0
+      out: ""
+      err: ""
+- id: test_kdestroy_all
+  input:
+    state: absent
+    kdestroy_all: true
+  output:
+    changed: true
+  mocks:
+    run_command:
+    - command: [/testbin/kdestroy, -A]
+      environ: *env-norc
+      rc: 0
+      out: ""
+      err: ""