We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
sudoers module does not handle state: absent properly
### Summary

The `community.general.sudoers` module does not handle `state: absent` properly.
If the file specified by `name` does not exist, it gets created, with possibly invalid content. As a side effect, `commands` and `user`/`group` is required, although these options do not make a lot of sense with `state: absent`.
If the file specified by `name` exists, it is (correctly) deleted. Of course, the next invocation of the play creates it again.

### Issue Type

Bug Report

### Component Name

sudoers

### Ansible Version

```console (paste below)
$ ansible --version
ansible [core 2.12.5]
  config file = ...
  configured module search path = [...]
  ansible python module location = /usr/lib/python3.9/site-packages/ansible
  ansible collection location = /usr/share/ansible/collections
  executable location = /usr/lib/python-exec/python3.9/ansible
  python version = 3.9.12 (main, Jun 17 2022, 16:04:33) [GCC 11.3.0]
  jinja version = 3.1.2
  libyaml = True
```

### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
# /usr/lib/python3.9/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 4.8.0  
```

### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

Target OS is Debian 11.

### Steps to Reproduce

```yaml (paste below)
---
- hosts: all
  gather_facts: false
  tasks:
    - community.general.sudoers:
        name: test
        commands: []
        user: root
        state: absent

    - ansible.builtin.stat:
        path: /etc/sudoers.d/test
      register: _stat_sudoers

    - ansible.builtin.assert:
        that:
          - "not _stat_sudoers.stat.exists"
```
Note: `/etc/sudoers.d/test` is assumed not to exist prior to running this play.

### Expected Results

The play never fails, i.e. `/etc/sudoers.d/test` gets deleted/not created.

### Actual Results

The assertion fails on every second invocation of the play (starting with the first). The file `/etc/sudoers.d/test` gets created by the `sudoers` module.
The assertion passes on every other invocation of the play (starting with the second).

```console (paste below)
PLAY [all] **********************************************************************************************

TASK [community.general.sudoers] ************************************************************************
changed: [test]

TASK [ansible.builtin.stat] *****************************************************************************
ok: [test]

