summaryrefslogtreecommitdiffstats
path: root/ansible_collections/vultr/cloud/plugins
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/vultr/cloud/plugins
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/vultr/cloud/plugins')
-rw-r--r--ansible_collections/vultr/cloud/plugins/doc_fragments/__init__.py0
-rw-r--r--ansible_collections/vultr/cloud/plugins/doc_fragments/vultr_v2.py51
-rw-r--r--ansible_collections/vultr/cloud/plugins/inventory/vultr.py348
-rw-r--r--ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py368
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/__init__.py0
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/account_info.py117
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/block_storage.py271
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/block_storage_info.py139
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/dns_domain.py155
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/dns_domain_info.py109
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/dns_record.py267
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/firewall_group.py138
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/firewall_group_info.py114
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py297
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/firewall_rule_info.py162
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/instance.py739
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/instance_info.py271
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/os_info.py115
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/plan_info.py140
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/plan_metal_info.py153
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/region_info.py106
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py290
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/snapshot.py218
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/snapshot_info.py132
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/ssh_key.py148
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/ssh_key_info.py117
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/startup_script.py196
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/startup_script_info.py120
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/user.py229
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/user_info.py125
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/vpc.py182
-rw-r--r--ansible_collections/vultr/cloud/plugins/modules/vpc_info.py122
32 files changed, 5939 insertions, 0 deletions
diff --git a/ansible_collections/vultr/cloud/plugins/doc_fragments/__init__.py b/ansible_collections/vultr/cloud/plugins/doc_fragments/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/doc_fragments/__init__.py
diff --git a/ansible_collections/vultr/cloud/plugins/doc_fragments/vultr_v2.py b/ansible_collections/vultr/cloud/plugins/doc_fragments/vultr_v2.py
new file mode 100644
index 000000000..692103e8c
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/doc_fragments/vultr_v2.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021 René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+class ModuleDocFragment(object):
+
+ DOCUMENTATION = """
+options:
+ api_key:
+ description:
+ - API key of the Vultr API.
+ - Fallback environment variable C(VULTR_API_KEY).
+ type: str
+ required: true
+ api_timeout:
+ description:
+ - HTTP timeout to Vultr API.
+ - Fallback environment variable C(VULTR_API_TIMEOUT).
+ type: int
+ default: 180
+ api_retries:
+ description:
+ - Amount of retries in case of the Vultr API retuns an HTTP 503 code.
+ - Fallback environment variable C(VULTR_API_RETRIES).
+ type: int
+ default: 5
+ api_retry_max_delay:
+ description:
+ - Retry backoff delay in seconds is exponential up to this max. value, in seconds.
+ - Fallback environment variable C(VULTR_API_RETRY_MAX_DELAY).
+ type: int
+ default: 12
+ api_endpoint:
+ description:
+ - URL to API endpint (without trailing slash).
+ - Fallback environment variable C(VULTR_API_ENDPOINT).
+ type: str
+ default: https://api.vultr.com/v2
+ validate_certs:
+ description:
+ - Validate SSL certs of the Vultr API.
+ type: bool
+ default: true
+notes:
+ - Also see the API documentation on U(https://www.vultr.com/api/).
+"""
diff --git a/ansible_collections/vultr/cloud/plugins/inventory/vultr.py b/ansible_collections/vultr/cloud/plugins/inventory/vultr.py
new file mode 100644
index 000000000..c08446134
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/inventory/vultr.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) jasites <jsites@vultr.com>
+# Copyright: Contributors to the Ansible project
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+# flake8: noqa: E402
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+name: vultr
+short_description: Retrieves list of instances via Vultr v2 API
+description:
+ - Vultr inventory plugin.
+ - Retrieves list of instances via Vultr v2 API.
+ - Configuration of this plugin is done with files ending with '(vultr|vultr_hosts|vultr_instances).(yaml|yml)'
+version_added: '1.4.0'
+author:
+ - jasites (@jasites)
+extends_documentation_fragment:
+ - constructed
+ - inventory_cache
+options:
+ api_endpoint:
+ description:
+ - URL to API endpint (without trailing slash).
+ - Fallback environment variable C(VULTR_API_ENDPOINT).
+ type: str
+ env:
+ - name: VULTR_API_ENDPOINT
+ default: https://api.vultr.com/v2
+ api_key:
+ description:
+ - API key of the Vultr API.
+ - Fallback environment variable C(VULTR_API_KEY).
+ type: str
+ env:
+ - name: VULTR_API_KEY
+ required: true
+ api_results_per_page:
+ description:
+ - When receiving large numbers of instances, specify how many instances should be returned per call to API.
+ - This does not determine how many results are returned; all instances are returned according to other filters.
+ - Vultr API maximum is 500.
+ - Fallback environment variable C(VULTR_API_RESULTS_PER_PAGE).
+ type: int
+ env:
+ - name: VULTR_API_RESULTS_PER_PAGE
+ default: 100
+ api_timeout:
+ description:
+ - HTTP timeout to Vultr API.
+ - Fallback environment variable C(VULTR_API_TIMEOUT).
+ type: int
+ env:
+ - name: VULTR_API_TIMEOUT
+ default: 60
+ attributes:
+ description:
+ - Instance attributes to add as host variables to each host added to inventory.
+ - See U(https://www.vultr.com/api/#operation/list-instances) for valid values.
+ type: list
+ elements: str
+ default:
+ - id
+ - region
+ - label
+ - plan
+ - hostname
+ - main_ip
+ - v6_main_ip
+ - tags
+ filters:
+ description:
+ - Filter hosts with Jinja2 templates.
+ - If not provided, all hosts are added to inventory.
+ type: list
+ elements: str
+ default: []
+ instance_type:
+ description:
+ - Type of instance.
+ type: str
+ default: cloud
+ choices:
+ - cloud
+ - bare_metal
+ version_added: '1.8.0'
+ plugin:
+ description:
+ - Name of Vultr inventory plugin.
+ - This should always be C(vultr.cloud.vultr).
+ type: str
+ choices: ['vultr.cloud.vultr']
+ required: true
+ variable_prefix:
+ description:
+ - Prefix of generated variables (e.g. C(id) becomes C(vultr_id)).
+ type: str
+ default: 'vultr_'
+ validate_certs:
+ description:
+ - Validate SSL certs of the Vultr API.
+ type: bool
+ default: true
+notes:
+ - Also see the API documentation on U(https://www.vultr.com/api/).
+"""
+
+EXAMPLES = """
+---
+# File endings vultr{,-{hosts,instances}}.y{,a}ml
+# All configuration done via environment variables:
+plugin: vultr.cloud.vultr
+
+# Grouping and filtering configuration in inventory file
+plugin: vultr.cloud.vultr
+api_key: '{{ lookup("pipe"), "./get_vultr_api_key.sh" }}'
+keyed_groups:
+ - key: vultr_tags | lower
+ prefix: ''
+ separator: ''
+filters:
+ - '"vpc" in vultr_tags'
+ - 'vultr_plan == "vc2-2c-4gb"'
+
+# Unless you can connect to your servers via it's vultr label,
+# we suggest setting ansible_host with compose:
+plugin: vultr.cloud.vultr
+compose:
+ ansible_host: vultr_main_ip
+
+# Respectively for IPv6:
+plugin: vultr.cloud.vultr
+compose:
+ ansible_host: vultr_v6_main_ip
+
+# Prioritize IPv6 over IPv4 if available.
+plugin: vultr.cloud.vultr
+compose:
+ ansible_host: vultr_v6_main_ip or vultr_main_ip
+
+# Querying the bare metal instances
+plugin: vultr.cloud.vultr
+instance_type: bare_metal
+"""
+
+RETURN = r""" # """
+
+import json
+
+from ansible.errors import AnsibleError, AnsibleParserError
+from ansible.module_utils._text import to_native
+from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError
+from ansible.module_utils.urls import Request
+from ansible.plugins.inventory import BaseInventoryPlugin, Cacheable, Constructable
+
+from ..module_utils.vultr_v2 import VULTR_USER_AGENT
+
+
+class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
+
+ NAME = "vultr.cloud.vultr"
+
+ RESOURCES_PER_TYPE = {
+ "cloud": {
+ "resource": "instances",
+ "response": "instances",
+ },
+ "bare_metal": {
+ "resource": "bare-metals",
+ "response": "bare_metals",
+ },
+ }
+
+ def _get_instances(self):
+ instances = []
+ api_key = self.get_option("api_key")
+ if self.templar.is_template(api_key):
+ api_key = self.templar.template(api_key)
+
+ headers = {
+ "Content-Type": "application/json",
+ "User-Agent": VULTR_USER_AGENT,
+ "Authorization": "Bearer {0}".format(api_key),
+ }
+
+ self.req = Request(
+ headers=headers,
+ timeout=int(self.get_option("api_timeout")), # type: ignore
+ validate_certs=self.get_option("validate_certs"), # type: ignore
+ )
+
+ instance_type_config = self.get_option("instance_type") or "cloud"
+ self.display.vvv("Type is: {0}".format(instance_type_config))
+
+ instance_type = self.RESOURCES_PER_TYPE[instance_type_config]
+
+ api_endpoint = "{0}/{1}?per_page={2}".format(
+ self.get_option("api_endpoint"),
+ instance_type["resource"], # type: ignore
+ self.get_option("api_results_per_page"),
+ )
+
+ cursor = ""
+ req_url = api_endpoint
+ try:
+ while True:
+ self.display.vvv("Querying API: {0}".format(req_url))
+
+ page = json.load(self.req.get(req_url))
+ instances.extend(page[instance_type["response"]]) # type: ignore
+ cursor = page["meta"]["links"]["next"]
+
+ if cursor == "":
+ return instances
+
+ req_url = "{0}&cursor={1}".format(api_endpoint, cursor)
+
+ except (KeyError, ValueError):
+ raise AnsibleParserError("Unable to parse JSON response.")
+ except (URLError, HTTPError) as err:
+ raise AnsibleParserError(err)
+
+ def _populate(self, instances):
+ attributes = self.get_option("attributes")
+ host_filters = self.get_option("filters")
+ strict = self.get_option("strict")
+ variable_prefix = self.get_option("variable_prefix")
+
+ for instance in instances:
+ instance_label = instance.get("label")
+
+ if not instance_label:
+ self.display.warning(msg="instance ID {0} has no label, skipping.".format(instance.get("id")))
+ continue
+
+ host_variables = {}
+ for k, v in instance.items():
+ if k in attributes:
+ host_variables["{0}{1}".format(variable_prefix, k)] = v
+
+ if not self._passes_filters(
+ host_filters,
+ host_variables,
+ instance_label,
+ strict, # type: ignore
+ ):
+ self.display.vvv("Host {0} excluded by filters".format(instance_label))
+ continue
+
+ self.inventory.add_host(instance_label) # type: ignore
+
+ for var_name, var_val in host_variables.items():
+ self.inventory.set_variable(instance_label, var_name, var_val) # type: ignore
+
+ self._set_composite_vars(
+ self.get_option("compose"),
+ self.inventory.get_host(instance_label).get_vars(), # type: ignore
+ instance_label,
+ strict, # type: ignore
+ )
+
+ self._add_host_to_composed_groups(
+ self.get_option("groups"),
+ dict(),
+ instance_label,
+ strict, # type: ignore
+ )
+
+ self._add_host_to_keyed_groups(
+ self.get_option("keyed_groups"),
+ dict(),
+ instance_label,
+ strict, # type: ignore
+ )
+
+ def _passes_filters(self, filters, variables, host, strict=False):
+ if filters and isinstance(filters, list):
+ for template in filters:
+ try:
+ if not self._compose(template, variables):
+ return False
+ except Exception as e:
+ if strict:
+ raise AnsibleError(
+ "Could not evaluate host filter {0} for {1}: {2}".format(
+ template,
+ host,
+ to_native(e),
+ ),
+ )
+ return False
+ return True
+
+ def verify_file(self, path):
+ valid = False
+ if super(InventoryModule, self).verify_file(path):
+ if path.endswith(
+ (
+ "vultr.yaml",
+ "vultr.yml",
+ "vultr_hosts.yaml",
+ "vultr_hosts.yml",
+ "vultr_instances.yaml",
+ "vultr_instances.yml",
+ )
+ ):
+ valid = True
+ else:
+ self.display.vvv(
+ "Skipping due to inventory configuration file name mismatch. "
+ "Valid filename endings: "
+ "vultr.yaml, vultr.yml, vultr_hosts.yaml, vultr_hosts.yml, "
+ "vultr_instances.yaml, vultr_instances.yml"
+ )
+ return valid
+
+ def parse(self, inventory, loader, path, cache=True):
+ super(InventoryModule, self).parse(inventory, loader, path)
+
+ self._read_config_data(path)
+
+ cache_key = self.get_cache_key(path)
+ use_cache = self.get_option("cache") and cache
+ update_cache = self.get_option("cache") and not cache
+
+ instances = None
+ if use_cache:
+ try:
+ instances = self._cache[cache_key]
+ except KeyError:
+ update_cache = True
+
+ if instances is None:
+ instances = self._get_instances()
+
+ if update_cache:
+ self._cache[cache_key] = instances
+
+ self._populate(instances)
diff --git a/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py b/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py
new file mode 100644
index 000000000..fb49d6180
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/module_utils/vultr_v2.py
@@ -0,0 +1,368 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+import random
+import time
+
+from ansible.module_utils._text import to_text
+from ansible.module_utils.basic import env_fallback
+from ansible.module_utils.six.moves.urllib.parse import quote
+from ansible.module_utils.urls import fetch_url
+
+VULTR_USER_AGENT = "Ansible Vultr v2"
+
+
+def vultr_argument_spec():
+ return dict(
+ api_endpoint=dict(
+ type="str",
+ fallback=(env_fallback, ["VULTR_API_ENDPOINT"]),
+ default="https://api.vultr.com/v2",
+ ),
+ api_key=dict(
+ type="str",
+ fallback=(env_fallback, ["VULTR_API_KEY"]),
+ no_log=True,
+ required=True,
+ ),
+ api_timeout=dict(
+ type="int",
+ fallback=(env_fallback, ["VULTR_API_TIMEOUT"]),
+ default=180,
+ ),
+ api_retries=dict(
+ type="int",
+ fallback=(env_fallback, ["VULTR_API_RETRIES"]),
+ default=5,
+ ),
+ api_retry_max_delay=dict(
+ type="int",
+ fallback=(env_fallback, ["VULTR_API_RETRY_MAX_DELAY"]),
+ default=12,
+ ),
+ validate_certs=dict(
+ type="bool",
+ default=True,
+ ),
+ )
+
+
+def backoff(retry, retry_max_delay=12):
+ randomness = random.randint(0, 1000) / 1000.0
+ delay = 2**retry + randomness
+ if delay > retry_max_delay:
+ delay = retry_max_delay + randomness
+ time.sleep(delay)
+
+
+class AnsibleVultr:
+ def __init__(
+ self,
+ module,
+ namespace,
+ resource_path,
+ ressource_result_key_singular,
+ ressource_result_key_plural=None,
+ resource_key_name=None,
+ resource_key_id="id",
+ resource_get_details=False,
+ resource_create_param_keys=None,
+ resource_update_param_keys=None,
+ resource_update_method="PATCH",
+ ):
+
+ self.module = module
+ self.namespace = namespace
+
+ # The API resource path e.g ssh_key
+ self.ressource_result_key_singular = ressource_result_key_singular
+
+ # The API result data key e.g ssh_keys
+ self.ressource_result_key_plural = ressource_result_key_plural or "%ss" % ressource_result_key_singular
+
+ # The API resource path e.g /ssh-keys
+ self.resource_path = resource_path
+
+ # The name key of the resource, usually 'name'
+ self.resource_key_name = resource_key_name
+
+ # The name key of the resource, usually 'id'
+ self.resource_key_id = resource_key_id
+
+ # Some resources need an additional GET request to get all attributes
+ self.resource_get_details = resource_get_details
+
+ # List of params used to create the resource
+ self.resource_create_param_keys = resource_create_param_keys or []
+
+ # List of params used to update the resource
+ self.resource_update_param_keys = resource_update_param_keys or []
+
+ # Some resources have PUT, many have PATCH
+ self.resource_update_method = resource_update_method
+
+ self.result = {
+ "changed": False,
+ namespace: dict(),
+ "diff": dict(before=dict(), after=dict()),
+ "vultr_api": {
+ "api_timeout": module.params["api_timeout"],
+ "api_retries": module.params["api_retries"],
+ "api_retry_max_delay": module.params["api_retry_max_delay"],
+ "api_endpoint": module.params["api_endpoint"],
+ },
+ }
+
+ self.headers = {
+ "Authorization": "Bearer %s" % self.module.params["api_key"],
+ "User-Agent": VULTR_USER_AGENT,
+ "Accept": "application/json",
+ }
+
+ # Hook custom configurations
+ self.configure()
+
+ def configure(self):
+ pass
+
+ def transform_resource(self, resource):
+ """
+ Transforms (optional) the resource dict queried from the API
+ """
+ return resource
+
+ def api_query(self, path, method="GET", data=None, query_params=None):
+ if query_params:
+ query = "?"
+ for k, v in query_params.items():
+ query += "&%s=%s" % (to_text(k), quote(to_text(v)))
+ path += query
+
+ if data:
+ data = self.module.jsonify(data)
+
+ retry_max_delay = self.module.params["api_retry_max_delay"]
+
+ info = dict()
+ resp_body = None
+ for retry in range(0, self.module.params["api_retries"]):
+ resp, info = fetch_url(
+ self.module,
+ self.module.params["api_endpoint"] + path,
+ method=method,
+ data=data,
+ headers=self.headers,
+ timeout=self.module.params["api_timeout"],
+ )
+
+ resp_body = resp.read() if resp is not None else ""
+
+ # Check for:
+ # 429 Too Many Requests
+ # 500 Internal Server Error
+ if info["status"] not in (429, 500):
+ break
+
+ # Vultr has a rate limiting requests per second, try to be polite
+ # Use exponential backoff plus a little bit of randomness
+ backoff(retry=retry, retry_max_delay=retry_max_delay)
+
+ # Success with content
+ if info["status"] in (200, 201, 202):
+ return self.module.from_json(to_text(resp_body, errors="surrogate_or_strict"))
+
+ # Success without content
+ if info["status"] in (404, 204):
+ return dict()
+
+ self.module.fail_json(
+ msg='Failure while calling the Vultr API v2 with %s for "%s".' % (method, path),
+ fetch_url_info=info,
+ )
+
+ def query_filter_list_by_name(
+ self,
+ path,
+ key_name,
+ result_key,
+ param_key=None,
+ key_id=None,
+ query_params=None,
+ get_details=False,
+ fail_not_found=False,
+ skip_transform=True,
+ ):
+ param_value = self.module.params.get(param_key or key_name)
+
+ found = dict()
+ for resource in self.query_list(path=path, result_key=result_key, query_params=query_params):
+ if resource.get(key_name) == param_value:
+ if found:
+ self.module.fail_json(msg="More than one record with name=%s found. " "Use multiple=true if module supports it." % param_value)
+ found = resource
+ if found:
+ if get_details:
+ return self.query_by_id(resource_id=found[key_id], skip_transform=skip_transform)
+ else:
+ if skip_transform:
+ return found
+ else:
+ return self.transform_resource(found)
+
+ elif fail_not_found:
+ self.module.fail_json(msg="No Resource %s with %s found: %s" % (path, key_name, param_value))
+
+ return dict()
+
+ def query_filter_list(self):
+ # Returns a single dict representing the resource queryied by name
+ return self.query_filter_list_by_name(
+ key_name=self.resource_key_name,
+ key_id=self.resource_key_id,
+ get_details=self.resource_get_details,
+ path=self.resource_path,
+ result_key=self.ressource_result_key_plural,
+ skip_transform=False,
+ )
+
+ def query_by_id(self, resource_id=None, path=None, result_key=None, skip_transform=True):
+ # Defaults
+ path = path or self.resource_path
+ result_key = result_key or self.ressource_result_key_singular
+
+ resource = self.api_query(path="%s%s" % (path, "/" + resource_id if resource_id else resource_id))
+ if resource:
+ if skip_transform:
+ return resource[result_key]
+ else:
+ return self.transform_resource(resource[result_key])
+
+ return dict()
+
+ def query(self):
+ # Returns a single dict representing the resource
+ return self.query_filter_list()
+
+ def query_list(self, path=None, result_key=None, query_params=None):
+ # Defaults
+ path = path or self.resource_path
+ result_key = result_key or self.ressource_result_key_plural
+
+ resources = self.api_query(path=path, query_params=query_params)
+ return resources[result_key] if resources else []
+
+ def wait_for_state(self, resource, key, states, cmp="="):
+ for retry in range(0, 60):
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id], skip_transform=False)
+ if cmp == "=":
+ if key not in resource or resource[key] in states or not resource[key]:
+ break
+ else:
+ if key not in resource or resource[key] not in states or not resource[key]:
+ break
+ backoff(retry=retry)
+ else:
+ if cmp == "=":
+ self.module.fail_json(msg="Wait for %s to become %s timed out" % (key, states))
+ else:
+ self.module.fail_json(msg="Wait for %s to not be in %s timed out" % (key, states))
+
+ return resource
+
+ def create_or_update(self):
+ resource = self.query()
+ if not resource:
+ resource = self.create()
+ else:
+ resource = self.update(resource)
+ return resource
+
+ def present(self):
+ self.get_result(self.create_or_update())
+
+ def create(self):
+ data = dict()
+ for param in self.resource_create_param_keys:
+ data[param] = self.module.params.get(param)
+
+ self.result["changed"] = True
+ resource = dict()
+
+ self.result["diff"]["before"] = dict()
+ self.result["diff"]["after"] = data
+
+ if not self.module.check_mode:
+ resource = self.api_query(
+ path=self.resource_path,
+ method="POST",
+ data=data,
+ )
+ return resource.get(self.ressource_result_key_singular) if resource else dict()
+
+ def is_diff(self, param, resource):
+ value = self.module.params.get(param)
+ if value is None:
+ return False
+
+ if param not in resource:
+ self.module.fail_json(msg="Can not diff, key %s not found in resource" % param)
+
+ if isinstance(value, list):
+ for v in value:
+ if v not in resource[param]:
+ return True
+ elif resource[param] != value:
+ return True
+
+ return False
+
+ def update(self, resource):
+ data = dict()
+
+ for param in self.resource_update_param_keys:
+ if self.is_diff(param, resource):
+ self.result["changed"] = True
+ data[param] = self.module.params.get(param)
+
+ if self.result["changed"]:
+ self.result["diff"]["before"] = dict(**resource)
+ self.result["diff"]["after"] = dict(**resource)
+ self.result["diff"]["after"].update(data)
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="%s/%s" % (self.resource_path, resource[self.resource_key_id]),
+ method=self.resource_update_method,
+ data=data,
+ )
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id])
+ return resource
+
+ def absent(self, resource=None):
+ if resource is None:
+ resource = self.query()
+
+ if resource:
+ self.result["changed"] = True
+
+ self.result["diff"]["before"] = dict(**resource)
+ self.result["diff"]["after"] = dict()
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="%s/%s" % (self.resource_path, resource[self.resource_key_id]),
+ method="DELETE",
+ )
+ self.get_result(resource)
+
+ def transform_result(self, resource):
+ return resource
+
+ def get_result(self, resource):
+ self.result[self.namespace] = self.transform_result(resource)
+ self.module.exit_json(**self.result)
diff --git a/ansible_collections/vultr/cloud/plugins/modules/__init__.py b/ansible_collections/vultr/cloud/plugins/modules/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/__init__.py
diff --git a/ansible_collections/vultr/cloud/plugins/modules/account_info.py b/ansible_collections/vultr/cloud/plugins/modules/account_info.py
new file mode 100644
index 000000000..ecf55e2b8
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/account_info.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: account_info
+short_description: Get information about the Vultr account
+description:
+ - Get infos about account balance, charges and payments.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Get Vultr account infos
+ vultr.cloud.account_info:
+ register: result
+
+- name: Print the infos
+ ansible.builtin.debug:
+ var: result.vultr_account_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_account:
+ description: Account used in the ini file to select the key.
+ returned: success
+ type: str
+ sample: default
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_account_info:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ balance:
+ description: Your account balance.
+ returned: success
+ type: float
+ sample: -214.69
+ pending_charges:
+ description: Charges pending.
+ returned: success
+ type: float
+ sample: 57.03
+ last_payment_date:
+ description: Date of the last payment.
+ returned: success
+ type: str
+ sample: "2021-11-07T05:57:59-05:00"
+ last_payment_amount:
+ description: The amount of the last payment transaction.
+ returned: success
+ type: float
+ sample: -250.0
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_account_info",
+ resource_path="/account",
+ ressource_result_key_singular="account",
+ )
+
+ vultr.get_result(vultr.query_by_id(resource_id=""))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/block_storage.py b/ansible_collections/vultr/cloud/plugins/modules/block_storage.py
new file mode 100644
index 000000000..59b8cc068
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/block_storage.py
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: block_storage
+short_description: Manages block storage volumes on Vultr
+description:
+ - Manage block storage volumes.
+version_added: "1.0.0"
+author:
+ - "René Moser (@resmo)"
+ - "Yanis Guenane (@Spredzy)"
+options:
+ label:
+ description:
+ - Name of the block storage volume.
+ required: true
+ aliases: [ name ]
+ type: str
+ size_gb:
+ description:
+ - Size of the block storage volume in GB.
+ - Required if I(state) is present.
+ - If it is larger than the volume's current size, the volume will be resized.
+ aliases: [ size ]
+ type: int
+ block_type:
+ description:
+ - The type of block storage volume that will be created.
+ default: high_perf
+ choices: [ high_perf, storage_opt ]
+ type: str
+ version_added: "1.2.0"
+ region:
+ description:
+ - Region the block storage volume is deployed into.
+ - Required if I(state) is present.
+ type: str
+ state:
+ description:
+ - State of the block storage volume.
+ default: present
+ choices: [ present, absent]
+ type: str
+ attached_to_instance:
+ description:
+ - The ID of the server instance the volume is attached to.
+ type: str
+ live:
+ description:
+ - Whether the volume should be attached/detached without restarting the instance.
+ type: bool
+ default: false
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+---
+- name: Ensure a block storage volume is present
+ vultr.cloud.block_storage:
+ name: myvolume
+ size_gb: 10
+ block_type: storage_opt
+ region: ams
+
+- name: Ensure a block storage volume is absent
+ vultr.cloud.block_storage:
+ name: myvolume
+ state: absent
+
+- name: Ensure a block storage volume exists and is attached a server instance
+ vultr.cloud.block_storage:
+ name: myvolume
+ attached_to_instance: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ size_gb: 50
+ block_type: high_perf
+
+- name: Ensure a block storage volume exists but is not attached to any server instance
+ vultr.cloud.block_storage:
+ name: myvolume
+ attached_to_instance: ""
+ size_gb: 50
+ block_type: high_perf
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_account:
+ description: Account used in the ini file to select the key.
+ returned: success
+ type: str
+ sample: default
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_block_storage:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ attached_to_instance:
+ description: The ID of the server instance the volume is attached to.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ cost:
+ description: Cost per month for the volume.
+ returned: success
+ type: float
+ sample: 1.00
+ date_created:
+ description: Date when the volume was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ id:
+ description: ID of the block storage volume.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ label:
+ description: Label of the volume.
+ returned: success
+ type: str
+ sample: my volume
+ region:
+ description: Region the volume was deployed into.
+ returned: success
+ type: str
+ sample: ews
+ size_gb:
+ description: Information about the volume size in GB.
+ returned: success
+ type: int
+ sample: 50
+ block_type:
+ description: HDD or NVMe (storage_opt or high_perf)
+ returned: success
+ type: str
+ sample: high_perf
+ status:
+ description: Status about the deployment of the volume.
+ returned: success
+ type: str
+ sample: active
+ mount_id:
+ description: Mount ID of the volume.
+ returned: success
+ type: str
+ sample: ewr-2f5d7a314fe44f
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrBlockStorage(AnsibleVultr):
+ def update(self, resource):
+ current_size = resource["size_gb"]
+ desired_size = self.module.params["size_gb"]
+ if desired_size < current_size:
+ self.module.params["size_gb"] = current_size
+ self.module.warn("Shrinking is not supported: current size %s, desired size %s" % (current_size, desired_size))
+ return super(AnsibleVultrBlockStorage, self).update(resource=resource)
+
+ def present(self):
+ resource = self.create_or_update() or dict()
+
+ instance_to_attach = self.module.params.get("attached_to_instance")
+ if instance_to_attach is None:
+ # exit and show result if no attach/detach needed.
+ self.get_result(resource)
+
+ instance_attached = resource.get("attached_to_instance", "")
+ if instance_attached != instance_to_attach:
+ self.result["changed"] = True
+
+ mode = "detach" if not instance_to_attach else "attach"
+ self.result["diff"]["after"].update({"attached_to_instance": instance_to_attach})
+
+ data = {
+ "instance_id": instance_to_attach if instance_to_attach else None,
+ "live": self.module.params.get("live"),
+ }
+
+ if not self.module.check_mode:
+ self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], mode),
+ method="POST",
+ data=data,
+ )
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id])
+
+ self.get_result(resource)
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ label=dict(type="str", required=True, aliases=["name"]),
+ size_gb=dict(type="int", aliases=["size"]),
+ block_type=dict(type="str", choices=["high_perf", "storage_opt"], default="high_perf"),
+ region=dict(type="str"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ attached_to_instance=dict(type="str"),
+ live=dict(type="bool", default=False),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "present", ["size_gb", "region"]],
+ ],
+ )
+
+ vultr = AnsibleVultrBlockStorage(
+ module=module,
+ namespace="vultr_block_storage",
+ resource_path="/blocks",
+ ressource_result_key_singular="block",
+ resource_create_param_keys=["label", "size_gb", "region", "block_type"],
+ resource_update_param_keys=["size_gb"],
+ resource_key_name="label",
+ # Query details information about block type
+ resource_get_details=True,
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/block_storage_info.py b/ansible_collections/vultr/cloud/plugins/modules/block_storage_info.py
new file mode 100644
index 000000000..a52591d94
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/block_storage_info.py
@@ -0,0 +1,139 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: block_storage_info
+short_description: Get information about the Vultr block storage
+version_added: "1.0.0"
+description:
+ - Get infos about block storages available.
+author:
+ - "René Moser (@resmo)"
+ - "Yanis Guenane (@Spredzy)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Get Vultr block_storage infos
+ vultr.cloud.block_storage_info:
+ register: result
+
+- name: Print the infos
+ ansible.builtin.debug:
+ var: result.vultr_block_storage_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_block_storage_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ attached_to_instance:
+ description: The ID of the server instance the volume is attached to.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ cost:
+ description: Cost per month for the volume.
+ returned: success
+ type: float
+ sample: 1.00
+ date_created:
+ description: Date when the volume was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ id:
+ description: ID of the block storage volume.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ label:
+ description: Label of the volume.
+ returned: success
+ type: str
+ sample: my volume
+ region:
+ description: Region the volume was deployed into.
+ returned: success
+ type: str
+ sample: ews
+ size_gb:
+ description: Information about the volume size in GB.
+ returned: success
+ type: int
+ sample: 50
+ status:
+ description: Status about the deployment of the volume.
+ returned: success
+ type: str
+ sample: active
+ mount_id:
+ description: Mount ID of the volume.
+ returned: success
+ type: str
+ sample: ewr-2f5d7a314fe44f
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_block_storage_info",
+ resource_path="/blocks",
+ ressource_result_key_singular="block",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/dns_domain.py b/ansible_collections/vultr/cloud/plugins/modules/dns_domain.py
new file mode 100644
index 000000000..3f0132b0c
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/dns_domain.py
@@ -0,0 +1,155 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: dns_domain
+short_description: Manages DNS domains on Vultr
+description:
+ - Create and remove DNS domains.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ domain:
+ description:
+ - The domain name.
+ required: true
+ aliases: [ name ]
+ type: str
+ ip:
+ description:
+ - The default server IP.
+ - Use M(vultr.cloud.dns_record) to change it once the domain is created.
+ - Required if C(state=present).
+ type: str
+ aliases: [ server_ip ]
+ dns_sec:
+ description:
+ - Ensure DNSSEC is enabled or disabled.
+ type: str
+ choices: [ enabled, disabled ]
+ default: disabled
+ state:
+ description:
+ - State of the DNS domain.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a domain exists with DNSSEC
+ vultr.cloud.dns_domain:
+ name: example.com
+ dns_sec: enabled
+ server_ip: 10.10.10.10
+
+- name: Ensure a domain is absent
+ vultr.cloud.dns_domain:
+ name: example.com
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_dns_domain:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ name:
+ description: Name of the DNS Domain.
+ returned: success
+ type: str
+ sample: example.com
+ dns_sec:
+ description: Whether DNSSEC is enabled or disabled.
+ returned: success
+ type: str
+ sample: disabled
+ date_created:
+ description: Date the DNS domain was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ domain=dict(type="str", required=True, aliases=["name"]),
+ ip=dict(type="str", aliases=["server_ip"]),
+ dns_sec=dict(type="str", choices=["enabled", "disabled"], default="disabled"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ("state", "present", ["ip"]),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_dns_domain",
+ resource_path="/domains",
+ ressource_result_key_singular="domain",
+ resource_create_param_keys=["domain", "dns_sec", "ip"],
+ resource_update_param_keys=["domain", "dns_sec"],
+ resource_key_name="domain",
+ resource_key_id="domain",
+ resource_update_method="PUT",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/dns_domain_info.py b/ansible_collections/vultr/cloud/plugins/modules/dns_domain_info.py
new file mode 100644
index 000000000..aa198b176
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/dns_domain_info.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: dns_domain_info
+short_description: Gather information about the Vultr DNS domains
+description:
+ - Gather information about DNS domains available.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr DNS domains information
+ vultr.cloud.dns_domain_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_dns_domain_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_dns_domain_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ domain:
+ description: Name of the DNS Domain.
+ returned: success
+ type: str
+ sample: example.com
+ dns_sec:
+ description: Whether DNSSEC is enabled or disabled.
+ returned: success
+ type: str
+ sample: disabled
+ date_created:
+ description: Date the DNS domain was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_dns_domain_info",
+ resource_path="/domains",
+ ressource_result_key_singular="domain",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/dns_record.py b/ansible_collections/vultr/cloud/plugins/modules/dns_record.py
new file mode 100644
index 000000000..2e4352e93
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/dns_record.py
@@ -0,0 +1,267 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: dns_record
+short_description: Manages DNS records on Vultr
+description:
+ - Create, update and remove DNS records.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - The record name.
+ type: str
+ default: ""
+ domain:
+ description:
+ - The domain the record is related to.
+ type: str
+ required: true
+ type:
+ description:
+ - Type of the record.
+ default: A
+ choices:
+ - A
+ - AAAA
+ - CNAME
+ - NS
+ - MX
+ - SRV
+ - TXT
+ - CAA
+ - SSHFP
+ aliases: [ record_type ]
+ type: str
+ data:
+ description:
+ - Data of the record.
+ - Required if C(state=present).
+ type: str
+ ttl:
+ description:
+ - TTL of the record.
+ default: 300
+ type: int
+ priority:
+ description:
+ - Priority of the record.
+ type: int
+ multiple:
+ description:
+ - Whether to use more than one record with similar I(name) including no name and I(type).
+ - Only allowed for a few record types, e.g. C(type=A), C(type=NS) or C(type=MX).
+ - I(data) will not be updated, instead it is used as a key to find existing records.
+ default: false
+ type: bool
+ state:
+ description:
+ - State of the DNS record.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+
+EXAMPLES = """
+- name: Ensure an A record exists
+ vultr.cloud.dns_record:
+ name: www
+ domain: example.com
+ data: 10.10.10.10
+ ttl: 3600
+
+- name: Ensure a second A record exists for round robin LB
+ vultr.cloud.dns_record:
+ name: www
+ domain: example.com
+ data: 10.10.10.11
+ ttl: 60
+ multiple: true
+
+- name: Ensure a CNAME record exists
+ vultr.cloud.dns_record:
+ name: web
+ type: CNAME
+ domain: example.com
+ data: www.example.com
+
+- name: Ensure MX record exists
+ vultr.cloud.dns_record:
+ type: MX
+ domain: example.com
+ data: "{{ item.data }}"
+ priority: "{{ item.priority }}"
+ multiple: true
+ with_items:
+ - { data: mx1.example.com, priority: 10 }
+ - { data: mx2.example.com, priority: 10 }
+ - { data: mx3.example.com, priority: 20 }
+
+- name: Ensure a record is absent
+ vultr.cloud.dns_record:
+ name: www
+ domain: example.com
+ state: absent
+
+- name: Ensure one MX record is absent if multiple exists
+ vultr.cloud.dns_record:
+ record_type: MX
+ domain: example.com
+ data: mx1.example.com
+ multiple: true
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+dns_record:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: The ID of the DNS record.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ name:
+ description: The name of the DNS record.
+ returned: success
+ type: str
+ sample: web
+ type:
+ description: The name of the DNS record.
+ returned: success
+ type: str
+ sample: A
+ data:
+ description: Data of the DNS record.
+ returned: success
+ type: str
+ sample: 10.10.10.10
+ priority:
+ description: Priority of the DNS record.
+ returned: success
+ type: int
+ sample: 10
+ ttl:
+ description: Time to live of the DNS record.
+ returned: success
+ type: int
+ sample: 300
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+RECORD_TYPES = ["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA", "SSHFP"]
+
+
+class AnsibleVultrDnsRecord(AnsibleVultr):
+ def query(self):
+ multiple = self.module.params.get("multiple")
+ name = self.module.params.get("name")
+ data = self.module.params.get("data")
+ record_type = self.module.params.get("type")
+
+ result = dict()
+ for resource in self.query_list():
+ if resource.get("type") != record_type:
+ continue
+
+ if resource.get("name") == name:
+ if not multiple:
+ if result:
+ self.module.fail_json(
+ msg="More than one record with record_type=%s and name=%s params. "
+ "Use multiple=true for more than one record." % (record_type, name)
+ )
+ else:
+ result = resource
+ elif resource.get("data") == data:
+ return resource
+ return result
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ domain=dict(type="str", required=True),
+ name=dict(type="str", default=""),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ttl=dict(type="int", default=300),
+ type=dict(type="str", choices=RECORD_TYPES, default="A", aliases=["record_type"]),
+ multiple=dict(type="bool", default=False),
+ priority=dict(type="int"),
+ data=dict(type="str"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ("state", "present", ["data"]),
+ ("multiple", True, ["data"]),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrDnsRecord(
+ module=module,
+ namespace="vultr_dns_record",
+ resource_path="/domains/%s/records" % module.params.get("domain"), # type: ignore
+ ressource_result_key_singular="record",
+ resource_create_param_keys=["name", "ttl", "data", "priority", "type"],
+ resource_update_param_keys=["name", "ttl", "data", "priority"],
+ resource_key_name="name",
+ ) # type: ignore
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/firewall_group.py b/ansible_collections/vultr/cloud/plugins/modules/firewall_group.py
new file mode 100644
index 000000000..962936a78
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/firewall_group.py
@@ -0,0 +1,138 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: firewall_group
+short_description: Manages firewall groups on Vultr
+description:
+ - Create and remove firewall groups.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ description:
+ description:
+ - Description of the firewall group.
+ required: true
+ aliases: [ name ]
+ type: str
+ state:
+ description:
+ - State of the firewall group.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: ensure a firewall group is present
+ vultr.cloud.firewall_group:
+ description: my http firewall.
+
+- name: ensure a firewall group is absent
+ vultr.cloud.firewall_group:
+ description: my http firewall.
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_firewall_group:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the firewall group.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description (name) of the firewall group
+ returned: success
+ type: str
+ sample: my firewall group
+ date_created:
+ description: Date the firewall group was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ date_modified:
+ description: Date the firewall group was modified.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ description=dict(type="str", required=True, aliases=["name"]),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_firewall_group",
+ resource_path="/firewalls",
+ ressource_result_key_singular="firewall_group",
+ resource_create_param_keys=["description"],
+ resource_update_param_keys=["description"],
+ resource_key_name="description",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/firewall_group_info.py b/ansible_collections/vultr/cloud/plugins/modules/firewall_group_info.py
new file mode 100644
index 000000000..866ac4818
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/firewall_group_info.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: firewall_group_info
+short_description: Gather information about the Vultr firewall groups
+description:
+ - Gather information about firewall groups available.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr firewall groups information
+ vultr.cloud.firewall_group_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_firewall_group_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_firewall_group_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the firewall group.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Name of the firewall group.
+ returned: success
+ type: str
+ sample: my firewall group
+ date_created:
+ description: Date the firewall group was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ date_modified:
+ description: Date the firewall group was modified.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_firewall_group_info",
+ resource_path="/firewalls",
+ ressource_result_key_singular="firewall_group",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py
new file mode 100644
index 000000000..474372d08
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule.py
@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: firewall_rule
+short_description: Manages firewall rules on Vultr
+description:
+ - Create and remove firewall rules.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ group:
+ description:
+ - Name of the firewall group.
+ required: true
+ type: str
+ ip_type:
+ description:
+ - IP address version
+ choices: [ v4, v6 ]
+ type: str
+ default: v4
+ protocol:
+ description:
+ - Protocol of the firewall rule.
+ choices: [ icmp, tcp, udp, gre, esp, ah ]
+ type: str
+ default: tcp
+ subnet:
+ description:
+ - The network or IP, e.g. 192.0.2.123 or 0.0.0.0.
+ - Mutally exclusive with I(source).
+ type: str
+ subnet_size:
+ description:
+ - The number of bits for the netmask in CIDR notation, e.g. C(32).
+ type: int
+ port:
+ description:
+ - Single port or port range, e.g. C(80) or C(8000:8080).
+ - Required if I(protocol) is tcp or udp and I(state=present).
+ aliases: [ port_range ]
+ type: str
+ source:
+ description:
+ - Possible values are C(cloudflare) or a loadbalancer label.
+ - Mutally exclusive with I(subnet).
+ type: str
+ notes:
+ description:
+ - Notes of the firewall rule.
+ type: str
+ state:
+ description:
+ - State of the firewall rule.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a firewall rule is present
+ vultr.cloud.firewall_rule:
+ group: web
+ port: 80
+ protocol: tcp
+ ip_type: v4
+ subnet: "0.0.0.0"
+ subnet_size: 0
+ notes: "open HTTP to the world"
+
+- name: Ensure a firewall rule with port range is present
+ vultr.cloud.firewall_rule:
+ group: apps
+ port: "8000:8999"
+ protocol: tcp
+ ip_type: v4
+ subnet: "10.10.10.0"
+ subnet_size: 24
+
+- name: Ensure a firewall rule is absent
+ vultr.cloud.firewall_rule:
+ group: apps
+ port: "443"
+ protocol: tcp
+ ip_type: v6
+ subnet: "::"
+ subnet_size: 0
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_firewall_rule:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the firewall rule.
+ returned: success
+ type: int
+ sample: 1
+ action:
+ description: Action of the firewall rule.
+ returned: success
+ type: str
+ sample: accept
+ protocol:
+ description: Protocol of the firewall rule.
+ returned: success
+ type: str
+ sample: tcp
+ port:
+ description: Port or port range of the firewall rule.
+ returned: success
+ type: str
+ sample: "80"
+ source:
+ description: Source string of the firewall rule.
+ returned: success
+ type: str
+ sample: cloudflare
+ notes:
+ description: Supplied description of the firewall rule.
+ returned: success
+ type: str
+ sample: my rule
+ subnet:
+ description: Subnet of the firewall rule.
+ returned: success
+ type: str
+ sample: 0.0.0.0
+ subnet_size:
+ description: Size of the subnet of the firewall rule.
+ returned: success
+ type: int
+ sample: 0
+ ip_type:
+ description: IP type of the firewall rule.
+ returned: success
+ type: str
+ sample: v4
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrFirewallRule(AnsibleVultr):
+ def get_firewall_group(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="group",
+ path="/firewalls",
+ result_key="firewall_groups",
+ fail_not_found=True,
+ )
+
+ def get_load_balancer(self):
+ return self.query_filter_list_by_name(
+ key_name="label",
+ param_key="source",
+ path="/load-balancers",
+ result_key="load_balancers",
+ fail_not_found=True,
+ )
+
+ def configure(self):
+ # Set firewall group id to resource path, ensures firewall group exists
+ self.resource_path = self.resource_path % self.get_firewall_group()["id"]
+
+ # Set loadbalancer ID for source
+ source = self.module.params.get("source")
+ if source is not None and source != "cloudflare":
+ self.module.params["source"] = self.get_load_balancer()["id"]
+
+ def query(self):
+ result = dict()
+ for resource in self.query_list():
+ for key in (
+ "ip_type",
+ "protocol",
+ "port",
+ "source",
+ "subnet",
+ "subnet_size",
+ ):
+ param = self.module.params.get(key)
+
+ if param is None:
+ continue
+
+ if resource.get(key) != param:
+ break
+ else:
+ result = resource
+
+ if result:
+ break
+
+ return result
+
+ def update(self, resource):
+ return resource
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ notes=dict(type="str"),
+ group=dict(type="str", required=True),
+ port=dict(type="str", aliases=["port_range"]),
+ subnet=dict(type="str"),
+ subnet_size=dict(type="int"),
+ source=dict(type="str"),
+ protocol=dict(
+ type="str",
+ choices=["icmp", "tcp", "udp", "gre", "esp", "ah"],
+ default="tcp",
+ ),
+ ip_type=dict(type="str", choices=["v4", "v6"], default="v4"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_one_of=(("source", "subnet"),),
+ mutually_exclusive=(("source", "subnet"),),
+ required_together=(("subnet", "subnet_size"),),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrFirewallRule(
+ module=module,
+ namespace="vultr_firewall_rule",
+ resource_path="/firewalls/%s/rules",
+ ressource_result_key_singular="firewall_rule",
+ resource_key_name="##unused##",
+ resource_create_param_keys=[
+ "notes",
+ "port",
+ "subnet",
+ "subnet_size",
+ "source",
+ "protocol",
+ "ip_type",
+ ],
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/firewall_rule_info.py b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule_info.py
new file mode 100644
index 000000000..6d51d6f9c
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/firewall_rule_info.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: firewall_rule_info
+short_description: Gather information about the Vultr firewall rules
+description:
+ - Gather information about firewall rules available.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ group:
+ description:
+ - Name of the firewall group.
+ required: true
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr firewall rule information
+ vultr.cloud.firewall_rule_info:
+ group: my group
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_firewall_rule_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_firewall_rule_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the firewall rule.
+ returned: success
+ type: int
+ sample: 1
+ action:
+ description: Action of the firewall rule.
+ returned: success
+ type: str
+ sample: accept
+ protocol:
+ description: Protocol of the firewall rule.
+ returned: success
+ type: str
+ sample: tcp
+ port:
+ description: Port or port range of the firewall rule.
+ returned: success
+ type: str
+ sample: "80"
+ source:
+ description: Source string of the firewall rule.
+ returned: success
+ type: str
+ sample: cloudflare
+ notes:
+ description: Supplied description of the firewall rule.
+ returned: success
+ type: str
+ sample: my rule
+ subnet:
+ description: Subnet of the firewall rule.
+ returned: success
+ type: str
+ sample: 0.0.0.0
+ subnet_size:
+ description: Size of the subnet of the firewall rule.
+ returned: success
+ type: int
+ sample: 0
+ ip_type:
+ description: IP type of the firewall rule.
+ returned: success
+ type: str
+ sample: v4
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrFirewallRuleInfo(AnsibleVultr):
+ def get_firewall_group(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="group",
+ path="/firewalls",
+ result_key="firewall_groups",
+ fail_not_found=True,
+ )
+
+ def configure(self):
+ # Set firewall group id to resource path, ensures firewall group exists
+ self.resource_path = self.resource_path % self.get_firewall_group()["id"]
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ group=dict(type="str", required=True),
+ ) # type: ignore
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrFirewallRuleInfo(
+ module=module,
+ namespace="vultr_firewall_rule_info",
+ resource_path="/firewalls/%s/rules",
+ ressource_result_key_singular="firewall_rule",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/instance.py b/ansible_collections/vultr/cloud/plugins/modules/instance.py
new file mode 100644
index 000000000..73099164d
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/instance.py
@@ -0,0 +1,739 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: instance
+short_description: Manages server instances on Vultr.
+description:
+ - Manage server instances on Vultr.
+version_added: "1.1.0"
+author:
+ - "René Moser (@resmo)"
+options:
+ label:
+ description:
+ - Name of the instance.
+ required: true
+ aliases: [ name ]
+ type: str
+ hostname:
+ description:
+ - The hostname to assign to this instance.
+ type: str
+ os:
+ description:
+ - The operating system name.
+ - Mutually exclusive with I(image) and I(app).
+ type: str
+ app:
+ description:
+ - The app deploy name of Vultr OneClick apps.
+ - Mutually exclusive with I(image) and I(os).
+ type: str
+ image:
+ description:
+ - The image deploy name of Vultr Marketplace apps.
+ - Mutually exclusive with I(os) and I(app).
+ type: str
+ firewall_group:
+ description:
+ - The firewall group description to assign this instance to.
+ type: str
+ plan:
+ description:
+ - The plan name to use for the instance.
+ - Required if the instance does not yet exist.
+ type: str
+ activation_email:
+ description:
+ - Whether to send an activation email when the instance is ready or not.
+ - Only considered on creation.
+ type: bool
+ default: false
+ backups:
+ description:
+ - Whether to enable automatic backups or not.
+ type: bool
+ ddos_protection:
+ description:
+ - Whether to enable ddos_protection or not.
+ type: bool
+ enable_ipv6:
+ description:
+ - Whether to enable IPv6 or not.
+ type: bool
+ tags:
+ description:
+ - Tags for the instance.
+ type: list
+ elements: str
+ user_data:
+ description:
+ - User data to be passed to the instance.
+ type: str
+ startup_script:
+ description:
+ - Name or ID of the startup script to execute on boot.
+ - Only considered while creating the instance.
+ type: str
+ ssh_keys:
+ description:
+ - List of SSH key names passed to the instance on creation.
+ type: list
+ elements: str
+ snapshot:
+ description:
+ - Description or ID of the snapshot.
+ - Only considered while creating the instance.
+ type: str
+ version_added: "1.7.0"
+ reserved_ipv4:
+ description:
+ - IP address of the floating IP to use as the main IP of this instance.
+ - Only considered on creation.
+ type: str
+ region:
+ description:
+ - Region the instance is deployed into.
+ type: str
+ required: true
+ vpcs:
+ description:
+ - A list of VPCs identified by their description to be assigned to the instance.
+ type: list
+ elements: str
+ version_added: "1.5.0"
+ state:
+ description:
+ - State of the instance.
+ - The state I(reinstalled) was added in version 1.8.0.
+ default: present
+ choices: [ present, absent, started, stopped, restarted, reinstalled ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+---
+- name: Create an instance using OS
+ vultr.cloud.instance:
+ label: my web server
+ hostname: my-hostname
+ user_data: |
+ #cloud-config
+ packages:
+ - nginx
+ firewall_group: my firewall group
+ plan: vc2-1c-2gb
+ ddos_protection: true
+ backups: true
+ enable_ipv6: true
+ ssh_keys:
+ - my ssh key
+ vpcs:
+ - my vpc description
+ tags:
+ - web
+ - project-genesis
+ region: ams
+ os: Debian 11 x64 (bullseye)
+
+- name: Deploy an instance of a marketplace app
+ vultr.cloud.instance:
+ label: git-server
+ hostname: git
+ firewall_group: my firewall group
+ plan: vc2-1c-2gb
+ ddos_protection: true
+ backups: true
+ enable_ipv6: true
+ region: ams
+ image: Gitea on Ubuntu 20.04
+
+- name: Stop an existing instance
+ vultr.cloud.instance:
+ label: my web server
+ region: ams
+ state: stopped
+
+- name: Start an existing instance
+ vultr.cloud.instance:
+ label: my web server
+ region: ams
+ state: started
+
+- name: Reinstall an instance
+ vultr.cloud.instance:
+ label: my web server
+ region: ams
+ state: reinstalled
+
+- name: Delete an instance
+ vultr.cloud.instance:
+ label: my web server
+ region: ams
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_instance:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the instance.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ v6_main_ip:
+ description: IPv6 of the instance.
+ returned: success
+ type: str
+ sample: ""
+ v6_network:
+ description: IPv6 network of the instance.
+ returned: success
+ type: str
+ sample: ""
+ v6_network_size:
+ description: IPv6 network size of the instance.
+ returned: success
+ type: int
+ sample: 0
+ main_ip:
+ description: IPv4 of the instance.
+ returned: success
+ type: str
+ sample: 95.179.189.95
+ netmask_v4:
+ description: Netmask IPv4 of the instance.
+ returned: success
+ type: str
+ sample: 255.255.254.0
+ hostname:
+ description: Hostname of the instance.
+ returned: success
+ type: str
+ sample: vultr.guest
+ internal_ip:
+ description: Internal IP of the instance.
+ returned: success
+ type: str
+ sample: ""
+ gateway_v4:
+ description: Gateway IPv4.
+ returned: success
+ type: str
+ sample: 95.179.188.1
+ kvm:
+ description: KVM of the instance.
+ returned: success
+ type: str
+ sample: "https://my.vultr.com/subs/vps/novnc/api.php?data=..."
+ disk:
+ description: Disk size of the instance.
+ returned: success
+ type: int
+ sample: 25
+ allowed_bandwidth:
+ description: Allowed bandwidth of the instance.
+ returned: success
+ type: int
+ sample: 1000
+ vcpu_count:
+ description: vCPUs of the instance.
+ returned: success
+ type: int
+ sample: 1
+ firewall_group_id:
+ description: Firewall group ID of the instance.
+ returned: success
+ type: str
+ sample: ""
+ plan:
+ description: Plan of the instance.
+ returned: success
+ type: str
+ sample: vc2-1c-1gb
+ image_id:
+ description: Image ID of the instance.
+ returned: success
+ type: str
+ sample: ""
+ os_id:
+ description: OS ID of the instance.
+ returned: success
+ type: int
+ sample: 186
+ app_id:
+ description: App ID of the instance.
+ returned: success
+ type: int
+ sample: 37
+ date_created:
+ description: Date when the instance was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ label:
+ description: Label of the instance.
+ returned: success
+ type: str
+ sample: my instance
+ region:
+ description: Region the instance was deployed into.
+ returned: success
+ type: str
+ sample: ews
+ status:
+ description: Status about the deployment of the instance.
+ returned: success
+ type: str
+ sample: active
+ server_status:
+ description: Server status of the instance.
+ returned: success
+ type: str
+ sample: installingbooting
+ power_status:
+ description: Power status of the instance.
+ returned: success
+ type: str
+ sample: running
+ ram:
+ description: RAM in MB of the instance.
+ returned: success
+ type: int
+ sample: 1024
+ os:
+ description: OS of the instance.
+ returned: success
+ type: str
+ sample: Application
+ tags:
+ description: Tags of the instance.
+ returned: success
+ type: list
+ sample: [ my-tag ]
+ features:
+ description: Features of the instance.
+ returned: success
+ type: list
+ sample: [ ddos_protection, ipv6, auto_backups ]
+ user_data:
+ description: Base64 encoded user data (cloud init) of the instance.
+ returned: success
+ type: str
+ sample: I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIGh0b3AK
+ backups:
+ description: Whether backups are enabled or disabled.
+ returned: success
+ type: str
+ sample: enabled
+ version_added: "1.3.0"
+ ddos_protection:
+ description: Whether DDOS protections is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ version_added: "1.3.0"
+ enable_ipv6:
+ description: Whether IPv6 is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ version_added: "1.3.0"
+ vpcs:
+ description: List of VPCs attached.
+ returned: success
+ type: list
+ version_added: "1.5.0"
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: 5536d2a4-66fd-4dfb-b839-7672fd5bc116
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: my vpc
+ ip_address:
+ description: IP assigned from the VPC.
+ returned: success
+ type: str
+ sample: "192.168.23.3"
+ mac_address:
+ description: MAC address of the network interface.
+ returned: success
+ type: str
+ sample: "5a:01:04:3d:5e:72"
+"""
+
+import base64
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrInstance(AnsibleVultr):
+ def get_ssh_key_ids(self):
+ ssh_key_names = list(self.module.params["ssh_keys"])
+ ssh_keys = self.query_list(path="/ssh-keys", result_key="ssh_keys")
+
+ ssh_key_ids = list()
+ for ssh_key in ssh_keys:
+ if ssh_key["name"] in ssh_key_names:
+ ssh_key_ids.append(ssh_key["id"])
+ ssh_key_names.remove(ssh_key["name"])
+
+ if ssh_key_names:
+ self.module.fail_json(msg="SSH key names not found: %s" % ", ".join(ssh_key_names))
+
+ return ssh_key_ids
+
+ def get_vpc_ids(self):
+ vpc_names = list(self.module.params["vpcs"])
+ vpcs = self.query_list(path="/vpcs", result_key="vpcs")
+
+ vpc_ids = list()
+ for vpc in vpcs:
+ if vpc["description"] in vpc_names:
+ vpc_ids.append(vpc["id"])
+ vpc_names.remove(vpc["description"])
+
+ if vpc_names:
+ self.module.fail_json(msg="VPCs not found: %s" % ", ".join(vpc_names))
+
+ return vpc_ids
+
+ def get_instance_vpcs(self, resource):
+ path = "/instances/%s/vpcs" % resource["id"]
+ vpcs = self.query_list(path=path, result_key="vpcs")
+
+ # Workaround to get the description field into the list
+ result = list()
+ for vpc in vpcs:
+ vpc_detail = self.query_by_id(resource_id=vpc["id"], path="/vpcs", result_key="vpc")
+ vpc["description"] = vpc_detail["description"]
+ result.append(vpc)
+ return result
+
+ def get_firewall_group(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="firewall_group",
+ path="/firewalls",
+ result_key="firewall_groups",
+ fail_not_found=True,
+ )
+
+ def get_snapshot(self):
+ return self.query_filter_list_by_name(
+ key_name="description",
+ param_key="snapshot",
+ path="/snapshots",
+ result_key="snapshots",
+ fail_not_found=True,
+ )
+
+ def get_startup_script(self):
+ return self.query_filter_list_by_name(
+ key_name="name",
+ param_key="startup_script",
+ path="/startup-scripts",
+ result_key="startup_scripts",
+ fail_not_found=True,
+ )
+
+ def get_os(self):
+ return self.query_filter_list_by_name(
+ key_name="name",
+ param_key="os",
+ path="/os",
+ result_key="os",
+ fail_not_found=True,
+ )
+
+ def get_app(self):
+ return self.query_filter_list_by_name(
+ key_name="deploy_name",
+ param_key="app",
+ path="/applications",
+ result_key="applications",
+ fail_not_found=True,
+ query_params={"type": "one-click"},
+ )
+
+ def get_image(self):
+ return self.query_filter_list_by_name(
+ key_name="deploy_name",
+ param_key="image",
+ path="/applications",
+ result_key="applications",
+ fail_not_found=True,
+ query_params={"type": "marketplace"},
+ )
+
+ def get_user_data(self, resource):
+ res = self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], "user-data"),
+ )
+ if res:
+ return str(res.get("user_data", dict()).get("data"))
+ return ""
+
+ def transform_resource(self, resource):
+ if not resource:
+ return resource
+
+ features = resource.get("features", list())
+ resource["backups"] = "enabled" if "auto_backups" in features else "disabled"
+ resource["enable_ipv6"] = "ipv6" in features
+ resource["ddos_protection"] = "ddos_protection" in features
+ resource["vpcs"] = self.get_instance_vpcs(resource=resource)
+
+ return resource
+
+ def get_detach_vpcs_ids(self, resource):
+ detach_vpc_ids = []
+ for vpc in resource.get("vpcs", list()):
+ if vpc["id"] not in list(self.module.params["attach_vpc"]):
+ detach_vpc_ids.append(vpc["id"])
+ return detach_vpc_ids
+
+ def configure(self):
+ if self.module.params["state"] != "absent":
+ if self.module.params["startup_script"] is not None:
+ self.module.params["script_id"] = self.get_startup_script()["id"]
+
+ if self.module.params["snapshot"] is not None:
+ self.module.params["snapshot_id"] = self.get_snapshot()["id"]
+
+ if self.module.params["firewall_group"] is not None:
+ self.module.params["firewall_group_id"] = self.get_firewall_group()["id"]
+
+ if self.module.params["os"] is not None:
+ self.module.params["os_id"] = self.get_os()["id"]
+
+ if self.module.params["app"] is not None:
+ self.module.params["app_id"] = self.get_app()["id"]
+
+ if self.module.params["image"] is not None:
+ self.module.params["image_id"] = self.get_image()["image_id"]
+
+ if self.module.params["user_data"] is not None:
+ self.module.params["user_data"] = base64.b64encode(self.module.params["user_data"].encode())
+
+ if self.module.params["ssh_keys"] is not None:
+ # sshkey_id ist a list of ids
+ self.module.params["sshkey_id"] = self.get_ssh_key_ids()
+
+ if self.module.params["backups"] is not None:
+ self.module.params["backups"] = "enabled" if self.module.params["backups"] else "disabled"
+
+ if self.module.params["vpcs"] is not None:
+ # attach_vpc is a list of ids used while creating
+ self.module.params["attach_vpc"] = self.get_vpc_ids()
+
+ def handle_power_status(self, resource, state, action, power_status, force=False, wait_for_state=True):
+ if state == self.module.params["state"] and (resource["power_status"] != power_status or force):
+ self.result["changed"] = True
+ if not self.module.check_mode:
+ resource = self.wait_for_state(resource=resource, key="server_status", states=["none", "locked"], cmp="!=")
+ self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], action),
+ method="POST",
+ )
+ if wait_for_state:
+ resource = self.wait_for_state(resource=resource, key="power_status", states=[power_status])
+ return resource
+
+ def create(self):
+ param_keys = ("os", "image", "app", "snapshot")
+ if not any(self.module.params.get(x) is not None for x in param_keys):
+ self.module.fail_json(msg="missing required arguements, one of the following required: %s" % ", ".join(param_keys))
+ return super(AnsibleVultrInstance, self).create()
+
+ def update(self, resource):
+ user_data = self.get_user_data(resource=resource)
+ resource["user_data"] = user_data.encode()
+
+ if self.module.params["vpcs"] is not None:
+ resource["attach_vpc"] = list()
+ for vpc in list(resource["vpcs"]):
+ resource["attach_vpc"].append(vpc["id"])
+
+ # detach_vpc is a list of ids to be detached
+ resource["detach_vpc"] = list()
+ self.module.params["detach_vpc"] = self.get_detach_vpcs_ids(resource=resource)
+
+ return super(AnsibleVultrInstance, self).update(resource=resource)
+
+ def create_or_update(self):
+ resource = super(AnsibleVultrInstance, self).create_or_update()
+ if resource:
+ resource = self.wait_for_state(resource=resource, key="status", states=["active"])
+ resource = self.wait_for_state(resource=resource, key="server_status", states=["none", "locked"], cmp="!=")
+
+ # Hanlde power status
+ resource = self.handle_power_status(resource=resource, state="stopped", action="halt", power_status="stopped")
+ resource = self.handle_power_status(resource=resource, state="started", action="start", power_status="running")
+ resource = self.handle_power_status(resource=resource, state="restarted", action="reboot", power_status="running", force=True)
+ resource = self.handle_power_status(
+ resource=resource,
+ state="reinstalled",
+ action="reinstall",
+ power_status="running",
+ force=True,
+ wait_for_state=False,
+ )
+
+ return resource
+
+ def transform_result(self, resource):
+ if resource:
+ resource["user_data"] = self.get_user_data(resource=resource)
+ return resource
+
+ def absent(self):
+ resource = self.query()
+ if resource and not self.module.check_mode:
+ resource = self.wait_for_state(resource=resource, key="server_status", states=["none", "locked"], cmp="!=")
+
+ return super(AnsibleVultrInstance, self).absent(resource=resource)
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ label=dict(type="str", required=True, aliases=["name"]),
+ hostname=dict(type="str"),
+ app=dict(type="str"),
+ image=dict(type="str"),
+ snapshot=dict(type="str"),
+ os=dict(type="str"),
+ plan=dict(type="str"),
+ activation_email=dict(type="bool", default=False),
+ ddos_protection=dict(type="bool"),
+ backups=dict(type="bool"),
+ enable_ipv6=dict(type="bool"),
+ tags=dict(type="list", elements="str"),
+ vpcs=dict(type="list", elements="str"),
+ reserved_ipv4=dict(type="str"),
+ firewall_group=dict(type="str"),
+ startup_script=dict(type="str"),
+ user_data=dict(type="str"),
+ ssh_keys=dict(type="list", elements="str", no_log=False),
+ region=dict(type="str", required=True),
+ state=dict(
+ choices=[
+ "present",
+ "absent",
+ "started",
+ "stopped",
+ "restarted",
+ "reinstalled",
+ ],
+ default="present",
+ ),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=(("state", "present", ("plan",)),),
+ mutually_exclusive=(("os", "app", "image", "snapshot"),),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrInstance(
+ module=module,
+ namespace="vultr_instance",
+ resource_path="/instances",
+ ressource_result_key_singular="instance",
+ resource_create_param_keys=[
+ "label",
+ "hostname",
+ "plan",
+ "app_id",
+ "os_id",
+ "iso_id",
+ "image_id",
+ "snapshot_id",
+ "script_id",
+ "region",
+ "enable_ipv6",
+ "reserved_ipv4",
+ "firewall_group_id",
+ "user_data",
+ "tags",
+ "activation_email",
+ "ddos_protection",
+ "sshkey_id",
+ "backups",
+ "attach_vpc",
+ ],
+ resource_update_param_keys=[
+ "plan",
+ "tags",
+ "firewall_group_id",
+ "enable_ipv6",
+ "ddos_protection",
+ "backups",
+ "user_data",
+ "attach_vpc",
+ "detach_vpc",
+ ],
+ resource_key_name="label",
+ )
+
+ state = module.params.get("state") # type: ignore
+ if state == "absent":
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/instance_info.py b/ansible_collections/vultr/cloud/plugins/modules/instance_info.py
new file mode 100644
index 000000000..2a5c311bb
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/instance_info.py
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: instance_info
+short_description: Get information about the Vultr instances
+description:
+ - Get infos about available instances.
+version_added: "1.5.0"
+author:
+ - "René Moser (@resmo)"
+options:
+ label:
+ description:
+ - Name of the instance.
+ aliases: [ name ]
+ type: str
+ region:
+ description:
+ - Filter instances by region.
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Get Vultr instance infos of region ams
+ vultr.cloud.instance_info:
+ region: ams
+
+- name: Get Vultr instance infos of a single host
+ vultr.cloud.instance_info:
+ label: myhost
+
+- name: Get all Vultr instance infos
+ vultr.cloud.instance_info:
+ register: results
+
+- name: Print the gathered infos
+ ansible.builtin.debug:
+ var: results.vultr_instance_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_instance_info:
+ description: Response from Vultr API as list.
+ returned: available
+ type: list
+ contains:
+ id:
+ description: ID of the instance.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ v6_main_ip:
+ description: IPv6 of the instance.
+ returned: success
+ type: str
+ sample: ""
+ v6_network:
+ description: IPv6 network of the instance.
+ returned: success
+ type: str
+ sample: ""
+ v6_network_size:
+ description: IPv6 network size of the instance.
+ returned: success
+ type: int
+ sample: 0
+ main_ip:
+ description: IPv4 of the instance.
+ returned: success
+ type: str
+ sample: 95.179.189.95
+ netmask_v4:
+ description: Netmask IPv4 of the instance.
+ returned: success
+ type: str
+ sample: 255.255.254.0
+ hostname:
+ description: Hostname of the instance.
+ returned: success
+ type: str
+ sample: vultr.guest
+ internal_ip:
+ description: Internal IP of the instance.
+ returned: success
+ type: str
+ sample: ""
+ gateway_v4:
+ description: Gateway IPv4.
+ returned: success
+ type: str
+ sample: 95.179.188.1
+ kvm:
+ description: KVM of the instance.
+ returned: success
+ type: str
+ sample: "https://my.vultr.com/subs/vps/novnc/api.php?data=..."
+ disk:
+ description: Disk size of the instance.
+ returned: success
+ type: int
+ sample: 25
+ allowed_bandwidth:
+ description: Allowed bandwidth of the instance.
+ returned: success
+ type: int
+ sample: 1000
+ vcpu_count:
+ description: vCPUs of the instance.
+ returned: success
+ type: int
+ sample: 1
+ firewall_group_id:
+ description: Firewall group ID of the instance.
+ returned: success
+ type: str
+ sample: ""
+ plan:
+ description: Plan of the instance.
+ returned: success
+ type: str
+ sample: vc2-1c-1gb
+ image_id:
+ description: Image ID of the instance.
+ returned: success
+ type: str
+ sample: ""
+ os_id:
+ description: OS ID of the instance.
+ returned: success
+ type: int
+ sample: 186
+ app_id:
+ description: App ID of the instance.
+ returned: success
+ type: int
+ sample: 37
+ date_created:
+ description: Date when the instance was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ label:
+ description: Label of the instance.
+ returned: success
+ type: str
+ sample: my instance
+ region:
+ description: Region the instance was deployed into.
+ returned: success
+ type: str
+ sample: ews
+ status:
+ description: Status about the deployment of the instance.
+ returned: success
+ type: str
+ sample: active
+ server_status:
+ description: Server status of the instance.
+ returned: success
+ type: str
+ sample: installingbooting
+ power_status:
+ description: Power status of the instance.
+ returned: success
+ type: str
+ sample: running
+ ram:
+ description: RAM in MB of the instance.
+ returned: success
+ type: int
+ sample: 1024
+ os:
+ description: OS of the instance.
+ returned: success
+ type: str
+ sample: Application
+ tags:
+ description: Tags of the instance.
+ returned: success
+ type: list
+ sample: [ my-tag ]
+ features:
+ description: Features of the instance.
+ returned: success
+ type: list
+ sample: [ ddos_protection, ipv6, auto_backups ]
+ user_data:
+ description: Base64 encoded user data (cloud init) of the instance.
+ returned: success
+ type: str
+ sample: I2Nsb3VkLWNvbmZpZwpwYWNrYWdlczoKICAtIGh0b3AK
+
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ region=dict(type="str", aliases=["name"]),
+ label=dict(type="str"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_instance_info",
+ resource_path="/instances",
+ ressource_result_key_singular="instance",
+ ressource_result_key_plural="instances",
+ )
+
+ query_params = dict()
+ if module.params["region"] is not None: # type: ignore
+ query_params.update({"region": module.params["region"]}) # type: ignore
+
+ if module.params["label"] is not None: # type: ignore
+ query_params.update({"label": module.params["label"]}) # type: ignore
+
+ vultr.get_result(vultr.query_list(query_params=query_params))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/os_info.py b/ansible_collections/vultr/cloud/plugins/modules/os_info.py
new file mode 100644
index 000000000..8edb7a7f1
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/os_info.py
@@ -0,0 +1,115 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: os_info
+short_description: Get information about the Vultr operation systems
+description:
+ - Get infos about operating systems available to boot servers.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Get Vultr OSes infos
+ vultr.cloud.os_info:
+ register: results
+
+- name: Print the gathered infos
+ ansible.builtin.debug:
+ var: results.vultr_os_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_os_info:
+ description: Response from Vultr API as list.
+ returned: available
+ type: list
+ contains:
+ arch:
+ description: OS Architecture.
+ returned: success
+ type: str
+ sample: x64
+ family:
+ description: OS family.
+ returned: success
+ type: str
+ sample: openbsd
+ name:
+ description: OS name.
+ returned: success
+ type: str
+ sample: OpenBSD 6 x64
+ windows:
+ description: OS is a MS Windows.
+ returned: success
+ type: bool
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_os_info",
+ resource_path="/os",
+ ressource_result_key_singular="os",
+ ressource_result_key_plural="os",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/plan_info.py b/ansible_collections/vultr/cloud/plugins/modules/plan_info.py
new file mode 100644
index 000000000..639a10731
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/plan_info.py
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: plan_info
+short_description: Gather information about the Vultr plans
+description:
+ - Gather information about plans available to boot servers.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr plans information
+ vultr.cloud.plan_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_plan_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_plan_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the plan.
+ returned: success
+ type: str
+ sample: vhf-8c-32gb
+ vcpu_count:
+ description: Amount of CPUs.
+ returned: success
+ type: int
+ sample: 8
+ ram:
+ description: Amount of RAM in MB.
+ returned: success
+ type: int
+ sample: 32768
+ disk:
+ description: Disk size in GB.
+ returned: success
+ type: int
+ sample: 512
+ disk_count:
+ description: Amount of disks.
+ returned: success
+ type: int
+ sample: 1
+ bandwidth:
+ description: Bandwidth in MB.
+ returned: success
+ type: int
+ sample: 6144
+ monthly_cost:
+ description: Monthly cost in $.
+ returned: success
+ type: int
+ sample: 192
+ type:
+ description: Type of plan.
+ returned: success
+ type: str
+ sample: vhf
+ locations:
+ description: List of locations the plan is available in.
+ returned: success
+ type: list
+ sample: ["ewr"]
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_plan_info",
+ resource_path="/plans",
+ ressource_result_key_singular="plan",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/plan_metal_info.py b/ansible_collections/vultr/cloud/plugins/modules/plan_metal_info.py
new file mode 100644
index 000000000..c7ff478bf
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/plan_metal_info.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2019, Nate River <vitikc@gmail.com>
+# Copyright (c) 2020, Simon Baerlocher <s.baerlocher@sbaerlocher.ch>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: plan_metal_info
+short_description: Gather information about the Vultr bare metal plans
+description:
+ - Gather information about plans available to boot servers.
+version_added: "1.0.0"
+author:
+ - "Nate River (@vitikc)"
+ - "Simon Baerlocher (@sbaerlocher)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr bare metal plans information
+ vultr.cloud.plan_metal_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_plan_metal_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_plan_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the plan.
+ returned: success
+ type: str
+ sample: vbm-4c-32gb
+ cpu_count:
+ description: Amount of CPUs.
+ returned: success
+ type: int
+ sample: 4
+ cpu_threads:
+ description: Amount of CPU threads.
+ returned: success
+ type: int
+ sample: 8
+ cpu_model:
+ description: CPU model.
+ returned: success
+ type: str
+ sample: E3-1270v6
+ ram:
+ description: Amount of RAM in MB.
+ returned: success
+ type: int
+ sample: 32768
+ disk:
+ description: Disk size in GB.
+ returned: success
+ type: int
+ sample: 240
+ disk_count:
+ description: Amount of disks.
+ returned: success
+ type: int
+ sample: 2
+ bandwidth:
+ description: Bandwidth in MB.
+ returned: success
+ type: int
+ sample: 5120
+ monthly_cost:
+ description: Monthly cost in $.
+ returned: success
+ type: int
+ sample: 300
+ type:
+ description: Type of plan.
+ returned: success
+ type: str
+ sample: SSD
+ locations:
+ description: List of locations the plan is available in.
+ returned: success
+ type: list
+ sample: ["ewr"]
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_plan_metal_info",
+ resource_path="/plans-metal",
+ ressource_result_key_singular="plan_metal",
+ ressource_result_key_plural="plans_metal",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/region_info.py b/ansible_collections/vultr/cloud/plugins/modules/region_info.py
new file mode 100644
index 000000000..1e222b9f9
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/region_info.py
@@ -0,0 +1,106 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: region_info
+short_description: Gather information about the Vultr regions
+description:
+ - Gather information about regions available to boot servers.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr regions information
+ vultr.cloud.region_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_region_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_region_info:
+ description: Response from Vultr API.
+ returned: success
+ type: list
+ sample: [
+ {
+ "block_storage": false,
+ "continent": "Europe",
+ "country": "GB",
+ "ddos_protection": true,
+ "id": 8,
+ "name": "London",
+ "regioncode": "LHR",
+ "state": ""
+ }
+ ]
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_region_info",
+ resource_path="/regions",
+ ressource_result_key_singular="region",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py b/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py
new file mode 100644
index 000000000..9bc2e254b
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/reserved_ip.py
@@ -0,0 +1,290 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: reserved_ip
+short_description: Manages reserved IPs on Vultr
+description:
+ - Create, attach, detach and remove reserved IPs.
+version_added: "1.0.0"
+author:
+ - "René Moser (@resmo)"
+options:
+ label:
+ description:
+ - Label of the reserved IP.
+ required: true
+ aliases: [ name ]
+ type: str
+ instance_name:
+ description:
+ - Name of the Instance the reserved IP should be attached to.
+ - Mutually exclusive with I(instance_id).
+ type: str
+ instance_id:
+ description:
+ - ID of the Instance the reserved IP should be attached to.
+ - Mutually exclusive with I(instance_name).
+ type: str
+ region:
+ description:
+ - Region of the reserved IP will be related to.
+ type: str
+ required: true
+ ip_type:
+ description:
+ - Type of the IP.
+ type: str
+ choices: [ v4, v6 ]
+ required: true
+ state:
+ description:
+ - State of the reserved IP.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a reserved IP present and attached to an instance
+ vultr.cloud.reserved_ip:
+ label: my attached IP
+ region: ewr
+ ip_type: v4
+ instance_name: web-01
+
+- name: Ensure a reserved IP is detached
+ vultr.cloud.reserved_ip:
+ label: my reserved IP
+ region: ewr
+ ip_type: v4
+ instance_id: ""
+
+- name: Ensure a reserved IP is absent
+ vultr.cloud.reserved_ip:
+ label: my attached IP
+ region: ewr
+ ip_type: v4
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_reserved_ip:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the reserved IP.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ label:
+ description: Name of the reserved IP.
+ returned: success
+ type: str
+ sample: example.com
+ region:
+ description: Region of the reserved IP is related to.
+ returned: success
+ type: str
+ sample: ewr
+ ip_type:
+ description: Type of the reserved IP.
+ returned: success
+ type: str
+ sample: v4
+ subnet:
+ description: Subnet of the reserved IP.
+ returned: success
+ type: str
+ sample: v4
+ subnet_size:
+ description: Size of the subnet of the reserved IP.
+ returned: success
+ type: int
+ sample: 32
+ instance_id:
+ description: ID of the Instance the reserved IP is attached to.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b
+"""
+
+import urllib
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrReservedIp(AnsibleVultr):
+ def configure(self):
+ self.instance_id = self.get_instance_id()
+
+ def get_instance_id(self):
+ instance_id = self.module.params["instance_id"]
+ if instance_id is not None:
+ return instance_id
+
+ instance_name = self.module.params["instance_name"]
+ if instance_name is not None:
+
+ # Empty string ID means detach instance
+ if len(instance_name) == 0:
+ return ""
+
+ # URL encode label
+ try:
+ label = urllib.quote(instance_name) # type: ignore
+ except AttributeError:
+ label = urllib.parse.quote(instance_name) # type: ignore
+
+ # Filter instances by label
+ resources = self.api_query(path="/instances?label=%s" % label) or dict()
+ if not resources or not resources["instances"]:
+ self.module.fail_json(msg="No instance with name found: %s" % instance_name)
+
+ if len(resources["instances"]) > 1:
+ self.module.fail_json(msg="More then one instance with name found: %s" % instance_name)
+
+ return resources["instances"][0]["id"]
+
+ def query_list(self, path=None, result_key=None):
+ resources = self.api_query(path=self.resource_path) or dict()
+
+ resources_filtered = list()
+ for resource in resources[self.ressource_result_key_plural]:
+ # Skip IP with different type
+ if resource["ip_type"] != self.module.params["ip_type"]:
+ continue
+ # Skip IP in different region
+ if resource["region"] != self.module.params["region"]:
+ continue
+ resources_filtered.append(resource)
+
+ return resources_filtered
+
+ def create(self):
+ resource = super().create() or dict()
+ if resource and self.instance_id:
+ if not self.module.check_mode:
+ # Attach instance
+ self.api_query(
+ path="%s/%s/%s"
+ % (
+ self.resource_path,
+ resource[self.resource_key_id],
+ "attach",
+ ),
+ method="POST",
+ data=dict(instance_id=self.instance_id),
+ )
+ # Refresh
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id])
+ return resource
+
+ def update(self, resource):
+ if self.instance_id is None:
+ return resource
+
+ # Detach instance
+ elif resource["instance_id"] and not self.instance_id:
+ self.result["changed"] = True
+ if not self.module.check_mode:
+ self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], "detach"),
+ method="POST",
+ data=dict(instance_id=self.instance_id),
+ )
+ # Refresh
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id])
+
+ # Attach instance or change attached instance
+ elif self.instance_id and resource["instance_id"] != self.instance_id:
+ self.result["changed"] = True
+ if not self.module.check_mode:
+ self.api_query(
+ path="%s/%s/%s" % (self.resource_path, resource[self.resource_key_id], "attach"),
+ method="POST",
+ data=dict(instance_id=self.instance_id),
+ )
+ # Refresh
+ resource = self.query_by_id(resource_id=resource[self.resource_key_id])
+
+ return resource
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ label=dict(type="str", required=True, aliases=["name"]),
+ instance_id=dict(type="str"),
+ instance_name=dict(type="str"),
+ ip_type=dict(type="str", required=True, choices=["v4", "v6"]),
+ region=dict(type="str", required=True),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=(["instance_id", "instance_name"],),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrReservedIp(
+ module=module,
+ namespace="vultr_reserved_ip",
+ resource_path="/reserved-ips",
+ ressource_result_key_singular="reserved_ip",
+ resource_create_param_keys=["region", "ip_type", "label"],
+ resource_key_name="label",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/snapshot.py b/ansible_collections/vultr/cloud/plugins/modules/snapshot.py
new file mode 100644
index 000000000..9dba03c74
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/snapshot.py
@@ -0,0 +1,218 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: snapshot
+short_description: Manages snapshots on Vultr
+description:
+ - Create and remove snapshots.
+version_added: "1.7.0"
+author: "René Moser (@resmo)"
+options:
+ description:
+ description:
+ - Description of the snapshot.
+ required: true
+ aliases: [ name ]
+ type: str
+ instance:
+ description:
+ - The description or ID of the instance from which to take the snapshot.
+ - Mutually exclusive with I(url).
+ - I(instance) or I(url) is required if I(state=present).
+ type: str
+ url:
+ description:
+ - The URL of the snapshot image (RAW) to be uploaded.
+ - Mutually exclusive with I(instance).
+ - I(instance) or I(url) is required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the snapshot.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a snapshot is present
+ vultr.cloud.snapshot:
+ description: my snapshot of my instance
+ instance: my instance
+
+- name: Ensure a snapshot is present
+ vultr.cloud.snapshot:
+ description: debian 11 generic
+ url: https://cloud.debian.org/images/cloud/bullseye/latest/debian-11-generic-amd64.raw
+
+- name: Ensure a snapshot is absent
+ vultr.cloud.snapshot:
+ description: my snapshot of my instance
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_snapshot:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the snapshot.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description of the snapshot.
+ returned: success
+ type: str
+ sample: my vpc
+ date_created:
+ description: Date the snapshot was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ size:
+ description: Size of the snapshot.
+ returned: success
+ type: int
+ sample: 42949672960
+ compressed_size:
+ description: Compressed size of the snapshot.
+ returned: success
+ type: int
+ sample: 949678560
+ status:
+ description: Status of the snapshot.
+ returned: success
+ type: str
+ sample: complete
+ os_id:
+ description: ID of the OS.
+ returned: success
+ type: int
+ sample: 215
+ app_id:
+ description: ID of the app.
+ returned: success
+ type: int
+ sample: 0
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrSnapshot(AnsibleVultr):
+ def get_instance(self):
+ return self.query_filter_list_by_name(
+ key_name="label",
+ param_key="instance",
+ path="/instances",
+ result_key="instances",
+ fail_not_found=True,
+ )
+
+ def create(self):
+ param_keys = ("url", "instance")
+ if not any(self.module.params.get(x) is not None for x in param_keys):
+ self.module.fail_json(msg="missing required arguements, one of the following required: %s" % ", ".join(param_keys))
+
+ if self.module.params.get("url") is not None:
+ self.resource_create_param_keys.append("url")
+ # Upload by URL has a different endpoint
+ self.resource_path = self.resource_path + "/create-from-url"
+ else:
+ self.module.params["instance_id"] = self.get_instance()["id"]
+ self.resource_create_param_keys.append("instance_id")
+
+ resource = super(AnsibleVultrSnapshot, self).create()
+
+ # Reset endpoint
+ self.resource_path = "/snapshots"
+
+ if resource:
+ resource = self.wait_for_state(resource=resource, key="status", states=["complete"])
+
+ return resource
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ description=dict(type="str", required=True, aliases=["name"]),
+ instance=dict(type="str"),
+ url=dict(type="str"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ mutually_exclusive=(("instance", "url"),),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrSnapshot(
+ module=module,
+ namespace="vultr_snapshot",
+ resource_path="/snapshots",
+ ressource_result_key_singular="snapshot",
+ resource_create_param_keys=[
+ "description",
+ ],
+ resource_update_param_keys=[
+ "description",
+ ],
+ resource_key_name="description",
+ resource_update_method="PUT",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/snapshot_info.py b/ansible_collections/vultr/cloud/plugins/modules/snapshot_info.py
new file mode 100644
index 000000000..38fa4b85e
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/snapshot_info.py
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2023, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: snapshot_info
+short_description: Gather information about the Vultr snapshots
+description:
+ - Gather information about snapshots available.
+version_added: "1.7.0"
+author:
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr snapshots information
+ vultr.cloud.snapshot_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_snapshot_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_snapshot_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the snapshot.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description of the snapshot.
+ returned: success
+ type: str
+ sample: my vpc
+ date_created:
+ description: Date the snapshot was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ size:
+ description: Size of the snapshot.
+ returned: success
+ type: int
+ sample: 42949672960
+ compressed_size:
+ description: Compressed size of the snapshot.
+ returned: success
+ type: int
+ sample: 949678560
+ status:
+ description: Status of the snapshot.
+ returned: success
+ type: str
+ sample: complete
+ os_id:
+ description: ID of the OS.
+ returned: success
+ type: int
+ sample: 215
+ app_id:
+ description: ID of the app.
+ returned: success
+ type: int
+ sample: 0
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_snapshot_info",
+ resource_path="/snapshots",
+ ressource_result_key_singular="snapshot",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/ssh_key.py b/ansible_collections/vultr/cloud/plugins/modules/ssh_key.py
new file mode 100644
index 000000000..2c85eebff
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/ssh_key.py
@@ -0,0 +1,148 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: ssh_key
+short_description: Manages ssh keys on Vultr.
+description:
+ - Create, update and remove ssh keys.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the ssh key.
+ required: true
+ type: str
+ ssh_key:
+ description:
+ - SSH public key.
+ - Required if C(state=present).
+ type: str
+ state:
+ description:
+ - State of the ssh key.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+
+"""
+
+EXAMPLES = """
+- name: ensure an SSH key is present
+ vultr.cloud.ssh_key:
+ name: my ssh key
+ ssh_key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
+
+- name: ensure an SSH key is absent
+ vultr.cloud.ssh_key:
+ name: my ssh key
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_ssh_key:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the ssh key.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ name:
+ description: Name of the ssh key.
+ returned: success
+ type: str
+ sample: my ssh key
+ date_created:
+ description: Date the ssh key was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ ssh_key:
+ description: SSH public key.
+ returned: success
+ type: str
+ sample: ssh-rsa AA... someother@example.com
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ name=dict(type="str", required=True),
+ ssh_key=dict(type="str", no_log=False),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ("state", "present", ["ssh_key"]),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_ssh_key",
+ resource_path="/ssh-keys",
+ ressource_result_key_singular="ssh_key",
+ resource_create_param_keys=["name", "ssh_key"],
+ resource_update_param_keys=["name", "ssh_key"],
+ resource_key_name="name",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/ssh_key_info.py b/ansible_collections/vultr/cloud/plugins/modules/ssh_key_info.py
new file mode 100644
index 000000000..24441885b
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/ssh_key_info.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: ssh_key_info
+short_description: Get information about the Vultr SSH keys
+description:
+ - Get infos about SSH keys available.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+
+"""
+
+EXAMPLES = """
+- name: Get Vultr SSH keys infos
+ vultr.cloud.ssh_key_info:
+ register: result
+
+- name: Print the infos
+ ansible.builtin.debug:
+ var: result.vultr_ssh_key_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_ssh_key_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the ssh key.
+ returned: success
+ type: str
+ sample: 7d726ffe-9be2-4f88-8cda-fa7eba1da2b5
+ name:
+ description: Name of the ssh key.
+ returned: success
+ type: str
+ sample: my ssh key
+ date_created:
+ description: Date the ssh key was created.
+ returned: success
+ type: str
+ sample: "2021-11-07T05:57:59-05:00"
+ ssh_key:
+ description: SSH public key.
+ returned: success
+ type: str
+ sample: "ssh-rsa AA... someother@example.com"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_ssh_key_info",
+ resource_path="/ssh-keys",
+ ressource_result_key_singular="ssh_key",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/startup_script.py b/ansible_collections/vultr/cloud/plugins/modules/startup_script.py
new file mode 100644
index 000000000..4211b1aba
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/startup_script.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: startup_script
+short_description: Manages startup scripts on Vultr
+description:
+ - Create, update and remove startup scripts.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - The script name.
+ required: true
+ type: str
+ type:
+ description:
+ - The script type, can not be changed once created.
+ default: boot
+ choices: [ boot, pxe ]
+ aliases: [ script_type ]
+ type: str
+ script:
+ description:
+ - The script source code.
+ - Required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the script.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: ensure a pxe script exists, source from a file
+ vultr.cloud.startup_script:
+ name: my_web_script
+ script_type: pxe
+ script: "{{ lookup('file', 'path/to/script') }}"
+
+- name: ensure a boot script exists
+ vultr.cloud.startup_script:
+ name: vultr_startup_script
+ script: "#!/bin/bash\necho Hello World > /root/hello"
+
+- name: ensure a script is absent
+ vultr.cloud.startup_script:
+ name: my_web_script
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_startup_script:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the startup script.
+ returned: success
+ type: str
+ sample: 7d726ffe-9be2-4f88-8cda-fa7eba1da2b5
+ name:
+ description: Name of the startup script.
+ returned: success
+ type: str
+ sample: my startup script
+ script:
+ description: The source code of the startup script.
+ returned: success
+ type: str
+ sample: "#!/bin/bash\necho Hello World > /root/hello"
+ type:
+ description: The type of the startup script.
+ returned: success
+ type: str
+ sample: pxe
+ date_created:
+ description: Date the startup script was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ date_modified:
+ description: Date the startup script was modified.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+import base64
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+class AnsibleVultrStartupScript(AnsibleVultr):
+ def configure(self):
+ if self.module.params["script"]:
+ self.module.params["script"] = base64.b64encode(self.module.params["script"].encode())
+
+ def update(self, resource):
+ resource["script"] = resource["script"].encode()
+ return super(AnsibleVultrStartupScript, self).update(resource=resource)
+
+ def transform_result(self, resource):
+ if resource:
+ resource["script"] = base64.b64decode(resource["script"]).decode()
+ return resource
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ name=dict(type="str", required=True),
+ script=dict(
+ type="str",
+ ),
+ type=dict(
+ type="str",
+ default="boot",
+ choices=["boot", "pxe"],
+ aliases=["script_type"],
+ ),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ("state", "present", ["script"]),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrStartupScript(
+ module=module,
+ namespace="vultr_startup_script",
+ resource_path="/startup-scripts",
+ ressource_result_key_singular="startup_script",
+ resource_get_details=True,
+ resource_create_param_keys=["name", "type", "script"],
+ resource_update_param_keys=["name", "script"],
+ resource_key_name="name",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/startup_script_info.py b/ansible_collections/vultr/cloud/plugins/modules/startup_script_info.py
new file mode 100644
index 000000000..dd91166aa
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/startup_script_info.py
@@ -0,0 +1,120 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: startup_script_info
+short_description: Gather information about the Vultr startup scripts
+description:
+ - Gather information about startup scripts available.
+version_added: "1.0.0"
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr startup scripts information
+ vultr.cloud.startup_script_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_startup_script_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_startup_script_info:
+ description: Response from Vultr API.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the startup script.
+ returned: success
+ type: str
+ sample: 56e5b8b5-120c-40b1-a087-3abc9cd8df57
+ name:
+ description: Name of the startup script.
+ returned: success
+ type: str
+ sample: my startup script
+ type:
+ description: The type of the startup script.
+ returned: success
+ type: str
+ sample: pxe
+ date_created:
+ description: Date the startup script was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ date_modified:
+ description: Date the startup script was modified.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_startup_script_info",
+ resource_path="/startup-scripts",
+ ressource_result_key_singular="startup_script",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/user.py b/ansible_collections/vultr/cloud/plugins/modules/user.py
new file mode 100644
index 000000000..dd5124345
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/user.py
@@ -0,0 +1,229 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: user
+short_description: Manages users on Vultr
+description:
+ - Create, update and remove users.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ name:
+ description:
+ - Name of the user
+ required: true
+ type: str
+ email:
+ description:
+ - Email of the user.
+ - Required if C(state=present).
+ type: str
+ password:
+ description:
+ - Password of the user.
+ - Only considered while creating a user or when C(force=true).
+ type: str
+ force:
+ description:
+ - Password will only be changed with enforcement.
+ default: false
+ type: bool
+ api_enabled:
+ description:
+ - Whether the API is enabled or not.
+ default: true
+ type: bool
+ acls:
+ description:
+ - List of ACLs this users should have.
+ - Required if C(state=present).
+ - One or more of the choices list, some depend on each other.
+ choices:
+ - manage_users
+ - subscriptions_view
+ - subscriptions
+ - provisioning
+ - billing
+ - support
+ - abuse
+ - dns
+ - upgrade
+ - objstore
+ - loadbalancer
+ aliases: [ acl ]
+ type: list
+ elements: str
+ state:
+ description:
+ - State of the user.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a user exists
+ vultr.cloud.user:
+ name: john
+ email: john.doe@example.com
+ password: s3cr3t
+ acls:
+ - manage_users
+ - subscriptions
+
+- name: Remove a user
+ vultr.cloud.user:
+ name: john
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_user:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the user.
+ returned: success
+ type: str
+ sample: 7d726ffe-9be2-4f88-8cda-fa7eba1da2b5
+ api_key:
+ description: API key of the user.
+ returned: only after resource was created
+ type: str
+ sample: 567E6K567E6K567E6K567E6K567E6K
+ name:
+ description: Name of the user.
+ returned: success
+ type: str
+ sample: john
+ email:
+ description: Email of the user.
+ returned: success
+ type: str
+ sample: "john@example.com"
+ api_enabled:
+ description: Whether the API is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ acls:
+ description: List of ACLs of the user.
+ returned: success
+ type: list
+ sample: [manage_users, support, upgrade]
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+ACLS = [
+ "manage_users",
+ "subscriptions_view",
+ "subscriptions",
+ "provisioning",
+ "billing",
+ "support",
+ "abuse",
+ "dns",
+ "upgrade",
+ "objstore",
+ "loadbalancer",
+]
+
+
+class AnsibleVultrUser(AnsibleVultr):
+ def create(self):
+ # Password is required in create mode.
+ self.module.fail_on_missing_params(required_params=["password"])
+ return super(AnsibleVultrUser, self).create()
+
+ def update(self, resource):
+ # Password is never returned and we can not compare.
+ # That is why we update it only if forced
+ force = self.module.params.get("force")
+ if force:
+ self.resource_update_param_keys.append("password")
+ return super(AnsibleVultrUser, self).update(resource=resource)
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ name=dict(type="str", required=True),
+ email=dict(type="str"),
+ password=dict(type="str", no_log=True),
+ force=dict(type="bool", default=False),
+ api_enabled=dict(type="bool", default=True),
+ acls=dict(type="list", elements="str", choices=ACLS, aliases=["acl"]),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=[
+ ("state", "present", ["email", "acls"]),
+ ],
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultrUser(
+ module=module,
+ namespace="vultr_user",
+ resource_path="/users",
+ ressource_result_key_singular="user",
+ resource_create_param_keys=["name", "email", "password", "api_enabled", "acls"],
+ resource_update_param_keys=["name", "email", "api_enabled", "acls"],
+ resource_key_name="name",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/user_info.py b/ansible_collections/vultr/cloud/plugins/modules/user_info.py
new file mode 100644
index 000000000..5d0a61efe
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/user_info.py
@@ -0,0 +1,125 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2018, Yanis Guenane <yanis+ansible@guenane.org>
+# Copyright (c) 2021, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: user_info
+short_description: Get information about the Vultr users
+version_added: "1.0.0"
+description:
+ - Get infos about users available.
+author:
+ - "Yanis Guenane (@Spredzy)"
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Get Vultr user infos
+ vultr.cloud.user_info:
+ register: result
+
+- name: Print the infos
+ ansible.builtin.debug:
+ var: result.vultr_user_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_user_info:
+ description: Response from Vultr API as list.
+ returned: available
+ type: list
+ contains:
+ id:
+ description: ID of the user.
+ returned: success
+ type: str
+ sample: 7d726ffe-9be2-4f88-8cda-fa7eba1da2b5
+ api_key:
+ description: API key of the user.
+ returned: only after resource was created
+ type: str
+ sample: 567E6K567E6K567E6K567E6K567E6K
+ name:
+ description: Name of the user.
+ returned: success
+ type: str
+ sample: john
+ email:
+ description: Email of the user.
+ returned: success
+ type: str
+ sample: "john@example.com"
+ api_enabled:
+ description: Whether the API is enabled or not.
+ returned: success
+ type: bool
+ sample: true
+ acls:
+ description: List of ACLs of the user.
+ returned: success
+ type: list
+ sample: [ manage_users, support, upgrade ]
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_user_info",
+ resource_path="/users",
+ ressource_result_key_singular="user",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/vpc.py b/ansible_collections/vultr/cloud/plugins/modules/vpc.py
new file mode 100644
index 000000000..83a183c8d
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/vpc.py
@@ -0,0 +1,182 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: vpc
+short_description: Manages VPCs on Vultr
+description:
+ - Create and remove VPCs.
+version_added: "1.0.0"
+author: "René Moser (@resmo)"
+options:
+ description:
+ description:
+ - Description of the VPC.
+ required: true
+ aliases: [ name ]
+ type: str
+ v4_subnet:
+ description:
+ - IPv4 subnet of the VPC.
+ - Required if I(state=present).
+ type: str
+ v4_subnet_mask:
+ description:
+ - IPv4 subnet mask of the VPC.
+ - Required if I(state=present).
+ type: int
+ region:
+ description:
+ - Region the VPC will be related to.
+ - Required if I(state=present).
+ type: str
+ state:
+ description:
+ - State of the VPC.
+ default: present
+ choices: [ present, absent ]
+ type: str
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Ensure a VPC is present
+ vultr.cloud.vpc:
+ description: my VPC.
+ subnet: 10.99.1.0
+ subnet_mask: 24
+ region: ewr
+
+- name: Ensure a VPC is absent
+ vultr.cloud.vpc:
+ description: my VPC.
+ state: absent
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_vpc:
+ description: Response from Vultr API.
+ returned: success
+ type: dict
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: cb676a46-66fd-4dfb-b839-443f2e6c0b60
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: my vpc
+ v4_subnet:
+ description: Subnet of the VPC.
+ returned: success
+ type: str
+ sample: 10.99.1.0
+ v4_subnet_maks:
+ description: Subnet mask of the VPC.
+ returned: success
+ type: str
+ sample: 10.99.1.0
+ date_created:
+ description: Date the VPC was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ date_modified:
+ description: Date the VPC was modified.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+ argument_spec.update(
+ dict(
+ description=dict(type="str", required=True, aliases=["name"]),
+ v4_subnet=dict(type="str"),
+ v4_subnet_mask=dict(type="int"),
+ region=dict(type="str"),
+ state=dict(type="str", choices=["present", "absent"], default="present"),
+ ) # type: ignore
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ required_if=(
+ (
+ "state",
+ "present",
+ ("v4_subnet", "v4_subnet_mask", "region"),
+ ),
+ ),
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_vpc",
+ resource_path="/vpcs",
+ ressource_result_key_singular="vpc",
+ resource_create_param_keys=[
+ "description",
+ "v4_subnet",
+ "v4_subnet_mask",
+ "region",
+ ],
+ resource_update_param_keys=["description"],
+ resource_key_name="description",
+ resource_update_method="PUT",
+ )
+
+ if module.params.get("state") == "absent": # type: ignore
+ vultr.absent()
+ else:
+ vultr.present()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/ansible_collections/vultr/cloud/plugins/modules/vpc_info.py b/ansible_collections/vultr/cloud/plugins/modules/vpc_info.py
new file mode 100644
index 000000000..5dc67a529
--- /dev/null
+++ b/ansible_collections/vultr/cloud/plugins/modules/vpc_info.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2022, René Moser <mail@renemoser.net>
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+
+DOCUMENTATION = """
+---
+module: vpc_info
+short_description: Gather information about the Vultr VPCs
+description:
+ - Gather information about VPCs available.
+version_added: "1.0.0"
+author:
+ - "René Moser (@resmo)"
+extends_documentation_fragment:
+ - vultr.cloud.vultr_v2
+"""
+
+EXAMPLES = """
+- name: Gather Vultr VPCs information
+ vultr.cloud.vpc_info:
+ register: result
+
+- name: Print the gathered information
+ ansible.builtin.debug:
+ var: result.vultr_vpc_info
+"""
+
+RETURN = """
+---
+vultr_api:
+ description: Response from Vultr API with a few additions/modification.
+ returned: success
+ type: dict
+ contains:
+ api_timeout:
+ description: Timeout used for the API requests.
+ returned: success
+ type: int
+ sample: 60
+ api_retries:
+ description: Amount of max retries for the API requests.
+ returned: success
+ type: int
+ sample: 5
+ api_retry_max_delay:
+ description: Exponential backoff delay in seconds between retries up to this max delay value.
+ returned: success
+ type: int
+ sample: 12
+ api_endpoint:
+ description: Endpoint used for the API requests.
+ returned: success
+ type: str
+ sample: "https://api.vultr.com/v2"
+vultr_vpc_info:
+ description: Response from Vultr API as list.
+ returned: success
+ type: list
+ contains:
+ id:
+ description: ID of the VPC.
+ returned: success
+ type: str
+ sample: "cb676a46-66fd-4dfb-b839-443f2e6c0b60"
+ description:
+ description: Description of the VPC.
+ returned: success
+ type: str
+ sample: myvpc
+ date_created:
+ description: Date when the VPC was created.
+ returned: success
+ type: str
+ sample: "2020-10-10T01:56:20+00:00"
+ region:
+ description: Region the VPC was deployed into.
+ returned: success
+ type: str
+ sample: ewr
+ v4_subnet:
+ description: IPv4 Network address
+ returned: success
+ type: str
+ sample: 192.168.42.0
+ v4_subnet_mask:
+ description: Ipv4 Network mask
+ returned: success
+ type: int
+ sample: 24
+"""
+
+from ansible.module_utils.basic import AnsibleModule
+
+from ..module_utils.vultr_v2 import AnsibleVultr, vultr_argument_spec
+
+
+def main():
+ argument_spec = vultr_argument_spec()
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ vultr = AnsibleVultr(
+ module=module,
+ namespace="vultr_vpc_info",
+ resource_path="/vpcs",
+ ressource_result_key_singular="vpc",
+ )
+
+ vultr.get_result(vultr.query_list())
+
+
+if __name__ == "__main__":
+ main()