Skip to content

Commit

Permalink
New inventory plugin
Browse files Browse the repository at this point in the history
The inventory plugin compiles a dynamic inventory from IPA domain, filters
servers by role(s).

Usage:

Create yml file, for example `freeipa.yml`:

    ---
    plugin: freeipa
    server: server.ipa.local
    ipaadmin_password: SomeADMINpassword

Get compiled inventory:

    ansible-inventory -i freeipa.yml --graph
  • Loading branch information
t-woerner committed May 3, 2024
1 parent cbff802 commit 36725f8
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 0 deletions.
84 changes: 84 additions & 0 deletions README-inventory-plugin-freeipa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
Inventory plugin
================

Description
-----------


The inventory plugin compiles a dynamic inventory from IPA domain. The servers can be filtered by their role(s).

This plugin is using the Python requests binding, that is only available for Python 3.7 and up.


Features
--------
* Dynamic inventory


Supported FreeIPA Versions
--------------------------

FreeIPA versions 4.6.0 and up are supported by the inventory plugin.


Requirements
------------

**Controller**
* Ansible version: 2.13+

**Node**
* Supported FreeIPA version (see above)


Usage
=====

Example inventory file "freeipa.yml":

```yml
---
plugin: freeipa
address: server.ipa.local
ipaadmin_password: SomeADMINpassword
```


How to use the plugin
---------------------

With the `ansible-inventory` command it is possible to show the generated inventorey:

```bash
ansible-inventory -v -i freeipa.yml --graph
```

Example inventory file "freeipa.yml" for use with `playbooks/config/retrieve-config.yml`:

```yml
---
plugin: freeipa
address: server.ipa.local
ipaadmin_password: SomeADMINpassword
inventory_group: ipaserver
```

```bash
ansible-playbook -u root -i ipa.yml playbooks/config/retrieve-config.yml
```

Variables
=========

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
`server` | The FQDN of server to start the scan. (string) | yes
`role` | The role(s) of the server. If several roles are given, only servers that have all the roles are returned. (list of strings) (choices: "IPA master", "CA server", "KRA server", "DNS server", "AD trust controller", "AD trust agent") | no
`inventory_group` | The inventory group to create. The default group name is "ipaservers". | no

Authors
=======

- Thomas Woerner
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Features
* Repair mode for clients
* Backup and restore, also to and from controller
* Smartcard setup for servers and clients
* Inventory plugin freeipa
* Modules for automembership rule management
* Modules for automount key management
* Modules for automount location management
Expand Down Expand Up @@ -469,4 +470,9 @@ Modules in plugin/modules
* [ipauser](README-user.md)
* [ipavault](README-vault.md)

Inventory plugins in plugin/inventory
=====================================

* [freeipa](README-inventory-plugin-freeipa.md)

If you want to write a new module please read [writing a new module](plugins/modules/README.md).
161 changes: 161 additions & 0 deletions plugins/inventory/freeipa.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# -*- coding: utf-8 -*-

# Authors:
# Thomas Woerner <twoerner@redhat.com>
#
# Copyright (C) 2024 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

ANSIBLE_METADATA = {
"metadata_version": "1.0",
"supported_by": "community",
"status": ["preview"],
}

DOCUMENTATION = """
---
name: freeipa
plugin_type: inventory
version_added: "1.13"
short_description: Compiles a dynamic inventory from IPA domain
description: |
Compiles a dynamic inventory from IPA domain, filters servers by role(s).
options:
plugin:
description: Marks this as an instance of the "freeipa" plugin.
required: True
choices: ['freeipa']
ipaadmin_principal:
description: The admin principal.
default: admin
type: str
ipaadmin_password:
description: The admin password.
required: true
type: str
server:
description: FQDN of server to start the scan.
type: str
required: true
role:
description: |
The role(s) of the server. If several roles are given, only servers
that have all the roles are returned.
type: list
elements: str
choices: ["IPA master", "CA server", "KRA server", "DNS server",
"AD trust controller", "AD trust agent"]
required: false
inventory_group:
description: |
The inventory group to create. The default group name is "ipaservers".
type: str
default: ipaservers
author:
- Thomas Woerner (@t-woerner)
"""

EXAMPLES = """
# inventory.config file in YAML format
plugin: freeipa
server: ipaserver-01.ipa.local
ipaadmin_password: SomeADMINpassword
"""

import os
import requests
try:
from requests.packages import urllib3
except ImportError:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

from ansible import constants
from ansible.errors import AnsibleParserError
from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.inventory import BaseInventoryPlugin
from ansible.module_utils.six.moves.urllib.parse import quote


class InventoryModule(BaseInventoryPlugin):

NAME = 'freeipa'

def verify_file(self, path):
# pylint: disable=super-with-arguments
if super(InventoryModule, self).verify_file(path):
_name, ext = os.path.splitext(path)
if ext in constants.YAML_FILENAME_EXTENSIONS:
return True
return False

def parse(self, inventory, loader, path, cache=False):
# pylint: disable=super-with-arguments
super(InventoryModule, self).parse(inventory, loader, path,
cache=cache)
self._read_config_data(path) # This also loads the cache

self.get_option("plugin")
ipaadmin_principal = self.get_option("ipaadmin_principal")
ipaadmin_password = self.get_option("ipaadmin_password")
server = self.get_option("server")
role = self.get_option("role")
inventory_group = self.get_option("inventory_group")

self.inventory.add_group(inventory_group)

ipa_url = "https://%s/ipa" % server

s = requests.Session()
s.headers.update({"referer": ipa_url})
s.headers.update({"Content-Type":
"application/x-www-form-urlencoded"})
s.headers.update({"Accept": "text/plain"})

data = 'user=%s&password=%s' % (quote(ipaadmin_principal, safe=''),
quote(ipaadmin_password, safe=''))
response = s.post("%s/session/login_password" % ipa_url,
data=data, verify=False)

# Now use json API
s.headers.update({"Content-Type": "application/json"})

kw_args = {}
if role is not None:
kw_args["servrole"] = role
json_data = {
"method" : "server_find",
"params": [[], kw_args],
"id": 0
}
response = s.post("%s/session/json" % ipa_url, json=json_data,
verify=False)
json_res = response.json()

error = json_res.get("error")
if error is not None:
raise AnsibleParserError("ERROR: %s" % to_native(error))

if "result" in json_res and "result" in json_res["result"]:
res = json_res["result"].get("result")
if isinstance(res, list):
for server in res:
self.inventory.add_host(server["cn"][0],
group=inventory_group)

0 comments on commit 36725f8

Please sign in to comment.