TASK [ansible.builtin.assert] ***************************************************************************
fatal: [test]: FAILED! => {
    "assertion": "not _stat_sudoers.stat.exists",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

PLAY RECAP **********************************************************************************************
test                       : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/modules/system/sudoers.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 
5 # Copyright: (c) 2019, Jon Ellis (@JonEllis) <ellis.jp@gmail.com>
6 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7 
8 from __future__ import absolute_import, division, print_function
9 __metaclass__ = type
10 
11 
12 DOCUMENTATION = '''
13 ---
14 module: sudoers
15 short_description: Manage sudoers files
16 version_added: "4.3.0"
17 description:
18   - This module allows for the manipulation of sudoers files.
19 author:
20   - "Jon Ellis (@JonEllis) <ellis.jp@gmail.com>"
21 options:
22   commands:
23     description:
24       - The commands allowed by the sudoers rule.
25       - Multiple can be added by passing a list of commands.
26       - Use C(ALL) for all commands.
27     type: list
28     elements: str
29   group:
30     description:
31       - The name of the group for the sudoers rule.
32       - This option cannot be used in conjunction with I(user).
33     type: str
34   name:
35     required: true
36     description:
37       - The name of the sudoers rule.
38       - This will be used for the filename for the sudoers file managed by this rule.
39     type: str
40   nopassword:
41     description:
42       - Whether a password will be required to run the sudo'd command.
43     default: true
44     type: bool
45   runas:
46     description:
47       - Specify the target user the command(s) will run as.
48     type: str
49     version_added: 4.7.0
50   sudoers_path:
51     description:
52       - The path which sudoers config files will be managed in.
53     default: /etc/sudoers.d
54     type: str
55   state:
56     default: "present"
57     choices:
58       - present
59       - absent
60     description:
61       - Whether the rule should exist or not.
62     type: str
63   user:
64     description:
65       - The name of the user for the sudoers rule.
66       - This option cannot be used in conjunction with I(group).
67     type: str
68 '''
69 
70 EXAMPLES = '''
71 - name: Allow the backup user to sudo /usr/local/bin/backup
72   community.general.sudoers:
73     name: allow-backup
74     state: present
75     user: backup
76     commands: /usr/local/bin/backup
77 
78 - name: Allow the bob user to run any commands as alice with sudo -u alice
79   community.general.sudoers:
80     name: bob-do-as-alice
81     state: present
82     user: bob
83     runas: alice
84     commands: ALL
85 
86 - name: >-
87     Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics
88     without requiring a password
89   community.general.sudoers:
90     name: monitor-app
91     group: monitoring
92     commands: /usr/local/bin/gather-app-metrics
93 
94 - name: >-
95     Allow the alice user to run sudo /bin/systemctl restart my-service or
96     sudo /bin/systemctl reload my-service, but a password is required
97   community.general.sudoers:
98     name: alice-service
99     user: alice
100     commands:
101       - /bin/systemctl restart my-service
102       - /bin/systemctl reload my-service
103     nopassword: false
104 
105 - name: Revoke the previous sudo grants given to the alice user
106   community.general.sudoers:
107     name: alice-service
108     state: absent
109 '''
110 
111 import os
112 from ansible.module_utils.basic import AnsibleModule
113 from ansible.module_utils.common.text.converters import to_native
114 
115 
116 class Sudoers(object):
117 
118     FILE_MODE = 0o440
119 
120     def __init__(self, module):
121         self.check_mode = module.check_mode
122         self.name = module.params['name']
123         self.user = module.params['user']
124         self.group = module.params['group']
125         self.state = module.params['state']
126         self.nopassword = module.params['nopassword']
127         self.runas = module.params['runas']
128         self.sudoers_path = module.params['sudoers_path']
129         self.file = os.path.join(self.sudoers_path, self.name)
130         self.commands = module.params['commands']
131 
132     def write(self):
133         if self.check_mode:
134             return
135 
136         with open(self.file, 'w') as f:
137             f.write(self.content())
138 
139         os.chmod(self.file, self.FILE_MODE)
140 
141     def delete(self):
142         if self.check_mode:
143             return
144 
145         os.remove(self.file)
146 
147     def exists(self):
148         return os.path.exists(self.file)
149 
150     def matches(self):
151         with open(self.file, 'r') as f:
152             content_matches = f.read() == self.content()
153 
154         current_mode = os.stat(self.file).st_mode & 0o777
155         mode_matches = current_mode == self.FILE_MODE
156 
157         return content_matches and mode_matches
158 
159     def content(self):
160         if self.user:
161             owner = self.user
162         elif self.group:
163             owner = '%{group}'.format(group=self.group)
164 
165         commands_str = ', '.join(self.commands)
166         nopasswd_str = 'NOPASSWD:' if self.nopassword else ''
167         runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''
168         return "{owner} ALL={runas}{nopasswd} {commands}\n".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)
169 
170     def run(self):
171         if self.state == 'absent' and self.exists():
172             self.delete()
173             return True
174 
175         if self.exists() and self.matches():
176             return False
177 
178         self.write()
179         return True
180 
181 
182 def main():
183     argument_spec = {
184         'commands': {
185             'type': 'list',
186             'elements': 'str',
187         },
188         'group': {},
189         'name': {
190             'required': True,
191         },
192         'nopassword': {
193             'type': 'bool',
194             'default': True,
195         },
196         'runas': {
197             'type': 'str',
198             'default': None,
199         },
200         'sudoers_path': {
201             'type': 'str',
202             'default': '/etc/sudoers.d',
203         },
204         'state': {
205             'default': 'present',
206             'choices': ['present', 'absent'],
207         },
208         'user': {},
209     }
210 
211     module = AnsibleModule(
212         argument_spec=argument_spec,
213         mutually_exclusive=[['user', 'group']],
214         supports_check_mode=True,
215         required_if=[('state', 'present', ['commands'])],
216     )
217 
218     sudoers = Sudoers(module)
219 
220     try:
221         changed = sudoers.run()
222         module.exit_json(changed=changed)
223     except Exception as e:
224         module.fail_json(msg=to_native(e))
225 
226 
227 if __name__ == '__main__':
228     main()
229
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/modules/system/sudoers.py b/plugins/modules/system/sudoers.py
--- a/plugins/modules/system/sudoers.py
+++ b/plugins/modules/system/sudoers.py
@@ -168,9 +168,12 @@
         return "{owner} ALL={runas}{nopasswd} {commands}\n".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)
 
     def run(self):
-        if self.state == 'absent' and self.exists():
-            self.delete()
-            return True
+        if self.state == 'absent':
+            if self.exists():
+                self.delete()
+                return True
+            else:
+                return False
 
         if self.exists() and self.matches():
             return False

{"golden_diff": "diff --git a/plugins/modules/system/sudoers.py b/plugins/modules/system/sudoers.py\n--- a/plugins/modules/system/sudoers.py\n+++ b/plugins/modules/system/sudoers.py\n@@ -168,9 +168,12 @@\n         return \"{owner} ALL={runas}{nopasswd} {commands}\\n\".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)\n \n     def run(self):\n-        if self.state == 'absent' and self.exists():\n-            self.delete()\n-            return True\n+        if self.state == 'absent':\n+            if self.exists():\n+                self.delete()\n+                return True\n+            else:\n+                return False\n \n         if self.exists() and self.matches():\n             return False\n", "issue": "sudoers module does not handle state: absent properly\n### Summary\n\nThe `community.general.sudoers` module does not handle `state: absent` properly.\r\nIf the file specified by `name` does not exist, it gets created, with possibly invalid content. As a side effect, `commands` and `user`/`group` is required, although these options do not make a lot of sense with `state: absent`.\r\nIf the file specified by `name` exists, it is (correctly) deleted. Of course, the next invocation of the play creates it again.\n\n### Issue Type\n\nBug Report\n\n### Component Name\n\nsudoers\n\n### Ansible Version\n\n```console (paste below)\r\n$ ansible --version\r\nansible [core 2.12.5]\r\n  config file = ...\r\n  configured module search path = [...]\r\n  ansible python module location = /usr/lib/python3.9/site-packages/ansible\r\n  ansible collection location = /usr/share/ansible/collections\r\n  executable location = /usr/lib/python-exec/python3.9/ansible\r\n  python version = 3.9.12 (main, Jun 17 2022, 16:04:33) [GCC 11.3.0]\r\n  jinja version = 3.1.2\r\n  libyaml = True\r\n```\n\n### Community.general Version\n\n```console (paste below)\r\n$ ansible-galaxy collection list community.general\r\n# /usr/lib/python3.9/site-packages/ansible_collections\r\nCollection        Version\r\n----------------- -------\r\ncommunity.general 4.8.0  \r\n```\n\n### Configuration\n\n```console (paste below)\r\n$ ansible-config dump --only-changed\r\n\r\n```\r\n\n\n### OS / Environment\n\nTarget OS is Debian 11.\n\n### Steps to Reproduce\n\n```yaml (paste below)\r\n---\r\n- hosts: all\r\n  gather_facts: false\r\n  tasks:\r\n    - community.general.sudoers:\r\n        name: test\r\n        commands: []\r\n        user: root\r\n        state: absent\r\n\r\n    - ansible.builtin.stat:\r\n        path: /etc/sudoers.d/test\r\n      register: _stat_sudoers\r\n\r\n    - ansible.builtin.assert:\r\n        that:\r\n          - \"not _stat_sudoers.stat.exists\"\r\n```\r\nNote: `/etc/sudoers.d/test` is assumed not to exist prior to running this play.\n\n### Expected Results\n\nThe play never fails, i.e. `/etc/sudoers.d/test` gets deleted/not created.\n\n### Actual Results\n\nThe assertion fails on every second invocation of the play (starting with the first). The file `/etc/sudoers.d/test` gets created by the `sudoers` module.\r\nThe assertion passes on every other invocation of the play (starting with the second).\r\n\r\n```console (paste below)\r\nPLAY [all] **********************************************************************************************\r\n\r\nTASK [community.general.sudoers] ************************************************************************\r\nchanged: [test]\r\n\r\nTASK [ansible.builtin.stat] *****************************************************************************\r\nok: [test]\r\n\r\nTASK [ansible.builtin.assert] ***************************************************************************\r\nfatal: [test]: FAILED! => {\r\n    \"assertion\": \"not _stat_sudoers.stat.exists\",\r\n    \"changed\": false,\r\n    \"evaluated_to\": false,\r\n    \"msg\": \"Assertion failed\"\r\n}\r\n\r\nPLAY RECAP **********************************************************************************************\r\ntest                       : ok=2    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0\r\n```\n\n### Code of Conduct\n\n- [X] I agree to follow the Ansible Code of Conduct\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n\n# Copyright: (c) 2019, Jon Ellis (@JonEllis) <ellis.jp@gmail.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nDOCUMENTATION = '''\n---\nmodule: sudoers\nshort_description: Manage sudoers files\nversion_added: \"4.3.0\"\ndescription:\n  - This module allows for the manipulation of sudoers files.\nauthor:\n  - \"Jon Ellis (@JonEllis) <ellis.jp@gmail.com>\"\noptions:\n  commands:\n    description:\n      - The commands allowed by the sudoers rule.\n      - Multiple can be added by passing a list of commands.\n      - Use C(ALL) for all commands.\n    type: list\n    elements: str\n  group:\n    description:\n      - The name of the group for the sudoers rule.\n      - This option cannot be used in conjunction with I(user).\n    type: str\n  name:\n    required: true\n    description:\n      - The name of the sudoers rule.\n      - This will be used for the filename for the sudoers file managed by this rule.\n    type: str\n  nopassword:\n    description:\n      - Whether a password will be required to run the sudo'd command.\n    default: true\n    type: bool\n  runas:\n    description:\n      - Specify the target user the command(s) will run as.\n    type: str\n    version_added: 4.7.0\n  sudoers_path:\n    description:\n      - The path which sudoers config files will be managed in.\n    default: /etc/sudoers.d\n    type: str\n  state:\n    default: \"present\"\n    choices:\n      - present\n      - absent\n    description:\n      - Whether the rule should exist or not.\n    type: str\n  user:\n    description:\n      - The name of the user for the sudoers rule.\n      - This option cannot be used in conjunction with I(group).\n    type: str\n'''\n\nEXAMPLES = '''\n- name: Allow the backup user to sudo /usr/local/bin/backup\n  community.general.sudoers:\n    name: allow-backup\n    state: present\n    user: backup\n    commands: /usr/local/bin/backup\n\n- name: Allow the bob user to run any commands as alice with sudo -u alice\n  community.general.sudoers:\n    name: bob-do-as-alice\n    state: present\n    user: bob\n    runas: alice\n    commands: ALL\n\n- name: >-\n    Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics\n    without requiring a password\n  community.general.sudoers:\n    name: monitor-app\n    group: monitoring\n    commands: /usr/local/bin/gather-app-metrics\n\n- name: >-\n    Allow the alice user to run sudo /bin/systemctl restart my-service or\n    sudo /bin/systemctl reload my-service, but a password is required\n  community.general.sudoers:\n    name: alice-service\n    user: alice\n    commands:\n      - /bin/systemctl restart my-service\n      - /bin/systemctl reload my-service\n    nopassword: false\n\n- name: Revoke the previous sudo grants given to the alice user\n  community.general.sudoers:\n    name: alice-service\n    state: absent\n'''\n\nimport os\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible.module_utils.common.text.converters import to_native\n\n\nclass Sudoers(object):\n\n    FILE_MODE = 0o440\n\n    def __init__(self, module):\n        self.check_mode = module.check_mode\n        self.name = module.params['name']\n        self.user = module.params['user']\n        self.group = module.params['group']\n        self.state = module.params['state']\n        self.nopassword = module.params['nopassword']\n        self.runas = module.params['runas']\n        self.sudoers_path = module.params['sudoers_path']\n        self.file = os.path.join(self.sudoers_path, self.name)\n        self.commands = module.params['commands']\n\n    def write(self):\n        if self.check_mode:\n            return\n\n        with open(self.file, 'w') as f:\n            f.write(self.content())\n\n        os.chmod(self.file, self.FILE_MODE)\n\n    def delete(self):\n        if self.check_mode:\n            return\n\n        os.remove(self.file)\n\n    def exists(self):\n        return os.path.exists(self.file)\n\n    def matches(self):\n        with open(self.file, 'r') as f:\n            content_matches = f.read() == self.content()\n\n        current_mode = os.stat(self.file).st_mode & 0o777\n        mode_matches = current_mode == self.FILE_MODE\n\n        return content_matches and mode_matches\n\n    def content(self):\n        if self.user:\n            owner = self.user\n        elif self.group:\n            owner = '%{group}'.format(group=self.group)\n\n        commands_str = ', '.join(self.commands)\n        nopasswd_str = 'NOPASSWD:' if self.nopassword else ''\n        runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''\n        return \"{owner} ALL={runas}{nopasswd} {commands}\\n\".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)\n\n    def run(self):\n        if self.state == 'absent' and self.exists():\n            self.delete()\n            return True\n\n        if self.exists() and self.matches():\n            return False\n\n        self.write()\n        return True\n\n\ndef main():\n    argument_spec = {\n        'commands': {\n            'type': 'list',\n            'elements': 'str',\n        },\n        'group': {},\n        'name': {\n            'required': True,\n        },\n        'nopassword': {\n            'type': 'bool',\n            'default': True,\n        },\n        'runas': {\n            'type': 'str',\n            'default': None,\n        },\n        'sudoers_path': {\n            'type': 'str',\n            'default': '/etc/sudoers.d',\n        },\n        'state': {\n            'default': 'present',\n            'choices': ['present', 'absent'],\n        },\n        'user': {},\n    }\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        mutually_exclusive=[['user', 'group']],\n        supports_check_mode=True,\n        required_if=[('state', 'present', ['commands'])],\n    )\n\n    sudoers = Sudoers(module)\n\n    try:\n        changed = sudoers.run()\n        module.exit_json(changed=changed)\n    except Exception as e:\n        module.fail_json(msg=to_native(e))\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/system/sudoers.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n\n# Copyright: (c) 2019, Jon Ellis (@JonEllis) <ellis.jp@gmail.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nDOCUMENTATION = '''\n---\nmodule: sudoers\nshort_description: Manage sudoers files\nversion_added: \"4.3.0\"\ndescription:\n  - This module allows for the manipulation of sudoers files.\nauthor:\n  - \"Jon Ellis (@JonEllis) <ellis.jp@gmail.com>\"\noptions:\n  commands:\n    description:\n      - The commands allowed by the sudoers rule.\n      - Multiple can be added by passing a list of commands.\n      - Use C(ALL) for all commands.\n    type: list\n    elements: str\n  group:\n    description:\n      - The name of the group for the sudoers rule.\n      - This option cannot be used in conjunction with I(user).\n    type: str\n  name:\n    required: true\n    description:\n      - The name of the sudoers rule.\n      - This will be used for the filename for the sudoers file managed by this rule.\n    type: str\n  nopassword:\n    description:\n      - Whether a password will be required to run the sudo'd command.\n    default: true\n    type: bool\n  runas:\n    description:\n      - Specify the target user the command(s) will run as.\n    type: str\n    version_added: 4.7.0\n  sudoers_path:\n    description:\n      - The path which sudoers config files will be managed in.\n    default: /etc/sudoers.d\n    type: str\n  state:\n    default: \"present\"\n    choices:\n      - present\n      - absent\n    description:\n      - Whether the rule should exist or not.\n    type: str\n  user:\n    description:\n      - The name of the user for the sudoers rule.\n      - This option cannot be used in conjunction with I(group).\n    type: str\n'''\n\nEXAMPLES = '''\n- name: Allow the backup user to sudo /usr/local/bin/backup\n  community.general.sudoers:\n    name: allow-backup\n    state: present\n    user: backup\n    commands: /usr/local/bin/backup\n\n- name: Allow the bob user to run any commands as alice with sudo -u alice\n  community.general.sudoers:\n    name: bob-do-as-alice\n    state: present\n    user: bob\n    runas: alice\n    commands: ALL\n\n- name: >-\n    Allow the monitoring group to run sudo /usr/local/bin/gather-app-metrics\n    without requiring a password\n  community.general.sudoers:\n    name: monitor-app\n    group: monitoring\n    commands: /usr/local/bin/gather-app-metrics\n\n- name: >-\n    Allow the alice user to run sudo /bin/systemctl restart my-service or\n    sudo /bin/systemctl reload my-service, but a password is required\n  community.general.sudoers:\n    name: alice-service\n    user: alice\n    commands:\n      - /bin/systemctl restart my-service\n      - /bin/systemctl reload my-service\n    nopassword: false\n\n- name: Revoke the previous sudo grants given to the alice user\n  community.general.sudoers:\n    name: alice-service\n    state: absent\n'''\n\nimport os\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible.module_utils.common.text.converters import to_native\n\n\nclass Sudoers(object):\n\n    FILE_MODE = 0o440\n\n    def __init__(self, module):\n        self.check_mode = module.check_mode\n        self.name = module.params['name']\n        self.user = module.params['user']\n        self.group = module.params['group']\n        self.state = module.params['state']\n        self.nopassword = module.params['nopassword']\n        self.runas = module.params['runas']\n        self.sudoers_path = module.params['sudoers_path']\n        self.file = os.path.join(self.sudoers_path, self.name)\n        self.commands = module.params['commands']\n\n    def write(self):\n        if self.check_mode:\n            return\n\n        with open(self.file, 'w') as f:\n            f.write(self.content())\n\n        os.chmod(self.file, self.FILE_MODE)\n\n    def delete(self):\n        if self.check_mode:\n            return\n\n        os.remove(self.file)\n\n    def exists(self):\n        return os.path.exists(self.file)\n\n    def matches(self):\n        with open(self.file, 'r') as f:\n            content_matches = f.read() == self.content()\n\n        current_mode = os.stat(self.file).st_mode & 0o777\n        mode_matches = current_mode == self.FILE_MODE\n\n        return content_matches and mode_matches\n\n    def content(self):\n        if self.user:\n            owner = self.user\n        elif self.group:\n            owner = '%{group}'.format(group=self.group)\n\n        commands_str = ', '.join(self.commands)\n        nopasswd_str = 'NOPASSWD:' if self.nopassword else ''\n        runas_str = '({runas})'.format(runas=self.runas) if self.runas is not None else ''\n        return \"{owner} ALL={runas}{nopasswd} {commands}\\n\".format(owner=owner, runas=runas_str, nopasswd=nopasswd_str, commands=commands_str)\n\n    def run(self):\n        if self.state == 'absent':\n            if self.exists():\n                self.delete()\n                return True\n            else:\n                return False\n\n        if self.exists() and self.matches():\n            return False\n\n        self.write()\n        return True\n\n\ndef main():\n    argument_spec = {\n        'commands': {\n            'type': 'list',\n            'elements': 'str',\n        },\n        'group': {},\n        'name': {\n            'required': True,\n        },\n        'nopassword': {\n            'type': 'bool',\n            'default': True,\n        },\n        'runas': {\n            'type': 'str',\n            'default': None,\n        },\n        'sudoers_path': {\n            'type': 'str',\n            'default': '/etc/sudoers.d',\n        },\n        'state': {\n            'default': 'present',\n            'choices': ['present', 'absent'],\n        },\n        'user': {},\n    }\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        mutually_exclusive=[['user', 'group']],\n        supports_check_mode=True,\n        required_if=[('state', 'present', ['commands'])],\n    )\n\n    sudoers = Sudoers(module)\n\n    try:\n        changed = sudoers.run()\n        module.exit_json(changed=changed)\n    except Exception as e:\n        module.fail_json(msg=to_native(e))\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/system/sudoers.py"}]}