summaryrefslogtreecommitdiffstats
path: root/ansible_collections/azure/azcollection/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/azure/azcollection/plugins')
-rw-r--r--ansible_collections/azure/azcollection/plugins/doc_fragments/azure.py16
-rw-r--r--ansible_collections/azure/azcollection/plugins/doc_fragments/azure_rm.py13
-rw-r--r--ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py99
-rw-r--r--ansible_collections/azure/azcollection/plugins/lookup/azure_keyvault_secret.py42
-rw-r--r--ansible_collections/azure/azcollection/plugins/lookup/azure_service_principal_attribute.py101
-rw-r--r--ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common.py266
-rw-r--r--ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common_rest.py2
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_accesstoken_info.py126
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_account_info.py50
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication.py307
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication_info.py136
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup.py211
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup_info.py141
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword.py159
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword_info.py56
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal.py71
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal_info.py99
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py188
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser_info.py91
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks.py177
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks_info.py35
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_akscredentials_info.py209
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_aksversion_info.py24
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement.py195
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement_info.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice.py78
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice_info.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_appgateway.py84
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall.py227
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm.py33
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm_info.py9
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount.py15
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount_info.py4
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_deployment.py8
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabarmtemplate_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifact_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_dnsrecordset.py6
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery.py89
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery_info.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage.py178
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion.py371
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion_info.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevice.py6
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevicemodule.py12
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey.py5
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey_info.py21
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret.py71
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret_info.py21
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_lock_info.py2
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_manageddisk.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_managementgroup.py76
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase.py4
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase_info.py4
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface.py2
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface_info.py128
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster.py126
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedclusterkubeconfig_info.py227
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py210
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase.py288
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py239
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py294
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py187
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver.py928
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver_info.py443
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednsrecordset.py6
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix.py455
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix_info.py296
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault.py9
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault_info.py44
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache_info.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscachefirewallrule.py3
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment.py11
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment_info.py5
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition.py30
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition_info.py5
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource.py12
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource_info.py25
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_securitygroup.py8
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_servicebus_info.py22
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_snapshot.py55
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance.py2
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance_info.py6
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey.py266
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey_info.py196
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount.py64
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount_info.py14
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageblob.py20
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_subnet.py4
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine.py255
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine_info.py18
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescaleset.py17
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance_info.py1
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualwan.py61
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy.py9
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy_info.py9
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_vpnsite.py107
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp.py43
-rw-r--r--ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp_info.py7
133 files changed, 7862 insertions, 1490 deletions
diff --git a/ansible_collections/azure/azcollection/plugins/doc_fragments/azure.py b/ansible_collections/azure/azcollection/plugins/doc_fragments/azure.py
index bc382e401..74c1286ac 100644
--- a/ansible_collections/azure/azcollection/plugins/doc_fragments/azure.py
+++ b/ansible_collections/azure/azcollection/plugins/doc_fragments/azure.py
@@ -34,7 +34,8 @@ options:
type: str
client_id:
description:
- - Azure client ID. Use when authenticating with a Service Principal.
+ - Azure client ID. Use when authenticating with a Service Principal or Managed Identity (msi).
+ - Can also be set via the C(AZURE_CLIENT_ID) environment variable.
type: str
secret:
description:
@@ -65,6 +66,19 @@ options:
type: str
choices: [ ignore, validate ]
version_added: '0.0.1'
+ disable_instance_discovery:
+ description:
+ - Determines whether or not instance discovery is performed when attempting to authenticate.
+ Setting this to true will completely disable both instance discovery and authority validation.
+ This functionality is intended for use in scenarios where the metadata endpoint cannot be reached
+ such as in private clouds or Azure Stack. The process of instance discovery entails retrieving
+ authority metadata from https://login.microsoft.com/ to validate the authority. By setting this
+ to **True**, the validation of the authority is disabled. As a result, it is crucial to ensure
+ that the configured authority host is valid and trustworthy.
+ - Set via credential file profile or the C(AZURE_DISABLE_INSTANCE_DISCOVERY) environment variable.
+ type: bool
+ default: False
+ version_added: '2.3.0'
auth_source:
description:
- Controls the source of the credentials to use for authentication.
diff --git a/ansible_collections/azure/azcollection/plugins/doc_fragments/azure_rm.py b/ansible_collections/azure/azcollection/plugins/doc_fragments/azure_rm.py
index 8d860d863..6677137a1 100644
--- a/ansible_collections/azure/azcollection/plugins/doc_fragments/azure_rm.py
+++ b/ansible_collections/azure/azcollection/plugins/doc_fragments/azure_rm.py
@@ -47,6 +47,11 @@ options:
expression in the list is evaluated for each host; when the expression is true, the host is excluded
from the inventory.
default: []
+ include_host_filters:
+ description: Include hosts from the inventory with a list of Jinja2 conditional expressions. Each
+ expression in the list is evaluated for each host; when the expression is true, the host is included
+ in the inventory, all hosts are includes in the inventory by default.
+ default: [true]
batch_fetch:
description: To improve performance, results are fetched using an unsupported batch API. Disabling
C(batch_fetch) uses a much slower serial fetch, resulting in many more round-trips. Generally only
@@ -61,8 +66,8 @@ options:
description:
- By default this plugin is using a general group name sanitization to create safe and usable group names for use in Ansible.
This option allows you to override that, in efforts to allow migration from the old inventory script and
- matches the sanitization of groups when the script's ``replace_dash_in_groups`` option is set to ``False``.
- To replicate behavior of ``replace_dash_in_groups = True`` with constructed groups,
+ matches the sanitization of groups when the script's C(replace_dash_in_groups) option is set to C(false).
+ To replicate behavior of C(replace_dash_in_groups = true) with constructed groups,
you will need to replace hyphens with underscores via the regex_replace filter for those entries.
- For this to work you should also turn off the TRANSFORM_INVALID_GROUP_CHARS setting,
otherwise the core engine will just use the standard sanitization on top.
@@ -86,9 +91,9 @@ options:
- Ignores expression if result is an empty string or None value.
- By default, inventory_hostname is generated to be globally unique based on the VM host name.
See C(plain_host_names) for more details on the default.
- - An expression of 'default' will force using the default hostname generator if no previous hostname expression
+ - An expression of C(default) will force using the default hostname generator if no previous hostname expression
resulted in a valid hostname.
- - Use ``default_inventory_hostname`` to access the default hostname generator's value in any of the Jinja2 expressions.
+ - Use C(default_inventory_hostname) to access the default hostname generator's value in any of the Jinja2 expressions.
type: list
elements: str
default: [default]
diff --git a/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py b/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py
index 3442aa124..12970dec3 100644
--- a/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py
+++ b/ansible_collections/azure/azcollection/plugins/inventory/azure_rm.py
@@ -105,6 +105,14 @@ exclude_host_filters:
- tags['tagkey2'] is defined and tags['tagkey2'] == 'tagkey2'
# excludes hosts that are powered off
- powerstate != 'running'
+
+# includes a host to the inventory when any of these expressions is true, can refer to any vars defined on the host
+include_host_filters:
+ # includes hosts that in the eastus region and power on
+ - location in ['eastus'] and powerstate == 'running'
+ # includes hosts in the eastus region and power on OR includes hosts in the eastus2 region and tagkey is tagkey
+ - location in ['eastus'] and powerstate == 'running'
+ - location in ['eastus2'] and tags['tagkey'] is defined and tags['tagkey'] == 'tagkey'
'''
# FUTURE: do we need a set of sane default filters, separate from the user-defineable ones?
@@ -130,12 +138,15 @@ from ansible.errors import AnsibleParserError, AnsibleError
from ansible.module_utils.parsing.convert_bool import boolean
from ansible.module_utils._text import to_native, to_bytes, to_text
from itertools import chain
+from os import environ
try:
from azure.core._pipeline_client import PipelineClient
from azure.core.pipeline.policies import BearerTokenCredentialPolicy
from azure.core.configuration import Configuration
from azure.mgmt.core.tools import parse_resource_id
+ from azure.core.pipeline import PipelineResponse
+ from azure.mgmt.core.polling.arm_polling import ARMPolling
except ImportError:
Configuration = object
parse_resource_id = object
@@ -199,7 +210,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
if re.match(r'.{0,}azure_rm\.y(a)?ml$', path):
return True
# display.debug("azure_rm inventory filename must end with 'azure_rm.yml' or 'azure_rm.yaml'")
- return False
+ raise AnsibleError("azure_rm inventory filename must end with 'azure_rm.yml' or 'azure_rm.yaml'")
def parse(self, inventory, loader, path, cache=True):
super(InventoryModule, self).parse(inventory, loader, path)
@@ -215,6 +226,8 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
self._filters = self.get_option('exclude_host_filters') + self.get_option('default_host_filters')
+ self._include_filters = self.get_option('include_host_filters')
+
try:
self._credential_setup()
self._get_hosts()
@@ -222,8 +235,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
raise
def _credential_setup(self):
+ auth_source = environ.get('ANSIBLE_AZURE_AUTH_SOURCE', None) or self.get_option('auth_source')
auth_options = dict(
- auth_source=self.get_option('auth_source'),
+ auth_source=auth_source,
profile=self.get_option('profile'),
subscription_id=self.get_option('subscription_id'),
client_id=self.get_option('client_id'),
@@ -238,6 +252,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
adfs_authority_url=self.get_option('adfs_authority_url')
)
+ if self.templar.is_template(auth_options["tenant"]):
+ auth_options["tenant"] = self.templar.template(variable=auth_options["tenant"], disable_lookups=False)
+
+ if self.templar.is_template(auth_options["client_id"]):
+ auth_options["client_id"] = self.templar.template(variable=auth_options["client_id"], disable_lookups=False)
+
+ if self.templar.is_template(auth_options["secret"]):
+ auth_options["secret"] = self.templar.template(variable=auth_options["secret"], disable_lookups=False)
+
+ if self.templar.is_template(auth_options["subscription_id"]):
+ auth_options["subscription_id"] = self.templar.template(variable=auth_options["subscription_id"], disable_lookups=False)
+
self.azure_auth = AzureRMAuth(**auth_options)
self._clientconfig = AzureRMRestConfiguration(self.azure_auth.azure_credential_track2, self.azure_auth.subscription_id,
@@ -297,12 +323,14 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
for h in self._hosts:
# FUTURE: track hostnames to warn if a hostname is repeated (can happen for legacy and for composed inventory_hostname)
inventory_hostname = self._get_hostname(h, hostnames=constructable_hostnames, strict=constructable_config_strict)
- if self._filter_host(inventory_hostname, h.hostvars):
+ if self._filter_exclude_host(inventory_hostname, h.hostvars):
+ continue
+ if not self._filter_include_host(inventory_hostname, h.hostvars):
continue
self.inventory.add_host(inventory_hostname)
# FUTURE: configurable default IP list? can already do this via hostvar_expressions
self.inventory.set_variable(inventory_hostname, "ansible_host",
- next(chain(h.hostvars['public_ipv4_addresses'], h.hostvars['private_ipv4_addresses']), None))
+ next(chain(h.hostvars['public_ipv4_address'], h.hostvars['private_ipv4_addresses']), None))
for k, v in iteritems(h.hostvars):
# FUTURE: configurable hostvar prefix? Makes docs harder...
self.inventory.set_variable(inventory_hostname, k, v)
@@ -313,10 +341,10 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
self._add_host_to_keyed_groups(constructable_config_keyed_groups, h.hostvars, inventory_hostname, strict=constructable_config_strict)
# FUTURE: fix underlying inventory stuff to allow us to quickly access known groupvars from reconciled host
- def _filter_host(self, inventory_hostname, hostvars):
+ def _filter_host(self, filter, inventory_hostname, hostvars):
self.templar.available_variables = hostvars
- for condition in self._filters:
+ for condition in filter:
# FUTURE: should warn/fail if conditional doesn't return True or False
conditional = "{{% if {0} %}} True {{% else %}} False {{% endif %}}".format(condition)
try:
@@ -329,6 +357,12 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
return False
+ def _filter_include_host(self, inventory_hostname, hostvars):
+ return self._filter_host(self._include_filters, inventory_hostname, hostvars)
+
+ def _filter_exclude_host(self, inventory_hostname, hostvars):
+ return self._filter_host(self._filters, inventory_hostname, hostvars)
+
def _get_hostname(self, host, hostnames=None, strict=False):
hostname = None
errors = []
@@ -441,8 +475,18 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
request_new = self.new_client.post(url, query_parameters, header_parameters, body_content)
response = self.new_client.send_request(request_new)
-
- return json.loads(response.body())
+ if response.status_code == 202:
+ try:
+ poller = ARMPolling(timeout=3)
+ poller.initialize(client=self.new_client,
+ initial_response=PipelineResponse(None, response, None),
+ deserialization_callback=lambda r: r)
+ poller.run()
+ return poller.resource().context['deserialized_data']
+ except Exception as ec:
+ raise
+ else:
+ return json.loads(response.body())
def send_request(self, url, api_version):
query_parameters = {'api-version': api_version}
@@ -525,9 +569,11 @@ class AzureHost(object):
network_interface_id=[],
security_group_id=[],
security_group=[],
- public_ipv4_addresses=[],
+ public_ip_address=[],
+ public_ipv4_address=[],
public_dns_hostnames=[],
private_ipv4_addresses=[],
+ subnet=[],
id=self._vm_model['id'],
location=self._vm_model['location'],
name=self._vm_model['name'],
@@ -550,25 +596,34 @@ class AzureHost(object):
resource_group=parse_resource_id(self._vm_model['id']).get('resource_group').lower(),
default_inventory_hostname=self.default_inventory_hostname,
creation_time=self._vm_model['properties']['timeCreated'],
+ license_type=self._vm_model['properties'].get('licenseType', 'Unknown')
)
# set nic-related values from the primary NIC first
for nic in sorted(self.nics, key=lambda n: n.is_primary, reverse=True):
# and from the primary IP config per NIC first
for ipc in sorted(nic._nic_model['properties']['ipConfigurations'], key=lambda i: i['properties'].get('primary', False), reverse=True):
- private_ip = ipc['properties'].get('privateIPAddress')
- if private_ip:
- new_hostvars['private_ipv4_addresses'].append(private_ip)
- pip_id = ipc['properties'].get('publicIPAddress', {}).get('id')
- if pip_id:
- new_hostvars['public_ip_id'] = pip_id
-
- pip = nic.public_ips[pip_id]
- new_hostvars['public_ip_name'] = pip._pip_model['name']
- new_hostvars['public_ipv4_addresses'].append(pip._pip_model['properties'].get('ipAddress', None))
- pip_fqdn = pip._pip_model['properties'].get('dnsSettings', {}).get('fqdn')
- if pip_fqdn:
- new_hostvars['public_dns_hostnames'].append(pip_fqdn)
+ try:
+ subnet = ipc['properties'].get('subnet')
+ if subnet:
+ new_hostvars['subnet'].append(subnet)
+ private_ip = ipc['properties'].get('privateIPAddress')
+ if private_ip:
+ new_hostvars['private_ipv4_addresses'].append(private_ip)
+ pip_id = ipc['properties'].get('publicIPAddress', {}).get('id')
+ if pip_id and pip_id in nic.public_ips:
+ pip = nic.public_ips[pip_id]
+ new_hostvars['public_ipv4_address'].append(pip._pip_model['properties'].get('ipAddress', None))
+ new_hostvars['public_ip_address'].append({
+ 'id': pip_id,
+ 'name': pip._pip_model['name'],
+ 'ipv4_address': pip._pip_model['properties'].get('ipAddress', None),
+ })
+ pip_fqdn = pip._pip_model['properties'].get('dnsSettings', {}).get('fqdn')
+ if pip_fqdn:
+ new_hostvars['public_dns_hostnames'].append(pip_fqdn)
+ except Exception:
+ continue
new_hostvars['mac_address'].append(nic._nic_model['properties'].get('macAddress'))
new_hostvars['network_interface'].append(nic._nic_model['name'])
diff --git a/ansible_collections/azure/azcollection/plugins/lookup/azure_keyvault_secret.py b/ansible_collections/azure/azcollection/plugins/lookup/azure_keyvault_secret.py
index ea2183a5a..5e693e4b3 100644
--- a/ansible_collections/azure/azcollection/plugins/lookup/azure_keyvault_secret.py
+++ b/ansible_collections/azure/azcollection/plugins/lookup/azure_keyvault_secret.py
@@ -32,6 +32,8 @@ options:
description: Secret of the service principal.
tenant_id:
description: Tenant id of service principal.
+ use_msi:
+ description: MSI token autodiscover, default is true.
notes:
- If version is not provided, this plugin will return the latest version of the secret.
- If ansible is running on Azure Virtual Machine with MSI enabled, client_id, secret and tenant isn't required.
@@ -74,7 +76,8 @@ EXAMPLE = """
vault_url=url,
client_id=client_id,
secret=secret,
- tenant_id=tenant
+ tenant_id=tenant,
+ use_msi=false
)
}}"
@@ -139,22 +142,6 @@ token_headers = {
'Metadata': 'true'
}
-token = None
-
-try:
- token_res = requests.get('http://169.254.169.254/metadata/identity/oauth2/token', params=token_params, headers=token_headers, timeout=(3.05, 27))
- if token_res.ok:
- token = token_res.json().get("access_token")
- if token is not None:
- TOKEN_ACQUIRED = True
- else:
- display.v('Successfully called MSI endpoint, but no token was available. Will use service principal if provided.')
- else:
- display.v("Unable to query MSI endpoint, Error Code %s. Will use service principal if provided" % token_res.status_code)
-except Exception:
- display.v('Unable to fetch MSI token. Will use service principal if provided.')
- TOKEN_ACQUIRED = False
-
def lookup_secret_non_msi(terms, vault_url, kwargs):
@@ -187,6 +174,27 @@ class LookupModule(LookupBase):
def run(self, terms, variables, **kwargs):
ret = []
vault_url = kwargs.pop('vault_url', None)
+ use_msi = kwargs.pop('use_msi', True)
+ TOKEN_ACQUIRED = False
+ token = None
+
+ if use_msi:
+ try:
+ token_res = requests.get('http://169.254.169.254/metadata/identity/oauth2/token',
+ params=token_params,
+ headers=token_headers,
+ timeout=(3.05, 27))
+ if token_res.ok:
+ token = token_res.json().get("access_token")
+ if token is not None:
+ TOKEN_ACQUIRED = True
+ else:
+ display.v('Successfully called MSI endpoint, but no token was available. Will use service principal if provided.')
+ else:
+ display.v("Unable to query MSI endpoint, Error Code %s. Will use service principal if provided" % token_res.status_code)
+ except Exception:
+ display.v('Unable to fetch MSI token. Will use service principal if provided.')
+
if vault_url is None:
raise AnsibleError('Failed to get valid vault url.')
if TOKEN_ACQUIRED:
diff --git a/ansible_collections/azure/azcollection/plugins/lookup/azure_service_principal_attribute.py b/ansible_collections/azure/azcollection/plugins/lookup/azure_service_principal_attribute.py
new file mode 100644
index 000000000..1b1941026
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/lookup/azure_service_principal_attribute.py
@@ -0,0 +1,101 @@
+# (c) 2018 Yunge Zhu, <yungez@microsoft.com>
+# (c) 2017 Ansible Project
+# 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 = """
+---
+name: azure_service_principal_attribute
+
+requirements:
+ - msgraph-sdk
+
+author:
+ - Yunge Zhu (@yungezz)
+
+version_added: "1.12.0"
+
+short_description: Look up Azure service principal attributes.
+
+description:
+ - Describes object id of your Azure service principal account.
+options:
+ azure_client_id:
+ description: azure service principal client id.
+ azure_secret:
+ description: azure service principal secret
+ azure_tenant:
+ description: azure tenant
+ azure_cloud_environment:
+ description: azure cloud environment
+"""
+
+EXAMPLES = """
+set_fact:
+ object_id: "{{ lookup('azure_service_principal_attribute',
+ azure_client_id=azure_client_id,
+ azure_secret=azure_secret,
+ azure_tenant=azure_secret) }}"
+"""
+
+RETURN = """
+_raw:
+ description:
+ Returns object id of service principal.
+"""
+
+from ansible.errors import AnsibleError
+from ansible.plugins.lookup import LookupBase
+from ansible.module_utils._text import to_native
+
+try:
+ from azure.cli.core import cloud as azure_cloud
+ from azure.identity._credentials.client_secret import ClientSecretCredential
+ import asyncio
+ from msgraph import GraphServiceClient
+ from msgraph.generated.service_principals.service_principals_request_builder import ServicePrincipalsRequestBuilder
+except ImportError:
+ pass
+
+
+class LookupModule(LookupBase):
+ def run(self, terms, variables, **kwargs):
+
+ self.set_options(direct=kwargs)
+
+ credentials = {}
+ credentials['azure_client_id'] = self.get_option('azure_client_id', None)
+ credentials['azure_secret'] = self.get_option('azure_secret', None)
+ credentials['azure_tenant'] = self.get_option('azure_tenant', 'common')
+
+ if credentials['azure_client_id'] is None or credentials['azure_secret'] is None:
+ raise AnsibleError("Must specify azure_client_id and azure_secret")
+
+ _cloud_environment = azure_cloud.AZURE_PUBLIC_CLOUD
+ if self.get_option('azure_cloud_environment', None) is not None:
+ _cloud_environment = azure_cloud.get_cloud_from_metadata_endpoint(credentials['azure_cloud_environment'])
+
+ try:
+ azure_credential_track2 = ClientSecretCredential(client_id=credentials['azure_client_id'],
+ client_secret=credentials['azure_secret'],
+ tenant_id=credentials['azure_tenant'],
+ authority=_cloud_environment.endpoints.active_directory)
+
+ client = GraphServiceClient(azure_credential_track2)
+
+ response = asyncio.get_event_loop().run_until_complete(self.get_service_principals(client, credentials['azure_client_id']))
+ if not response:
+ return []
+ return list(response.value)[0].id.split(',')
+ except Exception as ex:
+ raise AnsibleError("Failed to get service principal object id: %s" % to_native(ex))
+ return False
+
+ async def get_service_principals(self, _client, app_id):
+ request_configuration = ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetRequestConfiguration(
+ query_parameters=ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetQueryParameters(
+ filter="servicePrincipalNames/any(c:c eq '{0}')".format(app_id),
+ )
+ )
+ return await _client.service_principals.get(request_configuration=request_configuration)
diff --git a/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common.py b/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common.py
index 9c0e6e839..79b5167b1 100644
--- a/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common.py
+++ b/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common.py
@@ -15,10 +15,6 @@ import inspect
import traceback
import json
-try:
- from azure.graphrbac import GraphRbacManagementClient
-except Exception:
- pass
from os.path import expanduser
from ansible.module_utils.basic import \
@@ -53,6 +49,7 @@ AZURE_COMMON_ARGS = dict(
log_path=dict(type='str', no_log=True),
x509_certificate_path=dict(type='path', no_log=True),
thumbprint=dict(type='str', no_log=True),
+ disable_instance_discovery=dict(type='bool', default=False),
)
AZURE_CREDENTIAL_ENV_MAPPING = dict(
@@ -67,7 +64,8 @@ AZURE_CREDENTIAL_ENV_MAPPING = dict(
cert_validation_mode='AZURE_CERT_VALIDATION_MODE',
adfs_authority_url='AZURE_ADFS_AUTHORITY_URL',
x509_certificate_path='AZURE_X509_CERTIFICATE_PATH',
- thumbprint='AZURE_THUMBPRINT'
+ thumbprint='AZURE_THUMBPRINT',
+ disable_instance_discovery='AZURE_DISABLE_INSTANCE_DISCOVERY'
)
@@ -114,7 +112,28 @@ AZURE_API_PROFILES = {
'ManagementLockClient': '2016-09-01',
'DataLakeStoreAccountManagementClient': '2016-11-01',
'NotificationHubsManagementClient': '2016-03-01',
- 'EventHubManagementClient': '2018-05-04'
+ 'EventHubManagementClient': '2021-11-01',
+ 'GenericRestClient': 'latest',
+ 'DnsManagementClient': '2018-05-01',
+ 'PrivateDnsManagementClient': 'latest',
+ 'ContainerServiceClient': '2022-02-01',
+ 'SqlManagementClient': 'latest',
+ 'ContainerRegistryManagementClient': '2021-09-01',
+ 'MarketplaceOrderingAgreements': 'latest',
+ 'TrafficManagerManagementClient': 'latest',
+ 'MonitorManagementClient': '2016-03-01',
+ 'LogAnalyticsManagementClient': 'latest',
+ 'ServiceBusManagementClient': 'latest',
+ 'AutomationClient': 'latest',
+ 'IotHubClient': 'latest',
+ 'RecoveryServicesBackupClient': 'latest',
+ 'DataFactoryManagementClient': 'latest',
+ 'KeyVaultManagementClient': '2021-10-01',
+ 'HDInsightManagementClient': 'latest',
+ 'DevTestLabsClient': 'latest',
+ 'CosmosDBManagementClient': 'latest',
+ 'CdnManagementClient': '2017-04-02',
+ 'BatchManagementClient': 'latest',
},
'2019-03-01-hybrid': {
'StorageManagementClient': '2017-10-01',
@@ -220,11 +239,8 @@ except ImportError:
try:
from enum import Enum
- from msrestazure.azure_active_directory import AADTokenCredentials
- from msrestazure.azure_active_directory import MSIAuthentication
from azure.mgmt.core.tools import parse_resource_id, resource_id, is_valid_resource_id
from azure.cli.core import cloud as azure_cloud
- from azure.common.credentials import ServicePrincipalCredentials, UserPassCredentials
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.resource.resources import ResourceManagementClient
from azure.mgmt.managementgroups import ManagementGroupsAPI as ManagementGroupsClient
@@ -240,11 +256,11 @@ try:
from azure.mgmt.marketplaceordering import MarketplaceOrderingAgreements
from azure.mgmt.trafficmanager import TrafficManagerManagementClient
from azure.storage.blob import BlobServiceClient
- from msal.application import ClientApplication, ConfidentialClientApplication
from azure.mgmt.authorization import AuthorizationManagementClient
from azure.mgmt.sql import SqlManagementClient
from azure.mgmt.servicebus import ServiceBusManagementClient
from azure.mgmt.rdbms.postgresql import PostgreSQLManagementClient
+ from azure.mgmt.rdbms.postgresql_flexibleservers import PostgreSQLManagementClient as PostgreSQLFlexibleManagementClient
from azure.mgmt.rdbms.mysql import MySQLManagementClient
from azure.mgmt.rdbms.mariadb import MariaDBManagementClient
from azure.mgmt.containerregistry import ContainerRegistryManagementClient
@@ -257,7 +273,11 @@ try:
from azure.mgmt.iothub import models as IoTHubModels
from azure.mgmt.resource.locks import ManagementLockClient
from azure.mgmt.recoveryservicesbackup import RecoveryServicesBackupClient
- import azure.mgmt.recoveryservicesbackup.models as RecoveryServicesBackupModels
+ try:
+ # Older versions of the library exposed the modules at the root of the package
+ import azure.mgmt.recoveryservicesbackup.models as RecoveryServicesBackupModels
+ except ImportError:
+ import azure.mgmt.recoveryservicesbackup.activestamp.models as RecoveryServicesBackupModels
from azure.mgmt.search import SearchManagementClient
from azure.mgmt.datalake.store import DataLakeStoreAccountManagementClient
import azure.mgmt.datalake.store.models as DataLakeStoreAccountModel
@@ -266,6 +286,8 @@ try:
from azure.mgmt.datafactory import DataFactoryManagementClient
import azure.mgmt.datafactory.models as DataFactoryModel
from azure.identity._credentials import client_secret, user_password, certificate, managed_identity
+ from azure.identity import AzureCliCredential
+ from msgraph import GraphServiceClient
except ImportError as exc:
Authentication = object
@@ -415,6 +437,7 @@ class AzureRMModuleBase(object):
self._mysql_client = None
self._mariadb_client = None
self._postgresql_client = None
+ self._postgresql_flexible_client = None
self._containerregistry_client = None
self._containerinstance_client = None
self._containerservice_client = None
@@ -484,8 +507,8 @@ class AzureRMModuleBase(object):
'''
self.module.fail_json(msg=msg, **kwargs)
- def deprecate(self, msg, version=None):
- self.module.deprecate(msg, version)
+ def deprecate(self, msg, version=None, collection_name='azure.azcollection'):
+ self.module.deprecate(msg, version, collection_name=collection_name)
def log(self, msg, pretty_print=False):
if pretty_print:
@@ -675,11 +698,15 @@ class AzureRMModuleBase(object):
self.fail("Error {0} has a provisioning state of {1}. Expecting state to be {2}.".format(
azure_object.name, azure_object.provisioning_state, AZURE_SUCCESS_STATE))
- def get_blob_service_client(self, resource_group_name, storage_account_name):
+ def get_blob_service_client(self, resource_group_name, storage_account_name, auth_mode='key'):
try:
self.log("Getting storage account detail")
account = self.storage_client.storage_accounts.get_properties(resource_group_name=resource_group_name, account_name=storage_account_name)
- account_keys = self.storage_client.storage_accounts.list_keys(resource_group_name=resource_group_name, account_name=storage_account_name)
+ if auth_mode == 'login' and self.azure_auth.credentials.get('credential'):
+ credential = self.azure_auth.credentials['credential']
+ else:
+ account_keys = self.storage_client.storage_accounts.list_keys(resource_group_name=resource_group_name, account_name=storage_account_name)
+ credential = account_keys.keys[0].value
except Exception as exc:
self.fail("Error getting storage account detail for {0}: {1}".format(storage_account_name, str(exc)))
@@ -687,7 +714,7 @@ class AzureRMModuleBase(object):
self.log("Create blob service client")
return BlobServiceClient(
account_url=account.primary_endpoints.blob,
- credential=account_keys.keys[0].value,
+ credential=credential,
)
except Exception as exc:
self.fail("Error creating blob service client for storage account {0} - {1}".format(storage_account_name, str(exc)))
@@ -854,14 +881,17 @@ class AzureRMModuleBase(object):
# wrap basic strings in a dict that just defines the default
return dict(default_api_version=profile_raw)
- def get_graphrbac_client(self, tenant_id):
- cred = self.azure_auth.azure_credentials
- base_url = self.azure_auth._cloud_environment.endpoints.active_directory_graph_resource_id
- client = GraphRbacManagementClient(cred, tenant_id, base_url)
+ # The graphrbac has deprecated, migrate to msgraph
+ # def get_graphrbac_client(self, tenant_id):
+ # cred = self.azure_auth.azure_credentials
+ # base_url = self.azure_auth._cloud_environment.endpoints.active_directory_graph_resource_id
+ # client = GraphRbacManagementClient(cred, tenant_id, base_url)
+ # return client
- return client
+ def get_msgraph_client(self):
+ return GraphServiceClient(self.azure_auth.azure_credential_track2)
- def get_mgmt_svc_client(self, client_type, base_url=None, api_version=None, suppress_subscription_id=False, is_track2=False):
+ def get_mgmt_svc_client(self, client_type, base_url=None, api_version=None, suppress_subscription_id=False):
self.log('Getting management service client {0}'.format(client_type.__name__))
self.check_client_version(client_type)
@@ -883,16 +913,10 @@ class AzureRMModuleBase(object):
# Some management clients do not take a subscription ID as parameters.
if suppress_subscription_id:
- if is_track2:
- client_kwargs = dict(credential=self.azure_auth.azure_credential_track2, base_url=base_url, credential_scopes=[base_url + ".default"])
- else:
- client_kwargs = dict(credentials=self.azure_auth.azure_credentials, base_url=base_url)
+ client_kwargs = dict(credential=self.azure_auth.azure_credential_track2, base_url=base_url, credential_scopes=[base_url + ".default"])
else:
- if is_track2:
- client_kwargs = dict(credential=self.azure_auth.azure_credential_track2,
- subscription_id=mgmt_subscription_id, base_url=base_url, credential_scopes=[base_url + ".default"])
- else:
- client_kwargs = dict(credentials=self.azure_auth.azure_credentials, subscription_id=mgmt_subscription_id, base_url=base_url)
+ client_kwargs = dict(credential=self.azure_auth.azure_credential_track2,
+ subscription_id=mgmt_subscription_id, base_url=base_url, credential_scopes=[base_url + ".default"])
api_profile_dict = {}
@@ -926,13 +950,8 @@ class AzureRMModuleBase(object):
setattr(client, '_ansible_models', importlib.import_module(client_type.__module__).models)
client.models = types.MethodType(_ansible_get_models, client)
- if not is_track2:
- client.config = self.add_user_agent(client.config)
- if self.azure_auth._cert_validation_mode == 'ignore':
- client.config.session_configuration_callback = self._validation_ignore_callback
- else:
- if self.azure_auth._cert_validation_mode == 'ignore':
- client._config.session_configuration_callback = self._validation_ignore_callback
+ if self.azure_auth._cert_validation_mode == 'ignore':
+ client._config.session_configuration_callback = self._validation_ignore_callback
return client
@@ -992,7 +1011,6 @@ class AzureRMModuleBase(object):
if not self._storage_client:
self._storage_client = self.get_mgmt_svc_client(StorageManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-06-01')
return self._storage_client
@@ -1006,7 +1024,6 @@ class AzureRMModuleBase(object):
if not self._authorization_client:
self._authorization_client = self.get_mgmt_svc_client(AuthorizationManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2020-04-01-preview')
return self._authorization_client
@@ -1021,7 +1038,6 @@ class AzureRMModuleBase(object):
self._subscription_client = self.get_mgmt_svc_client(SubscriptionClient,
base_url=self._cloud_environment.endpoints.resource_manager,
suppress_subscription_id=True,
- is_track2=True,
api_version='2019-11-01')
return self._subscription_client
@@ -1036,7 +1052,6 @@ class AzureRMModuleBase(object):
self._management_group_client = self.get_mgmt_svc_client(ManagementGroupsClient,
base_url=self._cloud_environment.endpoints.resource_manager,
suppress_subscription_id=True,
- is_track2=True,
api_version='2020-05-01')
return self._management_group_client
@@ -1046,7 +1061,6 @@ class AzureRMModuleBase(object):
if not self._network_client:
self._network_client = self.get_mgmt_svc_client(NetworkManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-03-01')
return self._network_client
@@ -1061,7 +1075,6 @@ class AzureRMModuleBase(object):
if not self._resource_client:
self._resource_client = self.get_mgmt_svc_client(ResourceManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2019-10-01')
return self._resource_client
@@ -1076,7 +1089,6 @@ class AzureRMModuleBase(object):
if not self._image_client:
self._image_client = self.get_mgmt_svc_client(ComputeManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-04-01')
return self._image_client
@@ -1091,7 +1103,6 @@ class AzureRMModuleBase(object):
if not self._compute_client:
self._compute_client = self.get_mgmt_svc_client(ComputeManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-04-01')
return self._compute_client
@@ -1106,7 +1117,6 @@ class AzureRMModuleBase(object):
if not self._dns_client:
self._dns_client = self.get_mgmt_svc_client(DnsManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2018-05-01')
return self._dns_client
@@ -1121,7 +1131,6 @@ class AzureRMModuleBase(object):
if not self._private_dns_client:
self._private_dns_client = self.get_mgmt_svc_client(
PrivateDnsManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._private_dns_client
@@ -1136,7 +1145,6 @@ class AzureRMModuleBase(object):
if not self._web_client:
self._web_client = self.get_mgmt_svc_client(WebSiteManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-03-01')
return self._web_client
@@ -1146,7 +1154,6 @@ class AzureRMModuleBase(object):
if not self._containerservice_client:
self._containerservice_client = self.get_mgmt_svc_client(ContainerServiceClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2017-07-01')
return self._containerservice_client
@@ -1161,7 +1168,6 @@ class AzureRMModuleBase(object):
if not self._managedcluster_client:
self._managedcluster_client = self.get_mgmt_svc_client(ContainerServiceClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2022-02-01')
return self._managedcluster_client
@@ -1170,16 +1176,22 @@ class AzureRMModuleBase(object):
self.log('Getting SQL client')
if not self._sql_client:
self._sql_client = self.get_mgmt_svc_client(SqlManagementClient,
- base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True)
+ base_url=self._cloud_environment.endpoints.resource_manager)
return self._sql_client
@property
+ def postgresql_flexible_client(self):
+ self.log('Getting PostgreSQL client')
+ if not self._postgresql_flexible_client:
+ self._postgresql_flexible_client = self.get_mgmt_svc_client(PostgreSQLFlexibleManagementClient,
+ base_url=self._cloud_environment.endpoints.resource_manager)
+ return self._postgresql_flexible_client
+
+ @property
def postgresql_client(self):
self.log('Getting PostgreSQL client')
if not self._postgresql_client:
self._postgresql_client = self.get_mgmt_svc_client(PostgreSQLManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._postgresql_client
@@ -1188,7 +1200,6 @@ class AzureRMModuleBase(object):
self.log('Getting MySQL client')
if not self._mysql_client:
self._mysql_client = self.get_mgmt_svc_client(MySQLManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._mysql_client
@@ -1197,7 +1208,6 @@ class AzureRMModuleBase(object):
self.log('Getting MariaDB client')
if not self._mariadb_client:
self._mariadb_client = self.get_mgmt_svc_client(MariaDBManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._mariadb_client
@@ -1207,7 +1217,6 @@ class AzureRMModuleBase(object):
if not self._containerregistry_client:
self._containerregistry_client = self.get_mgmt_svc_client(ContainerRegistryManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-09-01')
return self._containerregistry_client
@@ -1218,7 +1227,6 @@ class AzureRMModuleBase(object):
if not self._containerinstance_client:
self._containerinstance_client = self.get_mgmt_svc_client(ContainerInstanceManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2018-06-01')
return self._containerinstance_client
@@ -1228,7 +1236,6 @@ class AzureRMModuleBase(object):
self.log('Getting marketplace agreement client')
if not self._marketplace_client:
self._marketplace_client = self.get_mgmt_svc_client(MarketplaceOrderingAgreements,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._marketplace_client
@@ -1237,7 +1244,6 @@ class AzureRMModuleBase(object):
self.log('Getting traffic manager client')
if not self._traffic_manager_management_client:
self._traffic_manager_management_client = self.get_mgmt_svc_client(TrafficManagerManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._traffic_manager_management_client
@@ -1247,8 +1253,7 @@ class AzureRMModuleBase(object):
if not self._monitor_autoscale_settings_client:
self._monitor_autoscale_settings_client = self.get_mgmt_svc_client(MonitorManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version="2015-04-01",
- is_track2=True)
+ api_version="2015-04-01")
return self._monitor_autoscale_settings_client
@property
@@ -1257,8 +1262,7 @@ class AzureRMModuleBase(object):
if not self._monitor_log_profiles_client:
self._monitor_log_profiles_client = self.get_mgmt_svc_client(MonitorManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version="2016-03-01",
- is_track2=True)
+ api_version="2016-03-01")
return self._monitor_log_profiles_client
@property
@@ -1267,8 +1271,7 @@ class AzureRMModuleBase(object):
if not self._monitor_diagnostic_settings_client:
self._monitor_diagnostic_settings_client = self.get_mgmt_svc_client(MonitorManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version="2021-05-01-preview",
- is_track2=True)
+ api_version="2021-05-01-preview")
return self._monitor_diagnostic_settings_client
@property
@@ -1276,7 +1279,6 @@ class AzureRMModuleBase(object):
self.log('Getting log analytics client')
if not self._log_analytics_client:
self._log_analytics_client = self.get_mgmt_svc_client(LogAnalyticsManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._log_analytics_client
@@ -1290,7 +1292,6 @@ class AzureRMModuleBase(object):
self.log('Getting servicebus client')
if not self._servicebus_client:
self._servicebus_client = self.get_mgmt_svc_client(ServiceBusManagementClient,
- is_track2=True,
api_version="2021-06-01-preview",
base_url=self._cloud_environment.endpoints.resource_manager)
return self._servicebus_client
@@ -1304,8 +1305,7 @@ class AzureRMModuleBase(object):
self.log('Getting automation client')
if not self._automation_client:
self._automation_client = self.get_mgmt_svc_client(AutomationClient,
- base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True)
+ base_url=self._cloud_environment.endpoints.resource_manager)
return self._automation_client
@property
@@ -1317,7 +1317,6 @@ class AzureRMModuleBase(object):
self.log('Getting iothub client')
if not self._IoThub_client:
self._IoThub_client = self.get_mgmt_svc_client(IotHubClient,
- is_track2=True,
api_version='2018-04-01',
base_url=self._cloud_environment.endpoints.resource_manager)
return self._IoThub_client
@@ -1332,7 +1331,6 @@ class AzureRMModuleBase(object):
if not self._lock_client:
self._lock_client = self.get_mgmt_svc_client(ManagementLockClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2016-09-01')
return self._lock_client
@@ -1346,7 +1344,6 @@ class AzureRMModuleBase(object):
self.log('Getting recovery services backup client')
if not self._recovery_services_backup_client:
self._recovery_services_backup_client = self.get_mgmt_svc_client(RecoveryServicesBackupClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._recovery_services_backup_client
@@ -1360,7 +1357,6 @@ class AzureRMModuleBase(object):
if not self._search_client:
self._search_client = self.get_mgmt_svc_client(SearchManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2020-08-01')
return self._search_client
@@ -1370,7 +1366,6 @@ class AzureRMModuleBase(object):
if not self._datalake_store_client:
self._datalake_store_client = self.get_mgmt_svc_client(DataLakeStoreAccountManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2016-11-01')
return self._datalake_store_client
@@ -1385,7 +1380,6 @@ class AzureRMModuleBase(object):
self._notification_hub_client = self.get_mgmt_svc_client(
NotificationHubsManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2016-03-01')
return self._notification_hub_client
@@ -1396,7 +1390,6 @@ class AzureRMModuleBase(object):
self._event_hub_client = self.get_mgmt_svc_client(
EventHubManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-11-01')
return self._event_hub_client
@@ -1405,7 +1398,6 @@ class AzureRMModuleBase(object):
self.log('Getting datafactory client...')
if not self._datafactory_client:
self._datafactory_client = self.get_mgmt_svc_client(DataFactoryManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
return self._datafactory_client
@@ -1425,7 +1417,8 @@ class AzureRMAuth(object):
def __init__(self, auth_source=None, profile=None, subscription_id=None, client_id=None, secret=None,
tenant=None, ad_user=None, password=None, cloud_environment='AzureCloud', cert_validation_mode='validate',
api_profile='latest', adfs_authority_url=None, fail_impl=None, is_ad_resource=False,
- x509_certificate_path=None, thumbprint=None, track1_cred=False, **kwargs):
+ x509_certificate_path=None, thumbprint=None, track1_cred=False,
+ disable_instance_discovery=False, **kwargs):
if fail_impl:
self._fail_impl = fail_impl
@@ -1448,7 +1441,8 @@ class AzureRMAuth(object):
api_profile=api_profile,
adfs_authority_url=adfs_authority_url,
x509_certificate_path=x509_certificate_path,
- thumbprint=thumbprint)
+ thumbprint=thumbprint,
+ disable_instance_discovery=disable_instance_discovery)
if not self.credentials:
if HAS_AZURE_CLI_CORE:
@@ -1467,6 +1461,12 @@ class AzureRMAuth(object):
if self._cert_validation_mode not in ['validate', 'ignore']:
self.fail('invalid cert_validation_mode: {0}'.format(self._cert_validation_mode))
+ # Disable instance discovery: module-arg, credential profile, env, "False"
+ self._disable_instance_discovery = disable_instance_discovery or \
+ self.credentials.get('disable_instance_discovery') or \
+ self._get_env('disable_instance_discovery') or \
+ False
+
# if cloud_environment specified, look up/build Cloud object
raw_cloud_env = self.credentials.get('cloud_environment')
if self.credentials.get('credentials') is not None and raw_cloud_env is not None:
@@ -1504,84 +1504,50 @@ class AzureRMAuth(object):
if self.credentials.get('auth_source') == 'msi':
# MSI Credentials
- if is_ad_resource or track1_cred:
- self.azure_credentials = self.credentials['credentials']
- self.azure_credential_track2 = self.credentials['credential']
+ self.azure_credential_track2 = self.credentials['credentials']
elif self.credentials.get('credentials') is not None:
# AzureCLI credentials
- if is_ad_resource or track1_cred:
- self.azure_credentials = self.credentials['credentials']
self.azure_credential_track2 = self.credentials['credentials']
elif self.credentials.get('client_id') is not None and \
self.credentials.get('secret') is not None and \
self.credentials.get('tenant') is not None:
-
- graph_resource = self._cloud_environment.endpoints.active_directory_graph_resource_id
- rm_resource = self._cloud_environment.endpoints.resource_manager
- if is_ad_resource or track1_cred:
- self.azure_credentials = ServicePrincipalCredentials(client_id=self.credentials['client_id'],
- secret=self.credentials['secret'],
- tenant=self.credentials['tenant'],
- cloud_environment=self._cloud_environment,
- resource=graph_resource if self.is_ad_resource else rm_resource,
- verify=self._cert_validation_mode == 'validate')
self.azure_credential_track2 = client_secret.ClientSecretCredential(client_id=self.credentials['client_id'],
client_secret=self.credentials['secret'],
tenant_id=self.credentials['tenant'],
- authority=self._adfs_authority_url)
+ authority=self._adfs_authority_url,
+ disable_instance_discovery=self._disable_instance_discovery)
elif self.credentials.get('client_id') is not None and \
self.credentials.get('tenant') is not None and \
self.credentials.get('thumbprint') is not None and \
self.credentials.get('x509_certificate_path') is not None:
- if is_ad_resource or track1_cred:
- self.azure_credentials = self.acquire_token_with_client_certificate(
- self._adfs_authority_url,
- self.credentials['x509_certificate_path'],
- self.credentials['thumbprint'],
- self.credentials['client_id'],
- self.credentials['tenant'])
-
self.azure_credential_track2 = certificate.CertificateCredential(tenant_id=self.credentials['tenant'],
client_id=self.credentials['client_id'],
certificate_path=self.credentials['x509_certificate_path'],
- authority=self._adfs_authority_url)
+ authority=self._adfs_authority_url,
+ disable_instance_discovery=self._disable_instance_discovery)
elif self.credentials.get('ad_user') is not None and \
self.credentials.get('password') is not None and \
self.credentials.get('client_id') is not None and \
self.credentials.get('tenant') is not None:
- if is_ad_resource or track1_cred:
- self.azure_credentials = self.acquire_token_with_username_password(
- self._adfs_authority_url,
- self.credentials['ad_user'],
- self.credentials['password'],
- self.credentials['client_id'],
- self.credentials['tenant'])
self.azure_credential_track2 = user_password.UsernamePasswordCredential(username=self.credentials['ad_user'],
password=self.credentials['password'],
tenant_id=self.credentials.get('tenant'),
client_id=self.credentials.get('client_id'),
- authority=self._adfs_authority_url)
+ authority=self._adfs_authority_url,
+ disable_instance_discovery=self._disable_instance_discovery)
elif self.credentials.get('ad_user') is not None and self.credentials.get('password') is not None:
- tenant = self.credentials.get('tenant')
- if not tenant:
- tenant = 'common' # SDK default
-
- if is_ad_resource or track1_cred:
- self.azure_credentials = UserPassCredentials(self.credentials['ad_user'],
- self.credentials['password'],
- tenant=tenant,
- cloud_environment=self._cloud_environment,
- verify=self._cert_validation_mode == 'validate')
-
- client_id = self.credentials.get('client_id', '04b07795-8ddb-461a-bbee-02f9e1bf7b46')
+ client_id = self.credentials.get('client_id')
+ if client_id is None:
+ client_id = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'
self.azure_credential_track2 = user_password.UsernamePasswordCredential(username=self.credentials['ad_user'],
password=self.credentials['password'],
tenant_id=self.credentials.get('tenant', 'organizations'),
client_id=client_id,
- authority=self._adfs_authority_url)
+ authority=self._adfs_authority_url,
+ disable_instance_discovery=self._disable_instance_discovery)
else:
self.fail("Failed to authenticate with provided credentials. Some attributes were missing. "
@@ -1640,7 +1606,7 @@ class AzureRMAuth(object):
except Exception as exc:
self.fail("cloud_environment {0} could not be resolved: {1}".format(_cloud_environment, str(exc)), exception=traceback.format_exc())
- credentials = MSIAuthentication(client_id=client_id, cloud_environment=cloud_environment)
+ client_id = client_id or self._get_env('client_id')
credential = managed_identity.ManagedIdentityCredential(client_id=client_id, cloud_environment=cloud_environment)
subscription_id = subscription_id or self._get_env('subscription_id')
if not subscription_id:
@@ -1653,8 +1619,7 @@ class AzureRMAuth(object):
self.fail("Failed to get MSI token: {0}. "
"Please check whether your machine enabled MSI or grant access to any subscription.".format(str(exc)))
return {
- 'credentials': credentials,
- 'credential': credential,
+ 'credentials': credential,
'subscription_id': subscription_id,
'cloud_environment': cloud_environment,
'auth_source': 'msi'
@@ -1669,12 +1634,13 @@ class AzureRMAuth(object):
except Exception as exc:
self.fail("Failed to load CLI profile {0}.".format(str(exc)))
- credentials, subscription_id, tenant = profile.get_login_credentials(
- subscription_id=subscription_id, resource=resource)
+ cred, subscription_id, tenant = profile.get_login_credentials(
+ subscription_id=subscription_id)
cloud_environment = get_cli_active_cloud()
+ az_cli = AzureCliCredential()
cli_credentials = {
- 'credentials': credentials,
+ 'credentials': az_cli if self.is_ad_resource else cred,
'subscription_id': subscription_id,
'cloud_environment': cloud_environment
}
@@ -1762,42 +1728,6 @@ class AzureRMAuth(object):
return None
- def acquire_token_with_username_password(self, authority, username, password, client_id, tenant):
- authority_uri = authority
-
- if tenant is not None:
- authority_uri = authority + '/' + tenant
-
- context = ClientApplication(client_id=client_id, authority=authority_uri)
- base_url = self._cloud_environment.endpoints.resource_manager
- if not base_url.endswith("/"):
- base_url += "/"
- scopes = [base_url + ".default"]
- token_response = context.acquire_token_by_username_password(username, password, scopes)
-
- return AADTokenCredentials(token_response)
-
- def acquire_token_with_client_certificate(self, authority, x509_private_key_path, thumbprint, client_id, tenant):
- authority_uri = authority
-
- if tenant is not None:
- authority_uri = authority + '/' + tenant
-
- x509_private_key = None
- with open(x509_private_key_path, 'r') as pem_file:
- x509_private_key = pem_file.read()
-
- base_url = self._cloud_environment.endpoints.resource_manager
- if not base_url.endswith("/"):
- base_url += "/"
- scopes = [base_url + ".default"]
- client_credential = {"thumbprint": thumbprint, "private_key": x509_private_key}
- context = ConfidentialClientApplication(client_id=client_id, authority=authority_uri, client_credential=client_credential)
-
- token_response = context.acquire_token_for_client(scopes=scopes)
-
- return AADTokenCredentials(token_response)
-
def log(self, msg, pretty_print=False):
pass
# Use only during module development
diff --git a/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common_rest.py b/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common_rest.py
index 6acb1e7b9..bc740824f 100644
--- a/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common_rest.py
+++ b/ansible_collections/azure/azcollection/plugins/module_utils/azure_rm_common_rest.py
@@ -82,7 +82,7 @@ class GenericRestClient(object):
response = self._client.send_request(request, **operation_config)
if response.status_code not in expected_status_codes:
- exp = SendRequestException(response, response.status_code)
+ exp = SendRequestException(response.text(), response.status_code)
raise exp
elif response.status_code == 202 and polling_timeout > 0:
def get_long_running_output(response):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_accesstoken_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_accesstoken_info.py
new file mode 100644
index 000000000..cf9569868
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_accesstoken_info.py
@@ -0,0 +1,126 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2023 Patrick Uiterwijk <@puiterwijk>
+#
+# 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: azure_rm_accesstoken_info
+
+version_added: "1.19.0"
+
+short_description: Get Azure API access token
+
+description:
+ - Get an access token for Azure APIs.
+
+options:
+ scopes:
+ description:
+ - The scopes to request.
+ type: list
+ elements: str
+ required: True
+ claims:
+ description:
+ - Additional claims required in the token.
+ type: list
+ elements: str
+ token_tenant_id:
+ description:
+ - Tenant to include in the token request.
+ type: str
+ enable_cae:
+ description:
+ - Whether to enable Continuous Access Evaluation (CAE) for the requested token.
+ default: false
+ type: bool
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - Patrick Uiterwijk (@puiterwijk)
+'''
+
+EXAMPLES = '''
+- name: Get access token for Microsoft Graph
+ azure.azcollection.azure_rm_accesstoken_info:
+ scopes:
+ - https://graph.microsoft.com/.default
+'''
+
+RETURN = '''
+access_token:
+ description:
+ - API access token.
+ returned: success
+ type: str
+ sample: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.L8i6g3PfcHlioHCCPURC9pmXT7gdJpx3kOoyAfNUwCc
+expires_on:
+ description:
+ - Timestamp the token expires on.
+ returned: success
+ type: int
+ sample: 1699337824
+'''
+
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+
+class AzureRMAccessToken(AzureRMModuleBase):
+
+ def __init__(self):
+
+ self.module_arg_spec = dict(
+ scopes=dict(type='list', elements='str', required=True),
+ claims=dict(type='list', elements='str'),
+ token_tenant_id=dict(type='str'),
+ enable_cae=dict(type='bool', default=False),
+ )
+
+ self.scopes = None
+ self.claims = None
+ self.token_tenant_id = None
+ self.enable_cae = False
+
+ self.results = dict(changed=False)
+
+ super(AzureRMAccessToken, self).__init__(derived_arg_spec=self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=False,
+ is_ad_resource=False)
+
+ def exec_module(self, **kwargs):
+ for key in list(self.module_arg_spec.keys()):
+ setattr(self, key, kwargs[key])
+
+ claims = None
+ if self.claims is not None:
+ claims = ' '.join(self.claims)
+
+ cred = self.azure_auth.azure_credential_track2
+ token = cred.get_token(
+ *self.scopes,
+ claims=claims,
+ tenant_id=self.token_tenant_id,
+ enable_cae=self.enable_cae,
+ )
+
+ self.results['access_token'] = token.token
+ self.results['expires_on'] = token.expires_on
+ return self.results
+
+
+def main():
+ AzureRMAccessToken()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_account_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_account_info.py
index 5061604e2..a3f2109bd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_account_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_account_info.py
@@ -102,14 +102,13 @@ account_info:
try:
- from azure.graphrbac import GraphRbacManagementClient
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.education.me.user.user_request_builder import UserRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
-from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMAuth
class AzureRMAccountInfo(AzureRMModuleBase):
@@ -126,19 +125,15 @@ class AzureRMAccountInfo(AzureRMModuleBase):
# Different return info is gathered using 2 different clients
# 1. All except "user" section of the return value uses azure.mgmt.subsctiption.operations.subscriptionoperations
- # 2. "user" section of the return value uses different client (graphrbac)
+ # 2. "user" section of the return value uses different client (GraphServiceClient)
super(AzureRMAccountInfo, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=True,
supports_tags=False,
- is_ad_resource=False)
+ is_ad_resource=True)
def exec_module(self, **kwargs):
-
- result = []
- result = self.list_items()
-
- self.results['account_info'] = result
+ self.results['account_info'] = self.list_items()
return self.results
def list_items(self):
@@ -179,7 +174,7 @@ class AzureRMAccountInfo(AzureRMModuleBase):
results['state'] = subscription_list_response[0].state
results['managedByTenants'] = self.get_managed_by_tenants_list(subscription_list_response[0].managed_by_tenants)
results['environmentName'] = self.azure_auth._cloud_environment.name
- results['user'] = self.get_aduser_info(subscription_list_response[0].tenant_id)
+ results['user'] = self.get_aduser_info()
return results
@@ -187,33 +182,32 @@ class AzureRMAccountInfo(AzureRMModuleBase):
return [dict(tenantId=item.tenant_id) for item in object_list]
- def get_aduser_info(self, tenant_id):
+ def get_aduser_info(self):
- # Create GraphRbacManagementClient for getting
+ # Create GraphServiceClient for getting
# "user": {
# "name": "mandar123456@abcdefg.onmicrosoft.com",
- # "type": "user"self.
+ # "type": "Member"
# }
- # Makes use of azure graphrbac
- # https://docs.microsoft.com/en-us/python/api/overview/azure/microsoft-graph?view=azure-python#client-library
+ # Makes use of azure MSGraph
+ # https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http
user = {}
- self.azure_auth_graphrbac = AzureRMAuth(is_ad_resource=True)
- cred = self.azure_auth_graphrbac.azure_credentials
- base_url = self.azure_auth_graphrbac._cloud_environment.endpoints.active_directory_graph_resource_id
- client = GraphRbacManagementClient(cred, tenant_id, base_url)
-
- try:
- user_info = client.signed_in_user.get()
- user['name'] = user_info.user_principal_name
- user['type'] = user_info.object_type
-
- except GraphErrorException as e:
- self.fail("failed to get ad user info {0}".format(str(e)))
+ user_info = asyncio.get_event_loop().run_until_complete(self.getAccount())
+ user['name'] = user_info.user_principal_name
+ user['type'] = user_info.user_type
return user
+ async def getAccount(self):
+ return await self.get_msgraph_client().me.get(
+ request_configuration=UserRequestBuilder.UserRequestBuilderGetRequestConfiguration(
+ query_parameters=UserRequestBuilder.UserRequestBuilderGetQueryParameters(
+ select=["userType", "userPrincipalName", "postalCode", "identities"], ),
+ )
+ )
+
def main():
AzureRMAccountInfo()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication.py
index 9f21728fc..b428463aa 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication.py
@@ -5,8 +5,8 @@
# 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
+__metaclass__ = type
DOCUMENTATION = '''
---
@@ -20,12 +20,6 @@ description:
- Manage Azure Active Directory application.
options:
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
-
app_id:
description:
- Application ID.
@@ -34,6 +28,7 @@ options:
display_name:
description:
- The display name of the application.
+ required: true
type: str
app_roles:
@@ -74,11 +69,24 @@ options:
- Any other character, including the space character, are not allowed.
type: str
- available_to_other_tenants:
+ sign_in_audience:
description:
- The application can be used from any Azure AD tenants.
- type: bool
+ - Microsoft Graph SDK deprecate I(available_to_other_tenants), replace by I(sign_in_audience).
+ - Refer to link U(https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-property-differences#application-property-differences)
+ type: str
+ choices:
+ - AzureADMyOrg
+ - AzureADMultipleOrgs
+ - AzureADandPersonalMicrosoftAccount
+ - PersonalMicrosoftAccount
+ available_to_other_tenants:
+ description:
+ - (Deprecated) The application can be used from any Azure AD tenants.
+ - This parameter was not supported after the migration to Microsoft Graph and was replaced by I(sign_in_audience).
+ - It will deprecated in next version(V3.0.0).
+ type: bool
credential_description:
description:
- The description of the password.
@@ -170,8 +178,27 @@ options:
- App password, aka 'client secret'.
type: str
- reply_urls:
+ web_reply_urls:
description:
+ - The web redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ - The value does not need to be a physical endpoint, but must be a valid URI.
+ type: list
+ elements: str
+ aliases:
+ - reply_urls
+
+ spa_reply_urls:
+ description:
+ - The spa redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ - The value does not need to be a physical endpoint, but must be a valid URI.
+ type: list
+ elements: str
+
+ public_client_reply_urls:
+ description:
+ - The public client redirect urls.
- Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
- The value does not need to be a physical endpoint, but must be a valid URI.
type: list
@@ -236,14 +263,24 @@ author:
EXAMPLES = '''
- name: Create ad application
azure_rm_adapplication:
- tenant: "{{ tenant_id }}"
display_name: "{{ display_name }}"
+- name: Create ad application with multi redirect urls
+ azure_rm_adapplication:
+ display_name: "{{ display_name }}"
+ web_reply_urls:
+ - https://web01.com
+ spa_reply_urls:
+ - https://spa01.com
+ - https://spa02.com
+ public_client_reply_urls:
+ - https://public01.com
+ - https://public02.com
+
- name: Create application with more parameter
azure_rm_adapplication:
- tenant: "{{ tenant_id }}"
display_name: "{{ display_name }}"
- available_to_other_tenants: false
+ sign_in_audience: AzureADandPersonalMicrosoftAccount
credential_description: "for test"
end_date: 2021-10-01
start_date: 2021-05-18
@@ -251,7 +288,6 @@ EXAMPLES = '''
- name: delete ad application
azure_rm_adapplication:
- tenant: "{{ tenant_id }}"
app_id: "{{ app_id }}"
state: absent
'''
@@ -281,12 +317,18 @@ output:
returned: always
type: str
sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ sign_in_audience:
+ description:
+ - The application can be used from any Azure AD tenants.
+ returned: always
+ type: str
+ sample: AzureADandPersonalMicrosoftAccount
available_to_other_tenants:
description:
- The application can be used from any Azure AD tenants.
returned: always
- type: bool
- sample: false
+ type: str
+ sample: AzureADandPersonalMicrosoftAccount
homepage:
description:
- The url where users can sign in and use your app.
@@ -311,8 +353,23 @@ output:
returned: always
type: list
sample: []
- reply_urls:
+ public_client_reply_urls:
+ description:
+ - The public client redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ returned: always
+ type: list
+ sample: []
+ web_reply_urls:
description:
+ - The web redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ returned: always
+ type: list
+ sample: []
+ spa_reply_urls:
+ description:
+ - The spa redirect urls.
- Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
returned: always
type: list
@@ -322,17 +379,22 @@ output:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt
try:
- from azure.graphrbac.models import GraphErrorException
import datetime
- from dateutil.relativedelta import relativedelta
import dateutil.parser
- from azure.graphrbac.models import ApplicationCreateParameters
import uuid
- from azure.graphrbac.models import ResourceAccess
- from azure.graphrbac.models import RequiredResourceAccess
- from azure.graphrbac.models import AppRole
- from azure.graphrbac.models import PasswordCredential, KeyCredential
- from azure.graphrbac.models import ApplicationUpdateParameters
+ import asyncio
+ from dateutil.relativedelta import relativedelta
+ from msgraph.generated.applications.applications_request_builder import ApplicationsRequestBuilder
+ from msgraph.generated.models.application import Application
+ from msgraph.generated.models.password_credential import PasswordCredential
+ from msgraph.generated.models.key_credential import KeyCredential
+ from msgraph.generated.models.required_resource_access import RequiredResourceAccess
+ from msgraph.generated.models.resource_access import ResourceAccess
+ from msgraph.generated.models.app_role import AppRole
+ from msgraph.generated.models.web_application import WebApplication
+ from msgraph.generated.models.spa_application import SpaApplication
+ from msgraph.generated.models.public_client_application import PublicClientApplication
+ from msgraph.generated.models.implicit_grant_settings import ImplicitGrantSettings
except ImportError:
# This is handled in azure_rm_common
pass
@@ -396,31 +458,40 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
def __init__(self):
self.module_arg_spec = dict(
- tenant=dict(type='str', required=True),
app_id=dict(type='str'),
- display_name=dict(type='str'),
+ display_name=dict(type='str', required=True),
app_roles=dict(type='list', elements='dict', options=app_role_spec),
- available_to_other_tenants=dict(type='bool'),
+ sign_in_audience=dict(
+ type='str',
+ choices=['AzureADMyOrg', 'AzureADMultipleOrgs', 'AzureADandPersonalMicrosoftAccount', 'PersonalMicrosoftAccount']
+ ),
+ available_to_other_tenants=dict(
+ type='bool',
+ removed_in_version='3.0.0',
+ removed_from_collection='azure.azcollection'
+ ),
credential_description=dict(type='str'),
end_date=dict(type='str'),
homepage=dict(type='str'),
allow_guests_sign_in=dict(type='bool'),
identifier_uris=dict(type='list', elements='str'),
- key_type=dict(type='str', default='AsymmetricX509Cert', choices=['AsymmetricX509Cert', 'Password', 'Symmetric']),
+ key_type=dict(type='str', default='AsymmetricX509Cert',
+ choices=['AsymmetricX509Cert', 'Password', 'Symmetric']),
key_usage=dict(type='str', default='Verify', choices=['Sign', 'Verify']),
key_value=dict(type='str', no_log=True),
native_app=dict(type='bool'),
oauth2_allow_implicit_flow=dict(type='bool'),
optional_claims=dict(type='list', elements='dict', options=optional_claims_spec),
password=dict(type='str', no_log=True),
- reply_urls=dict(type='list', elements='str'),
+ public_client_reply_urls=dict(type='list', elements='str'),
+ web_reply_urls=dict(type='list', elements='str', aliases=['reply_urls']),
+ spa_reply_urls=dict(type='list', elements='str'),
start_date=dict(type='str'),
required_resource_accesses=dict(type='list', elements='dict', options=required_resource_accesses_spec),
state=dict(type='str', default='present', choices=['present', 'absent']),
)
self.state = None
- self.tenant = None
self.app_id = None
self.display_name = None
self.app_roles = None
@@ -436,11 +507,15 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
self.oauth2_allow_implicit_flow = None
self.optional_claims = None
self.password = None
- self.reply_urls = None
+ self.public_client_reply_urls = None
+ self.spa_reply_urls = None
+ self.web_reply_urls = None
self.start_date = None
self.required_resource_accesses = None
self.allow_guests_sign_in = None
self.results = dict(changed=False)
+ self._client = None
+ self.sign_in_audience = None
super(AzureRMADApplication, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=False,
@@ -448,12 +523,11 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
is_ad_resource=True)
def exec_module(self, **kwargs):
-
+ self._client = self.get_msgraph_client()
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
response = self.get_resource()
-
if response:
if self.state == 'present':
if self.check_update(response):
@@ -475,90 +549,113 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
if self.identifier_uris:
self.fail("'identifier_uris' is not required for creating a native application")
else:
- password_creds, key_creds = self.build_application_creds(self.password, self.key_value, self.key_type, self.key_usage,
- self.start_date, self.end_date, self.credential_description)
+ password_creds, key_creds = self.build_application_creds(self.password, self.key_value, self.key_type,
+ self.key_usage,
+ self.start_date, self.end_date,
+ self.credential_description)
if self.required_resource_accesses:
required_accesses = self.build_application_accesses(self.required_resource_accesses)
if self.app_roles:
app_roles = self.build_app_roles(self.app_roles)
- client = self.get_graphrbac_client(self.tenant)
- app_create_param = ApplicationCreateParameters(available_to_other_tenants=self.available_to_other_tenants,
- display_name=self.display_name,
- identifier_uris=self.identifier_uris,
- homepage=self.homepage,
- reply_urls=self.reply_urls,
- key_credentials=key_creds,
- password_credentials=password_creds,
- oauth2_allow_implicit_flow=self.oauth2_allow_implicit_flow,
- required_resource_access=required_accesses,
- app_roles=app_roles,
- allow_guests_sign_in=self.allow_guests_sign_in,
- optional_claims=self.optional_claims)
- response = client.applications.create(app_create_param)
+ create_app = Application(
+ sign_in_audience=self.sign_in_audience,
+ web=WebApplication(
+ home_page_url=self.homepage,
+ redirect_uris=self.web_reply_urls,
+ implicit_grant_settings=ImplicitGrantSettings(
+ enable_access_token_issuance=self.oauth2_allow_implicit_flow,
+ ),
+ ),
+ spa=SpaApplication(redirect_uris=self.spa_reply_urls),
+ public_client=PublicClientApplication(redirect_uris=self.public_client_reply_urls),
+ display_name=self.display_name,
+ identifier_uris=self.identifier_uris,
+ key_credentials=key_creds,
+ password_credentials=password_creds,
+ required_resource_access=required_accesses,
+ app_roles=app_roles,
+ optional_claims=self.optional_claims
+ # allow_guests_sign_in=self.allow_guests_sign_in,
+ )
+ response = asyncio.get_event_loop().run_until_complete(self.create_application(create_app))
self.results['changed'] = True
self.results.update(self.to_dict(response))
return response
- except GraphErrorException as ge:
- self.fail("Error creating application, display_name {0} - {1}".format(self.display_name, str(ge)))
+ except Exception as ge:
+ self.fail("Error creating application, display_name: {0} - {1}".format(self.display_name, str(ge)))
def update_resource(self, old_response):
try:
- client = self.get_graphrbac_client(self.tenant)
key_creds, password_creds, required_accesses, app_roles, optional_claims = None, None, None, None, None
if self.native_app:
if self.identifier_uris:
self.fail("'identifier_uris' is not required for creating a native application")
else:
- password_creds, key_creds = self.build_application_creds(self.password, self.key_value, self.key_type, self.key_usage,
- self.start_date, self.end_date, self.credential_description)
+ password_creds, key_creds = self.build_application_creds(self.password, self.key_value, self.key_type,
+ self.key_usage,
+ self.start_date, self.end_date,
+ self.credential_description)
if self.required_resource_accesses:
required_accesses = self.build_application_accesses(self.required_resource_accesses)
if self.app_roles:
app_roles = self.build_app_roles(self.app_roles)
- app_update_param = ApplicationUpdateParameters(available_to_other_tenants=self.available_to_other_tenants,
- display_name=self.display_name,
- identifier_uris=self.identifier_uris,
- homepage=self.homepage,
- reply_urls=self.reply_urls,
- key_credentials=key_creds,
- password_credentials=password_creds,
- oauth2_allow_implicit_flow=self.oauth2_allow_implicit_flow,
- required_resource_access=required_accesses,
- allow_guests_sign_in=self.allow_guests_sign_in,
- app_roles=app_roles,
- optional_claims=self.optional_claims)
- client.applications.patch(old_response['object_id'], app_update_param)
+
+ app_update_param = Application(
+ sign_in_audience=self.sign_in_audience,
+ web=WebApplication(
+ home_page_url=self.homepage,
+ redirect_uris=self.web_reply_urls,
+ implicit_grant_settings=ImplicitGrantSettings(
+ enable_access_token_issuance=self.oauth2_allow_implicit_flow,
+ ),
+ ),
+ spa=SpaApplication(redirect_uris=self.spa_reply_urls),
+ public_client=PublicClientApplication(redirect_uris=self.public_client_reply_urls),
+ display_name=self.display_name,
+ identifier_uris=self.identifier_uris,
+ key_credentials=key_creds,
+ password_credentials=password_creds,
+ required_resource_access=required_accesses,
+ # allow_guests_sign_in=self.allow_guests_sign_in,
+ app_roles=app_roles,
+ optional_claims=self.optional_claims)
+ asyncio.get_event_loop().run_until_complete(self.update_application(
+ obj_id=old_response['object_id'], update_app=app_update_param))
+
self.results['changed'] = True
self.results.update(self.get_resource())
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("Error updating the application app_id {0} - {1}".format(self.app_id, str(ge)))
def delete_resource(self, response):
try:
- client = self.get_graphrbac_client(self.tenant)
- client.applications.delete(response.get('object_id'))
+ asyncio.get_event_loop().run_until_complete(self.delete_application(response.get('object_id')))
self.results['changed'] = True
return True
- except GraphErrorException as ge:
- self.fail("Error deleting application app_id {0} display_name {1} - {2}".format(self.app_id, self.display_name, str(ge)))
+ except Exception as ge:
+ self.fail(
+ "Error deleting application app_id {0} display_name {1} - {2}".format(self.app_id, self.display_name,
+ str(ge)))
def get_resource(self):
try:
- client = self.get_graphrbac_client(self.tenant)
existing_apps = []
if self.app_id:
- existing_apps = list(client.applications.list(filter="appId eq '{0}'".format(self.app_id)))
+ ret = asyncio.get_event_loop().run_until_complete(self.get_application_by_app_id(self.app_id))
+ existing_apps = ret.value
+
if not existing_apps:
return False
result = existing_apps[0]
return self.to_dict(result)
- except GraphErrorException as ge:
- self.log("Did not find the graph instance instance {0} - {1}".format(self.app_id, str(ge)))
- return False
+ except Exception as ge:
+ self.fail(ge)
+ # self.log("Did not find the graph instance instance {0} - {1}".format(self.app_id, str(ge)))
+ # return False
def check_update(self, response):
for key in list(self.module_arg_spec.keys()):
@@ -575,19 +672,22 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
'is_enabled': app_role.is_enabled,
'value': app_role.value,
"description": app_role.description
- }for app_role in object.app_roles]
+ } for app_role in object.app_roles]
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
app_roles=app_roles,
- available_to_other_tenants=object.available_to_other_tenants,
- homepage=object.homepage,
+ available_to_other_tenants=object.sign_in_audience,
+ sign_in_audience=object.sign_in_audience,
+ homepage=object.web.home_page_url,
identifier_uris=object.identifier_uris,
- oauth2_allow_implicit_flow=object.oauth2_allow_implicit_flow,
+ oauth2_allow_implicit_flow=object.web.implicit_grant_settings.enable_access_token_issuance,
optional_claims=object.optional_claims,
- allow_guests_sign_in=object.allow_guests_sign_in,
- reply_urls=object.reply_urls
+ # allow_guests_sign_in=object.allow_guests_sign_in,
+ web_reply_urls=object.web.redirect_uris,
+ spa_reply_urls=object.spa.redirect_uris,
+ public_client_reply_urls=object.public_client.redirect_uris
)
def build_application_creds(self, password=None, key_value=None, key_type=None, key_usage=None,
@@ -615,14 +715,16 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
password_creds = None
key_creds = None
if password:
- password_creds = [PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(self.gen_guid()),
- value=password, custom_key_identifier=custom_key_id)]
+ password_creds = [PasswordCredential(start_date_time=start_date, end_date_time=end_date,
+ key_id=self.gen_guid(), secret_text=password,
+ custom_key_identifier=custom_key_id)] # value ? secret_text
elif key_value:
key_creds = [
- KeyCredential(start_date=start_date, end_date=end_date, key_id=str(self.gen_guid()), value=key_value,
+ KeyCredential(start_date_time=start_date, end_date_time=end_date, key_id=self.gen_guid(), key=key_value,
+ # value ? key
usage=key_usage, type=key_type, custom_key_identifier=custom_key_id)]
- return (password_creds, key_creds)
+ return password_creds, key_creds
def encode_custom_key_description(self, key_description):
# utf16 is used by AAD portal. Do not change it to other random encoding
@@ -640,7 +742,6 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
self.log('Getting "requiredResourceAccess" from a full manifest')
required_resource_accesses = required_resource_accesses.get('required_resource_access', [])
for x in required_resource_accesses:
-
accesses = [ResourceAccess(id=y['id'], type=y['type']) for y in x['resource_access']]
required_accesses.append(RequiredResourceAccess(resource_app_id=x['resource_app_id'],
resource_access=accesses))
@@ -657,10 +758,34 @@ class AzureRMADApplication(AzureRMModuleBaseExt):
role = AppRole(id=x.get('id', None) or self.gen_guid(),
allowed_member_types=x.get('allowed_member_types', None),
description=x.get('description', None), display_name=x.get('display_name', None),
- is_enabled=x.get('is_enabled', None), value=x.get('value', None))
+ is_enabled=x.get('is_enabled', None), value=x.get('value', None)) # value ? additional_data
result.append(role)
return result
+ async def create_application(self, creat_app):
+ return await self._client.applications.post(body=creat_app)
+
+ async def update_application(self, obj_id, update_app):
+ return await self._client.applications.by_application_id(obj_id).patch(body=update_app)
+
+ async def get_application_by_app_id(self, app_id):
+ request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
+ query_parameters=ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
+ filter=(" appId eq '{0}'".format(app_id)), ),
+ )
+
+ return await self._client.applications.get(request_configuration=request_configuration)
+
+ async def delete_application(self, obj_id):
+ await self._client.applications.by_application_id(obj_id).delete()
+
+ async def get_applications(self, filters):
+ request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
+ query_parameters=ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
+ filter=(' and '.join(filters))
+ ))
+ return await self._client.applications.get(request_configuration=request_configuration)
+
def main():
AzureRMADApplication()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication_info.py
index 939058815..167b82552 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adapplication_info.py
@@ -6,8 +6,8 @@
from __future__ import absolute_import, division, print_function
-__metaclass__ = type
+__metaclass__ = type
DOCUMENTATION = '''
module: azure_rm_adapplication_info
@@ -24,18 +24,17 @@ options:
description:
- The application ID.
type: str
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
object_id:
description:
- - It's application's object ID.
+ - The application's object ID.
type: str
identifier_uri:
description:
- - It's identifier_uri's object ID.
+ - The identifier_uri's object ID.
+ type: str
+ app_display_name:
+ description:
+ - The applications' Name.
type: str
extends_documentation_fragment:
@@ -45,23 +44,25 @@ author:
haiyuan_zhang (@haiyuazhang)
Fred-sun (@Fred-sun)
guopeng_lin (@guopenglin)
+ Xu Zhang (@xuzhang)
'''
EXAMPLES = '''
- name: get ad app info by App ID
azure_rm_adapplication_info:
app_id: "{{ app_id }}"
- tenant: "{{ tenant_id }}"
- name: get ad app info ---- by object ID
azure_rm_adapplication_info:
object_id: "{{ object_id }}"
- tenant: "{{ tenant_id }}"
- name: get ad app info ---- by identifier uri
azure_rm_adapplication_info:
identifier_uri: "{{ identifier_uri }}"
- tenant: "{{ tenant_id }}"
+
+- name: get ad app info ---- by display name
+ azure_rm_adapplication_info:
+ app_display_name: "{{ display_name }}"
'''
RETURN = '''
@@ -95,12 +96,47 @@ applications:
returned: always
type: str
sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ sign_in_audience:
+ description:
+ - The application can be used from any Azure AD tenants
+ type: str
+ returned: always
+ sample: AzureADandPersonalMicrosoftAccount
+ available_to_other_tenants:
+ description:
+ - The application can be used from any Azure AD tenants
+ type: str
+ returned: always
+ sample: AzureADandPersonalMicrosoftAccount
+ public_client_reply_urls:
+ description:
+ - The public client redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ returned: always
+ type: list
+ sample: []
+ web_reply_urls:
+ description:
+ - The web redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ returned: always
+ type: list
+ sample: []
+ spa_reply_urls:
+ description:
+ - The spa redirect urls.
+ - Space-separated URIs to which Azure AD will redirect in response to an OAuth 2.0 request.
+ returned: always
+ type: list
+ sample: []
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.applications.applications_request_builder import ApplicationsRequestBuilder
+ from kiota_abstractions.api_error import APIError
except ImportError:
# This is handled in azure_rm_common
pass
@@ -110,25 +146,17 @@ class AzureRMADApplicationInfo(AzureRMModuleBase):
def __init__(self):
self.module_arg_spec = dict(
- app_id=dict(
- type='str'
- ),
- object_id=dict(
- type='str'
- ),
- identifier_uri=dict(
- type='str'
- ),
- tenant=dict(
- type='str',
- required=True
- )
+ app_id=dict(type='str'),
+ object_id=dict(type='str'),
+ identifier_uri=dict(type='str'),
+ app_display_name=dict(type='str')
)
- self.tenant = None
self.app_id = None
+ self.app_display_name = None
self.object_id = None
self.identifier_uri = None
self.results = dict(changed=False)
+ self._client = None
super(AzureRMADApplicationInfo, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=True,
supports_tags=False,
@@ -139,21 +167,26 @@ class AzureRMADApplicationInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
applications = []
+
try:
- client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
if self.object_id:
- applications = [client.applications.get(self.object_id)]
+ applications = [asyncio.get_event_loop().run_until_complete(self.get_application(self.object_id))]
else:
sub_filters = []
if self.identifier_uri:
sub_filters.append("identifierUris/any(s:s eq '{0}')".format(self.identifier_uri))
if self.app_id:
sub_filters.append("appId eq '{0}'".format(self.app_id))
- # applications = client.applications.list(filter=(' and '.join(sub_filters)))
- applications = list(client.applications.list(filter=(' and '.join(sub_filters))))
-
+ if self.app_display_name:
+ sub_filters.append("displayName eq '{0}'".format(self.app_display_name))
+ apps = asyncio.get_event_loop().run_until_complete(self.get_applications(sub_filters))
+ applications = list(apps)
self.results['applications'] = [self.to_dict(app) for app in applications]
- except GraphErrorException as ge:
+ except APIError as e:
+ if e.response_status_code != 404:
+ self.fail("failed to get application info {0}".format(str(e)))
+ except Exception as ge:
self.fail("failed to get application info {0}".format(str(ge)))
return self.results
@@ -161,11 +194,46 @@ class AzureRMADApplicationInfo(AzureRMModuleBase):
def to_dict(self, object):
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
app_display_name=object.display_name,
- identifier_uris=object.identifier_uris
+ identifier_uris=object.identifier_uris,
+ available_to_other_tenants=object.sign_in_audience,
+ sign_in_audience=object.sign_in_audience,
+ web_reply_urls=object.web.redirect_uris,
+ spa_reply_urls=object.spa.redirect_uris,
+ public_client_reply_urls=object.public_client.redirect_uris
)
+ async def get_application(self, obj_id):
+ return await self._client.applications.by_application_id(obj_id).get()
+
+ async def get_applications(self, sub_filters):
+ if sub_filters:
+ request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
+ query_parameters=ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
+ filter=(' and '.join(sub_filters)),
+ ),
+ )
+ applications = await self._client.applications.get(request_configuration=request_configuration)
+ return applications.value
+ else:
+ applications_list = []
+ applications = await self._client.applications.get()
+ for app in applications.value:
+ applications_list.append(app)
+
+ if applications.odata_next_link:
+ next_link = applications.odata_next_link
+ else:
+ next_link = None
+
+ while next_link:
+ applications = await self._client.applications.with_url(next_link).get()
+ next_link = applications.odata_next_link
+ for app in applications.value:
+ applications_list.append(app)
+ return applications_list
+
def main():
AzureRMADApplicationInfo()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup.py
index 812b6953c..1693794a7 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup.py
@@ -5,6 +5,7 @@
# 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 = '''
@@ -14,11 +15,6 @@ short_description: Manage Azure Active Directory group
description:
- Create, update or delete Azure Active Directory group.
options:
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
state:
description:
- Assert the state of the resource group. Use C(present) to create or update and C(absent) to delete.
@@ -67,6 +63,10 @@ options:
- The azure ad objects asserted to not be owners of the group.
type: list
elements: str
+ description:
+ description:
+ - An optional description for the group.
+ type: str
extends_documentation_fragment:
- azure.azcollection.azure
author:
@@ -76,46 +76,41 @@ author:
EXAMPLES = '''
- name: Create Group
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
+ description: 'fortest'
state: 'present'
- name: Delete Group using display_name and mail_nickname
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
state: 'absent'
- name: Delete Group using object_id
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
state: 'absent'
- name: Ensure Users are Members of a Group using display_name and mail_nickname
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
state: 'present'
present_members:
- - "https://graph.windows.net/{{ tenant_id }}/directoryObjects/{{ ad_object_1_object_id }}"
- - "https://graph.windows.net/{{ tenant_id }}/directoryObjects/{{ ad_object_2_object_id }}"
+ - "{{ ad_object_1_object_id }}"
+ - "{{ ad_object_2_object_id }}"
- name: Ensure Users are Members of a Group using object_id
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
state: 'present'
present_members:
- - "https://graph.windows.net/{{ ad_object_1_tenant_id }}/directoryObjects/{{ ad_object_1_object_id }}"
- - "https://graph.windows.net/{{ ad_object_2_tenant_id }}/directoryObjects/{{ ad_object_2_object_id }}"
+ - "{{ ad_object_1_object_id }}"
+ - "{{ ad_object_2_object_id }}"
- name: Ensure Users are not Members of a Group using display_name and mail_nickname
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
state: 'present'
@@ -124,7 +119,6 @@ EXAMPLES = '''
- name: Ensure Users are Members of a Group using object_id
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
state: 'present'
absent_members:
@@ -132,26 +126,23 @@ EXAMPLES = '''
- name: Ensure Users are Owners of a Group using display_name and mail_nickname
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
state: 'present'
present_owners:
- - "https://graph.windows.net/{{ tenant_id }}/directoryObjects/{{ ad_object_1_object_id }}"
- - "https://graph.windows.net/{{ tenant_id }}/directoryObjects/{{ ad_object_2_object_id }}"
+ - "{{ ad_object_1_object_id }}"
+ - "{{ ad_object_2_object_id }}"
- name: Ensure Users are Owners of a Group using object_id
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
state: 'present'
present_owners:
- - "https://graph.windows.net/{{ ad_object_1_tenant_id }}/directoryObjects/{{ ad_object_1_object_id }}"
- - "https://graph.windows.net/{{ ad_object_2_tenant_id }}/directoryObjects/{{ ad_object_2_object_id }}"
+ - "{{ ad_object_1_object_id }}"
+ - "{{ ad_object_2_object_id }}"
- name: Ensure Users are not Owners of a Group using display_name and mail_nickname
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
display_name: "Group-Name"
mail_nickname: "Group-Mail-Nickname"
state: 'present'
@@ -161,7 +152,6 @@ EXAMPLES = '''
- name: Ensure Users are Owners of a Group using object_id
azure_rm_adgroup:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
state: 'present'
absent_owners:
@@ -216,13 +206,23 @@ group_members:
- The members of the group.
returned: always
type: list
+description:
+ description:
+ - An optional description for the group.
+ type: str
+ returned: always
+ sample: 'fortest'
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
- from azure.graphrbac.models import GroupCreateParameters
+ import asyncio
+ from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
+ from msgraph.generated.models.group import Group
+ from msgraph.generated.groups.item.transitive_members.transitive_members_request_builder import \
+ TransitiveMembersRequestBuilder
+ from msgraph.generated.models.reference_create import ReferenceCreate
except ImportError:
# This is handled in azure_rm_common
pass
@@ -239,7 +239,7 @@ class AzureRMADGroup(AzureRMModuleBase):
present_owners=dict(type='list', elements='str'),
absent_members=dict(type='list', elements='str'),
absent_owners=dict(type='list', elements='str'),
- tenant=dict(type='str', required=True),
+ description=dict(type='str'),
state=dict(
type='str',
default='present',
@@ -247,7 +247,6 @@ class AzureRMADGroup(AzureRMModuleBase):
),
)
- self.tenant = None
self.display_name = None
self.mail_nickname = None
self.object_id = None
@@ -257,6 +256,7 @@ class AzureRMADGroup(AzureRMModuleBase):
self.absent_owners = []
self.state = None
self.results = dict(changed=False)
+ self._client = None
super(AzureRMADGroup, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=False,
@@ -264,7 +264,6 @@ class AzureRMADGroup(AzureRMModuleBase):
is_ad_resource=True)
def exec_module(self, **kwargs):
-
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
@@ -272,53 +271,61 @@ class AzureRMADGroup(AzureRMModuleBase):
ad_groups = []
try:
- client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
ad_groups = []
if self.display_name and self.mail_nickname:
- ad_groups = list(client.groups.list(filter="displayName eq '{0}' and mailNickname eq '{1}'".format(self.display_name, self.mail_nickname)))
-
+ filter = "displayName eq '{0}' and mailNickname eq '{1}'".format(self.display_name, self.mail_nickname)
+ ad_groups = asyncio.get_event_loop().run_until_complete(self.get_group_list(filter))
if ad_groups:
- self.object_id = ad_groups[0].object_id
+ self.object_id = ad_groups[0].id
elif self.object_id:
- ad_groups = [client.groups.get(self.object_id)]
+ ad_groups = [asyncio.get_event_loop().run_until_complete(self.get_group(self.object_id))]
if ad_groups:
if self.state == "present":
self.results["changed"] = False
elif self.state == "absent":
- ad_groups = [client.groups.delete(self.object_id)]
+ asyncio.get_event_loop().run_until_complete(self.delete_group(self.object_id))
+ ad_groups = []
self.results["changed"] = True
else:
if self.state == "present":
if self.display_name and self.mail_nickname:
- ad_groups = [client.groups.create(GroupCreateParameters(display_name=self.display_name, mail_nickname=self.mail_nickname))]
+ group = Group(
+ mail_enabled=False,
+ security_enabled=True,
+ group_types=[],
+ display_name=self.display_name,
+ mail_nickname=self.mail_nickname,
+ description=self.description
+ )
+
+ ad_groups = [asyncio.get_event_loop().run_until_complete(self.create_group(group))]
self.results["changed"] = True
else:
- raise ValueError('The group does not exist. Both display_name : {0} and mail_nickname : {1} must be passed to create a new group'
- .format(self.display_name, self.mail_nickname))
+ raise ValueError(
+ 'The group does not exist. Both display_name : {0} and mail_nickname : {1} must be passed to create a new group'
+ .format(self.display_name, self.mail_nickname))
elif self.state == "absent":
self.results["changed"] = False
- if ad_groups[0] is not None:
- self.update_members(ad_groups[0].object_id, client)
- self.update_owners(ad_groups[0].object_id, client)
- self.results.update(self.set_results(ad_groups[0], client))
+ if ad_groups and ad_groups[0] is not None:
+ self.update_members(ad_groups[0].id)
+ self.update_owners(ad_groups[0].id)
+ self.results.update(self.set_results(ad_groups[0]))
- except GraphErrorException as e:
- self.fail(e)
- except ValueError as e:
+ except Exception as e:
self.fail(e)
-
return self.results
- def update_members(self, group_id, client):
-
+ def update_members(self, group_id):
current_members = []
if self.present_members or self.absent_members:
- current_members = [object.object_id for object in list(client.groups.get_group_members(group_id))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_members(group_id))
+ current_members = [object.id for object in ret.value]
if self.present_members:
present_members_by_object_id = self.dictionary_from_object_urls(self.present_members)
@@ -327,8 +334,8 @@ class AzureRMADGroup(AzureRMModuleBase):
if members_to_add:
for member_object_id in members_to_add:
- client.groups.add_member(group_id, present_members_by_object_id[member_object_id])
-
+ asyncio.get_event_loop().run_until_complete(
+ self.add_group_member(group_id, present_members_by_object_id[member_object_id]))
self.results["changed"] = True
if self.absent_members:
@@ -336,24 +343,25 @@ class AzureRMADGroup(AzureRMModuleBase):
if members_to_remove:
for member in members_to_remove:
- client.groups.remove_member(group_id, member)
+ asyncio.get_event_loop().run_until_complete(self.delete_group_member(group_id, member))
self.results["changed"] = True
- def update_owners(self, group_id, client):
+ def update_owners(self, group_id):
current_owners = []
if self.present_owners or self.absent_owners:
- current_owners = [object.object_id for object in list(client.groups.list_owners(group_id))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_owners(group_id))
+ current_owners = [object.id for object in ret.value]
if self.present_owners:
present_owners_by_object_id = self.dictionary_from_object_urls(self.present_owners)
-
owners_to_add = list(set(present_owners_by_object_id.keys()) - set(current_owners))
if owners_to_add:
for owner_object_id in owners_to_add:
- client.groups.add_owner(group_id, present_owners_by_object_id[owner_object_id])
+ asyncio.get_event_loop().run_until_complete(
+ self.add_gropup_owner(group_id, present_owners_by_object_id[owner_object_id]))
self.results["changed"] = True
if self.absent_owners:
@@ -361,7 +369,7 @@ class AzureRMADGroup(AzureRMModuleBase):
if owners_to_remove:
for owner in owners_to_remove:
- client.groups.remove_owner(group_id, owner)
+ asyncio.get_event_loop().run_until_complete(self.remove_gropup_owner(group_id, owner))
self.results["changed"] = True
def dictionary_from_object_urls(self, object_urls):
@@ -383,24 +391,25 @@ class AzureRMADGroup(AzureRMModuleBase):
def serviceprincipal_to_dict(self, object):
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
app_display_name=object.display_name,
app_role_assignment_required=object.app_role_assignment_required
)
def group_to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
mail_nickname=object.mail_nickname,
mail_enabled=object.mail_enabled,
security_enabled=object.security_enabled,
- mail=object.mail
+ mail=object.mail,
+ description=object.description
)
def user_to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
user_principal_name=object.user_principal_name,
mail_nickname=object.mail_nickname,
@@ -410,28 +419,92 @@ class AzureRMADGroup(AzureRMModuleBase):
)
def result_to_dict(self, object):
- if object.object_type == "Group":
+ if object.odata_type == "#microsoft.graph.group":
return self.group_to_dict(object)
- elif object.object_type == "User":
+ elif object.odata_type == "#microsoft.graph.user":
return self.user_to_dict(object)
- elif object.object_type == "Application":
+ elif object.odata_type == "#microsoft.graph.application":
return self.application_to_dict(object)
- elif object.object_type == "ServicePrincipal":
+ elif object.odata_type == "#microsoft.graph.servicePrincipal":
return self.serviceprincipal_to_dict(object)
else:
- return object.object_type
+ return object.odata_type
- def set_results(self, object, client):
+ def set_results(self, object):
results = self.group_to_dict(object)
if results["object_id"] and (self.present_owners or self.absent_owners):
- results["group_owners"] = [self.result_to_dict(object) for object in list(client.groups.list_owners(results["object_id"]))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_owners(results["object_id"]))
+ results["group_owners"] = [self.result_to_dict(object) for object in ret.value]
if results["object_id"] and (self.present_members or self.absent_members):
- results["group_members"] = [self.result_to_dict(object) for object in list(client.groups.get_group_members(results["object_id"]))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_members(results["object_id"]))
+ results["group_members"] = [self.result_to_dict(object) for object in ret.value]
return results
+ async def create_group(self, create_group):
+ return await self._client.groups.post(body=create_group)
+
+ async def delete_group(self, group_id):
+ await self._client.groups.by_group_id(group_id).delete()
+
+ async def get_group(self, group_id):
+ return await self._client.groups.by_group_id(group_id).get()
+
+ async def get_group_list(self, filter=None):
+ if filter:
+ request_configuration = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
+ query_parameters=GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
+ count=True,
+ filter=filter,
+ ),
+ )
+ groups = await self._client.groups.get(request_configuration=request_configuration)
+ else:
+ groups = await self._client.groups.get()
+
+ if groups and groups.value:
+ return groups.value
+ return []
+
+ async def get_group_members(self, group_id, filters=None):
+ request_configuration = TransitiveMembersRequestBuilder.TransitiveMembersRequestBuilderGetRequestConfiguration(
+ query_parameters=TransitiveMembersRequestBuilder.TransitiveMembersRequestBuilderGetQueryParameters(
+ count=True,
+ ),
+ )
+ if filters:
+ request_configuration.query_parameters.filter = filters
+ return await self._client.groups.by_group_id(group_id).transitive_members.get(
+ request_configuration=request_configuration)
+
+ async def add_group_member(self, group_id, obj_id):
+ request_body = ReferenceCreate(
+ odata_id="https://graph.microsoft.com/v1.0/directoryObjects/{0}".format(obj_id),
+ )
+ await self._client.groups.by_group_id(group_id).members.ref.post(body=request_body)
+
+ async def delete_group_member(self, group_id, member_id):
+ await self._client.groups.by_group_id(group_id).members.by_directory_object_id(member_id).ref.delete()
+
+ async def get_group_owners(self, group_id):
+ request_configuration = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
+ query_parameters=GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
+ count=True,
+ ),
+ )
+ return await self._client.groups.by_group_id(group_id).owners.get(request_configuration=request_configuration)
+
+ async def add_gropup_owner(self, group_id, obj_id):
+ request_body = ReferenceCreate(
+ odata_id="https://graph.microsoft.com/v1.0/users/{0}".format(obj_id),
+ )
+ await self._client.groups.by_group_id(group_id).owners.ref.post(body=request_body)
+
+ async def remove_gropup_owner(self, group_id, obj_id):
+ await self._client.groups.by_group_id(group_id).owners.by_directory_object_id(obj_id).ref.delete()
+
def main():
AzureRMADGroup()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup_info.py
index 37bc1febb..3525bdf1b 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adgroup_info.py
@@ -1,10 +1,11 @@
#!/usr/bin/python
#
-# Copyright (c) 2021 Cole Neubauer, (@coleneubauer)
+# Copyright (c) 2021 Cole Neubauer, (@coleneubauer), xuzhang3 (@xuzhang3)
#
# 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 = '''
@@ -14,11 +15,6 @@ short_description: Get Azure Active Directory group info
description:
- Get Azure Active Directory group info.
options:
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
object_id:
description:
- The object id for the ad group.
@@ -70,53 +66,46 @@ extends_documentation_fragment:
- azure.azcollection.azure
author:
- Cole Neubauer(@coleneubauer)
+ - Xu Zhang(@xuzhang)
'''
EXAMPLES = '''
- name: Return a specific group using object_id
azure_rm_adgroup_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return a specific group using object_id and return the owners of the group
azure_rm_adgroup_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
return_owners: true
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return a specific group using object_id and return the owners and members of the group
azure_rm_adgroup_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
return_owners: true
return_group_members: true
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return a specific group using object_id and return the groups the group is a member of
azure_rm_adgroup_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
return_member_groups: true
- tenant: "{{ tenant_id }}"
- name: Return a specific group using object_id and check an ID for membership
azure_rm_adgroup_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
check_membership: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return a specific group using displayName for attribute_name
azure_rm_adgroup_info:
attribute_name: "displayName"
attribute_value: "Display-Name-Of-AD-Group"
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return groups matching odata_filter
azure_rm_adgroup_info:
odata_filter: "mailNickname eq 'Mail-Nickname-Of-AD-Group'"
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Return all groups
azure_rm_adgroup_info:
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
all: true
'''
@@ -167,13 +156,23 @@ group_members:
- The members of the group.
returned: always
type: list
+description:
+ description:
+ - An optional description for the group.
+ type: str
+ returned: always
+ sample: 'fortest'
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
- from azure.graphrbac.models import CheckGroupMembershipParameters
+ import asyncio
+ from msgraph.generated.groups.groups_request_builder import GroupsRequestBuilder
+ from msgraph.generated.groups.item.transitive_members.transitive_members_request_builder import \
+ TransitiveMembersRequestBuilder
+ from msgraph.generated.groups.item.get_member_groups.get_member_groups_post_request_body import \
+ GetMemberGroupsPostRequestBody
except ImportError:
# This is handled in azure_rm_common
pass
@@ -192,10 +191,8 @@ class AzureRMADGroupInfo(AzureRMModuleBase):
return_group_members=dict(type='bool', default=False),
return_member_groups=dict(type='bool', default=False),
all=dict(type='bool', default=False),
- tenant=dict(type='str', required=True),
)
- self.tenant = None
self.object_id = None
self.attribute_name = None
self.attribute_value = None
@@ -207,6 +204,7 @@ class AzureRMADGroupInfo(AzureRMModuleBase):
self.all = False
self.results = dict(changed=False)
+ self._client = None
mutually_exclusive = [['odata_filter', 'attribute_name', 'object_id', 'all']]
required_together = [['attribute_name', 'attribute_value']]
@@ -228,20 +226,19 @@ class AzureRMADGroupInfo(AzureRMModuleBase):
ad_groups = []
try:
- client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
if self.object_id is not None:
- ad_groups = [client.groups.get(self.object_id)]
+ ad_groups = [asyncio.get_event_loop().run_until_complete(self.get_group(self.object_id))]
elif self.attribute_name is not None and self.attribute_value is not None:
- ad_groups = list(client.groups.list(filter="{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))
+ ad_groups = asyncio.get_event_loop().run_until_complete(
+ self.get_group_list(filter="{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))
elif self.odata_filter is not None: # run a filter based on user input
- ad_groups = list(client.groups.list(filter=self.odata_filter))
+ ad_groups = asyncio.get_event_loop().run_until_complete(self.get_group_list(filter=self.odata_filter))
elif self.all:
- ad_groups = list(client.groups.list())
-
- self.results['ad_groups'] = [self.set_results(group, client) for group in ad_groups]
-
- except GraphErrorException as e:
+ ad_groups = asyncio.get_event_loop().run_until_complete(self.get_group_list())
+ self.results['ad_groups'] = [self.set_results(group) for group in ad_groups]
+ except Exception as e:
self.fail("failed to get ad group info {0}".format(str(e)))
return self.results
@@ -256,24 +253,25 @@ class AzureRMADGroupInfo(AzureRMModuleBase):
def serviceprincipal_to_dict(self, object):
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
app_display_name=object.display_name,
app_role_assignment_required=object.app_role_assignment_required
)
def group_to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
mail_nickname=object.mail_nickname,
mail_enabled=object.mail_enabled,
security_enabled=object.security_enabled,
- mail=object.mail
+ mail=object.mail,
+ description=object.description
)
def user_to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
user_principal_name=object.user_principal_name,
mail_nickname=object.mail_nickname,
@@ -283,35 +281,94 @@ class AzureRMADGroupInfo(AzureRMModuleBase):
)
def result_to_dict(self, object):
- if object.object_type == "Group":
+ if object.odata_type == "#microsoft.graph.group":
return self.group_to_dict(object)
- elif object.object_type == "User":
+ elif object.odata_type == "#microsoft.graph.user":
return self.user_to_dict(object)
- elif object.object_type == "Application":
+ elif object.odata_type == "#microsoft.graph.application":
return self.application_to_dict(object)
- elif object.object_type == "ServicePrincipal":
+ elif object.odata_type == "#microsoft.graph.servicePrincipal":
return self.serviceprincipal_to_dict(object)
else:
- return object.object_type
+ return object.odata_type
- def set_results(self, object, client):
+ def set_results(self, object):
results = self.group_to_dict(object)
if results["object_id"] and self.return_owners:
- results["group_owners"] = [self.result_to_dict(object) for object in list(client.groups.list_owners(results["object_id"]))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_owners(results["object_id"]))
+ results["group_owners"] = [self.result_to_dict(object) for object in ret.value]
if results["object_id"] and self.return_group_members:
- results["group_members"] = [self.result_to_dict(object) for object in list(client.groups.get_group_members(results["object_id"]))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_members(results["object_id"]))
+ results["group_members"] = [self.result_to_dict(object) for object in ret.value]
if results["object_id"] and self.return_member_groups:
- results["member_groups"] = [self.result_to_dict(object) for object in list(client.groups.get_member_groups(results["object_id"], False))]
+ ret = asyncio.get_event_loop().run_until_complete(self.get_member_groups(results["object_id"]))
+ results["member_groups"] = [self.result_to_dict(object) for object in list(ret.value)]
if results["object_id"] and self.check_membership:
- results["is_member_of"] = client.groups.is_member_of(
- CheckGroupMembershipParameters(group_id=results["object_id"], member_id=self.check_membership)).value
+ filter = "id eq '{0}' ".format(self.check_membership)
+ ret = asyncio.get_event_loop().run_until_complete(self.get_group_members(results["object_id"], filter))
+ results["is_member_of"] = True if ret.value and len(ret.value) != 0 else False
return results
+ async def get_group(self, group_id):
+ return await self._client.groups.by_group_id(group_id).get()
+
+ async def get_group_list(self, filter=None):
+ kwargs = {}
+ if filter:
+ request_configuration = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
+ query_parameters=GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
+ count=True,
+ filter=filter,
+ ),
+ )
+ kwargs["request_configuration"] = request_configuration
+
+ groups = []
+ # paginated response can be quite large
+ response = await self._client.groups.get(**kwargs)
+ if response:
+ groups += response.value
+ while response is not None and response.odata_next_link is not None:
+ response = await self._client.groups.with_url(response.odata_next_link).get(**kwargs)
+ if response:
+ groups += response.value
+
+ return groups
+
+ async def get_group_owners(self, group_id):
+ request_configuration = GroupsRequestBuilder.GroupsRequestBuilderGetRequestConfiguration(
+ query_parameters=GroupsRequestBuilder.GroupsRequestBuilderGetQueryParameters(
+ count=True,
+ select=['id', 'displayName', 'userPrincipalName', 'mailNickname', 'mail', 'accountEnabled', 'userType',
+ 'appId', 'appRoleAssignmentRequired']
+
+ ),
+ )
+ return await self._client.groups.by_group_id(group_id).owners.get(request_configuration=request_configuration)
+
+ async def get_group_members(self, group_id, filters=None):
+ request_configuration = TransitiveMembersRequestBuilder.TransitiveMembersRequestBuilderGetRequestConfiguration(
+ query_parameters=TransitiveMembersRequestBuilder.TransitiveMembersRequestBuilderGetQueryParameters(
+ count=True,
+ select=['id', 'displayName', 'userPrincipalName', 'mailNickname', 'mail', 'accountEnabled', 'userType',
+ 'appId', 'appRoleAssignmentRequired']
+
+ ),
+ )
+ if filters:
+ request_configuration.query_parameters.filter = filters
+ return await self._client.groups.by_group_id(group_id).transitive_members.get(
+ request_configuration=request_configuration)
+
+ async def get_member_groups(self, obj_id):
+ request_body = GetMemberGroupsPostRequestBody(security_enabled_only=False)
+ return await self._client.groups.by_group_id(obj_id).get_member_groups.post(body=request_body)
+
def main():
AzureRMADGroupInfo()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword.py
index 587d842b5..c6a634db9 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword.py
@@ -32,21 +32,17 @@ options:
key_id:
description:
- The password key ID.
+ - It isn't supported anymore in the create operation. See the Azure documentation for more information
+ U(https://learn.microsoft.com/en-us/graph/api/application-addpassword?view=graph-rest-1.0&tabs=http#request-body).
type: str
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
end_date:
description:
- Date or datemtime after which credentials expire.
- Default value is one year after current time.
type: str
- value:
+ display_name:
description:
- - The application password value.
- - Length greater than 18 characters.
+ - The friendly name of the application password.
type: str
app_object_id:
description:
@@ -77,8 +73,7 @@ EXAMPLES = '''
azure_rm_adpassword:
app_id: "{{ app_id }}"
state: present
- value: "$abc12345678"
- tenant: "{{ tenant_id }}"
+ display_name: "Password friendly name"
'''
RETURN = '''
@@ -102,17 +97,26 @@ start_date:
type: str
returned: always
sample: "2020-06-28T06:00:32.637070+00:00"
-
+secret_text:
+ description:
+ - The application password value.
+ - API only returns the application password value at creation.
+ type: str
+ returned: created
+ sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
-import uuid
import datetime
try:
- from azure.graphrbac.models import GraphErrorException
- from azure.graphrbac.models import PasswordCredential
- from azure.graphrbac.models import ApplicationUpdateParameters
+ import asyncio
+ from msgraph.generated.models.password_credential import PasswordCredential
+ from msgraph.generated.applications.item.add_password.add_password_post_request_body import \
+ AddPasswordPostRequestBody
+ from msgraph.generated.applications.item.remove_password.remove_password_post_request_body import \
+ RemovePasswordPostRequestBody
+ from msgraph.generated.applications.applications_request_builder import ApplicationsRequestBuilder
from dateutil.relativedelta import relativedelta
except ImportError:
# This is handled in azure_rm_common
@@ -127,19 +131,17 @@ class AzureRMADPassword(AzureRMModuleBase):
service_principal_object_id=dict(type='str'),
app_object_id=dict(type='str'),
key_id=dict(type='str'),
- tenant=dict(type='str', required=True),
- value=dict(type='str'),
+ display_name=dict(type='str'),
end_date=dict(type='str'),
state=dict(type='str', default='present', choices=['present', 'absent']),
)
self.state = None
- self.tenant = None
self.app_id = None
self.service_principal_object_id = None
self.app_object_id = None
self.key_id = None
- self.value = None
+ self.display_name = None
self.end_date = None
self.results = dict(changed=False)
@@ -154,13 +156,13 @@ class AzureRMADPassword(AzureRMModuleBase):
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
- self.client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
self.resolve_app_obj_id()
passwords = self.get_all_passwords()
if self.state == 'present':
if self.key_id and self.key_exists(passwords):
- self.update(passwords)
+ self.update_password(passwords)
else:
self.create_password(passwords)
else:
@@ -173,7 +175,7 @@ class AzureRMADPassword(AzureRMModuleBase):
def key_exists(self, old_passwords):
for pd in old_passwords:
- if pd.key_id == self.key_id:
+ if str(pd.key_id) == self.key_id:
return True
return False
@@ -183,27 +185,31 @@ class AzureRMADPassword(AzureRMModuleBase):
return
elif self.app_id or self.service_principal_object_id:
if not self.app_id:
- sp = self.client.service_principals.get(self.service_principal_object_id)
+ sp = asyncio.get_event_loop().run_until_complete(self.get_service_principal())
self.app_id = sp.app_id
if not self.app_id:
- self.fail("can't resolve app via service principal object id {0}".format(self.service_principal_object_id))
+ self.fail("can't resolve app via service principal object id {0}".format(
+ self.service_principal_object_id))
- result = list(self.client.applications.list(filter="appId eq '{0}'".format(self.app_id)))
+ apps = asyncio.get_event_loop().run_until_complete(self.get_applications())
+ result = list(apps.value)
if result:
- self.app_object_id = result[0].object_id
+ self.app_object_id = result[0].id
else:
self.fail("can't resolve app via app id {0}".format(self.app_id))
else:
self.fail("one of the [app_id, app_object_id, service_principal_id] must be set")
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("error in resolve app_object_id {0}".format(str(ge)))
def get_all_passwords(self):
try:
- return list(self.client.applications.list_password_credentials(self.app_object_id))
- except GraphErrorException as ge:
+ application = asyncio.get_event_loop().run_until_complete(self.get_application())
+ passwordCredentials = application.password_credentials
+ return passwordCredentials
+ except Exception as ge:
self.fail("failed to fetch passwords for app {0}: {1}".format(self.app_object_id, str(ge)))
def delete_all_passwords(self, old_passwords):
@@ -212,9 +218,10 @@ class AzureRMADPassword(AzureRMModuleBase):
self.results['changed'] = False
return
try:
- self.client.applications.patch(self.app_object_id, ApplicationUpdateParameters(password_credentials=[]))
+ for pd in old_passwords:
+ asyncio.get_event_loop().run_until_complete(self.remove_password(pd.key_id))
self.results['changed'] = True
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("fail to purge all passwords for app: {0} - {1}".format(self.app_object_id, str(ge)))
def delete_password(self, old_passwords):
@@ -225,46 +232,38 @@ class AzureRMADPassword(AzureRMModuleBase):
num_of_passwords_before_delete = len(old_passwords)
for pd in old_passwords:
- if pd.key_id == self.key_id:
- old_passwords.remove(pd)
+ if str(pd.key_id) == self.key_id:
+ try:
+ asyncio.get_event_loop().run_until_complete(self.remove_password(pd.key_id))
+
+ num_of_passwords_after_delete = len(self.get_all_passwords())
+ if num_of_passwords_after_delete != num_of_passwords_before_delete:
+ self.results['changed'] = True
+ except Exception as ge:
+ self.fail("failed to delete password with key id {0} - {1}".format(self.app_id, str(ge)))
break
- try:
- self.client.applications.patch(self.app_object_id, ApplicationUpdateParameters(password_credentials=old_passwords))
- num_of_passwords_after_delete = len(self.get_all_passwords())
- if num_of_passwords_after_delete != num_of_passwords_before_delete:
- self.results['changed'] = True
-
- except GraphErrorException as ge:
- self.fail("failed to delete password with key id {0} - {1}".format(self.app_id, str(ge)))
def create_password(self, old_passwords):
-
- def gen_guid():
- return uuid.uuid4()
-
- if self.value is None:
- self.fail("when creating a new password, module parameter value can't be None")
-
start_date = datetime.datetime.now(datetime.timezone.utc)
end_date = self.end_date or start_date + relativedelta(years=1)
- value = self.value
- key_id = self.key_id or str(gen_guid())
-
- new_password = PasswordCredential(start_date=start_date, end_date=end_date, key_id=key_id,
- value=value, custom_key_identifier=None)
- old_passwords.append(new_password)
+ display_name = self.display_name
+ num_of_passwords_before_add = len(old_passwords)
try:
- client = self.get_graphrbac_client(self.tenant)
- app_patch_parameters = ApplicationUpdateParameters(password_credentials=old_passwords)
- client.applications.patch(self.app_object_id, app_patch_parameters)
-
- new_passwords = self.get_all_passwords()
- for pd in new_passwords:
- if pd.key_id == key_id:
- self.results['changed'] = True
- self.results.update(self.to_dict(pd))
- except GraphErrorException as ge:
+ request_body = AddPasswordPostRequestBody(
+ password_credential=PasswordCredential(
+ start_date_time=start_date,
+ end_date_time=end_date,
+ display_name=display_name
+ ),
+ )
+ pd = asyncio.get_event_loop().run_until_complete(self.add_password(request_body))
+
+ num_of_passwords_after_add = len(self.get_all_passwords())
+ if num_of_passwords_after_add != num_of_passwords_before_add:
+ self.results['changed'] = True
+ self.results.update(self.to_dict(pd))
+ except Exception as ge:
self.fail("failed to create new password: {0}".format(str(ge)))
def update_password(self, old_passwords):
@@ -272,10 +271,36 @@ class AzureRMADPassword(AzureRMModuleBase):
def to_dict(self, pd):
return dict(
- end_date=pd.end_date,
- start_date=pd.start_date,
- key_id=pd.key_id
+ end_date=str(pd.end_date_time),
+ start_date=str(pd.start_date_time),
+ key_id=str(pd.key_id),
+ secret_text=str(pd.secret_text)
+ )
+
+ async def get_service_principal(self):
+ return await self._client.service_principals.by_service_principal_id(self.service_principal_object_id).get()
+
+ async def get_applications(self):
+ request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
+ query_parameters=ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
+ filter="appId eq '{0}'".format(self.app_id),
+ ),
)
+ return await self._client.applications.get(request_configuration=request_configuration)
+
+ async def get_application(self):
+ return await self._client.applications.by_application_id(self.app_object_id).get()
+
+ async def remove_password(self, key_id):
+ request_body = RemovePasswordPostRequestBody(
+ key_id=key_id,
+ )
+ return await self._client.applications.by_application_id(self.app_object_id).remove_password.post(
+ body=request_body)
+
+ async def add_password(self, request_body):
+ return await self._client.applications.by_application_id(self.app_object_id).add_password.post(
+ body=request_body)
def main():
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword_info.py
index 7c82b7b9f..229ef100d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adpassword_info.py
@@ -8,7 +8,6 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
-
DOCUMENTATION = '''
module: azure_rm_adpassword_info
@@ -32,11 +31,6 @@ options:
description:
- The password key ID.
type: str
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
end_date:
description:
- Date or datemtime after which credentials expire.
@@ -64,7 +58,6 @@ EXAMPLES = '''
- name: get ad password info
azure_rm_adpassword_info:
app_id: "{{ app_id }}"
- tenant: "{{ tenant_id }}"
key_id: "{{ key_id }}"
'''
@@ -107,7 +100,8 @@ passwords:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.applications.applications_request_builder import ApplicationsRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
@@ -121,12 +115,10 @@ class AzureRMADPasswordInfo(AzureRMModuleBase):
app_object_id=dict(type='str'),
service_principal_object_id=dict(type='str'),
key_id=dict(type='str'),
- tenant=dict(type='str', required=True),
value=dict(type='str'),
end_date=dict(type='str'),
)
- self.tenant = None
self.app_id = None
self.service_principal_object_id = None
self.app_object_id = None
@@ -147,12 +139,12 @@ class AzureRMADPasswordInfo(AzureRMModuleBase):
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
- self.client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
self.resolve_app_obj_id()
passwords = self.get_all_passwords()
if self.key_id:
- filtered = [pd for pd in passwords if pd.key_id == self.key_id]
+ filtered = [pd for pd in passwords if str(pd.key_id) == self.key_id]
self.results['passwords'] = [self.to_dict(pd) for pd in filtered]
else:
self.results['passwords'] = [self.to_dict(pd) for pd in passwords]
@@ -165,37 +157,55 @@ class AzureRMADPasswordInfo(AzureRMModuleBase):
return
elif self.app_id or self.service_principal_object_id:
if not self.app_id:
- sp = self.client.service_principals.get(self.service_principal_id)
+ sp = asyncio.get_event_loop().run_until_complete(self.get_service_principal())
self.app_id = sp.app_id
if not self.app_id:
- self.fail("can't resolve app via service principal object id {0}".format(self.service_principal_object_id))
+ self.fail("can't resolve app via service principal object id {0}".format(
+ self.service_principal_object_id))
- result = list(self.client.applications.list(filter="appId eq '{0}'".format(self.app_id)))
+ apps = asyncio.get_event_loop().run_until_complete(self.get_applications())
+ result = list(apps.value)
if result:
- self.app_object_id = result[0].object_id
+ self.app_object_id = result[0].id
else:
self.fail("can't resolve app via app id {0}".format(self.app_id))
else:
- self.fail("one of the [app_id, app_object_id, service_principal_id] must be set")
+ self.fail("one of the [app_id, app_object_id, service_principal_object_id] must be set")
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("error in resolve app_object_id {0}".format(str(ge)))
def get_all_passwords(self):
try:
- return list(self.client.applications.list_password_credentials(self.app_object_id))
- except GraphErrorException as ge:
+ application = asyncio.get_event_loop().run_until_complete(self.get_application())
+ passwordCredentials = application.password_credentials
+ return passwordCredentials
+ except Exception as ge:
self.fail("failed to fetch passwords for app {0}: {1}".format(self.app_object_id, str(ge)))
def to_dict(self, pd):
return dict(
- end_date=pd.end_date,
- start_date=pd.start_date,
- key_id=pd.key_id,
+ end_date=pd.end_date_time,
+ start_date=pd.start_date_time,
+ key_id=str(pd.key_id),
custom_key_identifier=str(pd.custom_key_identifier)
)
+ async def get_service_principal(self):
+ return await self._client.service_principals.by_service_principal_id(self.service_principal_object_id).get()
+
+ async def get_applications(self):
+ request_configuration = ApplicationsRequestBuilder.ApplicationsRequestBuilderGetRequestConfiguration(
+ query_parameters=ApplicationsRequestBuilder.ApplicationsRequestBuilderGetQueryParameters(
+ filter="appId eq '{0}'".format(self.app_id),
+ ),
+ )
+ return await self._client.applications.get(request_configuration=request_configuration)
+
+ async def get_application(self):
+ return await self._client.applications.by_application_id(self.app_object_id).get()
+
def main():
AzureRMADPasswordInfo()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal.py
index a7d3b39fd..ef12d642d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal.py
@@ -5,8 +5,8 @@
# 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
+__metaclass__ = type
DOCUMENTATION = '''
---
@@ -25,11 +25,6 @@ options:
- The application ID.
type: str
required: True
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
app_role_assignment_required:
description:
- Whether the Role of the Service Principal is set.
@@ -57,7 +52,6 @@ EXAMPLES = '''
azure_rm_adserviceprincipal:
app_id: "{{ app_id }}"
state: present
- tenant: "{{ tenant_id }}"
'''
RETURN = '''
@@ -89,13 +83,15 @@ object_id:
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt
+
try:
- from azure.graphrbac.models import ServicePrincipalCreateParameters
+ from msgraph.generated.models.service_principal import ServicePrincipal
except Exception:
pass
try:
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.service_principals.service_principals_request_builder import ServicePrincipalsRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
@@ -106,13 +102,11 @@ class AzureRMADServicePrincipal(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
app_id=dict(type='str', required=True),
- tenant=dict(type='str', required=True),
state=dict(type='str', default='present', choices=['present', 'absent']),
app_role_assignment_required=dict(type='bool')
)
self.state = None
- self.tenant = None
self.app_id = None
self.app_role_assignment_required = None
self.object_id = None
@@ -128,6 +122,8 @@ class AzureRMADServicePrincipal(AzureRMModuleBaseExt):
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
+ self._client = self.get_msgraph_client()
+
response = self.get_resource()
if response:
@@ -146,46 +142,47 @@ class AzureRMADServicePrincipal(AzureRMModuleBaseExt):
def create_resource(self):
try:
- client = self.get_graphrbac_client(self.tenant)
- response = client.service_principals.create(ServicePrincipalCreateParameters(app_id=self.app_id, account_enabled=True))
+ response = asyncio.get_event_loop().run_until_complete(self.create_service_principal())
self.results['changed'] = True
self.results.update(self.to_dict(response))
return response
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("Error creating service principle, app id {0} - {1}".format(self.app_id, str(ge)))
def update_resource(self, old_response):
try:
- client = self.get_graphrbac_client(self.tenant)
- to_update = {}
+ request_body = ServicePrincipal(
+ app_role_assignment_required=None,
+ )
if self.app_role_assignment_required is not None:
- to_update['app_role_assignment_required'] = self.app_role_assignment_required
+ request_body = ServicePrincipal(
+ app_role_assignment_required=self.app_role_assignment_required
+ )
- client.service_principals.update(old_response['object_id'], to_update)
+ asyncio.get_event_loop().run_until_complete(self.update_service_principal(old_response, request_body))
self.results['changed'] = True
self.results.update(self.get_resource())
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("Error updating the service principal app_id {0} - {1}".format(self.app_id, str(ge)))
def delete_resource(self, response):
try:
- client = self.get_graphrbac_client(self.tenant)
- client.service_principals.delete(response.get('object_id'))
+ asyncio.get_event_loop().run_until_complete(self.delete_service_principal(response))
self.results['changed'] = True
return True
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("Error deleting service principal app_id {0} - {1}".format(self.app_id, str(ge)))
def get_resource(self):
try:
- client = self.get_graphrbac_client(self.tenant)
- result = list(client.service_principals.list(filter="servicePrincipalNames/any(c:c eq '{0}')".format(self.app_id)))
+ sps = asyncio.get_event_loop().run_until_complete(self.get_service_principals())
+ result = list(sps.value)
if not result:
return False
result = result[0]
return self.to_dict(result)
- except GraphErrorException as ge:
+ except Exception as ge:
self.log("Did not find the graph instance instance {0} - {1}".format(self.app_id, str(ge)))
return False
@@ -198,11 +195,33 @@ class AzureRMADServicePrincipal(AzureRMModuleBaseExt):
def to_dict(self, object):
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
app_display_name=object.display_name,
app_role_assignment_required=object.app_role_assignment_required
)
+ async def create_service_principal(self):
+ request_body = ServicePrincipal(
+ app_id=self.app_id,
+ account_enabled=True
+ )
+ return await self._client.service_principals.post(body=request_body)
+
+ async def update_service_principal(self, old_response, request_body):
+ return await self._client.service_principals.by_service_principal_id(old_response['object_id']).patch(
+ body=request_body)
+
+ async def delete_service_principal(self, response):
+ return await self._client.service_principals.by_service_principal_id(response.get('object_id')).delete()
+
+ async def get_service_principals(self):
+ request_configuration = ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetRequestConfiguration(
+ query_parameters=ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetQueryParameters(
+ filter="servicePrincipalNames/any(c:c eq '{0}')".format(self.app_id),
+ )
+ )
+ return await self._client.service_principals.get(request_configuration=request_configuration)
+
def main():
AzureRMADServicePrincipal()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal_info.py
index db27ccae8..d2acfac04 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_adserviceprincipal_info.py
@@ -5,8 +5,8 @@
# 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
+__metaclass__ = type
DOCUMENTATION = '''
module: azure_rm_adserviceprincipal_info
@@ -23,11 +23,6 @@ options:
description:
- The application ID.
type: str
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
object_id:
description:
- It's service principal's object ID.
@@ -45,34 +40,43 @@ EXAMPLES = '''
- name: get ad sp info
azure_rm_adserviceprincipal_info:
app_id: "{{ app_id }}"
- tenant: "{{ tenant_id }}"
+- name: get all service principals
+ azure_rm_adserviceprincipal_info:
'''
RETURN = '''
-app_display_name:
- description:
- - Object's display name or its prefix.
- type: str
- returned: always
- sample: sp
-app_id:
+service_principals:
description:
- - The application ID.
+ - A list of service principals in the tenant. If app_id or object_id is set, the maximum length
+ of this list should be one.
+ type: list
+ elements: dict
returned: always
- type: str
- sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
-app_role_assignment_required:
- description:
- - Whether the Role of the Service Principal is set.
- type: bool
- returned: always
- sample: false
-object_id:
- description:
- - It's service principal's object ID.
- returned: always
- type: str
- sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ contains:
+ app_display_name:
+ description:
+ - Object's display name or its prefix.
+ type: str
+ returned: always
+ sample: sp
+ app_id:
+ description:
+ - The application ID.
+ returned: always
+ type: str
+ sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ app_role_assignment_required:
+ description:
+ - Whether the Role of the Service Principal is set.
+ type: bool
+ returned: always
+ sample: false
+ object_id:
+ description:
+ - It's service principal's object ID.
+ returned: always
+ type: str
+ sample: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
'''
@@ -80,7 +84,8 @@ object_id:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.service_principals.service_principals_request_builder import ServicePrincipalsRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
@@ -92,10 +97,8 @@ class AzureRMADServicePrincipalInfo(AzureRMModuleBase):
self.module_arg_spec = dict(
app_id=dict(type='str'),
object_id=dict(type='str'),
- tenant=dict(type='str', required=True),
)
- self.tenant = None
self.app_id = None
self.object_id = None
self.results = dict(changed=False)
@@ -110,17 +113,18 @@ class AzureRMADServicePrincipalInfo(AzureRMModuleBase):
for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])
+ self._client = self.get_msgraph_client()
+
service_principals = []
try:
- client = self.get_graphrbac_client(self.tenant)
if self.object_id is None:
- service_principals = list(client.service_principals.list(filter="servicePrincipalNames/any(c:c eq '{0}')".format(self.app_id)))
+ service_principals = asyncio.get_event_loop().run_until_complete(self.get_service_principals())
else:
- service_principals = [client.service_principals.get(self.object_id)]
+ service_principals = [asyncio.get_event_loop().run_until_complete(self.get_service_principal())]
self.results['service_principals'] = [self.to_dict(sp) for sp in service_principals]
- except GraphErrorException as ge:
+ except Exception as ge:
self.fail("failed to get service principal info {0}".format(str(ge)))
return self.results
@@ -128,11 +132,32 @@ class AzureRMADServicePrincipalInfo(AzureRMModuleBase):
def to_dict(self, object):
return dict(
app_id=object.app_id,
- object_id=object.object_id,
+ object_id=object.id,
app_display_name=object.display_name,
app_role_assignment_required=object.app_role_assignment_required
)
+ async def get_service_principal(self):
+ return await self._client.service_principals.by_service_principal_id(self.object_id).get()
+
+ async def get_service_principals(self):
+ kwargs = {}
+ if self.app_id is not None:
+ request_configuration = ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetRequestConfiguration(
+ query_parameters=ServicePrincipalsRequestBuilder.ServicePrincipalsRequestBuilderGetQueryParameters(
+ filter="servicePrincipalNames/any(c:c eq '{0}')".format(self.app_id))
+ )
+ kwargs['request_configuration'] = request_configuration
+ service_principals = []
+ response = await self._client.service_principals.get(**kwargs)
+ if response:
+ service_principals += response.value
+ while response is not None and response.odata_next_link is not None:
+ response = await self._client.service_principals.with_url(response.odata_next_link).get(**kwargs)
+ if response:
+ service_principals += response.value
+ return service_principals
+
def main():
AzureRMADServicePrincipalInfo()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py
index b41c5ae7e..1e0a238c0 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser.py
@@ -5,6 +5,7 @@
# 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 = '''
@@ -18,11 +19,6 @@ description:
- Create, delete, and update an Azure Active Directory user.
options:
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
state:
description:
- State of the ad user. Use C(present) to create or update an ad user and C(absent) to delete an ad user.
@@ -57,11 +53,13 @@ options:
- The surname for the user.
- Used when either creating or updating a user account.
type: str
- immutable_id:
+ on_premises_immutable_id:
description:
- - The immutable_id of the user.
+ - The on_premises_immutable_id of the user.
- Used when either creating or updating a user account.
type: str
+ aliases:
+ - immutable_id
mail:
description:
- The primary email address of the user.
@@ -114,6 +112,13 @@ options:
- Filter that can be used to specify a user to update or delete.
- Mutually exclusive with I(object_id), I(attribute_name), and I(user_principal_name).
type: str
+ company_name:
+ description:
+ - The name of the company that the user is associated with.
+ - This property can be useful for describing the company that an external user comes from.
+ - The maximum length is 64 characters.Returned only on $select.
+ - Supports $filter (eq, ne, not, ge, le, in, startsWith, and eq on null values).
+ type: str
extends_documentation_fragment:
- azure.azcollection.azure
@@ -126,30 +131,28 @@ EXAMPLES = '''
- name: Create user
azure_rm_aduser:
user_principal_name: "{{ user_id }}"
- tenant: "{{ tenant_id }}"
state: "present"
account_enabled: "True"
display_name: "Test_{{ user_principal_name }}_Display_Name"
password_profile: "password"
mail_nickname: "Test_{{ user_principal_name }}_mail_nickname"
- immutable_id: "{{ object_id }}"
+ on_premises_immutable_id: "{{ object_id }}"
given_name: "First"
surname: "Last"
user_type: "Member"
usage_location: "US"
mail: "{{ user_principal_name }}@contoso.com"
+ company_name: 'Test Company'
- name: Update user with new value for account_enabled
azure_rm_aduser:
user_principal_name: "{{ user_id }}"
- tenant: "{{ tenant_id }}"
state: "present"
account_enabled: "False"
- name: Delete user
azure_rm_aduser:
user_principal_name: "{{ user_id }}"
- tenant: "{{ tenant_id }}"
state: "absent"
'''
@@ -196,15 +199,21 @@ user_type:
returned: always
type: str
sample: Member
+company_name:
+ description:
+ - The name of the company that the user is associated with.
+ type: str
+ returned: always
+ sample: 'Test Company'
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import UserUpdateParameters
- from azure.graphrbac.models import UserCreateParameters
- from azure.graphrbac.models import PasswordProfile
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.models.password_profile import PasswordProfile
+ from msgraph.generated.models.user import User
+ from msgraph.generated.users.users_request_builder import UsersRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
@@ -224,16 +233,15 @@ class AzureRMADUser(AzureRMModuleBase):
display_name=dict(type='str'),
password_profile=dict(type='str', no_log=True),
mail_nickname=dict(type='str'),
- immutable_id=dict(type='str'),
+ on_premises_immutable_id=dict(type='str', aliases=['immutable_id']),
usage_location=dict(type='str'),
given_name=dict(type='str'),
surname=dict(type='str'),
user_type=dict(type='str'),
mail=dict(type='str'),
- tenant=dict(type='str', required=True),
+ company_name=dict(type='str')
)
- self.tenant = None
self.user_principal_name = None
self.state = None
self.object_id = None
@@ -244,12 +252,13 @@ class AzureRMADUser(AzureRMModuleBase):
self.display_name = None
self.password_profile = None
self.mail_nickname = None
- self.immutable_id = None
+ self.on_premises_immutable_id = None
self.usage_location = None
self.given_name = None
self.surname = None
self.user_type = None
self.mail = None
+ self.company_name = None
self.log_path = None
self.log_mode = None
@@ -273,9 +282,9 @@ class AzureRMADUser(AzureRMModuleBase):
setattr(self, key, kwargs[key])
try:
- client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
- ad_user = self.get_exisiting_user(client)
+ ad_user = self.get_exisiting_user()
if self.state == 'present':
@@ -284,11 +293,13 @@ class AzureRMADUser(AzureRMModuleBase):
password = None
if self.password_profile:
- password = PasswordProfile(password=self.password_profile)
+ password = PasswordProfile(
+ password=self.password_profile,
+ )
should_update = False
- if self.immutable_id and ad_user.immutable_id != self.immutable_id:
+ if self.on_premises_immutable_id and ad_user.on_premises_immutable_id != self.on_premises_immutable_id:
should_update = True
if should_update or self.usage_location and ad_user.usage_location != self.usage_location:
should_update = True
@@ -308,84 +319,73 @@ class AzureRMADUser(AzureRMModuleBase):
should_update = True
if should_update or self.mail_nickname and ad_user.mail_nickname != self.mail_nickname:
should_update = True
+ if should_update or self.company_name and ad_user.company_name != self.company_name:
+ should_update = True
if should_update:
- parameters = UserUpdateParameters(immutable_id=self.immutable_id,
- usage_location=self.usage_location,
- given_name=self.given_name,
- surname=self.surname,
- user_type=self.user_type,
- account_enabled=self.account_enabled,
- display_name=self.display_name,
- password_profile=password,
- user_principal_name=self.user_principal_name,
- mail_nickname=self.mail_nickname)
-
- client.users.update(upn_or_object_id=ad_user.object_id, parameters=parameters)
+ asyncio.get_event_loop().run_until_complete(self.update_user(ad_user, password))
self.results['changed'] = True
# Get the updated versions of the users to return
# the update method, has no return value so it needs to be explicitely returned in a call
- ad_user = self.get_exisiting_user(client)
+ ad_user = self.get_exisiting_user()
else:
self.results['changed'] = False
else: # Create, changed
- password = PasswordProfile(password=self.password_profile)
- parameters = UserCreateParameters(account_enabled=self.account_enabled,
- display_name=self.display_name,
- password_profile=password,
- user_principal_name=self.user_principal_name,
- mail_nickname=self.mail_nickname,
- immutable_id=self.immutable_id,
- usage_location=self.usage_location,
- given_name=self.given_name,
- surname=self.surname,
- user_type=self.user_type,
- mail=self.mail)
- ad_user = client.users.create(parameters=parameters)
+ asyncio.get_event_loop().run_until_complete(self.create_user())
self.results['changed'] = True
+ ad_user = self.get_exisiting_user()
self.results['ad_user'] = self.to_dict(ad_user)
elif self.state == 'absent':
if ad_user: # Delete, changed
- client.users.delete(ad_user.object_id)
+ asyncio.get_event_loop().run_until_complete(self.delete_user(ad_user))
self.results['changed'] = True
else: # Do nothing unchanged
self.results['changed'] = False
- except GraphErrorException as e:
+ except Exception as e:
self.fail("failed to get ad user info {0}".format(str(e)))
return self.results
- def get_exisiting_user(self, client):
+ def get_exisiting_user(self):
ad_user = None
try:
if self.user_principal_name is not None:
- ad_user = client.users.get(self.user_principal_name)
+ ad_user = asyncio.get_event_loop().run_until_complete(self.get_user(self.user_principal_name))
elif self.object_id is not None:
- ad_user = client.users.get(self.object_id)
+ ad_user = asyncio.get_event_loop().run_until_complete(self.get_user(self.object_id))
elif self.attribute_name is not None and self.attribute_value is not None:
try:
- ad_user = list(client.users.list(filter="{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))[0]
- except GraphErrorException as e:
+ users = asyncio.get_event_loop().run_until_complete(
+ self.get_users_by_filter("{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))
+ ad_users = list(users.value)
+ ad_user = ad_users[0]
+ except Exception as e:
# the type doesn't get more specific. Could check the error message but no guarantees that message doesn't change in the future
# more stable to try again assuming the first error came from the attribute being a list
try:
- ad_user = list(client.users.list(filter="{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value)))[0]
- except GraphErrorException as sub_e:
+ users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter(
+ "{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value)))
+ ad_users = list(users.value)
+ ad_user = ad_users[0]
+ except Exception as sub_e:
raise
elif self.odata_filter is not None: # run a filter based on user input to return based on any given attribute/query
- ad_user = list(client.users.list(filter=self.odata_filter))[0]
- except GraphErrorException as e:
+ users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter(self.odata_filter))
+ ad_users = list(users.value)
+ ad_user = ad_users[0]
+ except Exception as e:
# User was not found
err_msg = str(e)
- if err_msg == "Resource '{0}' does not exist or one of its queried reference-property objects are not present.".format(self.user_principal_name):
+ if "Resource '{0}' does not exist or one of its queried reference-property objects are not present.".format(
+ self.user_principal_name) in err_msg:
ad_user = None
else:
raise
@@ -393,13 +393,75 @@ class AzureRMADUser(AzureRMModuleBase):
def to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
user_principal_name=object.user_principal_name,
mail_nickname=object.mail_nickname,
mail=object.mail,
account_enabled=object.account_enabled,
- user_type=object.user_type
+ user_type=object.user_type,
+ company_name=object.company_name
+ )
+
+ async def update_user(self, ad_user, password):
+ request_body = User(
+ on_premises_immutable_id=self.on_premises_immutable_id,
+ usage_location=self.usage_location,
+ given_name=self.given_name,
+ surname=self.surname,
+ user_type=self.user_type,
+ account_enabled=self.account_enabled,
+ display_name=self.display_name,
+ password_profile=password,
+ user_principal_name=self.user_principal_name,
+ mail_nickname=self.mail_nickname,
+ company_name=self.company_name
+ )
+ return await self._client.users.by_user_id(ad_user.id).patch(body=request_body)
+
+ async def create_user(self):
+ password = PasswordProfile(
+ password=self.password_profile
+ )
+ request_body = User(
+ account_enabled=self.account_enabled,
+ display_name=self.display_name,
+ password_profile=password,
+ user_principal_name=self.user_principal_name,
+ mail_nickname=self.mail_nickname,
+ on_premises_immutable_id=self.on_premises_immutable_id,
+ usage_location=self.usage_location,
+ given_name=self.given_name,
+ surname=self.surname,
+ user_type=self.user_type,
+ mail=self.mail,
+ company_name=self.company_name
+ )
+ return await self._client.users.post(body=request_body)
+
+ async def delete_user(self, ad_user):
+ return await self._client.users.by_user_id(ad_user.id).delete()
+
+ async def get_user(self, object):
+ request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
+ query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
+ select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType",
+ "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName"]
+ ),
+ )
+ return await self._client.users.by_user_id(object).get(request_configuration=request_configuration)
+
+ async def get_users_by_filter(self, filter):
+ return await self._client.users.get(
+ request_configuration=UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
+ query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
+ filter=filter,
+ select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
+ "userType", "onPremisesImmutableId", "usageLocation", "givenName", "surname", "companyName"],
+ count=True
+ ),
+ headers={'ConsistencyLevel': "eventual", }
+ )
)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser_info.py
index 85460e741..98c30be57 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aduser_info.py
@@ -5,6 +5,7 @@
# 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 = '''
@@ -18,11 +19,6 @@ description:
- Get Azure Active Directory user info.
options:
- tenant:
- description:
- - The tenant ID.
- type: str
- required: True
object_id:
description:
- The object id for the user.
@@ -74,34 +70,28 @@ EXAMPLES = '''
- name: Using user_principal_name
azure.azcollection.azure_rm_aduser_info:
user_principal_name: user@contoso.com
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Using object_id
azure.azcollection.azure_rm_aduser_info:
object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Using attribute mailNickname - not a collection
azure.azcollection.azure_rm_aduser_info:
attribute_name: mailNickname
attribute_value: users_mailNickname
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Using attribute proxyAddresses - a collection
azure.azcollection.azure_rm_aduser_info:
attribute_name: proxyAddresses
attribute_value: SMTP:user@contoso.com
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Using Filter mailNickname
azure.azcollection.azure_rm_aduser_info:
odata_filter: mailNickname eq 'user@contoso.com'
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: Using Filter proxyAddresses
azure.azcollection.azure_rm_aduser_info:
odata_filter: proxyAddresses/any(c:c eq 'SMTP:user@contoso.com')
- tenant: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
'''
RETURN = '''
@@ -147,12 +137,19 @@ user_type:
returned: always
type: str
sample: Member
+company_name:
+ description:
+ - The name of the company that the user is associated with.
+ type: str
+ returned: always
+ sample: "Test Company"
'''
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBase
try:
- from azure.graphrbac.models import GraphErrorException
+ import asyncio
+ from msgraph.generated.users.users_request_builder import UsersRequestBuilder
except ImportError:
# This is handled in azure_rm_common
pass
@@ -168,10 +165,8 @@ class AzureRMADUserInfo(AzureRMModuleBase):
attribute_value=dict(type='str'),
odata_filter=dict(type='str'),
all=dict(type='bool'),
- tenant=dict(type='str', required=True),
)
- self.tenant = None
self.user_principal_name = None
self.object_id = None
self.attribute_name = None
@@ -203,44 +198,88 @@ class AzureRMADUserInfo(AzureRMModuleBase):
ad_users = []
try:
- client = self.get_graphrbac_client(self.tenant)
+ self._client = self.get_msgraph_client()
if self.user_principal_name is not None:
- ad_users = [client.users.get(self.user_principal_name)]
+ ad_users = [asyncio.get_event_loop().run_until_complete(self.get_user(self.user_principal_name))]
elif self.object_id is not None:
- ad_users = [client.users.get(self.object_id)]
+ ad_users = [asyncio.get_event_loop().run_until_complete(self.get_user(self.object_id))]
elif self.attribute_name is not None and self.attribute_value is not None:
try:
- ad_users = list(client.users.list(filter="{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))
- except GraphErrorException as e:
+ users = asyncio.get_event_loop().run_until_complete(
+ self.get_users_by_filter("{0} eq '{1}'".format(self.attribute_name, self.attribute_value)))
+ ad_users = list(users.value)
+ except Exception as e:
# the type doesn't get more specific. Could check the error message but no guarantees that message doesn't change in the future
# more stable to try again assuming the first error came from the attribute being a list
try:
- ad_users = list(client.users.list(filter="{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value)))
- except GraphErrorException as sub_e:
+ users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter(
+ "{0}/any(c:c eq '{1}')".format(self.attribute_name, self.attribute_value)))
+ ad_users = list(users.value)
+ except Exception as sub_e:
raise
elif self.odata_filter is not None: # run a filter based on user input to return based on any given attribute/query
- ad_users = list(client.users.list(filter=self.odata_filter))
+ users = asyncio.get_event_loop().run_until_complete(self.get_users_by_filter(self.odata_filter))
+ ad_users = list(users.value)
elif self.all:
- ad_users = list(client.users.list())
+ # this returns as a list, since we parse multiple pages
+ ad_users = asyncio.get_event_loop().run_until_complete(self.get_users())
self.results['ad_users'] = [self.to_dict(user) for user in ad_users]
- except GraphErrorException as e:
+ except Exception as e:
self.fail("failed to get ad user info {0}".format(str(e)))
return self.results
def to_dict(self, object):
return dict(
- object_id=object.object_id,
+ object_id=object.id,
display_name=object.display_name,
user_principal_name=object.user_principal_name,
mail_nickname=object.mail_nickname,
mail=object.mail,
account_enabled=object.account_enabled,
- user_type=object.user_type
+ user_type=object.user_type,
+ company_name=object.company_name
+ )
+
+ async def get_user(self, object):
+ request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
+ query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
+ select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType", "companyName"]
+ ),
+ )
+ return await self._client.users.by_user_id(object).get(request_configuration=request_configuration)
+
+ async def get_users(self):
+ request_configuration = UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
+ query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
+ select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName", "userType", "companyName"]
+ ),
)
+ users = []
+ # paginated response can be quite large
+ response = await self._client.users.get(request_configuration=request_configuration)
+ if response:
+ users += response.value
+ while response is not None and response.odata_next_link is not None:
+ response = await self._client.users.with_url(response.odata_next_link).get(request_configuration=request_configuration)
+ if response:
+ users += response.value
+
+ return users
+
+ async def get_users_by_filter(self, filter):
+ return await self._client.users.get(
+ request_configuration=UsersRequestBuilder.UsersRequestBuilderGetRequestConfiguration(
+ query_parameters=UsersRequestBuilder.UsersRequestBuilderGetQueryParameters(
+ filter=filter,
+ select=["accountEnabled", "displayName", "mail", "mailNickname", "id", "userPrincipalName",
+ "userType", "companyName"],
+ count=True
+ ),
+ ))
def main():
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks.py
index bb034b48b..0fb5095fe 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks.py
@@ -284,7 +284,7 @@ options:
type: str
managed:
description:
- - Whether to enable manged AAD.
+ - Whether to enable managed AAD.
type: bool
default: false
admin_group_object_ids:
@@ -356,6 +356,57 @@ options:
- Name of the resource group containing agent pool nodes.
- Unable to update.
type: str
+ pod_identity_profile:
+ description:
+ - Config pod identities in managed Kubernetes cluster.
+ type: dict
+ suboptions:
+ enabled:
+ description:
+ - Whether the pod identity addon is enabled.
+ type: bool
+ allow_network_plugin_kubenet:
+ description:
+ - Whether using Kubenet network plugin with AAD Pod Identity.
+ type: bool
+ user_assigned_identities:
+ description:
+ - The pod identities to use in the cluster.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - The name of the pod identity.
+ type: str
+ required: true
+ namespace:
+ description:
+ - The namespace of the pod identity.
+ type: str
+ required: true
+ binding_selector:
+ description:
+ - The binding selector to use for the AzureIdentityBinding resource.
+ type: str
+ identity:
+ description:
+ - The user assigned identity details.
+ type: dict
+ required: true
+ suboptions:
+ resource_id:
+ description:
+ - The resource ID of the user assigned identity.
+ type: str
+ object_id:
+ description:
+ - The object ID of the user assigned identity.
+ type: str
+ client_id:
+ description:
+ - The client ID of the user assigned identity.
+ type: str
extends_documentation_fragment:
- azure.azcollection.azure
@@ -463,6 +514,45 @@ EXAMPLES = '''
type: VirtualMachineScaleSets
enable_auto_scaling: false
+- name: Create an AKS instance wit pod_identity_profile settings
+ azure_rm_aks:
+ name: "aks{{ rpfx }}"
+ resource_group: "{{ resource_group }}"
+ location: eastus
+ dns_prefix: "aks{{ rpfx }}"
+ kubernetes_version: "{{ versions.azure_aks_versions[0] }}"
+ service_principal:
+ client_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ client_secret: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ linux_profile:
+ admin_username: azureuser
+ ssh_key: ssh-rsa AAAAB3Ip6***************
+ agent_pool_profiles:
+ - name: default
+ count: 1
+ vm_size: Standard_B2s
+ type: VirtualMachineScaleSets
+ mode: System
+ node_labels: {"release":"stable"}
+ max_pods: 42
+ availability_zones:
+ - 1
+ - 2
+ node_resource_group: "node{{ noderpfx }}"
+ enable_rbac: true
+ network_profile:
+ load_balancer_sku: standard
+ pod_identity_profile:
+ enabled: false
+ allow_network_plugin_kubenet: false
+ user_assigned_identities:
+ - name: fredtest
+ namespace: fredtest
+ binding_selector: test
+ identity:
+ client_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ object_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+
- name: Remove a managed Azure Container Services (AKS) instance
azure_rm_aks:
name: myAKS
@@ -490,7 +580,7 @@ state:
changed: false
dns_prefix: aks9860bdcd89
id: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.ContainerService/managedClusters/aks9860bdc"
- kube_config: "......"
+ kube_config: ["......"]
kubernetes_version: 1.14.6
linux_profile:
admin_username: azureuser
@@ -500,13 +590,29 @@ state:
provisioning_state: Succeeded
service_principal_profile:
client_id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ pod_identity_profile: {
+ "allow_network_plugin_kubenet": false,
+ "user_assigned_identities": [
+ {
+ "binding_selector": "test",
+ "identity": {
+ "client_id": xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
+ "object_id": xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ },
+ "name": "fredtest",
+ "namespace": "fredtest",
+ "provisioning_state": "Updating"
+ }
+ ]
+ }
tags: {}
type: Microsoft.ContainerService/ManagedClusters
'''
-from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt
try:
from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.exceptions import HttpResponseError
except ImportError:
# This is handled in azure_rm_common
pass
@@ -540,10 +646,19 @@ def create_aks_dict(aks):
api_server_access_profile=create_api_server_access_profile_dict(aks.api_server_access_profile),
addon=create_addon_dict(aks.addon_profiles),
fqdn=aks.fqdn,
- node_resource_group=aks.node_resource_group
+ node_resource_group=aks.node_resource_group,
+ pod_identity_profile=create_pod_identity_profile(aks.pod_identity_profile.as_dict()) if aks.pod_identity_profile else None
)
+def create_pod_identity_profile(pod_profile):
+ return dict(
+ enabled=pod_profile.get('enabled', False),
+ allow_network_plugin_kubenet=pod_profile.get('allow_network_plugin_kubenet', False),
+ user_assigned_identities=pod_profile.get('user_assigned_identities')
+ ) if pod_profile else {}
+
+
def create_network_profiles_dict(network):
return dict(
network_plugin=network.network_plugin,
@@ -715,7 +830,7 @@ api_server_access_profile_spec = dict(
)
-class AzureRMManagedCluster(AzureRMModuleBase):
+class AzureRMManagedCluster(AzureRMModuleBaseExt):
"""Configuration class for an Azure RM container service (AKS) resource"""
def __init__(self):
@@ -777,6 +892,31 @@ class AzureRMManagedCluster(AzureRMModuleBase):
),
node_resource_group=dict(
type='str'
+ ),
+ pod_identity_profile=dict(
+ type='dict',
+ options=dict(
+ enabled=dict(type='bool'),
+ allow_network_plugin_kubenet=dict(type='bool'),
+ user_assigned_identities=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ name=dict(type='str', required=True),
+ namespace=dict(type='str', required=True),
+ binding_selector=dict(type='str'),
+ identity=dict(
+ type='dict',
+ required=True,
+ options=dict(
+ resource_id=dict(type='str'),
+ client_id=dict(type='str'),
+ object_id=dict(type='str')
+ )
+ )
+ )
+ )
+ )
)
)
@@ -796,6 +936,7 @@ class AzureRMManagedCluster(AzureRMModuleBase):
self.api_server_access_profile = None
self.addon = None
self.node_resource_group = None
+ self.pod_identity_profile = None
required_if = [
('state', 'present', [
@@ -972,6 +1113,10 @@ class AzureRMManagedCluster(AzureRMModuleBase):
if not matched:
self.log("Agent Pool not found")
to_be_updated = True
+ if not self.default_compare({}, self.pod_identity_profile, response['pod_identity_profile'], '', dict(compare=[])):
+ to_be_updated = True
+ else:
+ self.pod_identity_profile = response['pod_identity_profile']
if update_agentpool:
self.log("Need to update agentpool")
@@ -1044,6 +1189,15 @@ class AzureRMManagedCluster(AzureRMModuleBase):
else:
linux_profile = None
+ if self.pod_identity_profile:
+ pod_identity_profile = self.managedcluster_models.ManagedClusterPodIdentityProfile(
+ enabled=self.pod_identity_profile.get('enabled'),
+ allow_network_plugin_kubenet=self.pod_identity_profile.get('allow_network_plugin_kubenet'),
+ user_assigned_identities=self.pod_identity_profile.get('user_assigned_identities')
+ )
+ else:
+ pod_identity_profile = None
+
parameters = self.managedcluster_models.ManagedCluster(
location=self.location,
dns_prefix=self.dns_prefix,
@@ -1058,7 +1212,8 @@ class AzureRMManagedCluster(AzureRMModuleBase):
aad_profile=self.create_aad_profile_instance(self.aad_profile),
api_server_access_profile=self.create_api_server_access_profile_instance(self.api_server_access_profile),
addon_profiles=self.create_addon_profile_instance(self.addon),
- node_resource_group=self.node_resource_group
+ node_resource_group=self.node_resource_group,
+ pod_identity_profile=pod_identity_profile
)
# self.log("service_principal_profile : {0}".format(parameters.service_principal_profile))
@@ -1169,10 +1324,12 @@ class AzureRMManagedCluster(AzureRMModuleBase):
:return: AKS instance kubeconfig
'''
- access_profile = self.managedcluster_client.managed_clusters.get_access_profile(resource_group_name=self.resource_group,
- resource_name=self.name,
- role_name="clusterUser")
- return access_profile.kube_config.decode('utf-8')
+ try:
+ access_profile = self.managedcluster_client.managed_clusters.list_cluster_user_credentials(self.resource_group, self.name)
+ except HttpResponseError as ec:
+ self.log("Lists the cluster user credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return []
+ return [item.value.decode('utf-8') for item in access_profile.kubeconfigs]
def create_agent_pool_profile_instance(self, agentpoolprofile):
'''
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks_info.py
index c97bd893e..2e4fd7a26 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aks_info.py
@@ -37,10 +37,14 @@ options:
description:
- Show kubeconfig of the AKS cluster.
- Note the operation will cost more network overhead, not recommended when listing AKS.
+ - I(show_kubeconfig=monitoring) to lists the cluster monitoring user credentials of a managed cluster.
+ - I(show_kubeconfig=admin) to lists the cluster admin credentials of a managed cluster.
+ - I(show_kubeconfig=user) to lists the cluster user credentials of a managed cluster.
type: str
choices:
- user
- admin
+ - monitoring
extends_documentation_fragment:
- azure.azcollection.azure
@@ -75,6 +79,7 @@ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common
try:
from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.exceptions import HttpResponseError
except Exception:
# handled in azure_rm_common
pass
@@ -91,7 +96,7 @@ class AzureRMManagedClusterInfo(AzureRMModuleBase):
name=dict(type='str'),
resource_group=dict(type='str'),
tags=dict(type='list', elements='str'),
- show_kubeconfig=dict(type='str', choices=['user', 'admin']),
+ show_kubeconfig=dict(type='str', choices=['user', 'admin', 'monitoring']),
)
self.results = dict(
@@ -196,11 +201,29 @@ class AzureRMManagedClusterInfo(AzureRMModuleBase):
:return: AKS instance kubeconfig
'''
- if not self.show_kubeconfig:
- return ''
- role_name = 'cluster{0}'.format(str.capitalize(self.show_kubeconfig))
- access_profile = self.managedcluster_client.managed_clusters.get_access_profile(resource_group, name, role_name)
- return access_profile.kube_config.decode('utf-8')
+ if self.show_kubeconfig == 'user':
+ try:
+ access_profile = self.managedcluster_client.managed_clusters.list_cluster_user_credentials(self.resource_group, self.name)
+ except HttpResponseError as ec:
+ self.log("Lists the cluster user credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return []
+ return [item.value.decode('utf-8') for item in access_profile.kubeconfigs]
+ elif self.show_kubeconfig == 'admin':
+ try:
+ access_profile = self.managedcluster_client.managed_clusters.list_cluster_admin_credentials(self.resource_group, self.name)
+ except HttpResponseError as ec:
+ self.log("Lists the cluster admin credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return []
+ return [item.value.decode('utf-8') for item in access_profile.kubeconfigs]
+ elif self.show_kubeconfig == 'monitoring':
+ try:
+ access_profile = self.managedcluster_client.managed_clusters.list_cluster_monitoring_user_credentials(self.resource_group, self.name)
+ except HttpResponseError as ec:
+ self.log("Lists the cluster monitoring credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return []
+ return [item.value.decode('utf-8') for item in access_profile.kubeconfigs]
+ else:
+ return []
def main():
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_akscredentials_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_akscredentials_info.py
new file mode 100644
index 000000000..95a7fb104
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_akscredentials_info.py
@@ -0,0 +1,209 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_akscredentials_info
+
+version_added: "2.3.0"
+
+short_description: List Azure Kubernetes Service Credentials facts
+
+description:
+ - List Azure Kubernetes Service Credentials facts.
+
+options:
+ cluster_name:
+ description:
+ - Azure Kubernetes Service or all Azure Kubernetes Services.
+ type: str
+ required: true
+ resource_group:
+ description:
+ - The resource group to search for the desired Azure Kubernetes Service.
+ type: str
+ required: true
+ show_admin_credentials:
+ description:
+ - Whether list Cluster Admin Credentials.
+ type: bool
+ default: false
+ show_user_credentials:
+ description:
+ - Whether list Cluster User Credentials.
+ type: bool
+ default: false
+ show_monitor_credentials:
+ description:
+ - Whether list Cluster Monitoring User Credentials.
+ type: bool
+ default: false
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred Sun (@Fred-sun)
+'''
+
+EXAMPLES = '''
+- name: Get managecluster admin credentials
+ azure_rm_akscredentials_info:
+ resource_group: "{{ resource_group }}"
+ cluster_name: cluter_name
+ show_admin_credentials: true
+
+- name: Get managecluster user credentials
+ azure_rm_akscredentials_info:
+ resource_group: "{{ resource_group }}"
+ cluster_name: cluster_name
+ show_user_credentials: true
+
+- name: Get managecluster monitor user credentials
+ azure_rm_akscredentials_info:
+ resource_group: "{{ resource_group }}"
+ cluster_name: cluster_name
+ show_monitor_credentials: true
+'''
+
+RETURN = '''
+cluster_credentials:
+ description:
+ - Lists the cluster user, admin or monitoring user credentials of a managed cluster
+ returned: always
+ type: complex
+ contains:
+ cluster_name:
+ description:
+ - Azure Kubernetes Service or all Azure Kubernetes Services.
+ type: str
+ returned: always
+ sample: testcluster01
+ resource_group:
+ description:
+ - The resource group to search for the desired Azure Kubernetes Service.
+ type: str
+ returned: always
+ sample:
+ name:
+ description:
+ - The name of the credential.
+ type: str
+ returned: always
+ sample:
+ value:
+ description:
+ - Base64-encoded Kubernetes configuration file.
+ type: str
+ returned: always
+ sample: "apiVersion: ************************"
+'''
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+try:
+ from azure.core.exceptions import HttpResponseError
+except Exception:
+ # handled in azure_rm_common
+ pass
+
+AZURE_OBJECT_CLASS = 'managedClustersCredentials'
+
+
+class AzureRMAksCredentialsInfo(AzureRMModuleBase):
+ """Utility class to get Azure Kubernetes Service Credentials facts"""
+
+ def __init__(self):
+
+ self.module_args = dict(
+ cluster_name=dict(type='str', required=True),
+ resource_group=dict(type='str', required=True),
+ show_admin_credentials=dict(type='bool', default=False),
+ show_user_credentials=dict(type='bool', default=False),
+ show_monitor_credentials=dict(type='bool', default=False),
+ )
+
+ self.results = dict(
+ changed=False,
+ cluster_credentials=[],
+ )
+
+ mutually_exclusive = [('show_admin_credentials', 'show_user_credentials', 'show_monitor_credentials')]
+
+ super(AzureRMAksCredentialsInfo, self).__init__(
+ derived_arg_spec=self.module_args,
+ supports_check_mode=True,
+ supports_tags=False,
+ mutually_exclusive=mutually_exclusive,
+ facts_module=True
+ )
+
+ def exec_module(self, **kwargs):
+
+ for key in self.module_args:
+ setattr(self, key, kwargs[key])
+
+ if self.show_user_credentials:
+ self.results['cluster_credentials'] = self.get_user_credentials()
+ elif self.show_admin_credentials:
+ self.results['cluster_credentials'] = self.get_admin_credentials()
+ elif self.show_monitor_credentials:
+ self.results['cluster_credentials'] = self.get_monitor_credentials()
+
+ self.results['resource_group'] = self.resource_group
+ self.results['cluster_name'] = self.cluster_name
+ return self.results
+
+ def get_user_credentials(self):
+ """Get The Azure Kubernetes Service USER Credentials"""
+ response = None
+
+ try:
+ response = self.managedcluster_client.managed_clusters.list_cluster_user_credentials(self.resource_group, self.cluster_name)
+ except HttpResponseError as ec:
+ self.fail("Lists the cluster user credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return [self.format_response(item) for item in response.kubeconfigs]
+
+ def get_admin_credentials(self):
+ """Get The Azure Kubernetes Service Admin Credentials"""
+ response = None
+
+ try:
+ response = self.managedcluster_client.managed_clusters.list_cluster_admin_credentials(self.resource_group, self.cluster_name)
+ except HttpResponseError as ec:
+ self.fail("Lists the cluster admin credentials of a managed cluster Failed, Exception as {0}".format(ec))
+
+ return [self.format_response(item) for item in response.kubeconfigs]
+
+ def get_monitor_credentials(self):
+ """Get The Azure Kubernetes Service Monitor Credentials"""
+ response = None
+
+ try:
+ response = self.managedcluster_client.managed_clusters.list_cluster_monitoring_user_credentials(self.resource_group, self.cluster_name)
+ except HttpResponseError as ec:
+ self.fail("Lists the cluster monitoring credentials of a managed cluster Failed, Exception as {0}".format(ec))
+ return [self.format_response(item) for item in response.kubeconfigs]
+
+ def format_response(self, item):
+ if not item:
+ return
+ return dict(name=item.name, value=item.value.decode('utf-8'))
+
+
+def main():
+ """Main module execution code path"""
+
+ AzureRMAksCredentialsInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aksversion_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aksversion_info.py
index 9e5af42ce..5d4f695a6 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aksversion_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_aksversion_info.py
@@ -29,6 +29,13 @@ options:
description:
- Get the upgrade versions available for a managed Kubernetes cluster version.
type: str
+ allow_preview:
+ description:
+ - Whether Kubernetes version is currently in preview.
+ - If I(allow_preview=True), returns the current preview status of the current Kubernetes version.
+ - If I(allow_preview=False), returns the Kubernetes version in a non-current preview state.
+ - If no set C(allow_preview), returns all the avaiable Kubernetes version.
+ type: bool
extends_documentation_fragment:
- azure.azcollection.azure
@@ -38,6 +45,10 @@ author:
'''
EXAMPLES = '''
+- name: Get current preview versions for AKS in location eastus
+ azure_rm_aksversion_info:
+ location: eastus
+ allow_preview: true
- name: Get available versions for AKS in location eastus
azure_rm_aksversion_info:
location: eastus
@@ -63,7 +74,8 @@ class AzureRMAKSVersion(AzureRMModuleBase):
self.module_args = dict(
location=dict(type='str', required=True),
- version=dict(type='str')
+ version=dict(type='str'),
+ allow_preview=dict(type='bool')
)
self.results = dict(
@@ -73,6 +85,7 @@ class AzureRMAKSVersion(AzureRMModuleBase):
self.location = None
self.version = None
+ self.allow_preview = None
super(AzureRMAKSVersion, self).__init__(
derived_arg_spec=self.module_args,
@@ -104,7 +117,14 @@ class AzureRMAKSVersion(AzureRMModuleBase):
response = self.containerservice_client.container_services.list_orchestrators(self.location, resource_type='managedClusters')
orchestrators = response.orchestrators
for item in orchestrators:
- result[item.orchestrator_version] = [x.orchestrator_version for x in item.upgrades] if item.upgrades else []
+ if self.allow_preview is None:
+ result[item.orchestrator_version] = [x.orchestrator_version for x in item.upgrades] if item.upgrades else []
+ elif self.allow_preview:
+ if item.is_preview:
+ result[item.orchestrator_version] = [x.orchestrator_version for x in item.upgrades] if item.upgrades else []
+ else:
+ if not item.is_preview:
+ result[item.orchestrator_version] = [x.orchestrator_version for x in item.upgrades] if item.upgrades else []
if version:
return result.get(version) or []
else:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement.py
index 97a7868d1..4e7e359bb 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement.py
@@ -296,41 +296,30 @@ class AzureApiManagement(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
service_name=dict(
type='str',
- updatable=False,
- disposition='serviceName',
required=True
),
api_id=dict(
type='str',
- updatable=False,
- disposition='apiId',
required=True
),
description=dict(
type='str',
- disposition='/properties/description'
),
authentication_settings=dict(
type='dict',
- disposition='/properties/authenticationSettings',
options=dict(
o_auth2=dict(
type='dict',
- disposition='oAuth2',
options=dict(
authorization_server_id=dict(
type='str',
- disposition='authorizationServerId'
),
scope=dict(
type='str',
- disposition='scope'
)
)
),
@@ -339,12 +328,10 @@ class AzureApiManagement(AzureRMModuleBaseExt):
options=dict(
openid_provider_id=dict(
type='str',
- disposition='openidProviderId'
),
bearer_token_sending_methods=dict(
type='list',
elements='str',
- disposition='bearerTokenSendingMethods',
choices=['authorizationHeader', 'query']
)
)
@@ -354,7 +341,6 @@ class AzureApiManagement(AzureRMModuleBaseExt):
subscription_key_parameter_names=dict(
type='dict',
no_log=True,
- disposition='/properties/subscriptionKeyParameterNames',
options=dict(
header=dict(
type='str',
@@ -368,63 +354,49 @@ class AzureApiManagement(AzureRMModuleBaseExt):
),
type=dict(
type='str',
- disposition='/properties/type',
choices=['http', 'soap']
),
api_revision=dict(
type='str',
- disposition='/properties/apiRevision'
),
api_version=dict(
type='str',
- disposition='/properties/apiVersion'
),
is_current=dict(
type='bool',
- disposition='/properties/isCurrent'
),
api_revision_description=dict(
type='str',
- disposition='/properties/apiRevisionDescription'
),
api_version_description=dict(
type='str',
- disposition='/properties/apiVersionDescription'
),
api_version_set_id=dict(
type='str',
- disposition='/properties/apiVersionSetId',
),
subscription_required=dict(
type='bool',
- disposition='/properties/subscriptionRequired'
),
source_api_id=dict(
type='str',
- disposition='/properties/sourceApiId',
),
display_name=dict(
type='str',
- disposition='/properties/displayName'
),
service_url=dict(
type='str',
- disposition='/properties/serviceUrl'
),
path=dict(
type='str',
- disposition='/properties/*',
),
protocols=dict(
type='list',
elements='str',
- disposition='/properties/protocols',
choices=['http',
'https']
),
api_version_set=dict(
type='dict',
- disposition='/properties/apiVersionSet',
options=dict(
id=dict(
type='str'
@@ -437,28 +409,23 @@ class AzureApiManagement(AzureRMModuleBaseExt):
),
versioning_scheme=dict(
type='str',
- disposition='versioningScheme',
choices=['Segment',
'Query',
'Header']
),
version_query_name=dict(
type='str',
- disposition='versionQueryName'
),
version_header_name=dict(
type='str',
- disposition='versionHeaderName'
)
)
),
value=dict(
type='str',
- disposition='/properties/*'
),
format=dict(
type='str',
- disposition='/properties/*',
choices=['wadl-xml',
'wadl-link-json',
'swagger-json',
@@ -471,21 +438,17 @@ class AzureApiManagement(AzureRMModuleBaseExt):
),
wsdl_selector=dict(
type='dict',
- disposition='/properties/wsdlSelector',
options=dict(
wsdl_service_name=dict(
type='str',
- disposition='wsdlServiceName'
),
wsdl_endpoint_name=dict(
type='str',
- disposition='wsdlEndpointName'
)
)
),
api_type=dict(
type='str',
- disposition='/properties/apiType',
choices=['http', 'soap']
),
state=dict(
@@ -507,8 +470,9 @@ class AzureApiManagement(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
self.query_parameters = {}
- self.query_parameters['api-version'] = '2020-06-01-preview'
+ self.query_parameters['api-version'] = '2022-08-01'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -527,16 +491,81 @@ class AzureApiManagement(AzureRMModuleBaseExt):
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
+ if key == 'description':
+ self.body['properties']['description'] = kwargs[key]
+ elif key == 'authentication_settings':
+ self.body['properties']['authenticationSettings'] = {}
+ if kwargs[key].get('o_auth2') is not None:
+ self.body['properties']['authenticationSettings']['oAuth2'] = {}
+ for item in kwargs[key]['o_auth2'].keys():
+ if item == 'authorization_server_id':
+ authorization_id = kwargs[key]['o_auth2']['authorization_server_id']
+ self.body['properties']['authenticationSettings']['oAuth2']['authorizationServerId'] = authorization_id
+ elif item == 'scope':
+ self.body['properties']['authenticationSettings']['oAuth2']['scope'] = kwargs[key]['o_auth2']['scope']
+ elif kwargs[key].get('openid') is not None:
+ self.body['properties']['authenticationSettings']['openid'] = {}
+ for item in kwargs[key]['openid'].keys():
+ if item == 'openid_provider_id' and kwargs[key]['openid'].get('openid_provider_id') is not None:
+ openid_pro = kwargs[key]['openid'].get('openid_provider_id')
+ self.body['properties']['authenticationSettings']['openid']['openidProviderId'] = openid_pro
+ elif item == 'bearer_token_sending_methods' and kwargs[key]['openid'].get('bearer_token_sending_methods') is not None:
+ bearer_token = kwargs[key]['openid']['bearer_token_sending_methods']
+ self.body['properties']['authenticationSettings']['openid']['bearerTokenSendingMethods'] = bearer_token
+ elif key == 'subscription_key_parameter_names':
+ self.body['properties']['subscriptionKeyParameterNames'] = kwargs[key]
+ elif key == 'type':
+ self.body['properties']['type'] = kwargs[key]
+ elif key == 'api_revision':
+ self.body['properties']['apiRevision'] = kwargs[key]
+ elif key == 'api_version':
+ self.body['properties']['apiVersion'] = kwargs[key]
+ elif key == 'is_current':
+ self.body['properties']['isCurrent'] = kwargs[key]
+ elif key == 'api_revision_description':
+ self.body['properties']['apiRevisionDescription'] = kwargs[key]
+ elif key == 'api_version_description':
+ self.body['properties']['apiVersionDescription'] = kwargs[key]
+ elif key == 'api_version_set_id':
+ self.body['properties']['apiVersionSetId'] = kwargs[key]
+ elif key == 'subscription_required':
+ self.body['properties']['subscriptionRequired'] = kwargs[key]
+ elif key == 'source_api_id':
+ self.body['properties']['sourceApiId'] = kwargs[key]
+ elif key == 'display_name':
+ self.body['properties']['displayName'] = kwargs[key]
+ elif key == 'service_url':
+ self.body['properties']['serviceUrl'] = kwargs[key]
+ elif key == 'protocols':
+ self.body['properties']['protocols'] = kwargs[key]
+ elif key == 'api_version_set':
+ self.body['properties']['apiVersionSet'] = {}
+ for item in kwargs[key].keys():
+ if item == 'versioning_scheme':
+ self.body['properties']['apiVersionSet'] = kwargs[key].get('versioning_scheme')
+ elif item == 'version_query_name':
+ self.body['properties']['versionQueryName'] = kwargs[key].get('version_query_name')
+ elif item == 'version_header_name':
+ self.body['properties']['versionHeaderName'] = kwargs[key].get('version_header_name')
+ else:
+ self.body['properties'][item] = kwargs[key].get(item)
+ elif key == 'wsdl_selector':
+ self.body['properties']['wsdlSelector'] = {}
+ for item in kwargs[key].keys():
+ if item == 'wsdl_service_name':
+ self.body['properties']['wsdlSelector']['wsdlServiceName'] = kwargs[key].get(item)
+ if item == 'wsdl_endpoint_name':
+ self.body['properties']['wsdlSelector']['wsdlEndpointName'] = kwargs[key].get(item)
+ elif key == 'api_type':
+ self.body['properties']['apiType'] = kwargs[key]
+ else:
+ self.body['properties'][key] = kwargs[key]
- # https://docs.microsoft.com/en-us/azure/templates/microsoft.apimanagement/service/apis
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
self.url = self.get_url()
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_resource()
@@ -552,11 +581,75 @@ class AzureApiManagement(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body['properties'].get('description') is not None and \
+ self.body['properties']['description'] != old_response['properties']['description']:
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('authenticationSettings') is not None:
+ if old_response['properties'].get('authenticationSettings') is None:
+ self.to_do = Actions.Update
+ elif (self.body['properties']['authenticationSettings'].get('oAuth2') is not None and
+ self.body['properties']['authenticationSettings']['oAuth2'] != old_response['properties']['authenticationSettings'].get('oAuth2')):
+ self.to_do = Actions.Update
+ elif (self.body['properties']['authenticationSettings'].get('openid') is not None and
+ self.body['properties']['authenticationSettings']['openid'] != old_response['properties']['authenticationSettings'].get('openid')):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('subscriptionKeyParameterNames') is not None:
+ tt = old_response['properties']
+ if old_response['properties'].get('subscriptionKeyParameterNames') is None:
+ self.to_do = Actions.Update
+ elif (not all(self.body['properties']['subscriptionKeyParameterNames'].get(item) == tt['subscriptionKeyParameterNames'].get(item)
+ for item in self.body['properties']['subscriptionKeyParameterNames'].keys())):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiRevision') is not None and
+ self.body['properties']['apiRevision'] != old_response['properties'].get('apiRevision')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiVersion') is not None and
+ self.body['properties']['apiVersion'] != old_response['properties'].get('apiVersion')):
+ self.to_do == Actions.Update
+ elif (self.body['properties'].get('isCurrent') is not None and
+ self.body['properties']['isCurrent'] != old_response['properties'].get('isCurrent')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiRevisionDescription') is not None and self.body['properties']['apiRevisionDescription'] !=
+ old_response['properties'].get('apiRevisionDescription')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiVersionDescription') is not None and
+ self.body['properties']['apiVersionDescription'] != old_response['properties'].get('apiVersionDescription')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiVersionSetId') is not None and
+ self.body['properties']['apiVersionSetId'] != old_response['properties'].get('apiVersionSetId')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('subscriptionRequired') is not None and
+ self.body['properties']['subscriptionRequired'] != old_response['properties'].get('subscriptionRequired')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('sourceApiId') is not None and
+ self.body['properties']['sourceApiId'] != old_response['properties'].get('sourceApiId')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('displayName') is not None and
+ self.body['properties']['displayName'] != old_response['properties'].get('displayName')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('serviceUrl') is not None and
+ self.body['properties']['serviceUrl'] != old_response['properties'].get('serviceUrl')):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('path') is not None and self.body['properties']['path'] != old_response['properties'].get('path'):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('protocols') is not None and
+ self.body['properties']['protocols'] != old_response['properties'].get('protocols')):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('type') is not None and self.body['properties']['type'] != old_response['properties'].get('type'):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('apiType') is not None and self.body['properties']['apiType'] != old_response['properties'].get('apiType'):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('value') is not None and self.body['properties']['value'] != old_response['properties'].get('value'):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('format') is not None and self.body['properties']['format'] != old_response['properties'].get('format'):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('wsdlSelector') is not None and
+ not all(self.body['properties']['wsdlSelector'][item] == old_response['properties']['wsdlSelector'].get(item)
+ for item in self.body['properties']['wsdlSelector'].keys())):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('apiVersionSet') is not None and
+ not all(self.body['properties']['apiVersionSet'][item] == old_response['properties']['apiVersionSet'].get(item)
+ for item in self.body['properties']['apiVersionSet'].keys())):
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
@@ -605,10 +698,12 @@ class AzureApiManagement(AzureRMModuleBaseExt):
except Exception as exc:
self.log('Error while creating/updating the Api instance.')
self.fail('Error creating the Api instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement_info.py
index 30120b5e5..51e99b733 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagement_info.py
@@ -139,7 +139,7 @@ class AzureApiManagementInfo(AzureRMModuleBaseExt):
self.status_code = [200]
self.query_parameters = {}
- self.query_parameters['api-version'] = '2020-06-01-preview'
+ self.query_parameters['api-version'] = '2022-08-01'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -155,7 +155,6 @@ class AzureApiManagementInfo(AzureRMModuleBaseExt):
self.body[key] = kwargs[key]
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice.py
index 8c694bec4..6d8a0aaf9 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice.py
@@ -113,32 +113,23 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='serviceName',
required=True
),
location=dict(
type='str',
- updatable=False,
- disposition='location'
),
publisher_name=dict(
type='str',
- disposition='/properties/publisherName'
),
publisher_email=dict(
type='str',
- disposition='/properties/publisherEmail'
),
sku_name=dict(
type='str',
- disposition='/sku/name',
choices=['Developer',
'Standard',
'Premium',
@@ -147,7 +138,6 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
),
sku_capacity=dict(
type='int',
- disposition='/sku/capacity'
),
state=dict(
type='str',
@@ -168,8 +158,10 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
+ self.body['sku'] = {}
self.query_parameters = {}
- self.query_parameters['api-version'] = '2020-06-01-preview'
+ self.query_parameters['api-version'] = '2022-08-01'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -182,15 +174,21 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
-
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
+ if key == 'publisher_name':
+ self.body['properties']['publisherName'] = kwargs[key]
+ elif key == 'publisher_email':
+ self.body['properties']['publisherEmail'] = kwargs[key]
+ elif key == 'sku_name':
+ self.body['sku']['name'] = kwargs[key]
+ elif key == 'sku_capacity':
+ self.body['sku']['capacity'] = kwargs[key]
+ else:
+ self.body[key] = kwargs[key]
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -225,12 +223,15 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
- self.to_do = Actions.Update
+ for key in self.body.keys():
+ if key == 'properties':
+ for key in self.body['properties'].keys():
+ if self.body['properties'][key] != old_response['properties'].get(key):
+ self.to_do = Actions.Update
+ elif key == 'sku':
+ for key in self.body['sku'].keys():
+ if self.body['sku'][key] != old_response['sku'].get(key):
+ self.to_do = Actions.Update
self.body['location'] = self.location
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
@@ -272,23 +273,34 @@ class AzureRMApiManagementService(AzureRMModuleBaseExt):
def create_update_resource(self):
# Creating / Updating the ApiManagementService instance.
try:
- response = self.mgmt_client.query(self.url,
- 'PUT',
- self.query_parameters,
- self.header_parameters,
- self.body,
- self.status_code,
- 600,
- 30)
+ if self.to_do == Actions.Create:
+ response = self.mgmt_client.query(self.url,
+ 'PUT',
+ self.query_parameters,
+ self.header_parameters,
+ self.body,
+ self.status_code,
+ 600,
+ 30)
+ else:
+ response = self.mgmt_client.query(self.url,
+ 'PATCH',
+ self.query_parameters,
+ self.header_parameters,
+ self.body,
+ self.status_code,
+ 600,
+ 30)
except Exception as exc:
self.log('Error attempting to create the ApiManagementService instance.')
self.fail('Error creating the ApiManagementService instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
- pass
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice_info.py
index 663e87e78..5d686c98d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_apimanagementservice_info.py
@@ -143,7 +143,7 @@ class AzureRMApiManagementServiceInfo(AzureRMModuleBaseExt):
self.status_code = [200]
self.query_parameters = {}
- self.query_parameters['api-version'] = '2020-06-01-preview'
+ self.query_parameters['api-version'] = '2022-08-01'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -156,7 +156,6 @@ class AzureRMApiManagementServiceInfo(AzureRMModuleBaseExt):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and self.name is not None):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_appgateway.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_appgateway.py
index f95766fa9..8d70dda89 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_appgateway.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_appgateway.py
@@ -519,6 +519,24 @@ options:
- Whether host header should be picked from the host name of the backend HTTP settings. Default value is false.
type: bool
default: False
+ port:
+ description:
+ - Custom port which will be used for probing the backend servers.
+ - The valid value ranges from 1 to 65535.
+ - In case not set, port from http settings will be used.
+ - This property is valid for C(Standard_v2) and C(WAF_v2) only.
+ type: int
+ match:
+ description:
+ - Criterion for classifying a healthy probe response.
+ type: dict
+ suboptions:
+ status_codes:
+ description:
+ - Allowed ranges of healthy status codes.
+ - Default range of healthy status codes is 200-399.
+ type: list
+ elements: str
backend_http_settings_collection:
description:
- Backend http settings of the application gateway resource.
@@ -1419,6 +1437,54 @@ EXAMPLES = '''
max_request_body_size_in_kb: 128
file_upload_limit_in_mb: 100
+- name: Create application gateway with multi parameters
+ azure_rm_appgateway:
+ resource_group: myResourceGroup
+ name: myappgateway
+ sku:
+ name: standard_v2
+ tier: standard_v2
+ capacity: 2
+ gateway_ip_configurations:
+ - subnet:
+ id: "{{ subnet_id }}"
+ name: app_gateway_ip_config
+ frontend_ip_configurations:
+ - name: sample_gateway_frontend_ip_config
+ public_ip_address: "pip{{ rpfx }}"
+ frontend_ports:
+ - port: 80
+ name: http_frontend_port
+ backend_address_pools:
+ - name: test_backend_address_pool # empty pool which will receive attachment to NIC.
+ backend_http_settings_collection:
+ - port: 80
+ protocol: http
+ cookie_based_affinity: enabled
+ name: sample_appgateway_http_settings
+ http_listeners:
+ - frontend_ip_configuration: sample_gateway_frontend_ip_config
+ frontend_port: http_frontend_port
+ protocol: http
+ name: http_listener
+ probes:
+ - name: testprobes01
+ protocol: http
+ path: '/'
+ timeout: 30
+ host: testazure
+ interval: 90
+ port: 80
+ match:
+ status_codes:
+ - 200
+ request_routing_rules:
+ - rule_type: basic
+ backend_address_pool: test_backend_address_pool
+ backend_http_settings: sample_appgateway_http_settings
+ http_listener: http_listener
+ name: rule1
+
- name: Stop an Application Gateway instance
azure_rm_appgateway:
resource_group: myResourceGroup
@@ -1517,6 +1583,11 @@ ssl_policy_spec = dict(
)
+match_spec = dict(
+ status_codes=dict(type='list', elements='str')
+)
+
+
probe_spec = dict(
host=dict(type='str'),
interval=dict(type='int'),
@@ -1525,7 +1596,9 @@ probe_spec = dict(
protocol=dict(type='str', choices=['http', 'https']),
timeout=dict(type='int'),
unhealthy_threshold=dict(type='int'),
- pick_host_name_from_backend_http_settings=dict(type='bool', default=False)
+ pick_host_name_from_backend_http_settings=dict(type='bool', default=False),
+ port=dict(type='int'),
+ match=dict(type='dict', options=match_spec)
)
@@ -2239,6 +2312,8 @@ class AzureRMApplicationGateways(AzureRMModuleBase):
self.parameters["web_application_firewall_configuration"] = kwargs[key]
elif key == "enable_http2":
self.parameters["enable_http2"] = kwargs[key]
+ elif key == "tags":
+ self.parameters["tags"] = kwargs[key]
response = None
@@ -2272,7 +2347,7 @@ class AzureRMApplicationGateways(AzureRMModuleBase):
(old_response['operational_state'] == 'Running' and self.gateway_state == 'started')):
self.to_do = Actions.NoAction
elif (self.parameters['location'] != old_response['location'] or
- self.parameters['enable_http2'] != old_response['enable_http2'] or
+ self.parameters['enable_http2'] != old_response.get('enable_http2', False) or
self.parameters['sku']['name'] != old_response['sku']['name'] or
self.parameters['sku']['tier'] != old_response['sku']['tier'] or
self.parameters['sku'].get('capacity', None) != old_response['sku'].get('capacity', None) or
@@ -2296,6 +2371,11 @@ class AzureRMApplicationGateways(AzureRMModuleBase):
else:
self.to_do = Actions.NoAction
+ update_tags, new_tags = self.update_tags(old_response.get('tags'))
+ if update_tags:
+ self.to_do = Actions.Update
+ self.parameters["tags"] = new_tags
+
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
self.log("Need to Create / Update the Application Gateway instance")
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall.py
index f960f024d..692628317 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall.py
@@ -354,40 +354,30 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- disposition='resource_group_name',
required=True
),
name=dict(
type='str',
- disposition='azure_firewall_name',
required=True
),
location=dict(
type='str',
- updatable=False,
- disposition='/',
- comparison='location'
),
application_rule_collections=dict(
type='list',
elements='dict',
- disposition='/properties/applicationRuleCollections',
options=dict(
priority=dict(
type='int',
- disposition='properties/*'
),
action=dict(
type='str',
choices=['allow',
'deny'],
- disposition='properties/action/type',
- pattern='camelize'
),
rules=dict(
type='list',
elements='raw',
- disposition='properties/*',
options=dict(
name=dict(
type='str'
@@ -398,7 +388,6 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
source_addresses=dict(
type='list',
elements='str',
- disposition='sourceAddresses'
),
protocols=dict(
type='list',
@@ -406,7 +395,6 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
options=dict(
type=dict(
type='str',
- disposition='protocolType'
),
port=dict(
type='str'
@@ -416,12 +404,10 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
target_fqdns=dict(
type='list',
elements='raw',
- disposition='targetFqdns'
),
fqdn_tags=dict(
type='list',
elements='raw',
- disposition='fqdnTags'
)
)
),
@@ -433,23 +419,18 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
nat_rule_collections=dict(
type='list',
elements='dict',
- disposition='/properties/natRuleCollections',
options=dict(
priority=dict(
type='int',
- disposition='properties/*'
),
action=dict(
type='str',
- disposition='properties/action/type',
choices=['snat',
'dnat'],
- pattern='camelize'
),
rules=dict(
type='list',
elements='dict',
- disposition='properties/*',
options=dict(
name=dict(
type='str'
@@ -460,17 +441,14 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
source_addresses=dict(
type='list',
elements='str',
- disposition='sourceAddresses'
),
destination_addresses=dict(
type='list',
elements='str',
- disposition='destinationAddresses'
),
destination_ports=dict(
type='list',
elements='str',
- disposition='destinationPorts'
),
protocols=dict(
type='list',
@@ -478,11 +456,9 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
),
translated_address=dict(
type='str',
- disposition='translatedAddress'
),
translated_port=dict(
type='str',
- disposition='translatedPort'
)
)
),
@@ -494,23 +470,18 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
network_rule_collections=dict(
type='list',
elements='dict',
- disposition='/properties/networkRuleCollections',
options=dict(
priority=dict(
type='int',
- disposition='properties/*'
),
action=dict(
type='str',
choices=['allow',
'deny'],
- disposition='properties/action/type',
- pattern='camelize'
),
rules=dict(
type='list',
elements='dict',
- disposition='properties/*',
options=dict(
name=dict(
type='str'
@@ -525,17 +496,14 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
source_addresses=dict(
type='list',
elements='str',
- disposition='sourceAddresses'
),
destination_addresses=dict(
type='list',
elements='str',
- disposition='destinationAddresses'
),
destination_ports=dict(
type='list',
elements='str',
- disposition='destinationPorts'
)
)
),
@@ -547,22 +515,12 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
ip_configurations=dict(
type='list',
elements='dict',
- disposition='/properties/ipConfigurations',
options=dict(
subnet=dict(
type='raw',
- disposition='properties/subnet/id',
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Network'
- '/virtualNetworks/{virtual_network_name}/subnets'
- '/{name}')
),
public_ip_address=dict(
type='raw',
- disposition='properties/publicIPAddress/id',
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Network'
- '/publicIPAddresses/{name}')
),
name=dict(
type='str'
@@ -579,6 +537,7 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
self.resource_group = None
self.name = None
self.body = {}
+ self.body['properties'] = {}
self.results = dict(changed=False)
self.mgmt_client = None
@@ -587,7 +546,6 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
self.status_code = [200, 201, 202]
self.to_do = Actions.NoAction
- self.body = {}
self.query_parameters = {}
self.query_parameters['api-version'] = '2018-11-01'
self.header_parameters = {}
@@ -602,15 +560,171 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
-
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
+ if key == 'application_rule_collections':
+ self.body['properties']['applicationRuleCollections'] = []
+ for item in kwargs[key]:
+ app_rule = dict(properties={})
+ if item.get('priority') is not None:
+ app_rule['properties']['priority'] = item['priority']
+ if item.get('action') is not None:
+ app_rule['properties']['action'] = dict(type=item['action'])
+ if item.get('name') is not None:
+ app_rule['name'] = item['name']
+ if item.get('rules') is not None:
+ app_rule['properties']['rules'] = []
+ for value in item['rules']:
+ rule_value = {}
+ if value.get('name') is not None:
+ rule_value['name'] = value['name']
+ if value.get('description') is not None:
+ rule_value['description'] = value['description']
+ if value.get('source_addresses') is not None:
+ rule_value['sourceAddresses'] = value.get('source_addresses')
+ if value.get('target_fqdns') is not None:
+ rule_value['targetFqdns'] = value.get('target_fqdns')
+ if value.get('fqdn_tags') is not None:
+ rule_value['fqdnTags'] = value.get('fqdn_tags')
+ if value.get('protocols') is not None:
+ rule_value['protocols'] = []
+ for pp in value['protocols']:
+ pro = {}
+ if pp.get('type') is not None:
+ pro['protocolType'] = pp.get('type')
+ if pp.get('port') is not None:
+ pro['port'] = pp.get('port')
+ rule_value['protocols'].append(pro)
+ app_rule['properties']['rules'].append(rule_value)
+ self.body['properties']['applicationRuleCollections'].append(app_rule)
+ elif key == 'nat_rule_collections':
+ self.body['properties']['natRuleCollections'] = []
+ for item in kwargs[key]:
+ nat_rule = dict(properties={})
+ if item.get('priority') is not None:
+ nat_rule['properties']['priority'] = item['priority']
+ if item.get('action') is not None:
+ nat_rule['properties']['action'] = dict(type=item['action'])
+ if item.get('name') is not None:
+ nat_rule['name'] = item['name']
+ if item.get('rules') is not None:
+ nat_rule['properties']['rules'] = []
+ for value in item['rules']:
+ nat_value = {}
+ if value.get('name') is not None:
+ nat_value['name'] = value.get('name')
+ if value.get('description') is not None:
+ nat_value['description'] = value.get('description')
+ if value.get('source_addresses') is not None:
+ nat_value['sourceAddresses'] = value.get('source_addresses')
+ if value.get('destination_addresses') is not None:
+ nat_value['destinationAddresses'] = value.get('destination_addresses')
+ if value.get('destination_ports') is not None:
+ nat_value['destinationPorts'] = value.get('destination_ports')
+ if value.get('protocols') is not None:
+ nat_value['protocols'] = value.get('protocols')
+ if value.get('translated_address') is not None:
+ nat_value['translatedAddress'] = value.get('translated_address')
+ if value.get('translated_port') is not None:
+ nat_value['translatedPort'] = value.get('translated_port')
+ nat_rule['properties']['rules'].append(nat_value)
+ self.body['properties']['natRuleCollections'].append(nat_rule)
+ elif key == 'network_rule_collections':
+ self.body['properties']['networkRuleCollections'] = []
+ for item in kwargs[key]:
+ network_rule = dict(properties={})
+ if item.get('priority') is not None:
+ network_rule['properties']['priority'] = item['priority']
+ if item.get('action') is not None:
+ network_rule['properties']['action'] = dict(type=item['action'])
+ if item.get('name') is not None:
+ network_rule['name'] = item['name']
+ if item.get('rules') is not None:
+ network_rule['properties']['rules'] = []
+ for value in item['rules']:
+ net_value = {}
+ if value.get('name') is not None:
+ net_value['name'] = value.get('name')
+ if value.get('description') is not None:
+ net_value['description'] = value.get('description')
+ if value.get('source_addresses') is not None:
+ net_value['sourceAddresses'] = value.get('source_addresses')
+ if value.get('destination_addresses') is not None:
+ net_value['destinationAddresses'] = value.get('destination_addresses')
+ if value.get('destination_ports') is not None:
+ net_value['destinationPorts'] = value.get('destination_ports')
+ if value.get('protocols') is not None:
+ net_value['protocols'] = value.get('protocols')
+ network_rule['properties']['rules'].append(net_value)
+ self.body['properties']['networkRuleCollections'].append(network_rule)
+ elif key == 'ip_configurations':
+ self.body['properties']['ipConfigurations'] = []
+ for item in kwargs[key]:
+ ipconfig = dict(properties={})
+ if item.get('subnet') is not None:
+ ipconfig['properties']['subnet'] = {}
+ if isinstance(item['subnet'], str):
+ ipconfig['properties']['subnet']['id'] = item['subnet']
+ elif isinstance(item['subnet'], dict):
+ if item['subnet'].get('id') is not None:
+ ipconfig['properties']['subnet']['id'] = item['subnet'].get('id')
+ elif (item['subnet'].get('resource_group') is not None and item['subnet'].get('name') is not None and
+ item['subnet'].get('virtual_network_name') is not None):
+ ipconfig['properties']['subnet']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ item['subnet'].get('resource_group') +
+ '/providers/Microsoft.Network/virtualNetworks/' +
+ item['subnet'].get('virtual_network_name') +
+ '/subnets/' +
+ item['subnet'].get('name'))
+ elif item['subnet'].get('name') is not None and item['subnet'].get('virtual_network_name') is not None:
+ ipconfig['properties']['subnet']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ self.resource_group +
+ '/providers/Microsoft.Network/virtualNetworks/' +
+ item['subnet'].get('virtual_network_name') +
+ '/subnets/' +
+ item['subnet'].get('name'))
+ else:
+ self.fail("The ip_configuration's subnet config error")
+ else:
+ self.fail("The ip_configuration's subnet config error")
+ if item.get('public_ip_address') is not None:
+ ipconfig['properties']['publicIPAddress'] = {}
+ if isinstance(item.get('public_ip_address'), str):
+ ipconfig['properties']['publicIPAddress']['id'] = item.get('public_ip_address')
+ elif isinstance(item.get('public_ip_address'), dict):
+ if item['public_ip_address'].get('id') is not None:
+ ipconfig['properties']['publicIPAddress']['id'] = item['public_ip_address'].get('id')
+ elif item['public_ip_address'].get('resource_group') is not None and item['public_ip_address'].get('name') is not None:
+ ipconfig['properties']['publicIPAddress']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ item['public_ip_address'].get('resource_group') +
+ '/providers/Microsoft.Network/publicIPAddresses/' +
+ item['public_ip_address'].get('name'))
+ elif item['public_ip_address'].get('name') is not None:
+ ipconfig['properties']['publicIPAddress']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ self.resource_group +
+ '/providers/Microsoft.Network/publicIPAddresses/' +
+ item['public_ip_address'].get('name'))
+ else:
+ self.fail("The ip_configuration's public ip address config error")
+ else:
+ self.fail("The ip_configuration's public ip address config error")
+
+ if item.get('name') is not None:
+ ipconfig['name'] = item['name']
+ self.body['properties']['ipConfigurations'].append(ipconfig)
+ else:
+ self.body[key] = kwargs[key]
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -642,11 +756,12 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ update_tags, new_tags = self.update_tags(old_response.get('tags'))
+ if update_tags:
+ self.to_do = Actions.Update
+ self.body['tags'] = new_tags
+
+ if not self.default_compare({}, self.body, old_response, '', dict(compare=[])):
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
@@ -705,10 +820,12 @@ class AzureRMAzureFirewalls(AzureRMModuleBaseExt):
self.log('Error attempting to create the AzureFirewall instance.')
self.fail('Error creating the AzureFirewall instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall_info.py
index d86932693..a565ef886 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_azurefirewall_info.py
@@ -140,7 +140,6 @@ class AzureRMAzureFirewallsInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and self.name is not None):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm.py
index 77d6d92ed..863839329 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm.py
@@ -255,7 +255,6 @@ class BackupAzureVM(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
changed = False
@@ -296,10 +295,12 @@ class BackupAzureVM(AzureRMModuleBaseExt):
self.fail(
'Error in creating/updating protection for Azure VM {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
@@ -322,10 +323,12 @@ class BackupAzureVM(AzureRMModuleBaseExt):
self.log('Error attempting to stop protection.')
self.fail('Error in disabling the protection: {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
@@ -348,10 +351,12 @@ class BackupAzureVM(AzureRMModuleBaseExt):
self.log('Error attempting to delete backup.')
self.fail('Error deleting the azure backup: {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
@@ -375,10 +380,12 @@ class BackupAzureVM(AzureRMModuleBaseExt):
self.fail(
'Error while taking on-demand backup: {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm_info.py
index b4ba22fc1..90ece65cb 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_backupazurevm_info.py
@@ -130,7 +130,6 @@ class BackupAzureVMInfo(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
response = self.get_recovery_point_info()
@@ -156,10 +155,12 @@ class BackupAzureVMInfo(AzureRMModuleBaseExt):
self.log('Error in fetching recovery point.')
self.fail('Error in fetching recovery point {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount.py
index ac237294c..b5ff4d923 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount.py
@@ -140,25 +140,19 @@ class AzureRMBatchAccount(AzureRMModuleBaseExt):
type='str'
),
location=dict(
- type='str',
- updatable=False,
- disposition='/'
+ type='str'
),
auto_storage_account=dict(
type='raw'
),
key_vault=dict(
type='raw',
- no_log=True,
- updatable=False,
- disposition='/'
+ no_log=True
),
pool_allocation_mode=dict(
default='batch_service',
type='str',
- choices=['batch_service', 'user_subscription'],
- updatable=False,
- disposition='/'
+ choices=['batch_service', 'user_subscription']
),
state=dict(
type='str',
@@ -212,8 +206,7 @@ class AzureRMBatchAccount(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(BatchManagementClient,
- base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True)
+ base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_batchaccount()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount_info.py
index fb61248e4..80930d6db 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_batchaccount_info.py
@@ -112,9 +112,7 @@ class AzureRMBatchAccountInfo(AzureRMModuleBaseExt):
response = []
self.mgmt_client = self.get_mgmt_svc_client(BatchManagementClient,
- base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True)
-
+ base_url=self._cloud_environment.endpoints.resource_manager)
if self.resource_group is not None and self.name is not None:
response = [self.get_batchaccount()]
elif self.resource_group is not None:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint.py
index 2f3a3d76f..ad4cb1303 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint.py
@@ -659,7 +659,6 @@ class AzureRMCdnendpoint(AzureRMModuleBase):
if not self.cdn_client:
self.cdn_client = self.get_mgmt_svc_client(CdnManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2017-04-02')
return self.cdn_client
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint_info.py
index 9f89408d0..46a759847 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnendpoint_info.py
@@ -232,7 +232,6 @@ class AzureRMCdnEndpointInfo(AzureRMModuleBase):
self.cdn_client = self.get_mgmt_svc_client(CdnManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2017-04-02')
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile.py
index 96761228f..ebac6f22f 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile.py
@@ -290,7 +290,6 @@ class AzureRMCdnprofile(AzureRMModuleBase):
if not self.cdn_client:
self.cdn_client = self.get_mgmt_svc_client(CdnManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2017-04-02')
return self.cdn_client
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile_info.py
index 92b9b7957..f4c599854 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cdnprofile_info.py
@@ -253,7 +253,6 @@ class AzureRMCdnprofileInfo(AzureRMModuleBase):
if not self.cdn_client:
self.cdn_client = self.get_mgmt_svc_client(CdnManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2017-04-02')
return self.cdn_client
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount.py
index d1f3dd987..98d2c7149 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount.py
@@ -426,7 +426,6 @@ class AzureRMCosmosDBAccount(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(CosmosDBManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount_info.py
index 61414272a..f80b2835e 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_cosmosdbaccount_info.py
@@ -431,7 +431,6 @@ class AzureRMCosmosDBAccountInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(CosmosDBManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name is not None:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore.py
index eaecb9df5..b46907339 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore.py
@@ -504,6 +504,7 @@ class AzureRMDatalakeStore(AzureRMModuleBase):
supports_tags=True)
def exec_module(self, **kwargs):
+ self.module.deprecate("The azure_rm_datalakestore.py will deprecated. Azure Data Lake Storage Gen1 retired on February 29,2024", version=(2.3, ))
for key in list(self.module_arg_spec.keys()) + ['tags']:
setattr(self, key, kwargs[key])
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore_info.py
index 2417ff74a..8444a4c1c 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_datalakestore_info.py
@@ -312,6 +312,7 @@ class AzureRMDatalakeStoreInfo(AzureRMModuleBase):
supports_tags=False)
def exec_module(self, **kwargs):
+ self.module.deprecate("The azure_rm_datalakestore_info.py will deprecated. Azure Data Lake Storage Gen1 retired on February 29,2024", version=(2.3, ))
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_deployment.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_deployment.py
index 07cf93d8e..ef32b19dc 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_deployment.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_deployment.py
@@ -84,7 +84,7 @@ options:
description:
- If I(state=present), template will be created.
- If I(state=present) and deployment exists, it will be updated.
- - If I(state=absent), the resource group will be removed.
+ - If I(state=absent), the deployment resource will be deleted.
default: present
type: str
choices:
@@ -498,7 +498,7 @@ class AzureRMDeploymentManager(AzureRMModuleBase):
else:
try:
if self.get_resource_group(self.resource_group):
- self.destroy_resource_group()
+ self.destroy_deployment_resource()
self.results['changed'] = True
self.results['msg'] = "deployment deleted"
except Exception:
@@ -572,12 +572,12 @@ class AzureRMDeploymentManager(AzureRMModuleBase):
return deployment_result
- def destroy_resource_group(self):
+ def destroy_deployment_resource(self):
"""
Destroy the targeted resource group
"""
try:
- result = self.rm_client.resource_groups.begin_delete(self.resource_group)
+ result = self.rm_client.deployments.begin_delete(self.resource_group, self.name)
result.wait() # Blocking wait till the delete is finished
except Exception as e:
if e.status_code == 404 or e.status_code == 204:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab.py
index 1424be8c9..4d0ce4a36 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab.py
@@ -157,7 +157,6 @@ class AzureRMDevTestLab(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2018-10-15')
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab_info.py
index 6add55e59..91dcf737a 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlab_info.py
@@ -184,7 +184,6 @@ class AzureRMDevTestLabInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.resource_group is not None:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabarmtemplate_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabarmtemplate_info.py
index 783a272dd..cf03aac99 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabarmtemplate_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabarmtemplate_info.py
@@ -162,7 +162,6 @@ class AzureRMDtlArmTemplateInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifact_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifact_info.py
index 9ec729257..35a084e5d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifact_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifact_info.py
@@ -175,7 +175,6 @@ class AzureRMArtifactInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource.py
index 87f19bbd9..7ae9290ea 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource.py
@@ -220,7 +220,6 @@ class AzureRMDevTestLabArtifactsSource(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2018-10-15')
old_response = self.get_devtestlabartifactssource()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource_info.py
index 036a3da45..0b3dbeaba 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabartifactsource_info.py
@@ -185,7 +185,6 @@ class AzureRMDtlArtifactSourceInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage.py
index f24c6f7e5..6017ca2ed 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage.py
@@ -205,7 +205,6 @@ class AzureRMDtlCustomImage(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_customimage()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage_info.py
index 2c6d559aa..e5c434f4a 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabcustomimage_info.py
@@ -162,7 +162,6 @@ class AzureRMDtlCustomImageInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment.py
index 214ddbbe1..7fd3737fd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment.py
@@ -190,7 +190,6 @@ class AzureRMDtlEnvironment(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment_info.py
index 4648ed1b7..31e2d1b42 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabenvironment_info.py
@@ -175,7 +175,6 @@ class AzureRMDtlEnvironmentInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy.py
index 2a3f2f953..7d19dd813 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy.py
@@ -200,7 +200,6 @@ class AzureRMDtlPolicy(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy_info.py
index a8f3b104a..8801d09ad 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabpolicy_info.py
@@ -173,7 +173,6 @@ class AzureRMDtlPolicyInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule.py
index 060b3baa0..1907794c8 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule.py
@@ -172,7 +172,6 @@ class AzureRMSchedule(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule_info.py
index e366e0f99..c5ded5ffd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabschedule_info.py
@@ -157,7 +157,6 @@ class AzureRMDtlScheduleInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
self.results['schedules'] = self.get()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine.py
index 62e7db77a..9a21fc532 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine.py
@@ -393,7 +393,6 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_virtualmachine()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine_info.py
index 24398136e..8a3692e2a 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualmachine_info.py
@@ -253,7 +253,6 @@ class AzureRMDtlVirtualMachineInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork.py
index a47a8c8f9..15e3b67f6 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork.py
@@ -157,7 +157,6 @@ class AzureRMDevTestLabVirtualNetwork(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2018-10-15')
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork_info.py
index 23392468b..0f6d59e02 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_devtestlabvirtualnetwork_info.py
@@ -152,7 +152,6 @@ class AzureRMDevTestLabVirtualNetworkInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(DevTestLabsClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_dnsrecordset.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_dnsrecordset.py
index 32448964a..1ac9983b4 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_dnsrecordset.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_dnsrecordset.py
@@ -360,8 +360,10 @@ class AzureRMRecordSet(AzureRMModuleBase):
resource_group=dict(type='str', required=True),
relative_name=dict(type='str', required=True),
zone_name=dict(type='str', required=True),
- record_type=dict(choices=RECORD_ARGSPECS.keys(), required=True, type='str'),
- record_mode=dict(choices=['append', 'purge'], default='purge'),
+ record_type=dict(choices=['A', 'AAAA', 'CNAME', 'MX', 'NS', 'PTR', 'SRV', 'TXT', 'SOA', 'CAA'],
+ required=True,
+ type='str'),
+ record_mode=dict(type='str', choices=['append', 'purge'], default='purge'),
state=dict(choices=['present', 'absent'], default='present', type='str'),
time_to_live=dict(type='int', default=3600),
records=dict(type='list', elements='dict'),
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery.py
index cb902ace7..9eb8ce397 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery.py
@@ -85,24 +85,17 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='galleryName',
required=True
),
location=dict(
- type='str',
- updatable=False,
- disposition='/'
+ type='str'
),
description=dict(
type='str',
- disposition='/properties/*'
),
state=dict(
type='str',
@@ -123,6 +116,7 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
self.query_parameters = {}
self.query_parameters['api-version'] = '2019-07-01'
self.header_parameters = {}
@@ -133,19 +127,19 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
supports_tags=True)
def exec_module(self, **kwargs):
- for key in list(self.module_arg_spec.keys()):
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
-
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
+ if key == 'description':
+ self.body['properties']['description'] = kwargs[key]
+ else:
+ self.body[key] = kwargs[key]
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -180,28 +174,36 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body.get('properties') is not None and self.body['properties']['description'] != old_response['properties']['description']:
self.to_do = Actions.Update
- self.body['properties'].pop('identifier', None)
+ else:
+ self.body['properties']['description'] = old_response['properties']['description']
- if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
- self.log('Need to Create / Update the Gallery instance')
+ update_tags, new_tags = self.update_tags(old_response.get('tags'))
+ if update_tags:
+ self.to_do = Actions.Update
+ self.body['tags'] = new_tags
+ if self.to_do == Actions.Create:
+ self.log('Need to Create the Gallery instance')
if self.check_mode:
self.results['changed'] = True
return self.results
-
- response = self.create_update_resource()
-
+ response = self.create_resource()
# if not old_response:
self.results['changed'] = True
# else:
# self.results['changed'] = old_response.__ne__(response)
- self.log('Creation / Update done')
+ self.log('Creation done')
+ elif self.to_do == Actions.Update:
+ self.log('Need to Update the Gallery instance')
+ if self.check_mode:
+ self.results['changed'] = True
+ return self.results
+ response = self.update_resource()
+ # if not old_response:
+ self.results['changed'] = True
+ self.log('Update done')
elif self.to_do == Actions.Delete:
self.log('Gallery instance deleted')
self.results['changed'] = True
@@ -225,8 +227,33 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
return self.results
- def create_update_resource(self):
- # self.log('Creating / Updating the Gallery instance {0}'.format(self.))
+ def update_resource(self):
+ # self.log('Updating the Gallery instance {0}'.format(self.))
+
+ try:
+ response = self.mgmt_client.query(self.url,
+ 'PATCH',
+ self.query_parameters,
+ self.header_parameters,
+ self.body,
+ self.status_code,
+ 600,
+ 30)
+ except Exception as exc:
+ self.log('Error attempting to update the Gallery instance.')
+ self.fail('Error updating the Gallery instance: {0}'.format(str(exc)))
+
+ if hasattr(response, 'body'):
+ response = json.loads(response.body())
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Updating fail, no match message return, return info as {0}".format(response))
+
+ return response
+
+ def create_resource(self):
+ # self.log('Creating the Gallery instance {0}'.format(self.))
try:
response = self.mgmt_client.query(self.url,
@@ -241,10 +268,12 @@ class AzureRMGalleries(AzureRMModuleBaseExt):
self.log('Error attempting to create the Gallery instance.')
self.fail('Error creating the Gallery instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery_info.py
index cd02bec28..d144c46fb 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_gallery_info.py
@@ -128,7 +128,6 @@ class AzureRMGalleriesInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and self.name is not None):
@@ -232,6 +231,8 @@ class AzureRMGalleriesInfo(AzureRMModuleBase):
return [self.format_item(x) for x in results['value']] if results['value'] else []
def format_item(self, item):
+ if not item:
+ return
d = {
'id': item['id'],
'name': item['name'],
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage.py
index c14aedd4a..9a4542458 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage.py
@@ -76,6 +76,14 @@ options:
- V1
- V2
type: str
+ architecture:
+ description:
+ - This property allows you to specify the hardware architecture of the Virtual Machines.
+ - Arm64 is only supported with Hyper V Version 2.
+ choices:
+ - Arm64
+ - x64
+ type: str
end_of_life_date:
description:
- The end of life date of the gallery Image Definition.
@@ -162,6 +170,22 @@ options:
description:
- The product ID.
type: str
+ features:
+ description:
+ - A list of gallery image features.
+ type: list
+ elements: dict
+ suboptions:
+ name:
+ description:
+ - The name of the gallery image feature.
+ type: str
+ required: True
+ value:
+ description:
+ - The value of the gallery image feature.
+ type: str
+ required: True
state:
description:
- Assert the state of the GalleryImage.
@@ -219,73 +243,60 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
gallery_name=dict(
type='str',
- updatable=False,
- disposition='galleryName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='galleryImageName',
required=True
),
location=dict(
type='str',
- updatable=False,
- disposition='/'
),
description=dict(
type='str',
- disposition='/properties/*'
),
eula=dict(
type='str',
- disposition='/properties/*'
),
privacy_statement_uri=dict(
type='str',
- disposition='/properties/privacyStatementUri'
),
release_note_uri=dict(
type='str',
- disposition='/properties/releaseNoteUri'
),
os_type=dict(
type='str',
- disposition='/properties/osType',
choices=['windows',
'linux']
),
os_state=dict(
type='str',
- disposition='/properties/osState',
choices=['generalized',
'specialized']
),
hypervgeneration=dict(
type='str',
- disposition='/properties/hyperVGeneration',
choices=['V1',
'V2']
),
+ architecture=dict(
+ type='str',
+ choices=['Arm64',
+ 'x64']
+ ),
end_of_life_date=dict(
type='str',
- disposition='/properties/endOfLifeDate'
),
identifier=dict(
type='dict',
- disposition='/properties/*',
options=dict(
publisher=dict(
type='str',
required=True,
- updatable=False
),
offer=dict(
type='str',
@@ -299,11 +310,9 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
),
recommended=dict(
type='dict',
- disposition='/properties/*',
options=dict(
v_cpus=dict(
type='dict',
- disposition='vCPUs',
options=dict(
min=dict(
type='int'
@@ -328,18 +337,15 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
),
disallowed=dict(
type='dict',
- disposition='/properties/*',
options=dict(
disk_types=dict(
type='list',
elements='str',
- disposition='diskTypes'
)
)
),
purchase_plan=dict(
type='dict',
- disposition='/properties/purchasePlan',
options=dict(
name=dict(
type='str'
@@ -352,6 +358,20 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
)
)
),
+ features=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ name=dict(
+ type='str',
+ required=True
+ ),
+ value=dict(
+ type='str',
+ required=True
+ )
+ )
+ ),
state=dict(
type='str',
default='present',
@@ -372,8 +392,9 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
self.query_parameters = {}
- self.query_parameters['api-version'] = '2019-07-01'
+ self.query_parameters['api-version'] = '2022-03-03'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -382,19 +403,51 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
supports_tags=True)
def exec_module(self, **kwargs):
- for key in list(self.module_arg_spec.keys()):
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
-
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
+ if key == 'description':
+ self.body['properties']['description'] = kwargs[key]
+ elif key == 'eula':
+ self.body['properties']['eula'] = kwargs[key]
+ elif key == 'privacy_statement_uri':
+ self.body['properties']['privacyStatementUri'] = kwargs[key]
+ elif key == 'release_note_uri':
+ self.body['properties']['releaseNoteUri'] = kwargs[key]
+ elif key == 'os_type':
+ self.body['properties']['osType'] = kwargs[key]
+ elif key == 'os_state':
+ self.body['properties']['osState'] = kwargs[key]
+ elif key == 'hypervgeneration':
+ self.body['properties']['hyperVGeneration'] = kwargs[key]
+ elif key == 'architecture':
+ self.body['properties']['architecture'] = kwargs[key]
+ elif key == 'end_of_life_date':
+ self.body['properties']['endOfLifeDate'] = kwargs[key]
+ elif key == 'identifier':
+ self.body['properties']['identifier'] = kwargs[key]
+ elif key == 'recommended':
+ self.body['properties']['recommended'] = {}
+ for item in kwargs[key].keys():
+ if item == 'v_cpus':
+ self.body['properties']['recommended']['vCPUs'] = kwargs[key].get('v_cpus')
+ elif item == 'memory':
+ self.body['properties']['recommended']['memory'] = kwargs[key].get('memory')
+ elif key == 'disallowed':
+ self.body['properties']['disallowed'] = {}
+ self.body['properties']['disallowed']['diskTypes'] = kwargs[key].get('disk_types')
+ elif key == 'purchase_plan':
+ self.body['properties']['purchasePlan'] = kwargs[key]
+ elif key == 'features':
+ self.body['properties']['features'] = kwargs[key]
+ else:
+ self.body[key] = kwargs[key]
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -432,11 +485,60 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body['properties'].get('description') is not None and \
+ self.body['properties']['description'] != old_response['properties'].get('description'):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('eula') is not None and self.body['properties']['eula'] != old_response['properties'].get('eula'):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('privacyStatementUri') is not None and
+ self.body['properties']['privacyStatementUri'] != old_response['properties'].get('privacyStatementUri')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('releaseNoteUri') is not None and
+ self.body['properties']['releaseNoteUri'] != old_response['properties'].get('releaseNoteUri')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('osType') is not None and
+ self.body['properties']['osType'].lower() != old_response['properties'].get('osType', '').lower()):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('osState') is not None and
+ self.body['properties']['osState'].lower() != old_response['properties'].get('osState', '').lower()):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('hyperVGeneration') is not None and
+ self.body['properties']['hyperVGeneration'] != old_response['properties'].get('hyperVGeneration')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('architecture') is not None and
+ self.body['properties']['architecture'] != old_response['properties'].get('architecture')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('endOfLifeDate') is not None and
+ self.body['properties']['endOfLifeDate'] != old_response['properties'].get('endOfLifeDate')):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('identifier') is not None and
+ self.body['properties']['identifier'].get('offer') != old_response['properties']['identifier'].get('offer') or
+ self.body['properties']['identifier'].get('sku') != old_response['properties']['identifier'].get('sku')):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('recommended') is not None:
+ if self.body['properties']['recommended'].get('vCPUS') is not None:
+ for item in self.body['properties']['recommended']['vCPUS'].keys():
+ if self.body['properties']['recommended']['vCPUS'].get(item) != old_response['properties']['recommended']['vCPUS'].get(item):
+ self.to_do = Actions.Update
+ elif (self.body['properties']['recommended'].get('memory') is not None and
+ not all(self.body['properties']['recommended']['memory'].get(item) == old_response['properties']['recommended']['memory'].get(item)
+ for item in self.body['properties']['recommended']['memory'].keys())):
+ self.to_do = Actions.Update
+ elif (self.body['properties'].get('disallowed') is not None and
+ self.body['properties']['disallowed'].get('diskTypes') != old_response['properties']['disallowed'].get('diskTypes')):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('purchasePlan') is not None:
+ for item in self.body['properties']['purchasePlan'].keys():
+ if self.body['properties']['purchasePlan'][item] != old_response['properties']['purchasePlan'].get(item):
+ self.to_do = Actions.Update
+ elif self.body['properties'].get('features') is not None:
+ if old_response['properties'].get('features') is None:
+ self.to_do = Actions.Update
+ else:
+ if not all(item in old_response['properties']['features'] for item in self.body['properties']['features']):
+ self.to_do = Actions.Update
+ update_tags, self.body['tags'] = self.update_tags(old_response.get('tags'))
+ if update_tags:
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
@@ -492,10 +594,12 @@ class AzureRMGalleryImages(AzureRMModuleBaseExt):
self.log('Error attempting to create the GalleryImage instance.')
self.fail('Error creating the GalleryImage instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage_info.py
index 0f1c5020f..1b48f4f22 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimage_info.py
@@ -159,7 +159,6 @@ class AzureRMGalleryImagesInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion.py
index 9d61bf874..66a4dea6b 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion.py
@@ -125,6 +125,62 @@ options:
description:
- Storage account type.
type: str
+ encryption:
+ description:
+ - Allows users to provide customer managed keys for encrypting the OS and data disks in the gallery artifact.
+ type: dict
+ suboptions:
+ data_disk_images:
+ description:
+ - A list of encryption specifications for data disk images.
+ type: list
+ elements: dict
+ suboptions:
+ disk_encryption_set_id:
+ description:
+ - A relative URI containing the resource ID of the disk encryption set.
+ type: str
+ lun:
+ description:
+ - This property specifies the logical unit number of the data disk.
+ - This value is used to identify data disks within the Virtual Machine and
+ therefore must be unique for each data disk attached to the Virtual Machine.
+ type: int
+ os_disk_image:
+ description:
+ - Contains encryption settings for an OS disk image.
+ type: dict
+ suboptions:
+ disk_encryption_set_id:
+ description:
+ - A relative URI containing the resource ID of the disk encryption set.
+ type: str
+ security_profile:
+ description:
+ - This property specifies the security profile of an OS disk image.
+ type: dict
+ suboptions:
+ confidential_vm_encryption_type:
+ description:
+ - Confidential VM encryption types.
+ type: dict
+ suboptions:
+ encrypted_vm_guest_state_only_with_pmk:
+ description:
+ - VM Guest State Only with PMK.
+ type: str
+ encrypted_with_cmk:
+ description:
+ - Encrypted with CMK.
+ type: str
+ encrypted_with_pmk:
+ description:
+ - Encrypted with PMK.
+ type: str
+ secure_vm_disk_encryption_set_id:
+ description:
+ - Secure VM disk encryption set id.
+ type: str
managed_image:
description:
- Managed image reference, could be resource ID, or dictionary containing I(resource_group) and I(name)
@@ -274,74 +330,40 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
gallery_name=dict(
type='str',
- updatable=False,
- disposition='galleryName',
required=True
),
gallery_image_name=dict(
type='str',
- updatable=False,
- disposition='galleryImageName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='galleryImageVersionName',
required=True
),
tags=dict(
type='dict',
- updatable=False,
- disposition='tags',
- comparison='tags'
),
location=dict(
type='str',
- updatable=False,
- disposition='/',
- comparison='location'
),
storage_profile=dict(
type='dict',
- updatable=False,
- disposition='/properties/storageProfile',
- comparison='ignore',
options=dict(
source_image=dict(
type='raw',
- disposition='source/id',
- purgeIfNone=True,
- pattern=[('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/images/{name}'),
- ('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/galleries/{gallery_name}/images/{gallery_image_name}'
- '/versions/{version}')]
),
os_disk=dict(
type='dict',
- disposition='osDiskImage',
- purgeIfNone=True,
- comparison='ignore',
options=dict(
source=dict(
type='raw',
- disposition='source/id',
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/snapshots/{name}')
),
host_caching=dict(
type='str',
- disposition='hostCaching',
default="None",
choices=["ReadOnly", "ReadWrite", "None"]
)
@@ -350,22 +372,15 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
data_disks=dict(
type='list',
elements='raw',
- disposition='dataDiskImages',
- purgeIfNone=True,
options=dict(
lun=dict(
type='int'
),
source=dict(
type='raw',
- disposition="source/id",
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/snapshots/{name}')
),
host_caching=dict(
type='str',
- disposition='hostCaching',
default="None",
choices=["ReadOnly", "ReadWrite", "None"]
)
@@ -375,57 +390,87 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
),
publishing_profile=dict(
type='dict',
- disposition='/properties/publishingProfile',
options=dict(
target_regions=dict(
type='list',
elements='raw',
- disposition='targetRegions',
options=dict(
name=dict(
type='str',
required=True,
- comparison='location'
),
regional_replica_count=dict(
type='int',
- disposition='regionalReplicaCount'
),
storage_account_type=dict(
type='str',
- disposition='storageAccountType'
+ ),
+ encryption=dict(
+ type='dict',
+ options=dict(
+ data_disk_images=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ disk_encryption_set_id=dict(
+ type='str',
+ ),
+ lun=dict(
+ type='int'
+ )
+ )
+ ),
+ os_disk_image=dict(
+ type='dict',
+ options=dict(
+ disk_encryption_set_id=dict(
+ type='str',
+ ),
+ security_profile=dict(
+ type='dict',
+ options=dict(
+ confidential_vm_encryption_type=dict(
+ type='dict',
+ options=dict(
+ encrypted_vm_guest_state_only_with_pmk=dict(
+ type='str',
+ ),
+ encrypted_with_cmk=dict(
+ type='str',
+ ),
+ encrypted_with_pmk=dict(
+ type='str',
+ )
+ )
+ ),
+ secure_vm_disk_encryption_set_id=dict(
+ type='str',
+ )
+ )
+ )
+ )
+ )
+ )
)
)
),
managed_image=dict(
type='raw',
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/images/{name}'),
- comparison='ignore'
),
snapshot=dict(
type='raw',
- pattern=('/subscriptions/{subscription_id}/resourceGroups'
- '/{resource_group}/providers/Microsoft.Compute'
- '/snapshots/{name}'),
- comparison='ignore'
),
replica_count=dict(
type='int',
- disposition='replicaCount'
),
exclude_from_latest=dict(
type='bool',
- disposition='excludeFromLatest'
),
end_of_life_date=dict(
type='str',
- disposition='endOfLifeDate'
),
storage_account_type=dict(
type='str',
- disposition='storageAccountType',
choices=['Standard_LRS',
'Standard_ZRS']
)
@@ -453,8 +498,9 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
self.query_parameters = {}
- self.query_parameters['api-version'] = '2019-07-01'
+ self.query_parameters['api-version'] = '2022-03-03'
self.header_parameters = {}
self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
@@ -469,9 +515,182 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
if key == 'tags':
self.body[key] = kwargs[key]
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
-
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
+ if key == 'location':
+ self.body['location'] = kwargs[key]
+ elif key == 'storage_profile':
+ self.body['properties']['storageProfile'] = {}
+ if kwargs[key].get('source_image') is not None:
+ self.body['properties']['storageProfile']['source'] = {}
+ if isinstance(kwargs[key].get('source_image'), str):
+ self.body['properties']['storageProfile']['source']['id'] = kwargs[key].get('source_image')
+ elif isinstance(kwargs[key].get('source_image'), dict):
+ if kwargs[key]['source_image'].get('id') is not None:
+ self.body['properties']['storageProfile']['source']['id'] = kwargs[key]['source_image'].get('id')
+ if kwargs[key]['source_image'].get('resource_group') is not None and kwargs[key]['source_image'].get('name') is not None:
+ self.body['properties']['storageProfile']['source']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ kwargs[key]['source_image'].get('resource_group') +
+ '/providers/Microsoft.Compute/images/' +
+ kwargs[key]['source_image'].get('name'))
+ elif (kwargs[key]['source_image'].get('resource_group') is not None and
+ kwargs[key]['source_image'].get('gallery_name') is not None and
+ kwargs[key]['source_image'].get('gallery_image_name') is not None and kwargs[key]['source_image'].get('version') is not None):
+ self.body['properties']['storageProfile']['source']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ kwargs[key]['source_image'].get('resource_group') +
+ '/providers/Microsoft.Compute/galleries/' +
+ kwargs[key]['source_image'].get('gallery_name') +
+ '/images/' +
+ kwargs[key]['source_image'].get('gallery_image_name') +
+ '/versions/' +
+ kwargs[key]['source_image'].get('version'))
+ else:
+ self.fail("The source_image parameters config errors")
+ else:
+ self.fail("The source_image parameters config errors")
+ if kwargs[key].get('os_disk') is not None:
+ self.body['properties']['storageProfile']['osDiskImage'] = {}
+ if kwargs[key]['os_disk'].get('host_caching') is not None:
+ self.body['properties']['storageProfile']['osDiskImage']['hostCaching'] = kwargs[key]['os_disk'].get('host_caching')
+ if kwargs[key]['os_disk'].get('source') is not None:
+ self.body['properties']['storageProfile']['osDiskImage']['source'] = {}
+ if isinstance(kwargs[key]['os_disk']['source'], str):
+ self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = kwargs[key]['os_disk']['source']
+ elif isinstance(kwargs[key]['os_disk']['source'], dict):
+ if kwargs[key]['os_disk']['source'].get('id') is not None:
+ self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = kwargs[key]['os_disk']['source'].get('id')
+ if kwargs[key]['os_disk']['source'].get('resource_group') is not None and \
+ kwargs[key]['os_disk']['source'].get('name') is not None:
+ resource_group = kwargs[key]['os_disk']['source'].get('resource_group')
+ self.body['properties']['storageProfile']['osDiskImage']['source']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ resource_group +
+ '/providers/Microsoft.Compute/snapshots/' +
+ kwargs[key]['os_disk']['source'].get('name'))
+ else:
+ self.fail("The os_disk.source parameters config errors")
+
+ else:
+ self.fail("The os_disk.source parameters config errors")
+
+ if kwargs[key].get('data_disks') is not None:
+ self.body['properties']['storageProfile']['dataDiskImages'] = []
+ data_disk = {}
+ for item in kwargs[key].get('data_disks'):
+ if item.get('lun') is not None:
+ data_disk['lun'] = item['lun']
+ if item.get('source') is not None:
+ data_disk['source'] = {}
+ if isinstance(item.get('source'), str):
+ data_disk['source']['id'] = item.get('source')
+ elif isinstance(item.get('source'), dict):
+ if item['source'].get('id') is not None:
+ data_disk['source']['id'] = item['source'].get('id')
+ elif item['source'].get('resource_group') is not None and item['source'].get('name') is not None:
+ data_disk['source']['id'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ item['source'].get('resource_group') +
+ '/providers/Microsoft.Compute/snapshots/' +
+ item['source'].get('name'))
+ else:
+ self.fail("The data_disk.source parameters config errors")
+ else:
+ self.fail("The data_disk.source parameters config errors")
+ if item.get('host_caching') is not None:
+ data_disk['hostCaching'] = item['host_caching']
+ elif key == 'publishing_profile':
+ self.body['properties']['publishingProfile'] = {}
+ if kwargs['publishing_profile'].get('target_regions') is not None:
+ self.body['properties']['publishingProfile']['targetRegions'] = []
+ for item in kwargs['publishing_profile']['target_regions']:
+ target_regions = {}
+ for value in item.keys():
+ if value == 'name':
+ target_regions[value] = item[value]
+ elif value == 'regional_replica_count':
+ target_regions['regionalReplicaCount'] = item[value]
+ elif value == 'storage_account_type':
+ target_regions['storageAccountType'] = item[value]
+ elif value == 'encryption':
+ target_regions['encryption'] = {}
+ if item[value].get('data_disk_images') is not None:
+ target_regions['encryption']['dataDiskImages'] = []
+ for tt in item[value]['data_disk_images']:
+ disk_image = {}
+ if tt.get('lun') is not None:
+ disk_image['lun'] = tt['lun']
+ if tt.get('disk_encryption_set_id') is not None:
+ disk_image['diskEncryptionSetId'] = tt['disk_encryption_set_id']
+ target_regions['encryption']['dataDiskImages'].append(disk_image)
+
+ if item['encryption'].get('os_disk_image') is not None:
+ target_regions['encryption']['osDiskImage'] = {}
+ if item['encryption']['os_disk_image'].get('disk_encryption_set_id') is not None:
+ disk_encryption_set_id = item['encryption']['os_disk_image']['disk_encryption_set_id']
+ target_regions['encryption']['osDiskImage']['diskEncryptionSetId'] = disk_encryption_set_id
+ if item['encryption']['os_disk_image'].get('security_profile') is not None:
+ target_regions['encryption']['osDiskImage']['securityProfile'] = {}
+ if item['encryption']['os_disk_image']['security_profile'].get('secure_vm_disk_encryption_set_id') is not None:
+ secure_id = item['encryption']['os_disk_image']['security_profile']['secure_vm_disk_encryption_set_id']
+ target_regions['encryption']['osDiskImage']['securityProfile']['secureVMDiskEncryptionSetId'] = secure_id
+
+ if item['encryption']['os_disk_image']['security_profile'].get('confidential_vm_encryption_type') is not None:
+ target_regions['encryption']['osDiskImage']['securityProfile']['confidentialVMEncryptionType'] = {}
+ security = item['encryption']['os_disk_image']['security_profile']['confidential_vm_encryption_type']
+ tt = target_regions['encryption']['osDiskImage']['securityProfile']['confidentialVMEncryptionType']
+ if security.get('encrypted_vm_guest_state_only_with_pmk') is not None:
+ tt['EncryptedVMGuestStateOnlyWithPmk'] = security.get('encrypted_vm_guest_state_only_with_pmk')
+ if security.get('encrypted_with_cmk') is not None:
+ tt['EncryptedWithCmk'] = security.get('encrypted_with_cmk')
+ if security.get('encrypted_with_pmk') is not None:
+ tt['EncryptedWithPmk'] = security.get('encrypted_with_pmk')
+ self.body['properties']['publishingProfile']['targetRegions'].append(target_regions)
+ if kwargs[key].get('managed_image') is not None:
+ if isinstance(kwargs[key]['managed_image'], str):
+ self.body['properties']['publishingProfile']['managed_image'] = kwargs[key]['managed_image']
+ elif isinstance(kwargs[key]['managed_image'], dict):
+ if kwargs[key]['managed_image'].get('id') is not None:
+ self.body['properties']['publishingProfile']['managed_image'] = kwargs[key]['managed_image']['id']
+ elif kwargs[key]['managed_image'].get('resource_group') is not None and kwargs[key]['managed_image'].get('name') is not None:
+ self.body['properties']['publishingProfile']['managed_image'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ kwargs[key]['managed_image'].get('resource_group') +
+ '/providers/Microsoft.Compute/images/' +
+ kwargs[key]['managed_image'].get('name'))
+ else:
+ self.fail("The managed_image parameters config errors")
+ else:
+ self.fail("The managed_image parameters config errors")
+ if kwargs[key].get('snapshot') is not None:
+ if isinstance(kwargs[key].get('snapshot'), str):
+ self.body['properties']['publishingProfile']['snapshot'] = kwargs[key].get('snapshot')
+ elif isinstance(kwargs[key].get('snapshot'), dict):
+ if kwargs[key]['snapshot'].get('id') is not None:
+ self.body['properties']['publishingProfile']['snapshot'] = kwargs[key]['snapshot'].get('id')
+ elif kwargs[key]['snapshot'].get('resource_group') is not None and kwargs[key]['snapshot'].get('name') is not None:
+ self.body['properties']['publishingProfile']['snapshot'] = ('/subscriptions/' +
+ self.subscription_id +
+ '/resourceGroups/' +
+ kwargs[key]['snapshot'].get('resource_group') +
+ '/providers/Microsoft.Compute/snapshots/' +
+ kwargs[key]['snapshot'].get('name'))
+ else:
+ self.fail("The managed_image parameters config errors")
+ else:
+ self.fail("The managed_image parameters config errors")
+ if kwargs[key].get('replica_count') is not None:
+ self.body['properties']['publishingProfile']['replicaCount'] = kwargs[key].get('replica_count')
+ if kwargs[key].get('exclude_from_latest') is not None:
+ self.body['properties']['publishingProfile']['excludeFromLatest'] = kwargs[key].get('exclude_from_latest')
+ if kwargs[key].get('end_of_life_date') is not None:
+ self.body['properties']['publishingProfile']['endOfLifeDate'] = kwargs[key].get('end_of_life_date')
+ if kwargs[key].get('storage_account_type') is not None:
+ self.body['properties']['publishingProfile']['storageAccountType'] = kwargs[key].get('storage_account_type')
# keep backward compatibility
snapshot = self.body.get('properties', {}).get('publishingProfile', {}).pop('snapshot', None)
@@ -485,7 +704,6 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -531,12 +749,19 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
self.tags = newtags
self.body['tags'] = self.tags
self.to_do = Actions.Update
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
- self.to_do = Actions.Update
+ if self.body['properties'].get('publishingProfile') is not None:
+ for key in self.body['properties']['publishingProfile'].keys():
+ if key == 'targetRegions':
+ result = dict(compare=[])
+ modifies = {'/*/name': {'updatable': True, 'comparison': 'location'}}
+ if not self.default_compare(modifies, self.body['properties']['publishingProfile'][key],
+ old_response['properties']['publishingProfile'][key], '', result):
+ self.to_do = Actions.Update
+ elif key == 'endOfLifeDate':
+ if self.body['properties']['publishingProfile'][key].lower() != old_response['properties']['publishingProfile'][key].lower():
+ self.to_do = Actions.Update
+ elif self.body['properties']['publishingProfile'].get(key) != old_response['properties']['publishingProfile'].get(key):
+ self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
self.log('Need to Create / Update the GalleryImageVersion instance')
@@ -583,10 +808,12 @@ class AzureRMGalleryImageVersions(AzureRMModuleBaseExt):
self.log('Error attempting to create the GalleryImageVersion instance.')
self.fail('Error creating the GalleryImageVersion instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
while response['properties']['provisioningState'] == 'Creating':
time.sleep(60)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion_info.py
index b4c7e89a2..2eebb73e7 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_galleryimageversion_info.py
@@ -150,7 +150,6 @@ class AzureRMGalleryImageVersionsInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and
@@ -258,6 +257,8 @@ class AzureRMGalleryImageVersionsInfo(AzureRMModuleBase):
return [self.format_item(x) for x in results['response']] if results['response'] else []
def format_item(self, item):
+ if not item:
+ return None
d = {
'id': item['id'],
'name': item['name'],
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster.py
index 2ce7e16f3..b2121cd27 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster.py
@@ -322,7 +322,6 @@ class AzureRMClusters(AzureRMModuleBase):
response = None
self.mgmt_client = self.get_mgmt_svc_client(HDInsightManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster_info.py
index afb3211ea..b50d51215 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_hdinsightcluster_info.py
@@ -229,7 +229,6 @@ class AzureRMHDInsightclusterInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(HDInsightManagementClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.name is not None:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevice.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevice.py
index be007b8d8..d26062244 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevice.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevice.py
@@ -378,8 +378,8 @@ class AzureRMIoTDevice(AzureRMModuleBase):
elif self.auth_method == 'self_signed':
response = self.mgmt_client.update_device_with_certificate_authority(self.name, self.status, iot_edge=self.edge_enabled)
elif self.auth_method == 'certificate_authority':
- response = self.mgmt_client.update_device_with_x509(self.name, device['etag'], self.primary_thumbprint,
- self.secondary_thumbprint, self.status, iot_edge=self.edge_enabled)
+ response = self.mgmt_client.update_device_with_x509(self.name, device['etag'], self.primary_key,
+ self.secondary_key, self.status, iot_edge=self.edge_enabled)
return self.format_item(response)
except Exception as exc:
@@ -394,7 +394,7 @@ class AzureRMIoTDevice(AzureRMModuleBase):
response = self.mgmt_client.create_device_with_certificate_authority(self.name, self.status, iot_edge=self.edge_enabled)
elif self.auth_method == 'certificate_authority':
response = self.mgmt_client.create_device_with_x509(self.name,
- self.primary_thumbprint, self.secondary_thumbprint, self.status, iot_edge=self.edge_enabled)
+ self.primary_key, self.secondary_key, self.status, iot_edge=self.edge_enabled)
return self.format_item(response)
except Exception as exc:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevicemodule.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevicemodule.py
index 2f2ad679d..274440754 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevicemodule.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_iotdevicemodule.py
@@ -291,11 +291,11 @@ class AzureRMIoTDeviceModule(AzureRMModuleBase):
try:
if self.auth_method == 'sas':
response = self.mgmt_client.update_module_with_sas(self.device, self.name, self.managed_by, self.etag, self.primary_key, self.secondary_key)
- elif self.auth_method == 'self_signed':
- response = self.mgmt_client.update_module_with_certificate_authority(self.device, self.name, self.managed_by, self.etag)
elif self.auth_method == 'certificate_authority':
+ response = self.mgmt_client.update_module_with_certificate_authority(self.device, self.name, self.managed_by, self.etag)
+ elif self.auth_method == 'self_signed':
response = self.mgmt_client.update_module_with_x509(self.device,
- self.name, self.managed_by, self.etag, self.primary_thumbprint, self.secondary_thumbprint)
+ self.name, self.managed_by, self.etag, self.primary_key, self.secondary_key)
return self.format_module(response)
except Exception as exc:
@@ -309,11 +309,11 @@ class AzureRMIoTDeviceModule(AzureRMModuleBase):
try:
if self.auth_method == 'sas':
response = self.mgmt_client.create_module_with_sas(self.device, self.name, self.managed_by, self.primary_key, self.secondary_key)
- elif self.auth_method == 'self_signed':
- response = self.mgmt_client.create_module_with_certificate_authority(self.device, self.name, self.managed_by)
elif self.auth_method == 'certificate_authority':
+ response = self.mgmt_client.create_module_with_certificate_authority(self.device, self.name, self.managed_by)
+ elif self.auth_method == 'self_signed':
response = self.mgmt_client.create_module_with_x509(self.device_id,
- self.name, self.managed_by, self.primary_thumbprint, self.secondary_thumbprint)
+ self.name, self.managed_by, self.primary_key, self.secondary_key)
return self.format_module(response)
except Exception as exc:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault.py
index efeddaacc..96d2d589e 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault.py
@@ -382,7 +382,6 @@ class AzureRMVaults(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(KeyVaultManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version="2021-10-01")
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault_info.py
index d7b54515f..af1a245e9 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvault_info.py
@@ -261,7 +261,6 @@ class AzureRMKeyVaultInfo(AzureRMModuleBase):
self._client = self.get_mgmt_svc_client(KeyVaultManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version="2021-10-01")
if self.name:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey.py
index 54cc6eff6..d094a4ac4 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey.py
@@ -117,6 +117,7 @@ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common
try:
from azure.keyvault.keys import KeyClient
+ from azure.core.exceptions import ResourceNotFoundError
from datetime import datetime
except ImportError:
# This is handled in azure_rm_common
@@ -191,10 +192,12 @@ class AzureRMKeyVaultKey(AzureRMModuleBase):
if self.state == 'absent':
changed = True
- except Exception:
+ except ResourceNotFoundError as ec:
# Key doesn't exist
if self.state == 'present':
changed = True
+ except Exception as ec:
+ self.fail("Find the key vault secret got exception, exception as {0}".format(ec))
self.results['changed'] = changed
self.results['state'] = results
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey_info.py
index b59f89b33..979d09f49 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultkey_info.py
@@ -195,6 +195,7 @@ keyvaults:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
try:
+ from azure.core.exceptions import ResourceNotFoundError
from azure.keyvault.keys import KeyClient
except ImportError:
# This is handled in azure_rm_common
@@ -282,7 +283,7 @@ def keyitem_to_dict(keyitem):
kid=keyitem._id,
version=keyitem.version,
tags=keyitem._tags,
- manged=keyitem._managed,
+ managed=keyitem._managed,
attributes=dict(
enabled=keyitem.enabled,
not_before=keyitem.not_before,
@@ -398,9 +399,10 @@ class AzureRMKeyVaultKeyInfo(AzureRMModuleBase):
self.log("Response : {0}".format(response))
results.append(response)
- except Exception as e:
- self.fail(e)
+ except ResourceNotFoundError as e:
self.log("Did not find the key vault key {0}: {1}".format(self.name, str(e)))
+ except Exception as ec:
+ self.fail("Find the key vault key got a exception as {0}".format(ec))
return results
def get_key_versions(self):
@@ -422,7 +424,7 @@ class AzureRMKeyVaultKeyInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log("Did not find key versions {0} : {1}.".format(self.name, str(e)))
+ self.fail("Did not find key versions {0} : {1}.".format(self.name, str(e)))
return results
def list_keys(self):
@@ -444,7 +446,7 @@ class AzureRMKeyVaultKeyInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log("Did not find key vault in current subscription {0}.".format(str(e)))
+ self.fail("Did not find key vault in current subscription {0}.".format(str(e)))
return results
def get_deleted_key(self):
@@ -465,8 +467,11 @@ class AzureRMKeyVaultKeyInfo(AzureRMModuleBase):
self.log("Response : {0}".format(response))
results.append(response)
- except Exception as e:
- self.log("Did not find the key vault key {0}: {1}".format(self.name, str(e)))
+ except ResourceNotFoundError as ec:
+ self.log("Did not find the key vault key {0}: {1}".format(self.name, str(ec)))
+ except Exception as ec:
+ self.fail("Find the key vault key got a exception {0}".format(str(ec)))
+
return results
def list_deleted_keys(self):
@@ -488,7 +493,7 @@ class AzureRMKeyVaultKeyInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log("Did not find key vault in current subscription {0}.".format(str(e)))
+ self.fail("Did not find key vault in current subscription {0}.".format(str(e)))
return results
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret.py
index 98a5e0e78..f36c86f1a 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret.py
@@ -43,6 +43,14 @@ options:
description:
- Optional valid-from datetime for secret
type: str
+ recover_if_need:
+ description:
+ - Whether to permanently recover delete secrets.
+ type: bool
+ purge_if_need:
+ description:
+ - Whether to permanently delete secrets.
+ type: bool
state:
description:
- Assert the state of the subnet. Use C(present) to create or update a secret and C(absent) to delete a secret .
@@ -76,6 +84,18 @@ EXAMPLES = '''
secret_name: MySecret
keyvault_uri: https://contoso.vault.azure.net/
state: absent
+
+- name: Recover a delete secret
+ azure_rm_keyvaultsecret:
+ secret_name: MySecret
+ keyvault_uri: https://contoso.vault.azure.net/
+ recover_if_need: true
+
+- name: Purge a delete secret
+ azure_rm_keyvaultsecret:
+ secret_name: MySecret
+ keyvault_uri: https://contoso.vault.azure.net/
+ purge_if_need: true
'''
RETURN = '''
@@ -96,6 +116,8 @@ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common
try:
from azure.keyvault.secrets import SecretClient
+ from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.exceptions import HttpResponseError
import dateutil.parser
except ImportError:
# This is handled in azure_rm_common
@@ -114,6 +136,8 @@ class AzureRMKeyVaultSecret(AzureRMModuleBase):
secret_expiry=dict(type='str', no_log=True),
keyvault_uri=dict(type='str', no_log=True, required=True),
state=dict(type='str', default='present', choices=['present', 'absent']),
+ recover_if_need=dict(type='bool'),
+ purge_if_need=dict(type='bool'),
content_type=dict(type='str')
)
@@ -135,6 +159,8 @@ class AzureRMKeyVaultSecret(AzureRMModuleBase):
self.data_creds = None
self.client = None
self.tags = None
+ self.recover_if_need = None
+ self.purge_if_need = None
self.content_type = None
super(AzureRMKeyVaultSecret, self).__init__(self.module_arg_spec,
@@ -162,10 +188,12 @@ class AzureRMKeyVaultSecret(AzureRMModuleBase):
elif self.secret_value and results['secret_value'] != self.secret_value:
changed = True
- except Exception as ec:
+ except ResourceNotFoundError as ec:
# Secret doesn't exist
if self.state == 'present':
changed = True
+ except Exception as ec2:
+ self.fail("Find the key vault secret got exception, exception as {0}".format(str(ec2)))
self.results['changed'] = changed
self.results['state'] = results
@@ -181,9 +209,21 @@ class AzureRMKeyVaultSecret(AzureRMModuleBase):
if not self.check_mode:
# Create secret
if self.state == 'present' and changed:
- results['secret_id'] = self.create_update_secret(self.secret_name, self.secret_value, self.tags, self.content_type, valid_from, expiry)
+ if self.get_delete_secret(self.secret_name):
+ if self.recover_if_need:
+ results['secret_id'] = self.recover_delete_secret(self.secret_name)
+ status = 'Recover'
+ elif self.purge_if_need:
+ self.purge_deleted_secret(self.secret_name)
+ status = 'Purged'
+ else:
+ self.fail("Secret {0} is currently in a deleted but recoverable state, and its name cannot be reused; in this state,\
+ the secret can only be recovered or purged.".format(self.secret_name))
+ else:
+ results['secret_id'] = self.create_update_secret(self.secret_name, self.secret_value, self.tags, self.content_type, valid_from, expiry)
+ status = 'Created'
self.results['state'] = results
- self.results['state']['status'] = 'Created'
+ self.results['state']['status'] = status
# Delete secret
elif self.state == 'absent' and changed:
results['secret_id'] = self.delete_secret(self.secret_name)
@@ -225,6 +265,31 @@ class AzureRMKeyVaultSecret(AzureRMModuleBase):
result = self.get_poller_result(deleted_secret)
return result.properties._id
+ def recover_delete_secret(self, name):
+ ''' Recover a delete secret '''
+ try:
+ recover_delete_secret = self.client.begin_recover_deleted_secret(name)
+ result = self.get_poller_result(recover_delete_secret)
+ return result._id
+ except HttpResponseError as ec:
+ self.fail("Recover the delete secret fail, detail info {0}".format(ec))
+
+ def purge_deleted_secret(self, name):
+ ''' Purge delete secret '''
+ try:
+ purge_deleted_secret = self.client.purge_deleted_secret(name)
+ return purge_deleted_secret
+ except HttpResponseError as ec:
+ self.fail("Purge delete secret fail, detail info {0}".format(ec))
+
+ def get_delete_secret(self, name):
+ ''' Get delete secret '''
+ try:
+ self.client.get_deleted_secret(name=name)
+ except ResourceNotFoundError:
+ return False
+ return True
+
def main():
AzureRMKeyVaultSecret()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret_info.py
index b612dce36..f03977285 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_keyvaultsecret_info.py
@@ -163,12 +163,14 @@ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common
try:
from azure.keyvault.secrets import SecretClient
+ from azure.core.exceptions import ResourceNotFoundError
except ImportError:
# This is handled in azure_rm_common
pass
def secretbundle_to_dict(bundle):
+
return dict(tags=bundle._properties._tags,
attributes=dict(
enabled=bundle._properties._attributes.enabled,
@@ -302,9 +304,11 @@ class AzureRMKeyVaultSecretInfo(AzureRMModuleBase):
self.log("Response : {0}".format(response))
results.append(response)
- except Exception as e:
+ except ResourceNotFoundError as ec:
self.log("Did not find the key vault secret {0}: {1}".format(
- self.name, str(e)))
+ self.name, str(ec)))
+ except Exception as ec2:
+ self.fail("Find the key vault secret got exception, exception as {0}".format(str(ec2)))
return results
def get_secret_versions(self):
@@ -326,7 +330,7 @@ class AzureRMKeyVaultSecretInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log("Did not find secret versions {0} : {1}.".format(
+ self.fail("Did not find secret versions {0} : {1}.".format(
self.name, str(e)))
return results
@@ -349,7 +353,7 @@ class AzureRMKeyVaultSecretInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log(
+ self.fail(
"Did not find key vault in current subscription {0}.".format(
str(e)))
return results
@@ -372,9 +376,12 @@ class AzureRMKeyVaultSecretInfo(AzureRMModuleBase):
self.log("Response : {0}".format(response))
results.append(response)
- except Exception as e:
+ except ResourceNotFoundError as ec:
self.log("Did not find the key vault secret {0}: {1}".format(
- self.name, str(e)))
+ self.name, str(ec)))
+ except Exception as ec2:
+ self.fail("Did not find the key vault secret {0}: {1}".format(
+ self.name, str(ec2)))
return results
def list_deleted_secrets(self):
@@ -396,7 +403,7 @@ class AzureRMKeyVaultSecretInfo(AzureRMModuleBase):
if self.has_tags(item['tags'], self.tags):
results.append(item)
except Exception as e:
- self.log(
+ self.fail(
"Did not find key vault in current subscription {0}.".format(
str(e)))
return results
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_lock_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_lock_info.py
index 39abbf3b7..0805cfa62 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_lock_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_lock_info.py
@@ -158,7 +158,7 @@ class AzureRMLockInfo(AzureRMModuleBase):
for key in self.module_arg_spec.keys():
setattr(self, key, kwargs[key])
- self._mgmt_client = self.get_mgmt_svc_client(GenericRestClient, is_track2=True, base_url=self._cloud_environment.endpoints.resource_manager)
+ self._mgmt_client = self.get_mgmt_svc_client(GenericRestClient, base_url=self._cloud_environment.endpoints.resource_manager)
changed = False
# construct scope id
scope = self.get_scope()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_manageddisk.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_manageddisk.py
index 2b27fbbff..6f6443994 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_manageddisk.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_manageddisk.py
@@ -491,12 +491,13 @@ class AzureRMManagedDisk(AzureRMModuleBase):
# unmount from the old virtual machine and mount to the new virtual machine
if self.managed_by or self.managed_by == '':
vm_name = parse_resource_id(disk_instance.get('managed_by', '')).get('name') if disk_instance else None
+ resource_group = parse_resource_id(disk_instance.get('managed_by', '')).get('resource_group') if disk_instance else None
vm_name = vm_name or ''
if self.managed_by != vm_name or self.is_attach_caching_option_different(vm_name, result):
changed = True
if not self.check_mode:
if vm_name:
- self.detach(self.resource_group, vm_name, result)
+ self.detach(resource_group, vm_name, result)
if self.managed_by:
self.attach(self.resource_group, self.managed_by, result)
result = self.get_managed_disk()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_managementgroup.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_managementgroup.py
index 35235ccbe..f4f03f121 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_managementgroup.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_managementgroup.py
@@ -222,17 +222,16 @@ class Actions:
class AzureRMManagementGroups(AzureRMModuleBaseExt):
def __init__(self):
self.module_arg_spec = dict(
- group_id=dict(type='str', updatable=False, required=True),
- name=dict(type='str', updatable=False),
+ group_id=dict(type='str', required=True),
+ name=dict(type='str'),
id=dict(type='str'),
type=dict(type='str'),
properties=dict(
type='dict',
- disposition="/",
options=dict(
- tenant_id=dict(type='str', disposition="tenantId"),
- display_name=dict(type='str', disposition="displayName"),
- parent_id=dict(type='str', disposition="details/parent/id")
+ tenant_id=dict(type='str'),
+ display_name=dict(type='str'),
+ parent_id=dict(type='str')
)
),
state=dict(type='str', default='present', choices=['present', 'absent']),
@@ -260,16 +259,25 @@ class AzureRMManagementGroups(AzureRMModuleBaseExt):
for key in list(self.module_arg_spec.keys()):
if hasattr(self, key):
setattr(self, key, kwargs[key])
+ elif key == 'properties' and kwargs[key] is not None:
+ self.body['properties'] = {}
+ for item in kwargs['properties'].keys():
+ if item == 'tenant_id':
+ self.body['properties']['tenantId'] = kwargs['properties'][item]
+ elif item == 'display_name':
+ self.body['properties']['displayName'] = kwargs['properties'][item]
+ elif item == 'parent_id':
+ self.body['properties']['details'] = {}
+ self.body['properties']['details']['parent'] = {}
+ self.body['properties']['details']['parent']['id'] = kwargs['properties'][item]
+
elif kwargs[key] is not None:
self.body[key] = kwargs[key]
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
-
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
self.url = ('/providers' +
@@ -293,13 +301,14 @@ class AzureRMManagementGroups(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.results['compare'] = []
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
-
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
- self.to_do = Actions.Update
+ for key in self.body.keys():
+ if key == 'properties':
+ if old_response.get('properties') is None or \
+ not all(self.body['properties'][item] == old_response['properties'].get(item)
+ for item in self.body['properties'].keys()):
+ self.to_do = Actions.Update
+ elif self.body[key] != old_response.get(key):
+ self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
self.log('Need to Create / Update the ManagementGroup instance')
@@ -346,23 +355,34 @@ class AzureRMManagementGroups(AzureRMModuleBaseExt):
# self.log('Creating / Updating the ManagementGroup instance {0}'.format(self.))
try:
- response = self.mgmt_client.query(self.url,
- 'PUT',
- self.query_parameters,
- self.header_parameters,
- self.body,
- self.status_code,
- 600,
- 30)
+ if self.to_do == Actions.Create:
+ response = self.mgmt_client.query(self.url,
+ 'PUT',
+ self.query_parameters,
+ self.header_parameters,
+ self.body,
+ self.status_code,
+ 600,
+ 30)
+ else:
+ response = self.mgmt_client.query(self.url,
+ 'PATCH',
+ self.query_parameters,
+ self.header_parameters,
+ self.body,
+ self.status_code,
+ 600,
+ 30)
except Exception as exc:
self.log('Error attempting to create the ManagementGroup instance.')
self.fail('Error creating the ManagementGroup instance: {0}'.format(str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
- pass
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase.py
index a8caa1745..e26ac2a96 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase.py
@@ -92,7 +92,7 @@ import time
try:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
- from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
from azure.core.polling import LROPoller
except ImportError:
# This is handled in azure_rm_common
@@ -282,6 +282,8 @@ class AzureRMMySqlDatabase(AzureRMModuleBase):
self.log("MySQL Database instance : {0} found".format(response.name))
except ResourceNotFoundError as e:
self.log('Did not find the MySQL Database instance.')
+ except HttpResponseError as e:
+ self.log("Get MySQL Database instance error. code: {0}, message: {1}".format(e.status_code, str(e.error)))
if found is True:
return response.as_dict()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase_info.py
index db2cb8cdf..aef0f1df6 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_mysqldatabase_info.py
@@ -101,7 +101,7 @@ databases:
try:
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
- from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.exceptions import ResourceNotFoundError, HttpResponseError
except ImportError:
# This is handled in azure_rm_common
pass
@@ -159,6 +159,8 @@ class AzureRMMySqlDatabaseInfo(AzureRMModuleBase):
self.log("Response : {0}".format(response))
except ResourceNotFoundError as e:
self.log('Could not get facts for Databases.')
+ except HttpResponseError as e:
+ self.log("Get MySQL Database instance error. code: {0}, message: {1}".format(e.status_code, str(e.error)))
if response is not None:
results.append(self.format_item(response))
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface.py
index bb17132d4..3343d5ac1 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface.py
@@ -816,7 +816,7 @@ class AzureRMNetworkInterface(AzureRMModuleBase):
if self.state == 'present':
subnet = self.network_models.SubResource(
id='/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/virtualNetworks/{2}/subnets/{3}'.format(
- self.virtual_network['subscription_id'],
+ self.virtual_network['subscription'] if self.virtual_network.get('subscription') else self.virtual_network['subscription_id'],
self.virtual_network['resource_group'],
self.virtual_network['name'],
self.subnet_name))
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface_info.py
index deeb2c8c6..2a71473d6 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_networkinterface_info.py
@@ -63,176 +63,214 @@ EXAMPLES = '''
'''
RETURN = '''
-azure_networkinterfaces:
- description:
- - List of network interface dicts.
- returned: always
- type: list
- example: [{
- "dns_settings": {
- "applied_dns_servers": [],
- "dns_servers": [],
- "internal_dns_name_label": null,
- "internal_fqdn": null
- },
- "enable_ip_forwarding": false,
- "etag": 'W/"59726bfc-08c4-44ed-b900-f6a559876a9d"',
- "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup/providers/Microsoft.Network/networkInterfaces/nic003",
- "ip_configuration": {
- "name": "default",
- "private_ip_address": "10.10.0.4",
- "private_ip_allocation_method": "Dynamic",
- "public_ip_address": {
- "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup/providers/Microsoft.Network/publicIPAddresses/publicip001",
- "name": "publicip001"
- },
- "subnet": {
- "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup/providers/Microsoft.Network/virtualNetworks/vnet001/subnets/subnet001",
- "name": "subnet001",
- "virtual_network_name": "vnet001"
- }
- },
- "location": "westus",
- "mac_address": null,
- "name": "nic003",
- "network_security_group": {
- "id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup/providers/Microsoft.Network/networkSecurityGroups/secgroup001",
- "name": "secgroup001"
- },
- "primary": null,
- "provisioning_state": "Succeeded",
- "tags": {},
- "type": "Microsoft.Network/networkInterfaces"
- }]
networkinterfaces:
description:
- List of network interface dicts. Each dict contains parameters can be passed to M(azure.azcollection.azure_rm_networkinterface) module.
- type: list
+ type: complex
returned: always
contains:
id:
description:
- Id of the network interface.
type: str
+ returned: always
+ sample: "/subscriptions/xxxx-xxxxx/resourceGroups/testRG/providers/Microsoft.Network/networkInterfaces/nic01"
resource_group:
description:
- Name of a resource group where the network interface exists.
type: str
+ returned: always
+ sample: testRG
name:
description:
- Name of the network interface.
type: str
+ returned: always
+ sample: nic01
location:
description:
- Azure location.
type: str
+ returned: always
+ sample: eastus
virtual_network:
description:
- An existing virtual network with which the network interface will be associated.
- It is a dict which contains I(name) and I(resource_group) of the virtual network.
- type: raw
+ type: complex
+ returned: always
+ contains:
+ name:
+ description:
+ - The name of the virtual network relate network interface.
+ type: str
+ returned: always
+ sample: vnetnic01
+ resource_gorup:
+ description:
+ - Resource groups name that exist on the virtual network.
+ type: str
+ returned: always
+ sample: testRG
+ subscription_id:
+ description:
+ - Virtual network Subscription ID.
+ type: str
+ returned: always
+ sample: xxxxxxx-xxxxxxxxxxxxx
+ subnet_id:
+ description:
+ - The subnet's ID.
+ type: str
+ returned: always
+ sample: "/subscriptions/xxx-xxxx/resourceGroups/testRG/providers/Microsoft.Network/virtualNetworks/nic01/subnets/sub01"
subnet:
description:
- Name of an existing subnet within the specified virtual network.
type: str
+ returned: always
+ sample: sub01
tags:
description:
- Tags of the network interface.
type: dict
+ returned: always
+ sample: {key1: value1, key2: value2}
ip_configurations:
description:
- List of IP configurations, if contains multiple configurations.
type: complex
+ returned: always
contains:
name:
description:
- Name of the IP configuration.
type: str
+ returned: always
+ sample: default
private_ip_address:
description:
- Private IP address for the IP configuration.
- type: list
+ type: str
+ returned: always
+ sample: 10.10.0.4
private_ip_allocation_method:
description:
- Private IP allocation method.
type: str
+ returned: always
+ sample: Dynamic
public_ip_address:
description:
- Name of the public IP address. None for disable IP address.
type: str
+ returned: always
+ sample: null
public_ip_allocation_method:
description:
- Public IP allocation method.
type: str
+ returned: always
+ sample: null
load_balancer_backend_address_pools:
description:
- List of existing load-balancer backend address pools associated with the network interface.
type: list
+ returned: always
+ sample: null
application_gateway_backend_address_pools:
description:
- List of existing application gateway backend address pools associated with the network interface.
version_added: "1.10.0"
type: list
+ returned: always
+ sample: null
primary:
description:
- Whether the IP configuration is the primary one in the list.
type: bool
+ returned: always
+ sample: true
application_security_groups:
description:
- List of Application security groups.
type: list
+ returned: always
sample: ['/subscriptions/<subsid>/resourceGroups/<rg>/providers/Microsoft.Network/applicationSecurityGroups/myASG']
enable_accelerated_networking:
description:
- Specifies whether the network interface should be created with the accelerated networking feature or not.
type: bool
+ returned: always
+ sample: false
create_with_security_group:
description:
- Specifies whether a default security group should be be created with the NIC. Only applies when creating a new NIC.
type: bool
+ returned: always
+ sample: false
security_group:
description:
- A security group resource ID with which to associate the network interface.
type: str
+ returned: always
+ sample: null
enable_ip_forwarding:
description:
- Whether to enable IP forwarding
type: bool
+ returned: always
+ sample: false
dns_servers:
description:
- Which DNS servers should the NIC lookup.
- List of IP addresses.
type: list
+ returned: always
+ sample: []
mac_address:
description:
- The MAC address of the network interface.
type: str
+ returned: always
+ sample: null
provisioning_state:
description:
- The provisioning state of the network interface.
type: str
+ returned: always
+ sample: Succeeded
dns_settings:
description:
- The DNS settings in network interface.
type: complex
+ returned: always
contains:
dns_servers:
description:
- List of DNS servers IP addresses.
type: list
+ returned: always
+ sample: []
applied_dns_servers:
description:
- If the VM that uses this NIC is part of an Availability Set, then this list will have the union of all DNS servers
from all NICs that are part of the Availability Set. This property is what is configured on each of those VMs.
type: list
+ returned: always
+ sample: []
internal_dns_name_label:
description:
- Relative DNS name for this NIC used for internal communications between VMs in the same virtual network.
type: str
+ returned: always
+ sample: null
internal_fqdn:
description:
- Fully qualified DNS name supporting internal communications between VMs in the same virtual network.
type: str
+ returned: always
+ sample: null
''' # NOQA
try:
from azure.core.exceptions import ResourceNotFoundError
@@ -268,6 +306,8 @@ def nic_to_dict(nic):
subnet = subnet_dict.get('subnets') if subnet_dict else None
virtual_network = dict(
resource_group=subnet_dict.get('resourceGroups'),
+ subnet_id=config.subnet.id if config and config.subnet else None,
+ subscription_id=subnet_dict.get('subscriptions'),
name=subnet_dict.get('virtualNetworks')) if subnet_dict else None
return dict(
id=nic.id,
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster.py
index cc579c998..b0c28a190 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster.py
@@ -412,86 +412,57 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='resourceName',
required=True
),
location=dict(
type='str',
- updatable=False,
- required=True,
- disposition='/'
+ required=True
),
cluster_profile=dict(
type='dict',
- disposition='/properties/clusterProfile',
default=dict(),
options=dict(
pull_secret=dict(
type='str',
no_log=True,
- updatable=False,
- disposition='pullSecret',
- purgeIfNone=True
),
cluster_resource_group_id=dict(
type='str',
- updatable=False,
- disposition='resourceGroupId',
- purgeIfNone=True
),
domain=dict(
type='str',
- updatable=False,
- disposition='domain',
- purgeIfNone=True
),
version=dict(
type='str',
- updatable=False,
- disposition='version',
- purgeIfNone=True
)
),
),
service_principal_profile=dict(
type='dict',
- disposition='/properties/servicePrincipalProfile',
options=dict(
client_id=dict(
type='str',
- updatable=False,
- disposition='clientId',
required=True
),
client_secret=dict(
type='str',
no_log=True,
- updatable=False,
- disposition='clientSecret',
required=True
)
)
),
network_profile=dict(
type='dict',
- disposition='/properties/networkProfile',
options=dict(
pod_cidr=dict(
type='str',
- updatable=False,
- disposition='podCidr'
),
service_cidr=dict(
type='str',
- updatable=False,
- disposition='serviceCidr'
)
),
default=dict(
@@ -501,21 +472,15 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
),
master_profile=dict(
type='dict',
- disposition='/properties/masterProfile',
options=dict(
vm_size=dict(
type='str',
- updatable=False,
- disposition='vmSize',
choices=['Standard_D8s_v3',
'Standard_D16s_v3',
'Standard_D32s_v3'],
- purgeIfNone=True
),
subnet_id=dict(
type='str',
- updatable=False,
- disposition='subnetId',
required=True
)
)
@@ -523,94 +488,66 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
worker_profiles=dict(
type='list',
elements='dict',
- disposition='/properties/workerProfiles',
options=dict(
name=dict(
type='str',
- disposition='name',
- updatable=False,
required=True,
choices=['worker']
),
count=dict(
type='int',
- disposition='count',
- updatable=False,
- purgeIfNone=True
),
vm_size=dict(
type='str',
- disposition='vmSize',
- updatable=False,
choices=['Standard_D4s_v3',
'Standard_D8s_v3'],
- purgeIfNone=True
),
subnet_id=dict(
type='str',
- disposition='subnetId',
- updatable=False,
required=True
),
disk_size=dict(
type='int',
- disposition='diskSizeGB',
- updatable=False,
- purgeIfNone=True
)
)
),
api_server_profile=dict(
type='dict',
- disposition='/properties/apiserverProfile',
options=dict(
visibility=dict(
type='str',
- disposition='visibility',
choices=['Public', 'Private'],
default='Public'
),
url=dict(
type='str',
- disposition='*',
- updatable=False
),
ip=dict(
type='str',
- disposition='*',
- updatable=False
)
)
),
ingress_profiles=dict(
type='list',
elements='dict',
- disposition='/properties/ingressProfiles',
options=dict(
name=dict(
type='str',
- disposition='name',
- updatable=False,
choices=['default'],
default='default'
),
visibility=dict(
type='str',
- disposition='visibility',
- updatable=False,
choices=['Public', 'Private'],
default='Public'
),
ip=dict(
type='str',
- disposition='*',
- updatable=False
)
)
),
provisioning_state=dict(
type='str',
- disposition='/properties/provisioningState'
),
state=dict(
type='str',
@@ -630,6 +567,7 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
self.to_do = Actions.NoAction
self.body = {}
+ self.body['properties'] = {}
self.query_parameters = {}
self.header_parameters = {}
@@ -645,13 +583,58 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- self.body[key] = kwargs[key]
+ if key == 'cluster_profile':
+ self.body['properties']['clusterProfile'] = {}
+ for item in ['pull_secret', 'cluster_resource_group_id', 'domain', 'version']:
+ if item == 'pull_secret':
+ self.body['properties']['clusterProfile']['pullSecret'] = kwargs[key].get(item)
+ elif item == 'cluster_resource_group_id':
+ self.body['properties']['clusterProfile']['resourceGroupId'] = kwargs[key].get(item)
+ elif item == 'domain':
+ self.body['properties']['clusterProfile']['domain'] = kwargs[key].get(item)
+ elif item == 'version':
+ self.body['properties']['clusterProfile']['version'] = kwargs[key].get(item)
+ elif key == 'service_principal_profile':
+ self.body['properties']['servicePrincipalProfile'] = {}
+ self.body['properties']['servicePrincipalProfile']['ClientId'] = kwargs[key].get('client_id')
+ self.body['properties']['servicePrincipalProfile']['clientSecret'] = kwargs[key].get('client_secret')
+ elif key == 'network_profile':
+ self.body['properties']['networkProfile'] = {}
+ for item in kwargs[key].keys():
+ if item == 'pod_cidr':
+ self.body['properties']['networkProfile']['podCidr'] = kwargs[key].get(item)
+ elif item == 'service_cidr':
+ self.body['properties']['networkProfile']['serviceCidr'] = kwargs[key].get(item)
+ elif key == 'master_profile':
+ self.body['properties']['masterProfile'] = {}
+ if 'subnet_id' in kwargs[key].keys():
+ self.body['properties']['masterProfile']['subnetId'] = kwargs[key].get('subnet_id')
+ self.body['properties']['masterProfile']['vmSize'] = kwargs[key].get('vm_size')
+ elif key == 'worker_profiles':
+ self.body['properties']['workerProfiles'] = []
+ for item in kwargs[key]:
+ worker_profile = {}
+ if item.get('name') is not None:
+ worker_profile['name'] = item['name']
+ if item.get('subnet_id') is not None:
+ worker_profile['subnetId'] = item['subnet_id']
+ worker_profile['count'] = item.get('count')
+ worker_profile['vmSize'] = item.get('vm_size')
+ worker_profile['diskSizeGB'] = item.get('disk_size')
+
+ self.body['properties']['workerProfiles'].append(worker_profile)
+ elif key == 'api_server_profile':
+ self.body['properties']['apiserverProfile'] = kwargs[key]
+ elif key == 'ingress_profiles':
+ self.body['properties']['ingressProfiles'] = kwargs[key]
+ elif key == 'provisioning_state':
+ self.body['properties']['provisioningState'] = kwargs[key]
+ else:
+ self.body[key] = kwargs[key]
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
self.url = ('/subscriptions' +
@@ -756,11 +739,12 @@ class AzureRMOpenShiftManagedClusters(AzureRMModuleBaseExt):
self.log('Error attempting to create the OpenShiftManagedCluster instance.')
self.fail('Error creating the OpenShiftManagedCluster instance: {0}'
'\n{1}'.format(str(self.body), str(exc)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
- pass
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster_info.py
index ed359c641..cbda2da8b 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedcluster_info.py
@@ -254,7 +254,6 @@ class AzureRMOpenShiftManagedClustersInfo(AzureRMModuleBaseExt):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if (self.resource_group is not None and self.name is not None):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedclusterkubeconfig_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedclusterkubeconfig_info.py
new file mode 100644
index 000000000..5b5f73a29
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_openshiftmanagedclusterkubeconfig_info.py
@@ -0,0 +1,227 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2020 Haiyuan Zhang <haiyzhan@micosoft.com>
+#
+# 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: azure_rm_openshiftmanagedclusterkubeconfig_info
+version_added: '1.17.0'
+short_description: Get admin kubeconfig of Azure Red Hat OpenShift Managed Cluster
+description:
+ - get kubeconfig of Azure Red Hat OpenShift Managed Cluster instance.
+options:
+ resource_group:
+ description:
+ - The name of the resource group.
+ required: true
+ type: str
+ name:
+ description:
+ - Resource name.
+ required: true
+ type: str
+ path:
+ description:
+ - Destination filepath of kubeconfig file
+ required: false
+ type: str
+extends_documentation_fragment:
+ - azure.azcollection.azure
+author:
+ - Maxim Babushkin (@maxbab)
+'''
+
+EXAMPLES = '''
+- name: Obtain kubeconfig file of ARO cluster
+ azure_rm_openshiftmanagedclusterkubeconfig_info:
+ name: myCluster
+ resource_group: myResourceGroup
+ register: kubeconf
+
+- name: Print registered kubeconfig file
+ debug:
+ msg: "{{ kubeconf['kubeconfig'] }}"
+
+- name: Fetch kubeconfig and save it as mycluster_kubeconfig filename
+ azure_rm_openshiftmanagedclusterkubeconfig_info:
+ name: myCluster
+ resource_group: myResourceGroup
+ path: ./files/mycluster_kubeconfig
+
+- name: Fetch kubeconfig and save it to specified directory (file will be named as kubeconfig by default)
+ azure_rm_openshiftmanagedclusterkubeconfig_info:
+ name: myCluster
+ resource_group: myResourceGroup
+ path: ./files/
+'''
+
+RETURN = '''
+kubeconfig:
+ description:
+ - kubeconfig value
+ returned: always
+ type: str
+'''
+
+import base64
+import filecmp
+import json
+import os
+import tempfile
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_ext import AzureRMModuleBaseExt
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common_rest import GenericRestClient
+
+
+class Actions:
+ NoAction, Create, Update, Delete = range(4)
+
+
+class AzureRMOpenShiftManagedClustersKubeconfigInfo(AzureRMModuleBaseExt):
+ def __init__(self):
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str', required=True
+ ),
+ name=dict(
+ type='str', required=True
+ ),
+ path=dict(
+ type='str', required=False
+ )
+ )
+
+ self.resource_group = None
+ self.name = None
+ self.path = None
+
+ self.results = dict(changed=False)
+ self.mgmt_client = None
+ self.state = None
+ self.url = None
+ self.status_code = [200]
+
+ self.query_parameters = {}
+ self.query_parameters['api-version'] = '2021-09-01-preview'
+ self.header_parameters = {}
+ self.header_parameters['Content-Type'] = 'application/json; charset=utf-8'
+
+ self.mgmt_client = None
+ super(AzureRMOpenShiftManagedClustersKubeconfigInfo, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=False)
+
+ def exec_module(self, **kwargs):
+
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient, is_track2=True,
+ base_url=self._cloud_environment.endpoints.resource_manager)
+ self.results = self.get_kubeconfig()
+ if self.path and self.path_is_valid():
+ self.write_kubeconfig_to_file()
+ return self.results
+
+ def get_kubeconfig(self):
+ response = None
+ results = {}
+ # prepare url
+ self.url = ('/subscriptions' +
+ '/{{ subscription_id }}' +
+ '/resourceGroups' +
+ '/{{ resource_group }}' +
+ '/providers' +
+ '/Microsoft.RedHatOpenShift' +
+ '/openShiftClusters' +
+ '/{{ cluster_name }}' +
+ '/listAdminCredentials')
+ self.url = self.url.replace('{{ subscription_id }}', self.subscription_id)
+ self.url = self.url.replace('{{ resource_group }}', self.resource_group)
+ self.url = self.url.replace('{{ cluster_name }}', self.name)
+ self.log("Fetch for kubeconfig from the cluster.")
+ try:
+ response = self.mgmt_client.query(self.url,
+ 'POST',
+ self.query_parameters,
+ self.header_parameters,
+ None,
+ self.status_code,
+ 600,
+ 30)
+ results = json.loads(response.body())
+ except Exception as e:
+ self.log('Could not get info for @(Model.ModuleOperationNameUpper).')
+ return self.format_item(results)
+
+ def format_item(self, item):
+ d = {
+ 'kubeconfig': item.get('kubeconfig'),
+ }
+ return d
+
+ def path_is_valid(self):
+ if not os.path.basename(self.path):
+ if os.path.isdir(self.path):
+ self.log("Path is dir. Appending file name.")
+ self.path += "kubeconfig"
+ else:
+ try:
+ self.log('Attempting to makedirs {0}'.format(self.path))
+ os.makedirs(self.path)
+ except IOError as exc:
+ self.fail("Failed to create directory {0} - {1}".format(self.path, str(exc)))
+ self.path += "kubeconfig"
+ else:
+ file_name = os.path.basename(self.path)
+ path = self.path.replace(file_name, '')
+ self.log('Checking path {0}'. format(path))
+ # If the "path" is not defined, it's cwd.
+ if path and not os.path.isdir(path):
+ try:
+ self.log('Attempting to makedirs {0}'. format(path))
+ os.makedirs(path)
+ except IOError as exc:
+ self.fail("Failed to create directory {0} - {1}".format(path, str(exc)))
+ self.log("Validated path - {0}". format(self.path))
+ return True
+
+ def write_kubeconfig_to_file(self):
+ decoded_bytes = base64.b64decode(self.results['kubeconfig'])
+ decoded_string = decoded_bytes.decode("utf-8")
+
+ if os.path.exists(self.path):
+ self.log('Existing kubeconfig file found. Compare, to decide if needs to override')
+ # If kubeconfig file already exists, compare it with the new file
+ # If equal, do nothing, otherwise, override.
+ tmp_kubeconfig = tempfile.TemporaryFile(mode='w')
+ tmp_kubeconfig.write(decoded_string)
+ tmp_kubeconfig.seek(0)
+
+ # No need to close the temp file as it's closed by filecmp.cmp.
+ if filecmp.cmp(tmp_kubeconfig.name, self.path):
+ self.log("Files are identical. No need to override.")
+ self.results['changed'] = False
+ return
+
+ self.log("Create {0} kubeconfig file.".format(self.path))
+ try:
+ with open(self.path, "w") as file:
+ file.write(decoded_string)
+ except Exception as exc:
+ self.fail("Failed to write kubeconfig output to file - {0} to {1} - {2}".format(self.results['kubeconfig'],
+ self.path, exc))
+ self.log("The {0} kubeconfig file has been created.")
+ self.results['changed'] = True
+ return
+
+
+def main():
+ AzureRMOpenShiftManagedClustersKubeconfigInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py
new file mode 100644
index 000000000..52810303b
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py
@@ -0,0 +1,210 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexibleconfiguration_info
+version_added: "2.2.0"
+short_description: Get Azure PostgreSQL Flexible Configuration facts
+description:
+ - Get facts of Azure PostgreSQL Flexible Configuration.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource.
+ required: True
+ type: str
+ server_name:
+ description:
+ - The name of the server.
+ required: True
+ type: str
+ name:
+ description:
+ - Setting name.
+ type: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Get specific setting of PostgreSQL configuration
+ azure_rm_postgresqlflexibleconfiguration_info:
+ resource_group: myResourceGroup
+ server_name: testpostgresqlserver
+ name: deadlock_timeout
+
+- name: Get all settings of PostgreSQL Flexible Configuration
+ azure_rm_postgresqlflexibleconfiguration_info:
+ resource_group: myResourceGroup
+ server_name: testpostgresqlserver
+'''
+
+RETURN = '''
+settings:
+ description:
+ - A list of dictionaries containing MySQL Server settings.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Setting resource ID.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx-xxx/resourceGroups/testRG/providers/Microsoft.DBforPostgreSQL/flexibleServers/post2/configurations/xmloption"
+ name:
+ description:
+ - Setting name.
+ returned: always
+ type: str
+ sample: deadlock_timeout
+ server_name:
+ description:
+ - The name of the post gresql flexible server.
+ type: str
+ returned: always
+ sample: post2
+ resource_group:
+ description:
+ - Name of the server's resource group.
+ type: str
+ returned: always
+ sample: testRG
+ value:
+ description:
+ - Setting value.
+ returned: always
+ type: raw
+ sample: 1000
+ description:
+ description:
+ - Description of the configuration.
+ returned: always
+ type: str
+ sample: Deadlock timeout.
+ source:
+ description:
+ - Source of the configuration.
+ returned: always
+ type: str
+ sample: system-default
+'''
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSQLFlexibleConfigurationInfo(AzureRMModuleBase):
+ def __init__(self):
+ # define user inputs into argument
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ server_name=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str'
+ )
+ )
+ # store the results of the module operation
+ self.results = dict(
+ changed=False
+ )
+ self.resource_group = None
+ self.server_name = None
+ self.name = None
+ super(AzureRMPostgreSQLFlexibleConfigurationInfo, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=False)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ if self.name is not None:
+ self.results['settings'] = self.get()
+ else:
+ self.results['settings'] = self.list_by_server()
+ return self.results
+
+ def get(self):
+ '''
+ Gets facts of the specified PostgreSQL Flexible Configuration.
+
+ :return: deserialized PostgreSQL Flexible Configurationinstance state dictionary
+ '''
+ response = None
+ try:
+ response = self.postgresql_flexible_client.configurations.get(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ configuration_name=self.name)
+ self.log("Response : {0}".format(response))
+ except ResourceNotFoundError as e:
+ self.log('Could not get requested setting, Exception as {0}'.format(e))
+ return []
+
+ return [self.format_item(response)]
+
+ def list_by_server(self):
+ '''
+ Gets facts of the specified PostgreSQL Flexible Configuration.
+
+ :return: deserialized PostgreSQL Flexible Configurationinstance state dictionary
+ '''
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.configurations.list_by_server(resource_group_name=self.resource_group,
+ server_name=self.server_name)
+ self.log("Response : {0}".format(response))
+ except Exception as e:
+ self.log('List the flexible server config get exception, except as {0}'.format(e))
+ return []
+
+ if response is not None:
+ for item in response:
+ results.append(self.format_item(item))
+
+ return results
+
+ def format_item(self, item):
+ d = item.as_dict()
+ d = {
+ 'resource_group': self.resource_group,
+ 'server_name': self.server_name,
+ 'id': d['id'],
+ 'name': d['name'],
+ 'value': d['value'],
+ 'description': d['description'],
+ 'source': d['source']
+ }
+ return d
+
+
+def main():
+ AzureRMPostgreSQLFlexibleConfigurationInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase.py
new file mode 100644
index 000000000..0656ddac6
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase.py
@@ -0,0 +1,288 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexibledatabase
+version_added: "2.2.0"
+short_description: Manage PostgreSQL Flexible Database instance
+description:
+ - Create, update and delete instance of PostgreSQL Flexible Database.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal.
+ required: True
+ type: str
+ server_name:
+ description:
+ - The name of the server.
+ required: True
+ type: str
+ name:
+ description:
+ - The name of the database.
+ required: True
+ type: str
+ charset:
+ description:
+ - The charset of the database.
+ type: str
+ collation:
+ description:
+ - The collation of the database.
+ type: str
+ state:
+ description:
+ - Assert the state of the PostgreSQL Flexible database. Use C(present) to create or update a database and C(absent) to delete it.
+ default: present
+ type: str
+ choices:
+ - absent
+ - present
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Create (or update) PostgreSQL Flexible Database
+ azure_rm_postgresqlflexibledatabase:
+ resource_group: myResourceGroup
+ server_name: testserver
+ name: db1
+ charset: UTF8
+ collation: en_US.utf8
+
+- name: Delete PostgreSQL Flexible Database
+ azure_rm_postgresqlflexibledatabase:
+ resource_group: myResourceGroup
+ server_name: testserver
+ name: db1
+'''
+
+RETURN = '''
+database:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible Database.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID of the postgresql flexible database.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx-xxx/resourceGroups/testRG/providers/Microsoft.DBforPostgreSQL/flexibleServers/postfle9/databases/freddatabase"
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: freddatabase
+ charset:
+ description:
+ - The charset of the database.
+ returned: always
+ type: str
+ sample: UTF-8
+ collation:
+ description:
+ - The collation of the database.
+ returned: always
+ type: str
+ sample: en_US.utf8
+ type:
+ description:
+ - The type of the resource.
+ returned: always
+ type: str
+ sample: Microsoft.DBforPostgreSQL/flexibleServers/databases
+'''
+
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.polling import LROPoller
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSqlFlexibleDatabases(AzureRMModuleBase):
+ """Configuration class for an Azure RM PostgreSQL Flexible Database resource"""
+
+ def __init__(self):
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ server_name=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str',
+ required=True
+ ),
+ charset=dict(
+ type='str'
+ ),
+ collation=dict(
+ type='str'
+ ),
+ state=dict(
+ type='str',
+ default='present',
+ choices=['present', 'absent']
+ )
+ )
+
+ self.resource_group = None
+ self.server_name = None
+ self.name = None
+ self.parameters = dict()
+
+ self.results = dict(changed=False)
+ self.state = None
+
+ super(AzureRMPostgreSqlFlexibleDatabases, self).__init__(derived_arg_spec=self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=False)
+
+ def exec_module(self, **kwargs):
+ """Main module execution method"""
+
+ for key in list(self.module_arg_spec.keys()):
+ if hasattr(self, key):
+ setattr(self, key, kwargs[key])
+ elif kwargs[key] is not None:
+ if key == "charset":
+ self.parameters["charset"] = kwargs[key]
+ elif key == "collation":
+ self.parameters["collation"] = kwargs[key]
+
+ old_response = None
+ response = None
+ changed = False
+
+ old_response = self.get_postgresqlflexibledatabase()
+
+ if not old_response:
+ self.log("PostgreSQL Flexible Database instance doesn't exist")
+ if self.state == 'absent':
+ self.log("Old instance didn't exist")
+ else:
+ changed = True
+ if not self.check_mode:
+ response = self.create_update_postgresqlflexibledatabase(self.parameters)
+ else:
+ self.log("PostgreSQL Flexible Database instance already exists")
+ if self.state == 'absent':
+ changed = True
+ if not self.check_mode:
+ response = self.delete_postgresqlflexibledatabase()
+ else:
+ if (self.parameters.get('charset') is not None and self.parameters['charset'] != old_response['charset']) or\
+ (self.parameters.get('collation') is not None and self.parameters['collation'] != old_response['collation']):
+ changed = True
+ if not self.check_mode:
+ self.fail("The Post Gresql Flexible database not support to update")
+ else:
+ response = old_response
+
+ self.results['database'] = response
+ self.results['changed'] = changed
+ return self.results
+
+ def create_update_postgresqlflexibledatabase(self, body):
+ '''
+ Creates or updates PostgreSQL Flexible Database with the specified configuration.
+
+ :return: deserialized PostgreSQL Flexible Database instance state dictionary
+ '''
+ self.log("Creating / Updating the PostgreSQL Flexible Database instance {0}".format(self.name))
+
+ try:
+ response = self.postgresql_flexible_client.databases.begin_create(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ database_name=self.name,
+ parameters=body)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+
+ except Exception as exc:
+ self.log('Error attempting to create the PostgreSQL Flexible Database instance.')
+ self.fail("Error creating the PostgreSQL Flexible Database instance: {0}".format(str(exc)))
+ return self.format_item(response)
+
+ def delete_postgresqlflexibledatabase(self):
+ '''
+ Deletes specified PostgreSQL Flexible Database instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Deleting the PostgreSQL Flexible Database instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.databases.begin_delete(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ database_name=self.name)
+ except Exception as ec:
+ self.log('Error attempting to delete the PostgreSQL Flexible Database instance.')
+ self.fail("Error deleting the PostgreSQL Flexible Database instance: {0}".format(str(ec)))
+
+ def get_postgresqlflexibledatabase(self):
+ '''
+ Gets the properties of the specified PostgreSQL Flexible Database.
+
+ :return: deserialized PostgreSQL Flexible Database instance state dictionary
+ '''
+ self.log("Checking if the PostgreSQL Flexible Database instance {0} is present".format(self.name))
+ found = False
+ try:
+ response = self.postgresql_flexible_client.databases.get(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ database_name=self.name)
+ found = True
+ self.log("Response : {0}".format(response))
+ self.log("PostgreSQL Flexible Database instance : {0} found".format(response.name))
+ except ResourceNotFoundError as e:
+ self.log('Did not find the PostgreSQL Flexible Database instance. Exception as {0}'.format(e))
+ if found is True:
+ return self.format_item(response)
+
+ return None
+
+ def format_item(self, item):
+ result = dict(
+ id=item.id,
+ name=item.name,
+ type=item.type,
+ charset=item.charset,
+ collation=item.collation
+ )
+ return result
+
+
+def main():
+ """Main execution"""
+ AzureRMPostgreSqlFlexibleDatabases()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py
new file mode 100644
index 000000000..545baf53f
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py
@@ -0,0 +1,239 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexibledatabase_info
+version_added: "2.2.0"
+short_description: Get Azure PostgreSQL Flexible Database facts
+description:
+ - Get facts of PostgreSQL Flexible Database.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal.
+ type: str
+ required: True
+ server_name:
+ description:
+ - The name of the post gresql server.
+ type: str
+ required: True
+ name:
+ description:
+ - The name of the post gresql database.
+ type: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: List instance of PostgreSQL Flexible Database by server name
+ azure_rm_postgresqlflexibledatabase_info:
+ resource_group: myResourceGroup
+ server_name: server_name
+
+- name: Get instances of PostgreSQL Flexible Database
+ azure_rm_postgresqlflexibledatabase_info:
+ resource_group: myResourceGroup
+ server_name: server_name
+ name: database_name
+'''
+
+RETURN = '''
+database:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible Database.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID of the postgresql flexible database.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx-xxx/resourceGroups/testRG/providers/Microsoft.DBforPostgreSQL/flexibleServers/postfle9/databases/freddatabase"
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: freddatabase
+ charset:
+ description:
+ - The charset of the database.
+ returned: always
+ type: str
+ sample: UTF-8
+ collation:
+ description:
+ - The collation of the database.
+ returned: always
+ type: str
+ sample: en_US.utf8
+ type:
+ description:
+ - The type of the resource.
+ returned: always
+ type: str
+ sample: Microsoft.DBforPostgreSQL/flexibleServers/databases
+ system_data:
+ description:
+ - The system metadata relating to this resource.
+ type: complex
+ returned: always
+ contains:
+ created_by:
+ description:
+ - The identity that created the resource.
+ type: str
+ returned: always
+ sample: null
+ created_by_type:
+ description:
+ - The type of identity that created the resource.
+ returned: always
+ type: str
+ sample: null
+ created_at:
+ description:
+ - The timestamp of resource creation (UTC).
+ returned: always
+ sample: null
+ type: str
+ last_modified_by:
+ description:
+ - The identity that last modified the resource.
+ type: str
+ returned: always
+ sample: null
+ last_modified_by_type:
+ description:
+ - The type of identity that last modified the resource.
+ returned: always
+ sample: null
+ type: str
+ last_modified_at:
+ description:
+ - The timestamp of resource last modification (UTC).
+ returned: always
+ sample: null
+ type: str
+'''
+
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSqlFlexibleDatabaseInfo(AzureRMModuleBase):
+ def __init__(self):
+ # define user inputs into argument
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ server_name=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str'
+ ),
+ )
+ # store the results of the module operation
+ self.results = dict(
+ changed=False
+ )
+ self.resource_group = None
+ self.name = None
+ self.server_name = None
+ super(AzureRMPostgreSqlFlexibleDatabaseInfo, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=False, facts_module=True)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ if self.name is not None:
+ self.results['databases'] = self.get()
+ else:
+ self.results['databases'] = self.list_all()
+ return self.results
+
+ def get(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.databases.get(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ database_name=self.name)
+ self.log("Response : {0}".format(response))
+ except ResourceNotFoundError:
+ self.log('Could not get facts for PostgreSQL Flexible Server.')
+
+ if response is not None:
+ results.append(self.format_item(response))
+
+ return results
+
+ def list_all(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.databases.list_by_server(resource_group_name=self.resource_group,
+ server_name=self.server_name)
+ self.log("Response : {0}".format(response))
+ except Exception as ec:
+ self.log('Could not get facts for PostgreSQL Flexible Servers.')
+
+ if response is not None:
+ for item in response:
+ results.append(self.format_item(item))
+
+ return results
+
+ def format_item(self, item):
+ result = dict(
+ id=item.id,
+ name=item.name,
+ system_data=dict(),
+ type=item.type,
+ charset=item.charset,
+ collation=item.collation
+ )
+ if item.system_data is not None:
+ result['system_data']['created_by'] = item.system_data.created_by
+ result['system_data']['created_by_type'] = item.system_data.created_by_type
+ result['system_data']['created_at'] = item.system_data.created_at
+ result['system_data']['last_modified_by'] = item.system_data.last_modified_by
+ result['system_data']['last_modified_by_type'] = item.system_data.last_modified_by_type
+ result['system_data']['last_modified_at'] = item.system_data.last_modified_at
+
+ return result
+
+
+def main():
+ AzureRMPostgreSqlFlexibleDatabaseInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py
new file mode 100644
index 000000000..c73843c46
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py
@@ -0,0 +1,294 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexiblefirewallrule
+version_added: "2.2.0"
+short_description: Manage PostgreSQL flexible firewall rule instance
+description:
+ - Create, update and delete instance of PostgreSQL flexible firewall rule.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal.
+ required: True
+ type: str
+ server_name:
+ description:
+ - The name of the server.
+ required: True
+ type: str
+ name:
+ description:
+ - The name of the PostgreSQL flexible firewall rule.
+ required: True
+ type: str
+ start_ip_address:
+ description:
+ - The start IP address of the PostgreSQL flexible firewall rule. Must be IPv4 format.
+ type: str
+ end_ip_address:
+ description:
+ - The end IP address of the PostgreSQL flexible firewall rule. Must be IPv4 format.
+ type: str
+ state:
+ description:
+ - Assert the state of the PostgreSQL flexible firewall rule.
+ - Use C(present) to create or update a PostgreSQL flexible firewall rule and C(absent) to delete it.
+ default: present
+ type: str
+ choices:
+ - absent
+ - present
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Create (or update) PostgreSQL flexible firewall rule
+ azure_rm_postgresqlflexiblefirewallrule:
+ resource_group: myResourceGroup
+ server_name: testserver
+ name: rule1
+ start_ip_address: 10.0.0.16
+ end_ip_address: 10.0.0.18
+'''
+
+RETURN = '''
+rules:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible Firewall Rule.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx-xxx/resourceGroups/testRG/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibled9b/firewallRules/firewalld9b"
+ server_name:
+ description:
+ - The name of the server.
+ returned: always
+ type: str
+ sample: testserver
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: rule1
+ start_ip_address:
+ description:
+ - The start IP address of the PostgreSQL firewall rule.
+ returned: always
+ type: str
+ sample: 10.0.0.16
+ end_ip_address:
+ description:
+ - The end IP address of the PostgreSQL firewall rule.
+ returned: always
+ type: str
+ sample: 10.0.0.18
+'''
+
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.polling import LROPoller
+ import logging
+ logging.basicConfig(filename='log.log', level=logging.INFO)
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSqlFlexibleFirewallRules(AzureRMModuleBase):
+ """Configuration class for an Azure RM PostgreSQL flexible firewall rule resource"""
+
+ def __init__(self):
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ server_name=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str',
+ required=True
+ ),
+ start_ip_address=dict(
+ type='str'
+ ),
+ end_ip_address=dict(
+ type='str'
+ ),
+ state=dict(
+ type='str',
+ default='present',
+ choices=['present', 'absent']
+ )
+ )
+
+ self.resource_group = None
+ self.server_name = None
+ self.name = None
+ self.start_ip_address = None
+ self.end_ip_address = None
+
+ self.results = dict(changed=False)
+ self.state = None
+ self.parameters = dict()
+
+ super(AzureRMPostgreSqlFlexibleFirewallRules, self).__init__(derived_arg_spec=self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=False)
+
+ def exec_module(self, **kwargs):
+ """Main module execution method"""
+ for key in list(self.module_arg_spec.keys()):
+ if hasattr(self, key):
+ setattr(self, key, kwargs[key])
+ if key in ['start_ip_address', 'end_ip_address']:
+ self.parameters[key] = kwargs[key]
+
+ old_response = None
+ response = None
+ changed = False
+
+ old_response = self.get_firewallrule()
+
+ if old_response is None:
+ self.log("PostgreSQL flexible firewall rule instance doesn't exist")
+ if self.state == 'absent':
+ self.log("Old instance didn't exist")
+ else:
+ changed = True
+ if not self.check_mode:
+ response = self.create_update_firewallrule(self.parameters)
+ else:
+ self.log("PostgreSQL flexible firewall rule instance already exists")
+ if self.state == 'absent':
+ changed = True
+ if self.check_mode:
+ response = old_response
+ else:
+ response = self.delete_firewallrule()
+ else:
+ self.log("Need to check if PostgreSQL flexible firewall rule instance has to be deleted or may be updated")
+ if (self.start_ip_address is not None) and (self.start_ip_address != old_response['start_ip_address']):
+ changed = True
+ else:
+ self.parameters['start_ip_address'] = old_response['start_ip_address']
+ if (self.end_ip_address is not None) and (self.end_ip_address != old_response['end_ip_address']):
+ changed = True
+ else:
+ self.parameters['end_ip_address'] = old_response['end_ip_address']
+ if changed:
+ if not self.check_mode:
+ response = self.create_update_firewallrule(self.parameters)
+ else:
+ response = old_response
+ else:
+ response = old_response
+ self.results['firewall_rule'] = response
+ self.results['changed'] = changed
+
+ return self.results
+
+ def create_update_firewallrule(self, body):
+ '''
+ Creates or updates PostgreSQL flexible firewall rule with the specified configuration.
+
+ :return: deserialized PostgreSQL flexible firewall rule instance state dictionary
+ '''
+ self.log("Creating / Updating the PostgreSQL flexible firewall rule instance {0}".format(self.name))
+
+ try:
+ response = self.postgresql_flexible_client.firewall_rules.begin_create_or_update(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ firewall_rule_name=self.name,
+ parameters=body)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+
+ except Exception as exc:
+ self.log('Error attempting to create the PostgreSQL flexible firewall rule instance.')
+ self.fail("Error creating the PostgreSQL flexible firewall rule instance: {0}".format(str(exc)))
+ return self.format_item(response)
+
+ def delete_firewallrule(self):
+ '''
+ Deletes specified PostgreSQL flexible firewall rule instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Deleting the PostgreSQL flexible firewall rule instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.firewall_rules.begin_delete(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ firewall_rule_name=self.name)
+ except Exception as e:
+ self.log('Error attempting to delete the PostgreSQL flexible firewall rule instance.')
+ self.fail("Error deleting the PostgreSQL flexible firewall rule instance: {0}".format(str(e)))
+
+ return True
+
+ def get_firewallrule(self):
+ '''
+ Gets the properties of the specified PostgreSQL flexible firewall rule.
+
+ :return: deserialized PostgreSQL flexible firewall rule instance state dictionary
+ '''
+ self.log("Checking if the PostgreSQL flexible firewall rule instance {0} is present".format(self.name))
+ try:
+ response = self.postgresql_flexible_client.firewall_rules.get(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ firewall_rule_name=self.name)
+ self.log("Response : {0}".format(response))
+ self.log("PostgreSQL flexible firewall rule instance : {0} found".format(response.name))
+ except ResourceNotFoundError as e:
+ self.log('Did not find the PostgreSQL flexible firewall rule instance. Exception as {0}'.format(str(e)))
+ return None
+ return self.format_item(response)
+
+ def format_item(self, item):
+ d = item.as_dict()
+ d = {
+ 'resource_group': self.resource_group,
+ 'id': d['id'],
+ 'server_name': self.server_name,
+ 'name': d['name'],
+ 'start_ip_address': d['start_ip_address'],
+ 'end_ip_address': d['end_ip_address']
+ }
+ return d
+
+
+def main():
+ """Main execution"""
+ AzureRMPostgreSqlFlexibleFirewallRules()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py
new file mode 100644
index 000000000..14eb029b4
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py
@@ -0,0 +1,187 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexiblefirewallrule_info
+version_added: "2.2.0"
+short_description: Get Azure PostgreSQL Flexible Firewall Rule facts
+description:
+ - Get facts of Azure PostgreSQL Flexible Firewall Rule.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group.
+ required: True
+ type: str
+ server_name:
+ description:
+ - The name of the server.
+ required: True
+ type: str
+ name:
+ description:
+ - The name of the server firewall rule.
+ type: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Get instance of PostgreSQL Flexible Firewall Rule
+ azure_rm_postgresqlflexiblefirewallrule_info:
+ resource_group: myResourceGroup
+ server_name: server_name
+ name: firewall_rule_name
+
+- name: List instances of PostgreSQL Flexible Firewall Rule
+ azure_rm_postgresqlflexiblefirewallrule_info:
+ resource_group: myResourceGroup
+ server_name: server_name
+'''
+
+RETURN = '''
+rules:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible Firewall Rule.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx-xxx/resourceGroups/testRG/providers/Microsoft.DBforPostgreSQL/flexibleServers/flexibled9b/firewallRules/firewalld9b"
+ server_name:
+ description:
+ - The name of the server.
+ returned: always
+ type: str
+ sample: testserver
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: rule1
+ start_ip_address:
+ description:
+ - The start IP address of the PostgreSQL firewall rule.
+ returned: always
+ type: str
+ sample: 10.0.0.16
+ end_ip_address:
+ description:
+ - The end IP address of the PostgreSQL firewall rule.
+ returned: always
+ type: str
+ sample: 10.0.0.18
+'''
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSQLFlexibleFirewallRulesInfo(AzureRMModuleBase):
+ def __init__(self):
+ # define user inputs into argument
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ server_name=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str'
+ )
+ )
+ # store the results of the module operation
+ self.results = dict(
+ changed=False
+ )
+ self.resource_group = None
+ self.server_name = None
+ self.name = None
+ super(AzureRMPostgreSQLFlexibleFirewallRulesInfo, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=False)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ if self.name is not None:
+ self.results['firewall_rules'] = self.get()
+ else:
+ self.results['firewall_rules'] = self.list_by_server()
+ return self.results
+
+ def get(self):
+ response = None
+ try:
+ response = self.postgresql_flexible_client.firewall_rules.get(resource_group_name=self.resource_group,
+ server_name=self.server_name,
+ firewall_rule_name=self.name)
+ self.log("Response : {0}".format(response))
+ except ResourceNotFoundError as e:
+ self.log('Could not get facts for FirewallRules. Exception as {0}'.format(str(e)))
+ return []
+
+ return [self.format_item(response)]
+
+ def list_by_server(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.firewall_rules.list_by_server(resource_group_name=self.resource_group,
+ server_name=self.server_name)
+ self.log("Response : {0}".format(response))
+ except Exception as e:
+ self.log('Could not get facts for FirewallRules. Exception as {0}'.format(str(e)))
+ return []
+
+ if response is not None:
+ for item in response:
+ results.append(self.format_item(item))
+
+ return results
+
+ def format_item(self, item):
+ d = item.as_dict()
+ d = {
+ 'resource_group': self.resource_group,
+ 'id': d['id'],
+ 'server_name': self.server_name,
+ 'name': d['name'],
+ 'start_ip_address': d['start_ip_address'],
+ 'end_ip_address': d['end_ip_address']
+ }
+ return d
+
+
+def main():
+ AzureRMPostgreSQLFlexibleFirewallRulesInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver.py
new file mode 100644
index 000000000..335dc53c8
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver.py
@@ -0,0 +1,928 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexibleserver
+version_added: "2.2.0"
+short_description: Manage PostgreSQL Flexible Server instance
+description:
+ - Create, update and delete instance of PostgreSQL Flexible Server.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource.
+ - You can obtain this value from the Azure Resource Manager API or the portal.
+ required: True
+ type: str
+ name:
+ description:
+ - The name of the flexible server.
+ required: True
+ type: str
+ sku:
+ description:
+ - The SKU (pricing tier) of the server.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - The name of the sku, typically, tier + family + cores, such as Standard_D4s_v3.
+ type: str
+ required: True
+ tier:
+ description:
+ - The tier of the particular
+ type: str
+ choices:
+ - Burstable
+ - GeneralPurpose
+ - MemoryOptimized
+ required: True
+ location:
+ description:
+ - Resource location. If not set, location from the resource group will be used as default.
+ type: str
+ storage:
+ description:
+ - Storage properties of a server.
+ type: dict
+ suboptions:
+ storage_size_gb:
+ description:
+ - The storage size for the server.
+ type: int
+ administrator_login:
+ description:
+ - The administrator's login name of a server.
+ - Can only be specified when the server is being created (and is required for creation).
+ type: str
+ administrator_login_password:
+ description:
+ - The administrator login password (required for server creation).
+ type: str
+ version:
+ description:
+ - PostgreSQL Server version.
+ type: str
+ choices:
+ - '11'
+ - '12'
+ - '13'
+ fully_qualified_domain_name:
+ description:
+ - The fully qualified domain name of a server.
+ type: str
+ backup:
+ description:
+ - Backup properties of a server.
+ type: dict
+ suboptions:
+ backup_retention_days:
+ description:
+ - Backup retention days for the server.
+ type: int
+ geo_redundant_backup:
+ description:
+ - A value indicating whether Geo-Redundant backup is enabled on the server.
+ type: str
+ choices:
+ - Enabled
+ - Disabled
+ network:
+ description:
+ - Network properties of a server.
+ type: dict
+ suboptions:
+ delegated_subnet_resource_id:
+ description:
+ - Delegated subnet arm resource id.
+ type: str
+ private_dns_zone_arm_resource_id:
+ description:
+ - Private dns zone arm resource id.
+ type: str
+ public_network_access:
+ description:
+ - Public network access is enabled or not.
+ type: str
+ choices:
+ - Enabled
+ - Disabled
+ high_availability:
+ description:
+ - High availability properties of a server.
+ type: dict
+ suboptions:
+ mode:
+ description:
+ - The HA mode for the server.
+ type: str
+ choices:
+ - Disabled
+ - ZoneRedundant
+ standby_availability_zone:
+ description:
+ - Availability zone information of the standby.
+ type: str
+ maintenance_window:
+ description:
+ - Maintenance window properties of a server.
+ type: dict
+ suboptions:
+ custom_window:
+ description:
+ - Indicates whether custom window is enabled or disabled.
+ type: str
+ start_hour:
+ description:
+ - Start hour for maintenance window.
+ type: int
+ start_minute:
+ description:
+ - Start minute for maintenance window.
+ type: int
+ day_of_week:
+ description:
+ - Day of week for maintenance window.
+ type: int
+ point_in_time_utc:
+ description:
+ - Restore point creation time (ISO8601 format), specifying the time to restore from.
+ - It's required when I(create_mode=PointInTimeRestore).
+ type: str
+ availability_zone:
+ description:
+ - Availability zone information of the server
+ type: str
+ create_mode:
+ description:
+ - The mode to create a new PostgreSQL server.
+ type: str
+ choices:
+ - Default
+ - Create
+ - Update
+ - PointInTimeRestore
+ source_server_resource_id:
+ description:
+ - The source server resource ID to restore from.
+ - It's required when I(create_mode=PointInTimeRestore)
+ type: str
+ state:
+ description:
+ - Assert the state of the PostgreSQL Flexible server.
+ - Use C(present) to create or update a server and C(absent) to delete it.
+ default: present
+ type: str
+ choices:
+ - present
+ - absent
+ is_restart:
+ description:
+ - Whether to restart the Post gresql server.
+ type: bool
+ default: False
+ is_stop:
+ description:
+ - Whether to stop the Post gresql server.
+ type: bool
+ default: False
+ is_start:
+ description:
+ - Whether to start the Post gresql server.
+ type: bool
+ default: False
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+ - azure.azcollection.azure_tags
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Create (or update) PostgreSQL Flexible Server
+ azure_rm_postgresqlflexibleserver:
+ resource_group: myResourceGroup
+ name: testserver
+ sku:
+ name: Standard_B1ms
+ tier: Burstable
+ administrator_login: azureuser
+ administrator_login_password: Fred@0329
+ version: 12
+ storage:
+ storage_size_gb: 128
+ fully_qualified_domain_name: st-private-dns-zone.postgres.database.azure.com
+ backup:
+ backup_retention_days: 7
+ geo_redundant_backup: Disabled
+ maintenance_window:
+ custom_window: Enabled
+ start_hour: 8
+ start_minute: 0
+ day_of_week: 0
+ point_in_time_utc: 2023-05-31T00:28:17.7279547+00:00
+ availability_zone: 1
+ create_mode: Default
+
+- name: Delete PostgreSQL Flexible Server
+ azure_rm_postgresqlflexibleserver:
+ resource_group: myResourceGroup
+ name: testserver
+ state: absent
+'''
+
+RETURN = '''
+servers:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible servers.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID of the postgresql flexible server.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforPostgreSQL/flexibleservers/postgresql3"
+ resource_group:
+ description:
+ - Resource group name.
+ returned: always
+ type: str
+ sample: myResourceGroup
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: postgreabdud1223
+ location:
+ description:
+ - The location the resource resides in.
+ returned: always
+ type: str
+ sample: eastus
+ sku:
+ description:
+ - The SKU of the server.
+ returned: always
+ type: complex
+ contains:
+ name:
+ description:
+ - The name of the SKU.
+ returned: always
+ type: str
+ sample: Standard_B1ms
+ tier:
+ description:
+ - The tier of the particular SKU.
+ returned: always
+ type: str
+ sample: Burstable
+ storage:
+ description:
+ - The maximum storage allowed for a server.
+ returned: always
+ type: complex
+ contains:
+ storage_size_gb:
+ description:
+ - ax storage allowed for a server.
+ type: int
+ returned: always
+ sample: 128
+ administrator_login:
+ description:
+ - The administrator's login name of a server.
+ returned: always
+ type: str
+ sample: azureuser
+ version:
+ description:
+ - Flexible Server version.
+ returned: always
+ type: str
+ sample: "12"
+ choices:
+ - '11'
+ - '12'
+ - '13'
+ fully_qualified_domain_name:
+ description:
+ - The fully qualified domain name of the flexible server.
+ returned: always
+ type: str
+ sample: postflexiblefredpgsqlflexible.postgres.database.azure.com
+ availability_zone:
+ description:
+ - Availability zone information of the server.
+ type: str
+ returned: always
+ sample: 1
+ backup:
+ description:
+ - Backup properties of a server.
+ type: complex
+ returned: always
+ contains:
+ backup_retention_days:
+ description:
+ - Backup retention days for the server.
+ type: int
+ returned: always
+ sample: 7
+ geo_redundant_backup:
+ description:
+ - A value indicating whether Geo-Redundant backup is enabled on the server.
+ type: str
+ returned: always
+ sample: Disabled
+ high_availability:
+ description:
+ - High availability properties of a server.
+ type: complex
+ returned: always
+ contains:
+ mode:
+ description:
+ - The HA mode for the server.
+ returned: always
+ sample: Disabled
+ type: str
+ standby_availability_zone:
+ description:
+ - availability zone information of the standby.
+ type: str
+ returned: always
+ sample: null
+ maintenance_window:
+ description:
+ - Maintenance window properties of a server.
+ type: complex
+ returned: always
+ contains:
+ custom_window:
+ description:
+ - Indicates whether custom window is enabled or disabled.
+ returned: always
+ sample: Enabled
+ type: str
+ day_of_week:
+ description:
+ - Day of week for maintenance window.
+ returned: always
+ sample: 0
+ type: int
+ start_hour:
+ description:
+ - Start hour for maintenance window.
+ type: int
+ returned: always
+ sample: 8
+ start_minute:
+ description:
+ - Start minute for maintenance window.
+ type: int
+ returned: always
+ sample: 0
+ network:
+ description:
+ - Network properties of a server.
+ type: complex
+ returned: always
+ contains:
+ delegated_subnet_resource_id:
+ description:
+ - Delegated subnet arm resource id.
+ type: str
+ returned: always
+ sample: null
+ private_dns_zone_arm_resource_id:
+ description:
+ - Private dns zone arm resource id.
+ type: str
+ returned: always
+ sample: null
+ public_network_access:
+ description:
+ - Public network access is enabled or not.
+ type: str
+ returned: always
+ sample: Enabled
+ point_in_time_utc:
+ description:
+ - Restore point creation time (ISO8601 format).
+ type: str
+ sample: null
+ returned: always
+ source_server_resource_id:
+ description:
+ - The source server resource ID to restore from.
+ type: str
+ returned: always
+ sample: null
+ system_data:
+ description:
+ - The system metadata relating to this resource.
+ type: complex
+ returned: always
+ contains:
+ created_by:
+ description:
+ - The identity that created the resource.
+ type: str
+ returned: always
+ sample: null
+ created_by_type:
+ description:
+ - The type of identity that created the resource.
+ returned: always
+ type: str
+ sample: null
+ created_at:
+ description:
+ - The timestamp of resource creation (UTC).
+ returned: always
+ sample: null
+ type: str
+ last_modified_by:
+ description:
+ - The identity that last modified the resource.
+ type: str
+ returned: always
+ sample: null
+ last_modified_by_type:
+ description:
+ - The type of identity that last modified the resource.
+ returned: always
+ sample: null
+ type: str
+ last_modified_at:
+ description:
+ - The timestamp of resource last modification (UTC).
+ returned: always
+ sample: null
+ type: str
+ tags:
+ description:
+ - Tags assigned to the resource. Dictionary of string:string pairs.
+ type: dict
+ returned: always
+ sample: { tag1: abc }
+'''
+
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+ from azure.core.polling import LROPoller
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+sku_spec = dict(
+ name=dict(type='str', required=True),
+ tier=dict(type='str', required=True, choices=["Burstable", "GeneralPurpose", "MemoryOptimized"])
+)
+
+
+maintenance_window_spec = dict(
+ custom_window=dict(type='str'),
+ start_hour=dict(type='int'),
+ start_minute=dict(type='int'),
+ day_of_week=dict(type='int'),
+)
+
+
+high_availability_spec = dict(
+ mode=dict(type='str', choices=["Disabled", "ZoneRedundant"]),
+ standby_availability_zone=dict(type='str')
+)
+
+
+network_spec = dict(
+ delegated_subnet_resource_id=dict(type='str'),
+ private_dns_zone_arm_resource_id=dict(type='str'),
+ public_network_access=dict(type='str', choices=["Enabled", "Disabled"])
+)
+
+
+backup_spec = dict(
+ backup_retention_days=dict(type='int'),
+ geo_redundant_backup=dict(type='str', choices=["Enabled", "Disabled"]),
+)
+
+
+storage_spec = dict(
+ storage_size_gb=dict(type='int')
+)
+
+
+class AzureRMPostgreSqlFlexibleServers(AzureRMModuleBase):
+ """Configuration class for an Azure RM PostgreSQL Flexible Server resource"""
+
+ def __init__(self):
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ required=True
+ ),
+ name=dict(
+ type='str',
+ required=True
+ ),
+ sku=dict(
+ type='dict',
+ options=sku_spec
+ ),
+ location=dict(
+ type='str'
+ ),
+ administrator_login=dict(
+ type='str'
+ ),
+ administrator_login_password=dict(
+ type='str',
+ no_log=True
+ ),
+ version=dict(
+ type='str',
+ choices=['11', '12', '13']
+ ),
+ fully_qualified_domain_name=dict(
+ type='str',
+ ),
+ storage=dict(
+ type='dict',
+ options=storage_spec
+ ),
+ backup=dict(
+ type='dict',
+ options=backup_spec
+ ),
+ network=dict(
+ type='dict',
+ options=network_spec
+ ),
+ high_availability=dict(
+ type='dict',
+ options=high_availability_spec
+ ),
+ maintenance_window=dict(
+ type='dict',
+ options=maintenance_window_spec
+ ),
+ point_in_time_utc=dict(
+ type='str'
+ ),
+ availability_zone=dict(
+ type='str'
+ ),
+ create_mode=dict(
+ type='str',
+ choices=['Default', 'Create', 'Update', 'PointInTimeRestore']
+ ),
+ is_start=dict(
+ type='bool',
+ default=False,
+ ),
+ is_restart=dict(
+ type='bool',
+ default=False
+ ),
+ is_stop=dict(
+ type='bool',
+ default=False
+ ),
+ source_server_resource_id=dict(
+ type='str'
+ ),
+ state=dict(
+ type='str',
+ default='present',
+ choices=['present', 'absent']
+ )
+ )
+
+ self.resource_group = None
+ self.name = None
+ self.parameters = dict()
+ self.update_parameters = dict()
+ self.tags = None
+ self.is_start = None
+ self.is_stop = None
+ self.is_restart = None
+
+ self.results = dict(changed=False)
+ self.state = None
+
+ super(AzureRMPostgreSqlFlexibleServers, self).__init__(derived_arg_spec=self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=True)
+
+ def exec_module(self, **kwargs):
+ """Main module execution method"""
+
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
+ if hasattr(self, key):
+ setattr(self, key, kwargs[key])
+ elif kwargs[key] is not None:
+ self.parameters[key] = kwargs[key]
+ for key in ['location', 'sku', 'administrator_login_password', 'storage', 'backup', 'high_availability', 'maintenance_window', 'create_mode']:
+ self.update_parameters[key] = kwargs[key]
+
+ old_response = None
+ response = None
+ changed = False
+
+ resource_group = self.get_resource_group(self.resource_group)
+
+ if "location" not in self.parameters:
+ self.parameters["location"] = resource_group.location
+ self.update_parameters["location"] = resource_group.location
+
+ old_response = self.get_postgresqlflexibleserver()
+
+ if not old_response:
+ self.log("PostgreSQL Flexible Server instance doesn't exist")
+ if self.state == 'present':
+ if not self.check_mode:
+ response = self.create_postgresqlflexibleserver(self.parameters)
+ if self.is_stop:
+ self.stop_postgresqlflexibleserver()
+ elif self.is_start:
+ self.start_postgresqlflexibleserver()
+ elif self.is_restart:
+ self.restart_postgresqlflexibleserver()
+ changed = True
+ else:
+ self.log("PostgreSQL Flexible Server instance doesn't exist, Don't need to delete")
+ else:
+ self.log("PostgreSQL Flexible Server instance already exists")
+ if self.state == 'present':
+ update_flag = False
+ if self.update_parameters.get('sku') is not None:
+ for key in self.update_parameters['sku'].keys():
+ if self.update_parameters['sku'][key] is not None and self.update_parameters['sku'][key] != old_response['sku'].get(key):
+ update_flag = True
+ else:
+ self.update_parameters['sku'][key] = old_response['sku'].get(key)
+
+ if self.update_parameters.get('storage') is not None and self.update_parameters['storage'] != old_response['storage']:
+ update_flag = True
+ else:
+ self.update_parameters['storage'] = old_response['storage']
+
+ if self.update_parameters.get('backup') is not None:
+ for key in self.update_parameters['backup'].keys():
+ if self.update_parameters['backup'][key] is not None and self.update_parameters['backup'][key] != old_response['backup'].get(key):
+ update_flag = True
+ else:
+ self.update_parameters['backup'][key] = old_response['backup'].get(key)
+
+ if self.update_parameters.get('high_availability') is not None:
+ for key in self.update_parameters['high_availability'].keys():
+ if (self.update_parameters['high_availability'][key] is not None) and\
+ (self.update_parameters['high_availability'][key] != old_response['high_availability'].get(key)):
+ update_flag = True
+ else:
+ self.update_parameters['high_availability'][key] = old_response['high_availability'].get(key)
+
+ if self.update_parameters.get('maintenance_window') is not None:
+ for key in self.update_parameters['maintenance_window'].keys():
+ if (self.update_parameters['maintenance_window'][key] is not None) and\
+ (self.update_parameters['maintenance_window'][key] != old_response['maintenance_window'].get(key)):
+ update_flag = True
+ else:
+ self.update_parameters['maintenance_window'][key] = old_response['maintenance_window'].get(key)
+
+ update_tags, new_tags = self.update_tags(old_response['tags'])
+ self.update_parameters['tags'] = new_tags
+ if update_tags:
+ update_flag = True
+
+ if update_flag:
+ changed = True
+ if not self.check_mode:
+ response = self.update_postgresqlflexibleserver(self.update_parameters)
+ else:
+ response = old_response
+ if self.is_stop:
+ self.stop_postgresqlflexibleserver()
+ changed = True
+ elif self.is_start:
+ self.start_postgresqlflexibleserver()
+ changed = True
+ elif self.is_restart:
+ self.restart_postgresqlflexibleserver()
+ changed = True
+ else:
+ if not self.check_mode:
+ if self.is_stop:
+ self.stop_postgresqlflexibleserver()
+ changed = True
+ elif self.is_start:
+ self.start_postgresqlflexibleserver()
+ changed = True
+ elif self.is_restart:
+ self.restart_postgresqlflexibleserver()
+ changed = True
+ response = old_response
+ else:
+ self.log("PostgreSQL Flexible Server instance already exist, will be deleted")
+ changed = True
+ if not self.check_mode:
+ response = self.delete_postgresqlflexibleserver()
+
+ self.results['changed'] = changed
+ self.results['state'] = response
+
+ return self.results
+
+ def update_postgresqlflexibleserver(self, body):
+ '''
+ Updates PostgreSQL Flexible Server with the specified configuration.
+ :return: deserialized PostgreSQL Flexible Server instance state dictionary
+ '''
+ self.log("Updating the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ # structure of parameters for update must be changed
+ response = self.postgresql_flexible_client.servers.begin_update(resource_group_name=self.resource_group,
+ server_name=self.name,
+ parameters=body)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+
+ except Exception as exc:
+ self.log('Error attempting to create the PostgreSQL Flexible Server instance.')
+ self.fail("Error updating the PostgreSQL Flexible Server instance: {0}".format(str(exc)))
+ return self.format_item(response)
+
+ def create_postgresqlflexibleserver(self, body):
+ '''
+ Creates PostgreSQL Flexible Server with the specified configuration.
+ :return: deserialized PostgreSQL Flexible Server instance state dictionary
+ '''
+ self.log("Creating the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ response = self.postgresql_flexible_client.servers.begin_create(resource_group_name=self.resource_group,
+ server_name=self.name,
+ parameters=body)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+
+ except Exception as exc:
+ self.log('Error attempting to create the PostgreSQL Flexible Server instance.')
+ self.fail("Error creating the PostgreSQL Flexible Server instance: {0}".format(str(exc)))
+ return self.format_item(response)
+
+ def delete_postgresqlflexibleserver(self):
+ '''
+ Deletes specified PostgreSQL Flexible Server instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Deleting the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.servers.begin_delete(resource_group_name=self.resource_group,
+ server_name=self.name)
+ except Exception as e:
+ self.log('Error attempting to delete the PostgreSQL Flexible Server instance.')
+ self.fail("Error deleting the PostgreSQL Flexible Server instance: {0}".format(str(e)))
+
+ def stop_postgresqlflexibleserver(self):
+ '''
+ Stop PostgreSQL Flexible Server instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Stop the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.servers.begin_stop(resource_group_name=self.resource_group,
+ server_name=self.name)
+ except Exception as e:
+ self.log('Error attempting to stop the PostgreSQL Flexible Server instance.')
+ self.fail("Error stop the PostgreSQL Flexible Server instance: {0}".format(str(e)))
+
+ def start_postgresqlflexibleserver(self):
+ '''
+ Start PostgreSQL Flexible Server instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Starting the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.servers.begin_start(resource_group_name=self.resource_group,
+ server_name=self.name)
+ except Exception as e:
+ self.log('Error attempting to start the PostgreSQL Flexible Server instance.')
+ self.fail("Error starting the PostgreSQL Flexible Server instance: {0}".format(str(e)))
+
+ def restart_postgresqlflexibleserver(self):
+ '''
+ Restart PostgreSQL Flexible Server instance in the specified subscription and resource group.
+
+ :return: True
+ '''
+ self.log("Restarting the PostgreSQL Flexible Server instance {0}".format(self.name))
+ try:
+ self.postgresql_flexible_client.servers.begin_restart(resource_group_name=self.resource_group,
+ server_name=self.name)
+ except Exception as e:
+ self.log('Error attempting to restart the PostgreSQL Flexible Server instance.')
+ self.fail("Error restarting the PostgreSQL Flexible Server instance: {0}".format(str(e)))
+
+ def get_postgresqlflexibleserver(self):
+ '''
+ Gets the properties of the specified PostgreSQL Flexible Server.
+
+ :return: deserialized PostgreSQL Flexible Server instance state dictionary
+ '''
+ self.log("Checking if the PostgreSQL Flexible Server instance {0} is present".format(self.name))
+ try:
+ response = self.postgresql_flexible_client.servers.get(resource_group_name=self.resource_group,
+ server_name=self.name)
+ self.log("Response : {0}".format(response))
+ self.log("PostgreSQL Flexible Server instance : {0} found".format(response.name))
+ except ResourceNotFoundError as e:
+ self.log('Did not find the PostgreSQL Flexible Server instance. Exception as {0}'.format(str(e)))
+ return None
+
+ return self.format_item(response)
+
+ def format_item(self, item):
+ result = dict(
+ id=item.id,
+ resource_group=self.resource_group,
+ name=item.name,
+ sku=dict(),
+ location=item.location,
+ tags=item.tags,
+ system_data=dict(),
+ administrator_login=item.administrator_login,
+ version=item.version,
+ minor_version=item.minor_version,
+ fully_qualified_domain_name=item.fully_qualified_domain_name,
+ storage=dict(),
+ backup=dict(),
+ network=dict(),
+ high_availability=dict(),
+ maintenance_window=dict(),
+ source_server_resource_id=item.source_server_resource_id,
+ point_in_time_utc=item.point_in_time_utc,
+ availability_zone=item.availability_zone,
+ )
+ if item.sku is not None:
+ result['sku']['name'] = item.sku.name
+ result['sku']['tier'] = item.sku.tier
+ if item.system_data is not None:
+ result['system_data']['created_by'] = item.system_data.created_by
+ result['system_data']['created_by_type'] = item.system_data.created_by_type
+ result['system_data']['created_at'] = item.system_data.created_at
+ result['system_data']['last_modified_by'] = item.system_data.last_modified_by
+ result['system_data']['last_modified_by_type'] = item.system_data.last_modified_by_type
+ result['system_data']['last_modified_at'] = item.system_data.last_modified_at
+ if item.storage is not None:
+ result['storage']['storage_size_gb'] = item.storage.storage_size_gb
+ if item.backup is not None:
+ result['backup']['backup_retention_days'] = item.backup.backup_retention_days
+ result['backup']['geo_redundant_backup'] = item.backup.geo_redundant_backup
+ if item.network is not None:
+ result['network']['public_network_access'] = item.network.public_network_access
+ result['network']['delegated_subnet_resource_id'] = item.network.delegated_subnet_resource_id
+ result['network']['private_dns_zone_arm_resource_id'] = item.network.private_dns_zone_arm_resource_id
+ if item.high_availability is not None:
+ result['high_availability']['mode'] = item.high_availability.mode
+ result['high_availability']['standby_availability_zone'] = item.high_availability.standby_availability_zone
+ if item.maintenance_window is not None:
+ result['maintenance_window']['custom_window'] = item.maintenance_window.custom_window
+ result['maintenance_window']['start_minute'] = item.maintenance_window.start_minute
+ result['maintenance_window']['start_hour'] = item.maintenance_window.start_hour
+ result['maintenance_window']['day_of_week'] = item.maintenance_window.day_of_week
+
+ return result
+
+
+def main():
+ """Main execution"""
+ AzureRMPostgreSqlFlexibleServers()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver_info.py
new file mode 100644
index 000000000..50fe9adc5
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_postgresqlflexibleserver_info.py
@@ -0,0 +1,443 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_postgresqlflexibleserver_info
+version_added: "2.2.0"
+short_description: Get Azure PostgreSQL Flexible Server facts
+description:
+ - Get facts of PostgreSQL Flexible Server.
+
+options:
+ resource_group:
+ description:
+ - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal.
+ type: str
+ name:
+ description:
+ - The name of the server.
+ type: str
+ tags:
+ description:
+ - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'.
+ type: list
+ elements: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Get instance of PostgreSQL Flexible Server
+ azure_rm_postgresqlflexibleserver_info:
+ resource_group: myResourceGroup
+ name: server_name
+
+- name: List instances of PostgreSQL Flexible Server
+ azure_rm_postgresqlflexibleserver_info:
+ resource_group: myResourceGroup
+ tags:
+ - key
+'''
+
+RETURN = '''
+servers:
+ description:
+ - A list of dictionaries containing facts for PostgreSQL Flexible servers.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID of the postgresql flexible server.
+ returned: always
+ type: str
+ sample: "/subscriptions/xxx/resourceGroups/myResourceGroup/providers/Microsoft.DBforPostgreSQL/flexibleservers/postgresql3"
+ resource_group:
+ description:
+ - Resource group name.
+ returned: always
+ type: str
+ sample: myResourceGroup
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: postgreabdud1223
+ location:
+ description:
+ - The location the resource resides in.
+ returned: always
+ type: str
+ sample: eastus
+ sku:
+ description:
+ - The SKU of the server.
+ returned: always
+ type: complex
+ contains:
+ name:
+ description:
+ - The name of the SKU.
+ returned: always
+ type: str
+ sample: Standard_B1ms
+ tier:
+ description:
+ - The tier of the particular SKU.
+ returned: always
+ type: str
+ sample: Burstable
+ storage:
+ description:
+ - The maximum storage allowed for a server.
+ returned: always
+ type: complex
+ contains:
+ storage_size_gb:
+ description:
+ - Max storage allowed for a server.
+ type: int
+ returned: always
+ sample: 128
+ administrator_login:
+ description:
+ - The administrator's login name of a server.
+ returned: always
+ type: str
+ sample: azureuser
+ version:
+ description:
+ - Flexible Server version.
+ returned: always
+ type: str
+ sample: "12"
+ fully_qualified_domain_name:
+ description:
+ - The fully qualified domain name of the flexible server.
+ returned: always
+ type: str
+ sample: postflexiblefredpgsqlflexible.postgres.database.azure.com
+ availability_zone:
+ description:
+ - availability zone information of the server.
+ type: str
+ returned: always
+ sample: 1
+ backup:
+ description:
+ - Backup properties of a server.
+ type: complex
+ returned: always
+ contains:
+ backup_retention_days:
+ description:
+ - Backup retention days for the server.
+ type: int
+ returned: always
+ sample: 7
+ geo_redundant_backup:
+ description:
+ - A value indicating whether Geo-Redundant backup is enabled on the server.
+ type: str
+ returned: always
+ sample: Disabled
+ high_availability:
+ description:
+ - High availability properties of a server.
+ type: complex
+ returned: always
+ contains:
+ mode:
+ description:
+ - The HA mode for the server.
+ returned: always
+ sample: Disabled
+ type: str
+ standby_availability_zone:
+ description:
+ - availability zone information of the standby.
+ type: str
+ returned: always
+ sample: null
+ maintenance_window:
+ description:
+ - Maintenance window properties of a server.
+ type: complex
+ returned: always
+ contains:
+ custom_window:
+ description:
+ - Indicates whether custom window is enabled or disabled.
+ returned: always
+ sample: Enabled
+ type: str
+ day_of_week:
+ description:
+ - Day of week for maintenance window.
+ returned: always
+ sample: 0
+ type: int
+ start_hour:
+ description:
+ - Start hour for maintenance window.
+ type: int
+ returned: always
+ sample: 8
+ start_minute:
+ description:
+ - Start minute for maintenance window.
+ type: int
+ returned: always
+ sample: 0
+ network:
+ description:
+ - Network properties of a server.
+ type: complex
+ returned: always
+ contains:
+ delegated_subnet_resource_id:
+ description:
+ - Delegated subnet arm resource id.
+ type: str
+ returned: always
+ sample: null
+ private_dns_zone_arm_resource_id:
+ description:
+ - Private dns zone arm resource id.
+ type: str
+ returned: always
+ sample: null
+ public_network_access:
+ description:
+ - Public network access is enabled or not.
+ type: str
+ returned: always
+ sample: Enabled
+ point_in_time_utc:
+ description:
+ - Restore point creation time (ISO8601 format).
+ type: str
+ sample: null
+ returned: always
+ source_server_resource_id:
+ description:
+ - The source server resource ID to restore from.
+ type: str
+ returned: always
+ sample: null
+ system_data:
+ description:
+ - The system metadata relating to this resource.
+ type: complex
+ returned: always
+ contains:
+ created_by:
+ description:
+ - The identity that created the resource.
+ type: str
+ returned: always
+ sample: null
+ created_by_type:
+ description:
+ - The type of identity that created the resource.
+ returned: always
+ type: str
+ sample: null
+ created_at:
+ description:
+ - The timestamp of resource creation (UTC).
+ returned: always
+ sample: null
+ type: str
+ last_modified_by:
+ description:
+ - The identity that last modified the resource.
+ type: str
+ returned: always
+ sample: null
+ last_modified_by_type:
+ description:
+ - The type of identity that last modified the resource.
+ returned: always
+ sample: null
+ type: str
+ last_modified_at:
+ description:
+ - The timestamp of resource last modification (UTC).
+ returned: always
+ sample: null
+ type: str
+ tags:
+ description:
+ - Tags assigned to the resource. Dictionary of string:string pairs.
+ type: dict
+ returned: always
+ sample: { tag1: abc }
+'''
+
+
+try:
+ from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+class AzureRMPostgreSqlFlexibleServersInfo(AzureRMModuleBase):
+ def __init__(self):
+ # define user inputs into argument
+ self.module_arg_spec = dict(
+ resource_group=dict(
+ type='str',
+ ),
+ name=dict(
+ type='str'
+ ),
+ tags=dict(
+ type='list',
+ elements='str'
+ )
+ )
+ # store the results of the module operation
+ self.results = dict(
+ changed=False
+ )
+ self.resource_group = None
+ self.name = None
+ self.tags = None
+ super(AzureRMPostgreSqlFlexibleServersInfo, self).__init__(self.module_arg_spec, supports_check_mode=True, supports_tags=False, facts_module=True)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ if self.resource_group is not None and self.name is not None:
+ self.results['servers'] = self.get()
+ elif self.resource_group is not None:
+ self.results['servers'] = self.list_by_resource_group()
+ else:
+ self.results['servers'] = self.list_all()
+ return self.results
+
+ def get(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.servers.get(resource_group_name=self.resource_group,
+ server_name=self.name)
+ self.log("Response : {0}".format(response))
+ except ResourceNotFoundError:
+ self.log('Could not get facts for PostgreSQL Flexible Server.')
+
+ if response and self.has_tags(response.tags, self.tags):
+ results.append(self.format_item(response))
+
+ return results
+
+ def list_by_resource_group(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.servers.list_by_resource_group(resource_group_name=self.resource_group)
+ self.log("Response : {0}".format(response))
+ except Exception:
+ self.log('Could not get facts for PostgreSQL Flexible Servers.')
+
+ if response is not None:
+ for item in response:
+ if self.has_tags(item.tags, self.tags):
+ results.append(self.format_item(item))
+
+ return results
+
+ def list_all(self):
+ response = None
+ results = []
+ try:
+ response = self.postgresql_flexible_client.servers.list()
+ self.log("Response : {0}".format(response))
+ except Exception:
+ self.log('Could not get facts for PostgreSQL Flexible Servers.')
+
+ if response is not None:
+ for item in response:
+ if self.has_tags(item.tags, self.tags):
+ results.append(self.format_item(item))
+
+ return results
+
+ def format_item(self, item):
+ result = dict(
+ id=item.id,
+ resource_group=self.resource_group,
+ name=item.name,
+ sku=dict(),
+ location=item.location,
+ tags=item.tags,
+ system_data=dict(),
+ administrator_login=item.administrator_login,
+ version=item.version,
+ minor_version=item.minor_version,
+ fully_qualified_domain_name=item.fully_qualified_domain_name,
+ storage=dict(),
+ backup=dict(),
+ network=dict(),
+ high_availability=dict(),
+ maintenance_window=dict(),
+ source_server_resource_id=item.source_server_resource_id,
+ point_in_time_utc=item.point_in_time_utc,
+ availability_zone=item.availability_zone,
+ )
+ if item.sku is not None:
+ result['sku']['name'] = item.sku.name
+ result['sku']['tier'] = item.sku.tier
+ if item.system_data is not None:
+ result['system_data']['created_by'] = item.system_data.created_by
+ result['system_data']['created_by_type'] = item.system_data.created_by_type
+ result['system_data']['created_at'] = item.system_data.created_at
+ result['system_data']['last_modified_by'] = item.system_data.last_modified_by
+ result['system_data']['last_modified_by_type'] = item.system_data.last_modified_by_type
+ result['system_data']['last_modified_at'] = item.system_data.last_modified_at
+ if item.storage is not None:
+ result['storage']['storage_size_gb'] = item.storage.storage_size_gb
+ if item.backup is not None:
+ result['backup']['backup_retention_days'] = item.backup.backup_retention_days
+ result['backup']['geo_redundant_backup'] = item.backup.geo_redundant_backup
+ if item.network is not None:
+ result['network']['public_network_access'] = item.network.public_network_access
+ result['network']['delegated_subnet_resource_id'] = item.network.delegated_subnet_resource_id
+ result['network']['private_dns_zone_arm_resource_id'] = item.network.private_dns_zone_arm_resource_id
+ if item.high_availability is not None:
+ result['high_availability']['mode'] = item.high_availability.mode
+ result['high_availability']['standby_availability_zone'] = item.high_availability.standby_availability_zone
+ if item.maintenance_window is not None:
+ result['maintenance_window']['custom_window'] = item.maintenance_window.custom_window
+ result['maintenance_window']['start_minute'] = item.maintenance_window.start_minute
+ result['maintenance_window']['start_hour'] = item.maintenance_window.start_hour
+ result['maintenance_window']['day_of_week'] = item.maintenance_window.day_of_week
+
+ return result
+
+
+def main():
+ AzureRMPostgreSqlFlexibleServersInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednsrecordset.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednsrecordset.py
index 3560139e7..f2e9e92f7 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednsrecordset.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_privatednsrecordset.py
@@ -324,8 +324,10 @@ class AzureRMPrivateDNSRecordSet(AzureRMModuleBase):
resource_group=dict(type='str', required=True),
relative_name=dict(type='str', required=True),
zone_name=dict(type='str', required=True),
- record_type=dict(choices=RECORD_ARGSPECS.keys(), required=True, type='str'),
- record_mode=dict(choices=['append', 'purge'], default='purge'),
+ record_type=dict(choices=['A', 'AAAA', 'CNAME', 'MX', 'PTR', 'SRV', 'TXT', 'SOA'],
+ required=True,
+ type='str'),
+ record_mode=dict(type='str', choices=['append', 'purge'], default='purge'),
state=dict(choices=['present', 'absent'], default='present', type='str'),
time_to_live=dict(type='int', default=3600),
records=dict(type='list', elements='dict')
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix.py
new file mode 100644
index 000000000..90bfafc41
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix.py
@@ -0,0 +1,455 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-Sun (@Fred-Sun)
+#
+# 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: azure_rm_publicipprefix
+
+version_added: "2.2.0"
+
+short_description: Manage Azure Public IP prefix
+
+description:
+ - Create, update and delete a Public IP prefix.
+
+options:
+ resource_group:
+ description:
+ - Name of resource group with which the Public IP prefix is associated.
+ required: true
+ type: str
+ name:
+ description:
+ - Name of the Public IP prefix.
+ required: true
+ type: str
+ state:
+ description:
+ - Assert the state of the Public IP. Use C(present) to create or update a and C(absent) to delete.
+ default: present
+ type: str
+ choices:
+ - absent
+ - present
+ location:
+ description:
+ - Valid Azure location. Defaults to location of the resource group.
+ type: str
+ sku:
+ description:
+ - The public IP prefix SKU.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - Name of a public IP prefix SKU.
+ type: str
+ choices:
+ - Standard
+ tier:
+ description:
+ - Tier of a public IP prefix SKU.
+ type: str
+ choices:
+ - Regional
+ - Global
+ custom_ip_prefix:
+ description:
+ - The Custom IP prefix that this prefix is associated with.
+ type: dict
+ suboptions:
+ id:
+ description:
+ - Resource ID.
+ type: str
+ extended_location:
+ description:
+ - The extended location of the public ip address.
+ type: str
+ ip_tags:
+ description:
+ - The list of tags associated with the public IP prefix.
+ type: list
+ elements: dict
+ suboptions:
+ ip_tag_type:
+ description:
+ - The IP tag type. Example as FirstPartyUsage.
+ type: str
+ tag:
+ description:
+ - The value of the IP tag associated with the public IP. Example as SQL.
+ type: str
+ public_ip_address_version:
+ description:
+ - The public IP address version.
+ type: str
+ choices:
+ - IPV4
+ - IPV6
+ zones:
+ description:
+ - A list of availability zones denoting the IP prefix allocated for the resource needs to come from.
+ type: list
+ elements: str
+ choices:
+ - '1'
+ - '2'
+ - '3'
+ prefix_length:
+ description:
+ - The Length of the Public IP Prefix.
+ type: int
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+ - azure.azcollection.azure_tags
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+'''
+
+EXAMPLES = '''
+- name: Create a public ip prefix
+ azure_rm_publicipprefix:
+ resource_group: myResourceGroup
+ name: my_public_ip
+ public_ip_address_version: IPV4
+ prefix_length: 29
+ sku:
+ name: Standard
+ tier: Regional
+ zones:
+ - 1
+ tags:
+ key1: value1
+
+- name: Delete public ip prefix
+ azure_rm_publicipprefix:
+ resource_group: myResourceGroup
+ name: my_public_ipprefix
+ state: absent
+'''
+
+RETURN = '''
+state:
+ description:
+ - List of public IP prefixes dicts.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: /subscriptions/xxx---xxxxx/resourceGroups/v-xisuRG/providers/Microsoft.Network/publicIPPrefixes/pipb57dc95224
+ name:
+ description:
+ - Name of the public IP prefix.
+ returned: always
+ type: str
+ sample: prefix57dc95224
+ type:
+ description:
+ - Resource type.
+ returned: always
+ type: str
+ sample: "Microsoft.Network/publicIPPrefixes"
+ location:
+ description:
+ - Resource location.
+ returned: always
+ type: str
+ sample: eastus
+ tags:
+ description:
+ - Resource tags.
+ returned: always
+ type: dict
+ sample: {
+ "delete": "on-exit",
+ "testing": "testing"
+ }
+ public_ip_address_version:
+ description:
+ - The public IP address version.
+ - Possible values are C(IPv4) and C(IPv6).
+ returned: always
+ type: str
+ sample: IPv4
+ ip_tags:
+ description:
+ - The list of tags associated with the public IP prefixes.
+ returned: always
+ type: list
+ sample: [{'type': 'FirstPartyUsage', 'value': 'Storage'}]
+ resource_guid:
+ description:
+ - The resource GUID property of the public IP prefix resource.
+ type: str
+ sample: "47cafa04-851d-4579-894d-74ad6afe3233"
+ custom_ip_prefix:
+ description:
+ - The customIpPrefix that this prefix is associated with.
+ type: dict
+ returned: always
+ sample: {}
+ public_ip_addresses:
+ description:
+ - The list of all referenced PublicIPAddresses.
+ type: list
+ sample: []
+'''
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+try:
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+
+def prefix_to_dict(prefix):
+ result = dict(
+ id=prefix.id,
+ name=prefix.name,
+ tags=prefix.tags,
+ type=prefix.type,
+ location=prefix.location,
+ public_ip_address_version=prefix.public_ip_address_version,
+ prefix_length=prefix.prefix_length,
+ provisioning_state=prefix.provisioning_state,
+ etag=prefix.etag,
+ zones=prefix.zones,
+ sku=dict(),
+ ip_tags=list(),
+ custom_ip_prefix=dict(),
+ ip_prefix=prefix.ip_prefix,
+ resource_guid=prefix.resource_guid,
+ public_ip_addresses=list(),
+ extended_location=prefix.extended_location
+ )
+ if prefix.public_ip_addresses:
+ result['public_ip_addresses'] = [x.id for x in prefix.public_ip_addresses]
+ if prefix.sku:
+ result['sku']['name'] = prefix.sku.name
+ result['sku']['tier'] = prefix.sku.tier
+ if prefix.custom_ip_prefix:
+ result['custom_ip_prefix']['id'] = prefix.custom_ip_prefix.id
+ if prefix.ip_tags:
+ result['ip_tags'] = [dict(type=x.ip_tag_type, value=x.tag) for x in prefix.ip_tags]
+ return result
+
+
+class AzureRMPublicIPPrefix(AzureRMModuleBase):
+
+ def __init__(self):
+
+ self.module_arg_spec = dict(
+ resource_group=dict(type='str', required=True),
+ name=dict(type='str', required=True),
+ state=dict(type='str', default='present', choices=['present', 'absent']),
+ location=dict(type='str'),
+ public_ip_address_version=dict(type='str', choices=['IPV4', 'IPV6']),
+ extended_location=dict(type='str'),
+ prefix_length=dict(type='int'),
+ custom_ip_prefix=dict(
+ type='dict',
+ options=dict(
+ id=dict(type='str')
+ )
+ ),
+ sku=dict(
+ type='dict',
+ options=dict(
+ name=dict(type='str', choices=['Standard']),
+ tier=dict(type='str', choices=['Regional', 'Global'])
+ )
+ ),
+ ip_tags=dict(
+ type='list',
+ elements='dict',
+ options=dict(
+ ip_tag_type=dict(type='str'),
+ tag=dict(type='str')
+ )
+ ),
+ zones=dict(type='list', elements='str', choices=['1', '2', '3'])
+ )
+
+ self.resource_group = None
+ self.name = None
+ self.location = None
+ self.state = None
+ self.tags = None
+ self.zones = None
+ self.sku = None
+ self.ip_tags = None
+ self.public_ip_address_version = None
+ self.prefix_length = None
+ self.custom_ip_prefix = None
+
+ self.results = dict(
+ changed=False,
+ state=dict()
+ )
+
+ super(AzureRMPublicIPPrefix, self).__init__(derived_arg_spec=self.module_arg_spec,
+ supports_check_mode=True)
+
+ def exec_module(self, **kwargs):
+
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
+ setattr(self, key, kwargs[key])
+
+ results = dict()
+ changed = False
+ prefix = None
+ update_tags = False
+
+ resource_group = self.get_resource_group(self.resource_group)
+ if not self.location:
+ # Set default location
+ self.location = resource_group.location
+
+ try:
+ self.log("Fetch public ip prefix {0}".format(self.name))
+ prefix = self.network_client.public_ip_prefixes.get(self.resource_group, self.name)
+ self.log("Public IP prefix {0} exists".format(self.name))
+
+ if self.state == 'present':
+ results = prefix_to_dict(prefix)
+
+ if self.public_ip_address_version is not None and \
+ self.public_ip_address_version.lower() != results['public_ip_address_version'].lower():
+ changed = False
+ results['public_ip_address_version'] = self.public_ip_address_version
+ self.fail("The public_ip_address_version can't be updated")
+
+ if self.prefix_length is not None and self.prefix_length != results['prefix_length']:
+ changed = False
+ results['prefix_length'] = self.prefix_length
+ self.fail("The prefix_length can't be updated")
+
+ if self.sku is not None:
+ for key in self.sku.keys():
+ if self.sku[key] != results['sku'].get(key):
+ changed = False
+ self.fail("The sku can't be updated")
+ results['sku'] = self.sku
+
+ if self.zones is not None and not all(key in results['zones'] for key in self.zones):
+ changed = False
+ results['zones'] = self.zones
+ self.fail("The zone can't be updated")
+
+ if self.extended_location is not None and self.extended_location != results['extended_location']:
+ changed = False
+ results['extended_location'] = self.extended_location
+ self.fail("The extended_location can't be updated")
+
+ if self.ip_tags is not None:
+ for key in self.ip_tags.keys():
+ if self.ip_tags[key] != results['ip_tags'].get(key):
+ changed = False
+ results['ip_tags'] = self.ip_tags
+ self.fail("The ip_tags can't be updated")
+
+ if self.custom_ip_prefix is not None:
+ if results.get('custom_ip_prefix') is None:
+ changed = False
+ results['custom_ip_prefix'] = self.custom_ip_prefix
+ self.fail("The custom_ip_prefix can't be updated")
+ elif self.custom_ip_prefix['id'].lower() != results['custom_ip_prefix']['id'].lower():
+ changed = False
+ results['custom_ip_prefix'] = self.custom_ip_prefix
+ self.fail("The custom_ip_prefix can't be updated")
+
+ update_tags, results['tags'] = self.update_tags(results['tags'])
+ if update_tags:
+ self.log("CHANGED: tags")
+ changed = True
+ self.tags = results['tags']
+
+ elif self.state == 'absent':
+ self.log("CHANGED: public ip prefix {0} exists but requested state is 'absent'".format(self.name))
+ changed = True
+ except ResourceNotFoundError:
+ self.log('Public ip prefix {0} does not exist'.format(self.name))
+ if self.state == 'present':
+ self.log("CHANGED: public IP prefix {0} does not exist but requested state is 'present'".format(self.name))
+ changed = True
+
+ self.results['state'] = results
+ self.results['changed'] = changed
+
+ if self.check_mode:
+ results['changed'] = True
+ return results
+
+ if update_tags:
+ self.results['state'] = self.update_prefix_tags(self.tags)
+
+ elif changed:
+ if self.state == 'present':
+ self.log("Create or Update Public IP prefix {0}".format(self.name))
+ prefix = self.network_models.PublicIPPrefix(
+ location=self.location,
+ public_ip_address_version=self.public_ip_address_version,
+ prefix_length=self.prefix_length,
+ zones=self.zones,
+ tags=self.tags,
+ sku=self.sku,
+ ip_tags=self.ip_tags,
+ custom_ip_prefix=self.custom_ip_prefix
+ )
+ self.results['state'] = self.create_or_update_prefix(prefix)
+
+ elif self.state == 'absent':
+ self.log('Delete public ip {0}'.format(self.name))
+ self.delete_prefix()
+
+ return self.results
+
+ def update_prefix_tags(self, tags):
+ try:
+ prefix = self.network_client.public_ip_prefixes.update_tags(self.resource_group, self.name, dict(tags=tags))
+ except Exception as exc:
+ self.fail("Error updating tags {0} - {1}".format(self.name, str(exc)))
+ return prefix_to_dict(prefix)
+
+ def create_or_update_prefix(self, prefix):
+ try:
+ poller = self.network_client.public_ip_prefixes.begin_create_or_update(self.resource_group, self.name, prefix)
+ prefix = self.get_poller_result(poller)
+ except Exception as exc:
+ self.fail("Error creating or updating {0} - {1}".format(self.name, str(exc)))
+ return prefix_to_dict(prefix)
+
+ def delete_prefix(self):
+ try:
+ poller = self.network_client.public_ip_prefixes.begin_delete(self.resource_group, self.name)
+ self.get_poller_result(poller)
+ except Exception as exc:
+ self.fail("Error deleting {0} - {1}".format(self.name, str(exc)))
+
+ self.results['state']['status'] = 'Deleted'
+ return True
+
+
+def main():
+ AzureRMPublicIPPrefix()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix_info.py
new file mode 100644
index 000000000..c8cf0eff1
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_publicipprefix_info.py
@@ -0,0 +1,296 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-Sun (@Fred-Sun)
+#
+# 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: azure_rm_publicipprefix_info
+
+version_added: "2.2.0"
+
+short_description: Get public IP prefix facts
+
+description:
+ - Get facts for a specific public IP prefix.
+ - Get all facts for a specific public IP prefixes within a resource group.
+
+options:
+ name:
+ description:
+ - The name of the public IP prefix.
+ type: str
+ resource_group:
+ description:
+ - Limit results by resource group. Required when using name parameter.
+ type: str
+ tags:
+ description:
+ - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'.
+ type: list
+ elements: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+'''
+
+EXAMPLES = '''
+- name: Get facts for one Public IP Prefix
+ azure_rm_publicipprefix_info:
+ resource_group: myResourceGroup
+ name: publicipprefix
+
+- name: Get facts for all Public IPs within a resource groups
+ azure_rm_publicipprefix_info:
+ resource_group: myResourceGroup
+ tags:
+ - key:value
+'''
+
+RETURN = '''
+publicipprefix:
+ description:
+ - List of public IP prefixes dicts.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: /subscriptions/xxx---xxxxx/resourceGroups/v-xisuRG/providers/Microsoft.Network/publicIPPrefixes/pipb57dc95224
+ name:
+ description:
+ - Name of the public IP prefix.
+ returned: always
+ type: str
+ sample: prefix57dc95224
+ type:
+ description:
+ - Resource type.
+ returned: always
+ type: str
+ sample: "Microsoft.Network/publicIPPrefixes"
+ location:
+ description:
+ - Resource location.
+ returned: always
+ type: str
+ sample: eastus
+ tags:
+ description:
+ - Resource tags.
+ returned: always
+ type: dict
+ sample: {
+ "delete": "on-exit",
+ "testing": "testing"
+ }
+ public_ip_address_version:
+ description:
+ - The public IP address version.
+ - Possible values are C(IPv4) and C(IPv6).
+ returned: always
+ type: str
+ sample: IPv4
+ ip_tags:
+ description:
+ - The list of tags associated with the public IP prefixes.
+ returned: always
+ type: list
+ sample: [
+ {
+ "type": "FirstPartyUsage",
+ "value": "Storage"
+ }
+ ]
+ provisioning_state:
+ description:
+ - The provisioning state of the PublicIP Prefix resource.
+ - Possible values is C(Succeeded).
+ returned: always
+ type: str
+ sample: Succeeded
+ etag:
+ description:
+ - A unique read-only string that changes whenever the resource is updated.
+ returned: always
+ type: str
+ sample: "W/'1905ee13-7623-45b1-bc6b-4a12b2fb9d15'"
+ sku:
+ description:
+ - The public IP prefix SKU.
+ returned: always
+ type: dict
+ sample: {'name': 'standard', 'tier': 'Regional'}
+ zones:
+ description:
+ - A list of availability zones denoting the IP allocated for the resource needs to come from.
+ returned: always
+ type: list
+ sample: ['1', '2']
+ prefix_length:
+ description:
+ - The Length of the Public IP Prefix.
+ type: int
+ returned: always
+ sample: 29
+ extended_location:
+ description:
+ - The extended location of the public ip address.
+ type: str
+ returned: always
+ sample: 'eastus2'
+ custom_ip_prefix:
+ description:
+ - The customIpPrefix that this prefix is associated with.
+ type: dict
+ returned: always
+ sample: {}
+ public_ip_addresses:
+ description:
+ - The list of all referenced PublicIPAddresses.
+ type: list
+ sample: []
+ resource_guid:
+ description:
+ - The resource GUID property of the public IP prefix resource.
+ type: str
+ sample: "47cafa04-851d-4579-894d-74ad6afe3233"
+ ip_prefix:
+ description:
+ - The allocated Prefix.
+ type: str
+ sample: 20.199.95.80/29
+'''
+try:
+ from azure.core.exceptions import ResourceNotFoundError
+except Exception:
+ # This is handled in azure_rm_common
+ pass
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+AZURE_OBJECT_CLASS = 'PublicIpPrefix'
+
+
+class AzureRMPublicIPPrefixInfo(AzureRMModuleBase):
+
+ def __init__(self):
+
+ self.module_arg_spec = dict(
+ name=dict(type='str'),
+ resource_group=dict(type='str'),
+ tags=dict(type='list', elements='str')
+ )
+
+ self.results = dict(
+ changed=False,
+ )
+
+ self.name = None
+ self.resource_group = None
+ self.tags = None
+
+ super(AzureRMPublicIPPrefixInfo, self).__init__(self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=False,
+ facts_module=True)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ result = []
+ if self.name is not None and self.resource_group is not None:
+ result = self.get_item()
+ elif self.resource_group:
+ result = self.list_resource_group()
+ else:
+ result = self.list_all()
+
+ raw = self.filter(result)
+
+ self.results['publicipprefixes'] = self.format(raw)
+
+ return self.results
+
+ def format(self, raw):
+ return [self.prefix_to_dict(item) for item in raw]
+
+ def filter(self, response):
+ return [item for item in response if self.has_tags(item.tags, self.tags)]
+
+ def prefix_to_dict(self, prefix):
+ result = dict(
+ id=prefix.id,
+ name=prefix.name,
+ tags=prefix.tags,
+ type=prefix.type,
+ location=prefix.location,
+ public_ip_address_version=prefix.public_ip_address_version,
+ prefix_length=prefix.prefix_length,
+ provisioning_state=prefix.provisioning_state,
+ etag=prefix.etag,
+ zones=prefix.zones,
+ sku=dict(),
+ ip_tags=list(),
+ custom_ip_prefix=dict(),
+ ip_prefix=prefix.ip_prefix,
+ resource_guid=prefix.resource_guid,
+ public_ip_addresses=list(),
+ extended_location=prefix.extended_location
+ )
+ if prefix.public_ip_addresses:
+ result['public_ip_addresses'] = [x.id for x in prefix.public_ip_addresses]
+ if prefix.sku:
+ result['sku']['name'] = prefix.sku.name
+ result['sku']['tier'] = prefix.sku.tier
+ if prefix.custom_ip_prefix:
+ result['custom_ip_prefix']['id'] = prefix.custom_ip_prefix.id
+ if prefix.ip_tags:
+ result['ip_tags'] = [dict(type=x.ip_tag_type, value=x.tag) for x in prefix.ip_tags]
+ return result
+
+ def get_item(self):
+ self.log('Get properties for {0}'.format(self.name))
+ item = None
+ try:
+ item = self.network_client.public_ip_prefixes.get(self.resource_group, self.name)
+ except ResourceNotFoundError:
+ pass
+ return [item] if item else []
+
+ def list_resource_group(self):
+ self.log('List items in resource groups')
+ try:
+ response = self.network_client.public_ip_prefixes.list(self.resource_group)
+ except ResourceNotFoundError as exc:
+ self.fail("Error listing items in resource groups {0} - {1}".format(self.resource_group, str(exc)))
+ return response
+
+ def list_all(self):
+ self.log('List all items')
+ try:
+ response = self.network_client.public_ip_prefixes.list_all()
+ except ResourceNotFoundError as exc:
+ self.fail("Error listing all items - {0}".format(str(exc)))
+ return response
+
+
+def main():
+ AzureRMPublicIPPrefixInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault.py
index b2c45b2d5..818d846bf 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault.py
@@ -208,7 +208,6 @@ class AzureRMRecoveryServicesVault(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_resource()
@@ -247,10 +246,12 @@ class AzureRMRecoveryServicesVault(AzureRMModuleBaseExt):
self.log('Error in creating Azure Recovery Service Vault.')
self.fail('Error in creating Azure Recovery Service Vault {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault_info.py
index 6b2dee776..bc26db2dd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_recoveryservicesvault_info.py
@@ -12,9 +12,9 @@ DOCUMENTATION = \
---
module: azure_rm_recoveryservicesvault_info
version_added: '1.1.0'
-short_description: Get Azure Recovery Services vault Details
+short_description: Get or list the Azure Recovery Services vault Details
description:
- - Get Azure Recovery Services vault Details.
+ - Get or list the Azure Recovery Services vault Details.
options:
resource_group:
description:
@@ -23,8 +23,8 @@ options:
type: str
name:
description:
+ - If this parameter is not configured, all Vaults in the resource group are listed.
- The name of the Azure Recovery Service Vault.
- required: true
type: str
extends_documentation_fragment:
- azure.azcollection.azure
@@ -34,6 +34,10 @@ author:
'''
EXAMPLES = '''
+- name: List all Azure Recovery Services Vault in same resource group
+ azure_rm_recoveryservicesvault_info:
+ resource_group: 'myResourceGroup'
+
- name: Get Azure Recovery Services Vault Details.
azure_rm_recoveryservicesvault_info:
resource_group: 'myResourceGroup'
@@ -110,7 +114,6 @@ class AzureRMRecoveryServicesVaultInfo(AzureRMModuleBaseExt):
),
name=dict(
type='str',
- required=True
)
)
@@ -137,13 +140,21 @@ class AzureRMRecoveryServicesVaultInfo(AzureRMModuleBaseExt):
return '2016-06-01'
def get_url(self):
- return '/subscriptions/' \
- + self.subscription_id \
- + '/resourceGroups/' \
- + self.resource_group \
- + '/providers/Microsoft.RecoveryServices' \
- + '/vaults' + '/' \
- + self.name
+ if self.name is not None:
+ return '/subscriptions/' \
+ + self.subscription_id \
+ + '/resourceGroups/' \
+ + self.resource_group \
+ + '/providers/Microsoft.RecoveryServices' \
+ + '/vaults' + '/' \
+ + self.name
+ else:
+ return '/subscriptions/' \
+ + self.subscription_id \
+ + '/resourceGroups/' \
+ + self.resource_group \
+ + '/providers/Microsoft.RecoveryServices' \
+ + '/vaults'
def exec_module(self, **kwargs):
for key in list(self.module_arg_spec.keys()):
@@ -160,10 +171,9 @@ class AzureRMRecoveryServicesVaultInfo(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
- changed = True
+ changed = False
response = self.get_recovery_service_vault_info()
self.results['response'] = response
@@ -188,10 +198,12 @@ class AzureRMRecoveryServicesVaultInfo(AzureRMModuleBaseExt):
self.log('Error in fetching Azure Recovery Service Vault Details.')
self.fail('Error in fetching Azure Recovery Service Vault Details {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache.py
index b93a52992..5a231cc3e 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache.py
@@ -510,8 +510,7 @@ class AzureRMRedisCaches(AzureRMModuleBase):
# get management client
self._client = self.get_mgmt_svc_client(RedisManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version='2018-03-01',
- is_track2=True)
+ api_version='2018-03-01')
# set location
resource_group = self.get_resource_group(self.resource_group)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache_info.py
index 44e47f4d5..782fb0417 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscache_info.py
@@ -269,8 +269,7 @@ class AzureRMRedisCacheInfo(AzureRMModuleBase):
# get management client
self._client = self.get_mgmt_svc_client(RedisManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version='2018-03-01',
- is_track2=True)
+ api_version='2018-03-01')
if self.name:
self.results['rediscaches'] = self.get_item()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscachefirewallrule.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscachefirewallrule.py
index 67b2c90b6..b0fbdb43e 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscachefirewallrule.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_rediscachefirewallrule.py
@@ -176,8 +176,7 @@ class AzureRMRedisCacheFirewallRule(AzureRMModuleBase):
# get management client
self._client = self.get_mgmt_svc_client(RedisManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- api_version='2018-03-01',
- is_track2=True)
+ api_version='2018-03-01')
# check if the firewall rule exists
old_response = self.get()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment.py
index a70542f80..8cffc7999 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment.py
@@ -141,11 +141,9 @@ class AzureRMRegistrationAssignment(AzureRMModuleBaseExt):
),
properties=dict(
type='dict',
- disposition='/properties',
options=dict(
registration_definition_id=dict(
type='str',
- disposition='registration_definition_id',
required=True
)
)
@@ -188,7 +186,6 @@ class AzureRMRegistrationAssignment(AzureRMModuleBaseExt):
self.mgmt_client = self.get_mgmt_svc_client(ManagedServicesClient,
base_url=self._cloud_environment.endpoints.resource_manager,
api_version='2019-09-01',
- is_track2=True,
suppress_subscription_id=True)
old_response = self.get_resource()
@@ -200,11 +197,9 @@ class AzureRMRegistrationAssignment(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body.get('properties') is not None and \
+ self.body['properties']['registration_definition_id'] != \
+ old_response['properties']['registration_definition_id']:
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment_info.py
index ef8875f7c..2dabe949e 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationassignment_info.py
@@ -126,7 +126,6 @@ class AzureRMRegistrationAssignmentInfo(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(ManagedServicesClient,
base_url=self._cloud_environment.endpoints.resource_manager,
api_version='2020-09-01',
- is_track2=True,
suppress_subscription_id=True)
if (self.scope is not None and self.registration_assignment_id is not None):
@@ -168,12 +167,14 @@ class AzureRMRegistrationAssignmentInfo(AzureRMModuleBase):
def format_item(self, item):
if hasattr(item, 'as_dict'):
return [item.as_dict()]
- else:
+ elif item is not None:
result = []
items = list(item)
for tmp in items:
result.append(tmp.as_dict())
return result
+ else:
+ return []
def main():
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition.py
index 0d89da3a9..0582df4cf 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition.py
@@ -261,63 +261,51 @@ class AzureRMRegistrationDefinition(AzureRMModuleBaseExt):
),
properties=dict(
type='dict',
- disposition='/properties',
options=dict(
description=dict(
type='str',
- disposition='description'
),
authorizations=dict(
type='list',
- disposition='authorizations',
required=True,
elements='dict',
options=dict(
principal_id=dict(
type='str',
- disposition='principal_id',
required=True
),
role_definition_id=dict(
type='str',
- disposition='role_definition_id',
required=True
)
)
),
registration_definition_name=dict(
type='str',
- disposition='registration_definition_name'
),
managed_by_tenant_id=dict(
type='str',
- disposition='managed_by_tenant_id',
required=True
)
)
),
plan=dict(
type='dict',
- disposition='/plan',
options=dict(
name=dict(
type='str',
- disposition='name',
required=True
),
publisher=dict(
type='str',
- disposition='publisher',
required=True
),
product=dict(
type='str',
- disposition='product',
required=True
),
version=dict(
type='str',
- disposition='version',
required=True
)
)
@@ -349,8 +337,6 @@ class AzureRMRegistrationDefinition(AzureRMModuleBaseExt):
elif kwargs[key] is not None:
self.body[key] = kwargs[key]
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
-
if self.registration_definition_id is None:
self.registration_definition_id = str(uuid.uuid4())
@@ -365,7 +351,6 @@ class AzureRMRegistrationDefinition(AzureRMModuleBaseExt):
self.mgmt_client = self.get_mgmt_svc_client(ManagedServicesClient,
base_url=self._cloud_environment.endpoints.resource_manager,
api_version='2019-09-01',
- is_track2=True,
suppress_subscription_id=True)
old_response = self.get_resource()
@@ -377,11 +362,16 @@ class AzureRMRegistrationDefinition(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body.get('plan') is not None:
+ if old_response.get('plan') is not None and \
+ not all(self.body['plan'][item] == old_response['plan'].get(item)
+ for item in self.body['plan'].keys()):
+ self.to_do = Actions.Update
+ else:
+ self.to_do = Actions.Update
+ elif (self.body.get('properties') is not None and
+ not all(self.body['properties'][item] == old_response['properties'].get(item)
+ for item in self.body['properties'].keys())):
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition_info.py
index 1c095420c..cad5b8e2c 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_registrationdefinition_info.py
@@ -190,7 +190,6 @@ class AzureRMRegistrationDefinitionInfo(AzureRMModuleBase):
self.mgmt_client = self.get_mgmt_svc_client(ManagedServicesClient,
base_url=self._cloud_environment.endpoints.resource_manager,
api_version='2019-09-01',
- is_track2=True,
suppress_subscription_id=True)
if self.registration_definition_id is not None:
@@ -223,12 +222,14 @@ class AzureRMRegistrationDefinitionInfo(AzureRMModuleBase):
def format_item(self, item):
if hasattr(item, 'as_dict'):
return [item.as_dict()]
- else:
+ elif item is not None:
result = []
items = list(item)
for tmp in items:
result.append(tmp.as_dict())
return result
+ else:
+ return []
def main():
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource.py
index a3a0544e2..4e786332f 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource.py
@@ -325,7 +325,6 @@ class AzureRMResource(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.state == 'absent':
@@ -416,10 +415,15 @@ class AzureRMResource(AzureRMModuleBase):
self.polling_timeout,
self.polling_interval)
if self.state == 'present' and self.method != 'DELETE':
- try:
- response = json.loads(response.body())
- except Exception:
+ if hasattr(response, 'body'):
+ try:
+ response = json.loads(response.body())
+ except Exception:
+ response = response.body()
+ elif hasattr(response, 'context'):
response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
else:
response = None
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource_info.py
index eb5cd93e4..176be9531 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_resource_info.py
@@ -76,6 +76,13 @@ options:
description:
- Subresource name.
type: str
+ tags:
+ description:
+ - A dictionary of tags to filter on.
+ - Each key-value pair in the dictionary specifies a tag name and its value to filter on differente resources.
+ type: dict
+ required: false
+ default: {}
extends_documentation_fragment:
- azure.azcollection.azure
@@ -98,6 +105,14 @@ EXAMPLES = '''
azure_rm_resource_info:
resource_group: "{{ resource_group }}"
resource_type: resources
+
+- name: Get all snapshots of all resource groups of a subscription but filtering with two tags.
+ azure_rm_resource_info:
+ provider: compute
+ resource_type: snapshots
+ tags:
+ enviroment: dev
+ department: hr
'''
RETURN = '''
@@ -346,7 +361,8 @@ class AzureRMResourceInfo(AzureRMModuleBase):
),
api_version=dict(
type='str'
- )
+ ),
+ tags=dict(type='dict', default={})
)
# store the results of the module operation
self.results = dict(
@@ -370,7 +386,6 @@ class AzureRMResourceInfo(AzureRMModuleBase):
for key in self.module_arg_spec:
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
if self.url is None:
@@ -450,6 +465,12 @@ class AzureRMResourceInfo(AzureRMModuleBase):
self.fail('Failed to parse response: ' + str(e))
if not skiptoken:
break
+ if kwargs['tags']:
+ filtered_response = []
+ for resource in self.results['response']:
+ if all(resource.get('tags', {}).get(tag_key) == tag_value for tag_key, tag_value in kwargs['tags'].items()):
+ filtered_response.append(resource)
+ self.results['response'] = filtered_response
return self.results
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition.py
index 8683142d8..86f085099 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition.py
@@ -224,7 +224,6 @@ class AzureRMRoleDefinition(AzureRMModuleBase):
# get management client
self._client = self.get_mgmt_svc_client(AuthorizationManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version="2018-01-01-preview")
self.scope = self.build_scope()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition_info.py
index ce8ee238e..8f7c9ac4d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_roledefinition_info.py
@@ -194,7 +194,6 @@ class AzureRMRoleDefinitionInfo(AzureRMModuleBase):
# get management client
self._client = self.get_mgmt_svc_client(AuthorizationManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version="2018-01-01-preview")
if self.id:
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_securitygroup.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_securitygroup.py
index 8b25449ca..bf247e216 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_securitygroup.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_securitygroup.py
@@ -44,8 +44,11 @@ options:
type: str
choices:
- Udp
+ - UDP
- Tcp
+ - TCP
- Icmp
+ - ICMP
- "*"
default: "*"
source_port_range:
@@ -164,8 +167,11 @@ options:
type: str
choices:
- Udp
+ - UDP
- Tcp
+ - TCP
- Icmp
+ - ICMP
- "*"
default: "*"
source_port_range:
@@ -722,7 +728,7 @@ def create_network_security_group_dict(nsg):
rule_spec = dict(
name=dict(type='str', required=True),
description=dict(type='str'),
- protocol=dict(type='str', choices=['Udp', 'Tcp', 'Icmp', '*'], default='*'),
+ protocol=dict(type='str', choices=['Udp', 'UDP', 'Tcp', 'TCP', 'Icmp', 'ICMP', '*'], default='*'),
source_port_range=dict(type='raw', default='*'),
destination_port_range=dict(type='raw', default='*'),
source_address_prefix=dict(type='raw', default='*'),
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_servicebus_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_servicebus_info.py
index 5ccd4dea7..b736eb6c2 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_servicebus_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_servicebus_info.py
@@ -378,6 +378,25 @@ servicebuses:
"type": "Microsoft.ServiceBus/Namespaces/Queues/AuthorizationRules"
}
}
+ private_endpoint_connections:
+ description:
+ - Properties of the PrivateEndpointConnection.
+ type: list
+ returned: always
+ sample: [{
+ "id": "/subscriptions/xxxxxx/resourceGroups/myRG/providers/Microsoft.ServiceBus/namespaces/fredVM/privateEndpointConnections/xxxxxxxx",
+ "name": "xxxxxx",
+ "private_endpoint": {
+ "id": "/subscriptions/xxxxx/resourceGroups/myRG/providers/Microsoft.Network/privateEndpoints/fredprivateendpoint"
+ },
+ "private_link_service_connection_state": {
+ "description": "Auto-Approved",
+ "status": "Approved"
+ },
+ "provisioning_state": "Succeeded",
+ "type": "Microsoft.ServiceBus/Namespaces/PrivateEndpointConnections"
+ }]
+
'''
try:
@@ -385,7 +404,6 @@ try:
except Exception:
# This is handled in azure_rm_common
pass
-
from ansible.module_utils.common.dict_transformations import _camel_to_snake
from ansible.module_utils._text import to_native
from datetime import datetime, timedelta
@@ -484,6 +502,8 @@ class AzureRMServiceBusInfo(AzureRMModuleBase):
result[attribute] = to_native(value)
elif attribute == 'max_size_in_megabytes':
result['max_size_in_mb'] = value
+ elif attribute == 'private_endpoint_connections':
+ result['private_endpoint_connections'] = [item.as_dict() for item in value]
else:
result[attribute] = value
if self.show_sas_policies and self.type != 'subscription':
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_snapshot.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_snapshot.py
index bd3acd9bd..b3db0a0d7 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_snapshot.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_snapshot.py
@@ -142,24 +142,17 @@ class AzureRMSnapshots(AzureRMModuleBaseExt):
self.module_arg_spec = dict(
resource_group=dict(
type='str',
- updatable=False,
- disposition='resourceGroupName',
required=True
),
name=dict(
type='str',
- updatable=False,
- disposition='snapshotName',
required=True
),
location=dict(
- type='str',
- updatable=False,
- disposition='/'
+ type='str'
),
sku=dict(
type='dict',
- disposition='/',
options=dict(
name=dict(
type='str',
@@ -174,29 +167,22 @@ class AzureRMSnapshots(AzureRMModuleBaseExt):
),
os_type=dict(
type='str',
- disposition='/properties/osType',
choices=['Windows',
'Linux']
),
incremental=dict(type='bool', default=False),
creation_data=dict(
type='dict',
- disposition='/properties/creationData',
options=dict(
create_option=dict(
type='str',
- disposition='createOption',
choices=['Import', 'Copy'],
),
source_uri=dict(
- type='str',
- disposition='sourceUri',
- purgeIfNone=True
+ type='str'
),
source_id=dict(
- type='str',
- disposition='sourceResourceId',
- purgeIfNone=True
+ type='str'
)
)
),
@@ -233,22 +219,27 @@ class AzureRMSnapshots(AzureRMModuleBaseExt):
supports_tags=True)
def exec_module(self, **kwargs):
- for key in list(self.module_arg_spec.keys()):
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
if key == 'incremental':
self.body['properties']['incremental'] = kwargs[key]
+ elif key == 'os_type':
+ self.body['properties']['osType'] = kwargs[key]
+ elif key == 'creation_data':
+ self.body['properties']['creationData'] = dict()
+ if kwargs[key].get('create_option') is not None:
+ self.body['properties']['creationData']['createOption'] = kwargs[key].get('create_option')
+ self.body['properties']['creationData']['sourceUri'] = kwargs[key].get('source_uri')
+ self.body['properties']['creationData']['sourceResourceId'] = kwargs[key].get('source_id')
else:
self.body[key] = kwargs[key]
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
-
old_response = None
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
resource_group = self.get_resource_group(self.resource_group)
@@ -283,12 +274,22 @@ class AzureRMSnapshots(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body.get('sku') is not None and \
+ not all(self.body['sku'][item] == old_response['sku'].get(item) for item in self.body['sku'].keys()):
+ self.to_do = Actions.Update
+ if self.body['properties'].get('incremental') is not None and \
+ self.body['properties']['incremental'] != old_response['properties']['incremental']:
+ self.to_do = Actions.Update
+ if self.body['properties'].get('osType') is not None and \
+ self.body['properties']['osType'] != old_response['properties'].get('osType'):
+ self.to_do = Actions.Update
+ if self.body['properties'].get('creationData') is not None and \
+ not all(self.body['properties']['creationData'][item] == old_response['properties']['creationData'].get(item)
+ for item in self.body['properties']['creationData'].keys()):
+ self.to_do = Actions.Update
+
+ update_tags, self.body['tags'] = self.update_tags(old_response.get('tags'))
+ if update_tags:
self.to_do = Actions.Update
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance.py
index b59421062..631931afd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance.py
@@ -262,7 +262,7 @@ sql_managed_instance:
sample: "/subscription/xxx-xxx/resourceGroups/testRG/providers/Microsoft.Sql/managedInstances/fredsqlinstance"
name:
description:
- - SQL manged instance name.
+ - SQL managed instance name.
returned: always
type: str
sample: testmanagedinstance
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance_info.py
index 5434cb6a4..3a85d00ac 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sqlmanagedinstance_info.py
@@ -14,7 +14,7 @@ module: azure_rm_sqlmanagedinstance_info
version_added: "0.15.0"
short_description: Get Azure SQL managed instance facts
description:
- - Get facts of Azure SQL manged instance facts.
+ - Get facts of Azure SQL managed instance facts.
options:
resource_group:
@@ -49,7 +49,7 @@ EXAMPLES = '''
azure_rm_sqlmanagedinstance_info:
resource_group: testrg
-- name: List SQL manged instance by subscription and filter by tags
+- name: List SQL managed instance by subscription and filter by tags
azure_rm_sqlmanagedinstance_info:
tags:
- foo
@@ -70,7 +70,7 @@ sql_managed_instance:
sample: "/subscription/xxx-xxx/resourceGroups/testRG/providers/Microsoft.Sql/managedInstances/fredsqlinstance"
name:
description:
- - SQL manged instance name.
+ - SQL managed instance name.
returned: always
type: str
sample: testmanagedinstance
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey.py
new file mode 100644
index 000000000..1c7a473b8
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey.py
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2023 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_sshpublickey
+version_added: "2.0.0"
+short_description: Manage ssh public key with vm
+description:
+ - Create, update or delete the vm public key.
+options:
+ resource_group:
+ description:
+ - Name of resource group.
+ required: true
+ type: str
+ location:
+ description:
+ - Valid Azure location. Defaults to location of the resource group.
+ type: str
+ name:
+ description:
+ - The name of the SSH public key.
+ required: true
+ type: str
+ public_key:
+ description:
+ - SSH public key used to authenticate to a virtual machine through ssh.
+ - If this property is not initially provided when the resource is created, the publicKey property will be populated when generateKeyPair is called.
+ - If the public key is provided upon resource creation, the provided public key needs to be at least 2048-bit and in ssh-rsa format.
+ type: str
+ state:
+ description:
+ - State of the SSH Public Key. Use C(present) to create or update and C(absent) to delete.
+ default: present
+ type: str
+ choices:
+ - absent
+ - present
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+ - azure.azcollection.azure_tags
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Create a SSH Public Key
+ azure_rm_sshpublickey:
+ resource_group: myResourceGroup
+ name: mySshPublicKey
+ public_key: "ssh-rsa ****************************@test.com"
+ tags:
+ testing: testing
+ delete: on-exit
+
+- name: Generate a pair SSH Public Key
+ azure_rm_sshpublickey:
+ resource_group: myResourceGroup
+ name: mySshPublicKey
+ tags:
+ testing: testing
+ delete: on-exit
+
+- name: Delete a SSH Public Key
+ azure_rm_sshpublickey:
+ resource_group: myResourceGroup
+ name: mySshPublicKey
+ state: absent
+'''
+RETURN = '''
+state:
+ description:
+ - Current state of the SSH Public Key.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: /subscriptions/xxxx/resourceGroups/xxx/providers/Microsoft.Compute/sshPublicKeys/mySshPublicKeyName
+ location:
+ description:
+ - The Geo-location where the resource lives.
+ returned: always
+ type: str
+ sample: eastus
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: mySshPublicKey
+ tags:
+ description:
+ - Resource tags, such as { 'tags1':'value1' }.
+ returned: always
+ type: dict
+ sample: { 'key1':'value1' }
+ public_key:
+ description:
+ - SSH public key used to authenticate to a virtual machine through ssh.
+ returned: always
+ type: str
+ sample: "ssh-rsa **************@test.com"
+'''
+
+try:
+ from azure.core.exceptions import ResourceNotFoundError
+except ImportError:
+ # This is handled in azure_rm_common
+ pass
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+
+class AzureRMSshPublicKey(AzureRMModuleBase):
+
+ def __init__(self):
+
+ self.module_arg_spec = dict(
+ resource_group=dict(type='str', required=True),
+ name=dict(type='str', required=True),
+ state=dict(type='str', default='present', choices=['present', 'absent']),
+ location=dict(type='str'),
+ public_key=dict(type='str'),
+ )
+
+ self.resource_group = None
+ self.name = None
+ self.state = None
+ self.location = None
+ self.public_key = None
+
+ self.body = dict()
+
+ self.results = dict(
+ changed=False,
+ state=dict()
+ )
+
+ super(AzureRMSshPublicKey, self).__init__(self.module_arg_spec,
+ supports_tags=True,
+ supports_check_mode=True)
+
+ def exec_module(self, **kwargs):
+
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
+ setattr(self, key, kwargs[key])
+ for key in ['tags', 'public_key']:
+ self.body[key] = kwargs[key]
+
+ resource_group = self.get_resource_group(self.resource_group)
+ if not self.location:
+ # Set default location
+ self.location = resource_group.location
+
+ changed = False
+ results = dict()
+
+ old_response = self.get_by_name()
+
+ if old_response is not None:
+ if self.state == 'present':
+ update_tags, self.body['tags'] = self.update_tags(old_response['tags'])
+ if update_tags or self.body['public_key'] is not None and self.body['public_key'] != old_response['public_key']:
+ changed = True
+ if not self.check_mode:
+ results = self.update_ssh_public_key(self.body)
+ else:
+ results = old_response
+ else:
+ changed = True
+ if not self.check_mode:
+ results = self.delete_ssh_public_key()
+ else:
+ if self.state == 'present':
+ changed = True
+ if not self.check_mode:
+ self.body['location'] = self.location
+ results = self.create_ssh_public_key(self.body)
+ else:
+ changed = False
+ self.log("The SSH Public Key is not exists")
+
+ self.results['changed'] = changed
+ self.results['state'] = results
+
+ return self.results
+
+ def get_by_name(self):
+ response = None
+ try:
+ response = self.compute_client.ssh_public_keys.get(self.resource_group, self.name)
+
+ except ResourceNotFoundError as exec:
+ self.log("Failed to get ssh public keys, Exception as {0}".format(exec))
+
+ return self.to_dict(response)
+
+ def create_ssh_public_key(self, body):
+ response = None
+ try:
+ if body.get('public_key') is None:
+ response = self.to_dict(self.compute_client.ssh_public_keys.create(self.resource_group, self.name, body))
+ response.update(self.to_dict(self.compute_client.ssh_public_keys.generate_key_pair(self.resource_group, self.name)))
+ else:
+ response = self.to_dict(self.compute_client.ssh_public_keys.create(self.resource_group, self.name, body))
+ except Exception as exc:
+ self.fail("Error creating SSH Public Key {0} - {1}".format(self.name, str(exc)))
+
+ return response
+
+ def update_ssh_public_key(self, body):
+ response = None
+ try:
+ response = self.compute_client.ssh_public_keys.update(self.resource_group, self.name, body)
+ except Exception as exc:
+ self.fail("Error updating SSH Public Key {0} - {1}".format(self.name, str(exc)))
+ return self.to_dict(response)
+
+ def delete_ssh_public_key(self):
+ try:
+ self.compute_client.ssh_public_keys.delete(self.resource_group, self.name)
+ except Exception as exc:
+ self.fail("Error deleting SSH Public Key {0} - {1}".format(self.name, str(exc)))
+
+ def to_dict(self, body):
+ results = None
+ if body is not None:
+ if hasattr(body, 'private_key'):
+ results = dict(
+ private_key=body.private_key,
+ id=body.id,
+ public_key=body.public_key
+ )
+ else:
+ results = dict(
+ id=body.id,
+ name=body.name,
+ location=body.location,
+ tags=body.tags,
+ public_key=body.public_key
+ )
+ return results
+
+
+def main():
+ AzureRMSshPublicKey()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey_info.py
new file mode 100644
index 000000000..3a84f2e74
--- /dev/null
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_sshpublickey_info.py
@@ -0,0 +1,196 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2023 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
+#
+# 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: azure_rm_sshpublickey_info
+
+version_added: "2.0.0"
+
+short_description: Get Ssh Public Key with VM facts
+
+description:
+ - Get Ssh Public Key with VM facts
+
+options:
+ resource_group:
+ description:
+ - Name of the resource group.
+ type: str
+ name:
+ description:
+ - Name of the SSH Public Key.
+ type: str
+ tags:
+ description:
+ - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'.
+ type: list
+ elements: str
+
+extends_documentation_fragment:
+ - azure.azcollection.azure
+
+author:
+ - xuzhang3 (@xuzhang3)
+ - Fred-sun (@Fred-sun)
+
+'''
+
+EXAMPLES = '''
+- name: Get facts of the VM's ssh public key by name
+ azure_rm_sshpublickey_info:
+ resource_group: myResourceGroup
+ name: mysshpublickey
+
+- name: Get facts of the VM's ssh public key by resource group
+ azure_rm_sshpublickey_info:
+ resource_group: myResourceGroup
+
+- name: Get facts by tags
+ azure_rm_sshpublickey_info:
+ resource_group: myResourceGroup
+ tags:
+ - testing
+ - foo:bar
+'''
+
+RETURN = '''
+ssh_keys:
+ description:
+ - Current state of the SSH Public Key.
+ returned: always
+ type: complex
+ contains:
+ id:
+ description:
+ - Resource ID.
+ returned: always
+ type: str
+ sample: /subscriptions/xxxx/resourceGroups/xxx/providers/Microsoft.Compute/sshPublicKeys/mySshPublicKeyName
+ location:
+ description:
+ - The Geo-location where the resource lives.
+ returned: always
+ type: str
+ sample: eastus
+ name:
+ description:
+ - Resource name.
+ returned: always
+ type: str
+ sample: mySshPublicKey
+ tags:
+ description:
+ - Resource tags, such as { 'tags1':'value1' }.
+ returned: always
+ type: dict
+ sample: { 'key1':'value1' }
+ public_key:
+ description:
+ - SSH public key used to authenticate to a virtual machine through ssh.
+ returned: always
+ type: str
+ sample: "ssh-rsa **************@test.com"
+'''
+
+try:
+ from azure.core.exceptions import ResourceNotFoundError
+except Exception:
+ # This is handled in azure_rm_common
+ pass
+
+from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+
+
+class AzureRMSshPublicKeyInfo(AzureRMModuleBase):
+
+ def __init__(self):
+
+ self.module_arg_spec = dict(
+ resource_group=dict(type='str'),
+ name=dict(type='str'),
+ tags=dict(type='list', elements='str')
+ )
+
+ self.results = dict(
+ changed=False,
+ ssh_keys=[]
+ )
+
+ self.resource_group = None
+ self.name = None
+ self.tags = None
+
+ super(AzureRMSshPublicKeyInfo, self).__init__(self.module_arg_spec,
+ supports_check_mode=True,
+ supports_tags=False,
+ facts_module=True)
+
+ def exec_module(self, **kwargs):
+ for key in self.module_arg_spec:
+ setattr(self, key, kwargs[key])
+
+ if self.name and self.resource_group:
+ response = [self.get_by_name()]
+ elif self.resource_group:
+ response = self.list_by_resourcegroup()
+ else:
+ response = self.list_all()
+
+ self.results['ssh_keys'] = [self.to_dict(item) for item in response if response is not None]
+
+ return self.results
+
+ def get_by_name(self):
+ response = None
+ try:
+ response = self.compute_client.ssh_public_keys.get(self.resource_group, self.name)
+
+ except ResourceNotFoundError as exec:
+ self.log("Failed to get ssh public keys, Exception as {0}".format(exec))
+
+ return response
+
+ def list_by_resourcegroup(self):
+ response = None
+ try:
+ response = self.compute_client.ssh_public_keys.list_by_resource_group(self.resource_group)
+ except Exception as exec:
+ self.log("Faild to list ssh public keys by resource group, exception as {0}".format(exec))
+ return response
+
+ def list_all(self):
+ response = None
+ try:
+ response = self.compute_client.ssh_public_keys.list_by_subscription()
+ except Exception as exc:
+ self.fail("Failed to list all items - {0}".format(str(exc)))
+
+ return response
+
+ def to_dict(self, body):
+ results = dict()
+ if body is not None and self.has_tags(body.tags, self.tags):
+ results = dict(
+ id=body.id,
+ name=body.name,
+ location=body.location,
+ tags=body.tags,
+ public_key=body.public_key
+ )
+ return results
+
+
+def main():
+ AzureRMSshPublicKeyInfo()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount.py
index c7c280ef4..42c5b2e42 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount.py
@@ -87,6 +87,10 @@ options:
- Account HierarchicalNamespace enabled if sets to true.
- When I(is_hns_enabled=True), I(kind) cannot be C(Storage).
type: bool
+ enable_nfs_v3:
+ description:
+ - NFS 3.0 protocol.
+ type: bool
access_tier:
description:
- The access tier for this storage account. Required when I(kind=BlobStorage).
@@ -240,6 +244,13 @@ options:
description:
- The absolute path of the custom 404 page.
type: str
+ large_file_shares_state:
+ description:
+ - Allow large file shares if sets to Enabled.
+ type: str
+ choices:
+ - Enabled
+ - Disabled
encryption:
description:
- The encryption settings on the storage account.
@@ -333,6 +344,16 @@ EXAMPLES = '''
tags:
testing: testing
+- name: Create storage account with I(enable_nfs_v3=false)
+ azure_rm_storageaccount:
+ resource_group: myResourceGroup
+ name: c1h0002
+ account_type: Premium_LRS
+ kind: FileStorage
+ enable_nfs_v3: false
+ static_website:
+ enabled: true
+
- name: configure firewall and virtual networks
azure_rm_storageaccount:
resource_group: myResourceGroup
@@ -462,6 +483,12 @@ state:
type: bool
returned: always
sample: true
+ enable_nfs_v3:
+ description:
+ - NFS 3.0 protocol.
+ type: bool
+ returned: always
+ sample: false
location:
description:
- Valid Azure location. Defaults to location of the resource group.
@@ -599,6 +626,12 @@ state:
returned: always
type: str
sample: "Microsoft.Storage/storageAccounts"
+ large_file_shares_state:
+ description:
+ - Allow large file shares if sets to Enabled.
+ type: str
+ returned: always
+ sample: Enabled
static_website:
description:
- Static website configuration for the storage account.
@@ -711,6 +744,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
blob_cors=dict(type='list', options=cors_rule_spec, elements='dict'),
static_website=dict(type='dict', options=static_website_spec),
is_hns_enabled=dict(type='bool'),
+ large_file_shares_state=dict(type='str', choices=['Enabled', 'Disabled']),
+ enable_nfs_v3=dict(type='bool'),
encryption=dict(
type='dict',
options=dict(
@@ -766,6 +801,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
self.static_website = None
self.encryption = None
self.is_hns_enabled = None
+ self.large_file_shares_state = None
+ self.enable_nfs_v3 = None
super(AzureRMStorageAccount, self).__init__(self.module_arg_spec,
supports_check_mode=True)
@@ -870,6 +907,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
allow_blob_public_access=account_obj.allow_blob_public_access,
network_acls=account_obj.network_rule_set,
is_hns_enabled=account_obj.is_hns_enabled if account_obj.is_hns_enabled else False,
+ enable_nfs_v3=account_obj.enable_nfs_v3 if hasattr(account_obj, 'enable_nfs_v3') else None,
+ large_file_shares_state=account_obj.large_file_shares_state,
static_website=dict(
enabled=False,
index_document=None,
@@ -1020,6 +1059,10 @@ class AzureRMStorageAccount(AzureRMModuleBase):
self.results['changed'] = True
self.update_network_rule_set()
+ if self.enable_nfs_v3 is not None and bool(self.enable_nfs_v3) != bool(self.account_dict.get('enable_nfs_v3')):
+ self.results['changed'] = True
+ self.account_dict['enable_nfs_v3'] = self.enable_nfs_v3
+
if self.is_hns_enabled is not None and bool(self.is_hns_enabled) != bool(self.account_dict.get('is_hns_enabled')):
self.results['changed'] = True
self.account_dict['is_hns_enabled'] = self.is_hns_enabled
@@ -1129,6 +1172,21 @@ class AzureRMStorageAccount(AzureRMModuleBase):
except Exception as exc:
self.fail("Failed to update access tier: {0}".format(str(exc)))
+ if self.large_file_shares_state is not None:
+ if self.large_file_shares_state != self.account_dict['large_file_shares_state']:
+ self.results['changed'] = True
+ self.account_dict['large_file_shares_state'] = self.large_file_shares_state
+
+ if self.results['changed'] and not self.check_mode:
+ if self.large_file_shares_state == 'Disabled':
+ parameters = self.storage_models.StorageAccountUpdateParameters(large_file_shares_state=None)
+ else:
+ parameters = self.storage_models.StorageAccountUpdateParameters(large_file_shares_state=self.large_file_shares_state)
+ try:
+ self.storage_client.storage_accounts.update(self.resource_group, self.name, parameters)
+ except Exception as exc:
+ self.fail("Failed to update large_file_shares_state: {0}".format(str(exc)))
+
update_tags, self.account_dict['tags'] = self.update_tags(self.account_dict['tags'])
if update_tags:
self.results['changed'] = True
@@ -1198,6 +1256,8 @@ class AzureRMStorageAccount(AzureRMModuleBase):
allow_blob_public_access=self.allow_blob_public_access,
encryption=self.encryption,
is_hns_enabled=self.is_hns_enabled,
+ enable_nfs_v3=self.enable_nfs_v3,
+ large_file_shares_state=self.large_file_shares_state,
tags=dict()
)
if self.tags:
@@ -1223,7 +1283,9 @@ class AzureRMStorageAccount(AzureRMModuleBase):
allow_blob_public_access=self.allow_blob_public_access,
encryption=self.encryption,
is_hns_enabled=self.is_hns_enabled,
- access_tier=self.access_tier)
+ enable_nfs_v3=self.enable_nfs_v3,
+ access_tier=self.access_tier,
+ large_file_shares_state=self.large_file_shares_state)
self.log(str(parameters))
try:
poller = self.storage_client.storage_accounts.begin_create(self.resource_group, self.name, parameters)
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount_info.py
index 75f179b6f..a530459dd 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageaccount_info.py
@@ -211,6 +211,12 @@ storageaccounts:
type: bool
returned: always
sample: true
+ enable_nfs_v3:
+ description:
+ - NFS 3.0 protocol.
+ type: bool
+ returned: always
+ sample: false
kind:
description:
- The kind of storage.
@@ -509,6 +515,12 @@ storageaccounts:
returned: always
type: dict
sample: { "tag1": "abc" }
+ large_file_shares_state:
+ description:
+ - Allow large file shares if sets to Enabled.
+ type: str
+ returned: always
+ sample: Enabled
static_website:
description:
- Static website configuration for the storage account.
@@ -674,6 +686,8 @@ class AzureRMStorageAccountInfo(AzureRMModuleBase):
public_network_access=account_obj.public_network_access,
allow_blob_public_access=account_obj.allow_blob_public_access,
is_hns_enabled=account_obj.is_hns_enabled if account_obj.is_hns_enabled else False,
+ large_file_shares_state=account_obj.large_file_shares_state,
+ enable_nfs_v3=account_obj.enable_nfs_v3 if hasattr(account_obj, 'enable_nfs_v3') else None,
static_website=dict(
enabled=False,
index_document=None,
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageblob.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageblob.py
index 868069f42..ad54b8422 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageblob.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_storageblob.py
@@ -22,6 +22,17 @@ description:
- the module can work exclusively in three modes, when C(batch_upload_src) is set, it is working in batch upload mode;
when C(src) is set, it is working in upload mode and when C(dst) is set, it is working in dowload mode.
options:
+ auth_mode:
+ description:
+ - The mode in which to run the command. C(login) mode will directly use your login credentials for the authentication.
+ - The legacy C(key) mode will attempt to query for an account key if no authentication parameters for the account are provided.
+ - Can also be set via the environment variable C(AZURE_STORAGE_AUTH_MODE).
+ default: key
+ type: str
+ choices:
+ - key
+ - login
+ version_added: "1.19.0"
storage_account_name:
description:
- Name of the storage account to use.
@@ -214,6 +225,7 @@ except ImportError:
pass
from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
+from ansible.module_utils.basic import env_fallback
class AzureRMStorageBlob(AzureRMModuleBase):
@@ -221,6 +233,12 @@ class AzureRMStorageBlob(AzureRMModuleBase):
def __init__(self):
self.module_arg_spec = dict(
+ auth_mode=dict(
+ type='str',
+ choices=['key', 'login'],
+ fallback=(env_fallback, ['AZURE_STORAGE_AUTH_MODE']),
+ default="key"
+ ),
storage_account_name=dict(required=True, type='str', aliases=['account_name', 'storage_account']),
blob=dict(type='str', aliases=['blob_name']),
blob_type=dict(type='str', default='block', choices=['block', 'page']),
@@ -281,7 +299,7 @@ class AzureRMStorageBlob(AzureRMModuleBase):
# add file path validation
- self.blob_service_client = self.get_blob_service_client(self.resource_group, self.storage_account_name)
+ self.blob_service_client = self.get_blob_service_client(self.resource_group, self.storage_account_name, self.auth_mode)
self.container_obj = self.get_container()
if self.blob:
self.blob_obj = self.get_blob()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_subnet.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_subnet.py
index ec7117c37..3fbe2b803 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_subnet.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_subnet.py
@@ -124,6 +124,7 @@ options:
required: True
type: str
choices:
+ - Microsoft.ContainerService/managedClusters
- Microsoft.Web/serverFarms
- Microsoft.ContainerInstance/containerGroups
- Microsoft.Netapp/volumes
@@ -352,7 +353,8 @@ delegations_spec = dict(
'Microsoft.DBforPostgreSQL/serversv2', 'Microsoft.AzureCosmosDB/clusters', 'Microsoft.MachineLearningServices/workspaces',
'Microsoft.DBforPostgreSQL/singleServers', 'Microsoft.DBforPostgreSQL/flexibleServers', 'Microsoft.DBforMySQL/serversv2',
'Microsoft.DBforMySQL/flexibleServers', 'Microsoft.ApiManagement/service', 'Microsoft.Synapse/workspaces',
- 'Microsoft.PowerPlatform/vnetaccesslinks', 'Microsoft.Network/managedResolvers', 'Microsoft.Kusto/clusters']
+ 'Microsoft.PowerPlatform/vnetaccesslinks', 'Microsoft.Network/managedResolvers', 'Microsoft.Kusto/clusters',
+ 'Microsoft.ContainerService/managedClusters']
),
actions=dict(
type='list',
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine.py
index 35435b821..e845e2fa1 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine.py
@@ -229,6 +229,10 @@ options:
description:
- Size of OS disk in GB.
type: int
+ os_disk_encryption_set:
+ description:
+ - ID of disk encryption set for OS disk.
+ type: str
os_type:
description:
- Base type of operating system.
@@ -245,7 +249,7 @@ options:
data_disks:
description:
- Describes list of data disks.
- - Use M(azure.azcollection.azure_rm_mangeddisk) to manage the specific disk.
+ - Use M(azure.azcollection.azure_rm_manageddisk) to manage the specific disk.
type: list
elements: dict
suboptions:
@@ -262,6 +266,10 @@ options:
- Size can be changed only when the virtual machine is deallocated.
- Not sure when I(managed_disk_id) defined.
type: int
+ disk_encryption_set:
+ description:
+ - ID of disk encryption set for data disk.
+ type: str
managed_disk_type:
description:
- Managed data disk type.
@@ -409,6 +417,7 @@ options:
zones:
description:
- A list of Availability Zones for your VM.
+ - A maximum of one zone can be configured.
type: list
elements: str
license_type:
@@ -569,6 +578,33 @@ options:
description:
- Specifies whether vTPM should be enabled on the virtual machine.
type: bool
+ swap_os_disk:
+ description:
+ - The swap OS disk parameters.
+ type: dict
+ suboptions:
+ os_disk_id:
+ description:
+ - The swap OS disk's ID.
+ type: str
+ os_disk_name:
+ description:
+ - The swap OS disk's name.
+ type: str
+ os_disk_resource_group:
+ description:
+ - The swap OS disk's resource group.
+ type: str
+ additional_capabilities:
+ description:
+ - Enables or disables a capability on the virtual machine.
+ type: dict
+ suboptions:
+ ultra_ssd_enabled:
+ description:
+ - The flag that enables or disables a capability to have one or more managed data disks with UltraSSD_LRS storage account type on the VM.
+ - Managed disks with storage account type UltraSSD_LRS can be added to a virtual machine set only if this property is enabled.
+ type: bool
extends_documentation_fragment:
- azure.azcollection.azure
@@ -760,6 +796,26 @@ EXAMPLES = '''
sku: '7.1'
version: latest
+- name: Update VM to swap OS disk
+ azure_rm_virtualmachine:
+ resource_group: "{{ resource_group }}"
+ name: "vmforimage{{ rpfx }}"
+ admin_username: testuser
+ ssh_password_enabled: false
+ managed_disk_type: Premium_LRS
+ ssh_public_keys:
+ - path: /home/testuser/.ssh/authorized_keys
+ key_data: "ssh-rsa *******************@qq.com"
+ vm_size: Standard_D4s_v3
+ swap_os_disk:
+ os_disk_name: "{{ os_disk_name }}"
+ os_disk_resource_group: "{{ os_disk_resource_group }}"
+ image:
+ offer: 0001-com-ubuntu-server-focal
+ publisher: Canonical
+ sku: 20_04-lts
+ version: latest
+
- name: Power Off
azure_rm_virtualmachine:
resource_group: myResourceGroup
@@ -856,6 +912,9 @@ azure_vm:
type: dict
sample: {
"properties": {
+ "additional_capabilities": {
+ "ultra_ssd_enabled": false
+ },
"availabilitySet": {
"id": "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroup/myResourceGroup/providers/Microsoft.Compute/availabilitySets/MYAVAILABILITYSET"
},
@@ -1088,6 +1147,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
storage_blob_name=dict(type='str', aliases=['storage_blob']),
os_disk_caching=dict(type='str', aliases=['disk_caching'], choices=['ReadOnly', 'ReadWrite']),
os_disk_size_gb=dict(type='int'),
+ os_disk_encryption_set=dict(type='str'),
managed_disk_type=dict(type='str', choices=['Standard_LRS', 'StandardSSD_LRS', 'StandardSSD_ZRS', 'Premium_LRS', 'Premium_ZRS', 'UltraSSD_LRS']),
os_disk_name=dict(type='str'),
proximity_placement_group=dict(type='dict', options=proximity_placement_group_spec),
@@ -1106,18 +1166,27 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
started=dict(type='bool'),
force=dict(type='bool', default=False),
generalized=dict(type='bool', default=False),
+ swap_os_disk=dict(
+ type='dict',
+ options=dict(
+ os_disk_resource_group=dict(type='str'),
+ os_disk_name=dict(type='str'),
+ os_disk_id=dict(type='str')
+ )
+ ),
data_disks=dict(
type='list',
elements='dict',
options=dict(
lun=dict(type='int', required=True),
disk_size_gb=dict(type='int'),
+ disk_encryption_set=dict(type='str'),
managed_disk_type=dict(type='str', choices=['Standard_LRS', 'StandardSSD_LRS',
'StandardSSD_ZRS', 'Premium_LRS', 'Premium_ZRS', 'UltraSSD_LRS']),
storage_account_name=dict(type='str'),
storage_container_name=dict(type='str', default='vhds'),
storage_blob_name=dict(type='str'),
- caching=dict(type='str', choices=['ReadOnly', 'ReadOnly'])
+ caching=dict(type='str', choices=['ReadOnly', 'ReadWrite'])
)
),
plan=dict(type='dict'),
@@ -1149,6 +1218,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
windows_config=dict(type='dict', options=windows_configuration_spec),
linux_config=dict(type='dict', options=linux_configuration_spec),
security_profile=dict(type='dict'),
+ additional_capabilities=dict(
+ type='dict',
+ options=dict(
+ ultra_ssd_enabled=dict(type='bool')
+ )
+ )
)
self.resource_group = None
@@ -1172,6 +1247,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
self.os_type = None
self.os_disk_caching = None
self.os_disk_size_gb = None
+ self.os_disk_encryption_set = None
self.managed_disk_type = None
self.os_disk_name = None
self.proximity_placement_group = None
@@ -1201,6 +1277,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
self.linux_config = None
self.windows_config = None
self.security_profile = None
+ self.additional_capabilities = None
+ self.swap_os_disk = None
self.results = dict(
changed=False,
@@ -1209,8 +1287,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
ansible_facts=dict(azure_vm=None)
)
+ required_if = [('os_disk_encryption_set', '*', ['managed_disk_type'])]
+
super(AzureRMVirtualMachine, self).__init__(derived_arg_spec=self.module_arg_spec,
- supports_check_mode=True)
+ supports_check_mode=True, required_if=required_if)
@property
def boot_diagnostics_present(self):
@@ -1277,12 +1357,16 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
vm_dict = None
image_reference = None
custom_image = False
+ swap_os_disk_flag = False
resource_group = self.get_resource_group(self.resource_group)
if not self.location:
# Set default location
self.location = resource_group.location
+ if self.swap_os_disk is not None:
+ swap_os_disk = self.get_os_disk(self.swap_os_disk)
+
self.location = normalize_location_name(self.location)
if self.state == 'present':
@@ -1411,6 +1495,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
vm_dict['network_profile']['network_interfaces'] = updated_nics
changed = True
+ if self.swap_os_disk is not None and swap_os_disk.get('name') != vm_dict['storage_profile']['os_disk']['name']:
+ self.log('CHANGED: Swap virtual machine {0} - OS disk name'.format(self.name))
+ differences.append('Swap OS Disk')
+ changed = True
+ swap_os_disk_flag = True
+
if self.os_disk_caching and \
self.os_disk_caching != vm_dict['storage_profile']['os_disk']['caching']:
self.log('CHANGED: virtual machine {0} - OS disk caching'.format(self.name))
@@ -1448,7 +1538,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
self.log('CHANGED: virtual machine {0} - short hostname'.format(self.name))
differences.append('Short Hostname')
changed = True
- vm_dict['os_orofile']['computer_name'] = self.short_hostname
+ vm_dict['os_profile']['computer_name'] = self.short_hostname
if self.started and vm_dict['powerstate'] not in ['starting', 'running'] and self.allocated:
self.log("CHANGED: virtual machine {0} not running and requested state 'running'".format(self.name))
@@ -1479,7 +1569,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
differences.append('Zones')
changed = True
- if self.license_type is not None and vm_dict.get('licenseType') != self.license_type:
+ if self.license_type is not None and vm_dict.get('license_type') != self.license_type:
differences.append('License Type')
changed = True
@@ -1516,7 +1606,7 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
if self.security_profile is not None:
update_security_profile = False
- if 'securityProfile' not in vm_dict.keys():
+ if 'security_profile' not in vm_dict.keys():
update_security_profile = True
differences.append('security_profile')
else:
@@ -1547,6 +1637,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
changed = True
differences.append('security_profile')
+ if self.additional_capabilities is not None:
+ if 'additional_capabilities' not in vm_dict.keys() or\
+ bool(self.additional_capabilities['ultra_ssd_enabled']) != bool(vm_dict['additional_capabilities']['ultra_ssd_enabled']):
+ changed = True
+ differences.append('additional_capabilities')
+
if self.windows_config is not None and vm_dict['os_profile'].get('windows_configuration') is not None:
if self.windows_config['enable_automatic_updates'] != vm_dict['os_profile']['windows_configuration']['enable_automatic_updates']:
self.fail("(PropertyChangeNotAllowed) Changing property 'windowsConfiguration.enableAutomaticUpdates' is not allowed.")
@@ -1559,6 +1655,11 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
vm_dict['os_profile']['linux_configuration']['disable_password_authentication']:
self.fail("(PropertyChangeNotAllowed) Changing property 'linuxConfiguration.disablePasswordAuthentication' is not allowed.")
+ current_os_des_id = vm_dict['storage_profile'].get('os_disk', {}).get('managed_disk', {}).get('disk_encryption_set', {}).get('id', None)
+ if self.os_disk_encryption_set is not None and current_os_des_id is not None:
+ if self.os_disk_encryption_set != current_os_des_id:
+ self.fail("(PropertyChangeNotAllowed) Changing property 'storage_profile.os_disk.managed_disk.disk_encryption_set' is not allowed.")
+
# Defaults for boot diagnostics
if 'diagnostics_profile' not in vm_dict:
vm_dict['diagnostics_profile'] = {}
@@ -1601,6 +1702,29 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
vm_dict['tags']['_own_sa_'] = own_sa
changed = True
+ if self.proximity_placement_group is not None:
+ if vm_dict.get('proximity_placement_group') is None:
+ changed = True
+ differences.append('proximity_placement_group')
+ if self.proximity_placement_group.get('name') is not None and self.proximity_placement_group.get('resource_group') is not None:
+ proximity_placement_group = self.get_proximity_placement_group(self.proximity_placement_group.get('resource_group'),
+ self.proximity_placement_group.get('name'))
+ self.proximity_placement_group['id'] = proximity_placement_group.id
+
+ elif self.proximity_placement_group.get('id') is not None:
+ if vm_dict['proximity_placement_group'].get('id', "").lower() != self.proximity_placement_group['id'].lower():
+ changed = True
+ differences.append('proximity_placement_group')
+ elif self.proximity_placement_group.get('name') is not None and self.proximity_placement_group.get('resource_group') is not None:
+ proximity_placement_group = self.get_proximity_placement_group(self.proximity_placement_group.get('resource_group'),
+ self.proximity_placement_group.get('name'))
+ if vm_dict['proximity_placement_group'].get('id', "").lower() != proximity_placement_group.id.lower():
+ changed = True
+ differences.append('proximity_placement_group')
+ self.proximity_placement_group['id'] = proximity_placement_group.id
+ else:
+ self.fail("Parameter error: Please recheck your proximity placement group ")
+
self.differences = differences
elif self.state == 'absent':
@@ -1700,6 +1824,11 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
vhd = self.compute_models.VirtualHardDisk(uri=requested_vhd_uri)
managed_disk = None
+ if managed_disk and self.os_disk_encryption_set:
+ managed_disk.disk_encryption_set = self.compute_models.DiskEncryptionSetParameters(
+ id=self.os_disk_encryption_set
+ )
+
plan = None
if self.plan:
plan = self.compute_models.Plan(name=self.plan.get('name'), product=self.plan.get('product'),
@@ -1865,6 +1994,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
else:
data_disk_vhd = None
data_disk_managed_disk = self.compute_models.ManagedDiskParameters(storage_account_type=data_disk['managed_disk_type'])
+ if data_disk.get('disk_encryption_set'):
+ data_disk_managed_disk.disk_encryption_set = self.compute_models.DiskEncryptionSetParameters(
+ id=data_disk['disk_encryption_set']
+ )
disk_name = self.name + "-datadisk-" + str(count)
count += 1
@@ -1916,6 +2049,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
)
vm_resource.security_profile = security_profile
+ if self.additional_capabilities is not None:
+ additional_capabilities = self.compute_models.AdditionalCapabilities(
+ ultra_ssd_enabled=self.additional_capabilities.get('ultra_ssd_enabled')
+ )
+ vm_resource.additional_capabilities = additional_capabilities
+
self.log("Create virtual machine with parameters:")
self.create_or_update_vm(vm_resource, 'all_autocreated' in self.remove_on_absent)
@@ -1938,11 +2077,18 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
)
proximity_placement_group_resource = None
- try:
- proximity_placement_group_resource = self.compute_models.SubResource(id=vm_dict['proximity_placement_group'].get('id'))
- except Exception:
- # pass if the proximity Placement Group
- pass
+ if self.proximity_placement_group is not None:
+ try:
+ proximity_placement_group_resource = self.compute_models.SubResource(id=self.proximity_placement_group.get('id'))
+ except Exception:
+ # pass if the proximity Placement Group
+ pass
+ else:
+ try:
+ proximity_placement_group_resource = self.compute_models.SubResource(id=vm_dict['proximity_placement_group'].get('id'))
+ except Exception:
+ # pass if the proximity Placement Group
+ pass
availability_set_resource = None
try:
@@ -1984,7 +2130,24 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
hardware_profile=self.compute_models.HardwareProfile(
vm_size=vm_dict['hardware_profile'].get('vm_size')
),
- storage_profile=self.compute_models.StorageProfile(
+ availability_set=availability_set_resource,
+ proximity_placement_group=proximity_placement_group_resource,
+ network_profile=self.compute_models.NetworkProfile(
+ network_interfaces=nics
+ )
+ )
+
+ if swap_os_disk_flag:
+ storage_profile = self.compute_models.StorageProfile(
+ os_disk=self.compute_models.OSDisk(
+ name=swap_os_disk.get('name'),
+ managed_disk=swap_os_disk.get('managed_disk'),
+ create_option=vm_dict['storage_profile']['os_disk'].get('create_option'),
+ ),
+ image_reference=image_reference
+ )
+ else:
+ storage_profile = self.compute_models.StorageProfile(
os_disk=self.compute_models.OSDisk(
name=vm_dict['storage_profile']['os_disk'].get('name'),
vhd=vhd,
@@ -1995,13 +2158,9 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
disk_size_gb=vm_dict['storage_profile']['os_disk'].get('disk_size_gb')
),
image_reference=image_reference
- ),
- availability_set=availability_set_resource,
- proximity_placement_group=proximity_placement_group_resource,
- network_profile=self.compute_models.NetworkProfile(
- network_interfaces=nics
)
- )
+
+ vm_resource.storage_profile = storage_profile
if self.license_type is not None:
vm_resource.license_type = self.license_type
@@ -2083,8 +2242,8 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
)
else:
vm_resource.os_profile.windows_configuration = self.compute_models.WindowsConfiguration(
- provision_vm_agent=windows_config.get('provisionVMAgent', True),
- enable_automatic_updates=windows_config.get('enableAutomaticUpdates', True)
+ provision_vm_agent=windows_config.get('provision_vm_agent', True),
+ enable_automatic_updates=windows_config.get('enable_automatic_updates', True)
)
# Add linux configuration, if applicable
@@ -2100,12 +2259,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
)
ssh_config = linux_config.get('ssh', None)
if ssh_config:
- public_keys = ssh_config.get('publicKeys')
+ public_keys = ssh_config.get('public_keys')
if public_keys:
vm_resource.os_profile.linux_configuration.ssh = self.compute_models.SshConfiguration(public_keys=[])
for key in public_keys:
vm_resource.os_profile.linux_configuration.ssh.public_keys.append(
- self.compute_models.SshPublicKey(path=key['path'], key_data=key['keyData'])
+ self.compute_models.SshPublicKey(path=key['path'], key_data=key['key_data'])
)
# data disk
@@ -2116,6 +2275,10 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
if data_disk.get('managed_disk'):
managed_disk_type = data_disk['managed_disk'].get('storage_account_type')
data_disk_managed_disk = self.compute_models.ManagedDiskParameters(storage_account_type=managed_disk_type)
+ if data_disk.get('disk_encryption_set'):
+ data_disk_managed_disk.disk_encryption_set = self.compute_models.DiskEncryptionSetParameters(
+ id=data_disk['disk_encryption_set']
+ )
data_disk_vhd = None
else:
data_disk_vhd = data_disk['vhd']['uri']
@@ -2146,6 +2309,12 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
)
vm_resource.security_profile = security_profile
+ if self.additional_capabilities is not None:
+ additional_capabilities = self.compute_models.AdditionalCapabilities(
+ ultra_ssd_enabled=self.additional_capabilities.get('ultra_ssd_enabled')
+ )
+ vm_resource.additional_capabilities = additional_capabilities
+
self.log("Update virtual machine with parameters:")
self.create_or_update_vm(vm_resource, False)
@@ -2182,6 +2351,32 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
return self.results
+ def get_os_disk(self, os_disk):
+ resource_group_name = None
+ os_disk_name = None
+ if os_disk.get('os_disk_id') is not None:
+ os_disk_dict = self.parse_resource_to_dict(os_disk.get('os_disk_id'))
+ os_disk_name = os_disk_dict.get('name')
+ resource_group_name = os_disk_dict.get('resource_group')
+ elif os_disk.get('os_disk_resource_group') is not None and os_disk.get('os_disk_name') is not None:
+ os_disk_name = os_disk.get('os_disk_name')
+ resource_group_name = os_disk.get('os_disk_resource_group')
+ elif os_disk.get('os_disk_name') is not None:
+ os_disk_name = os_disk.get('name')
+ resource_group_name = self.resource_group
+ else:
+ self.fail("The swap_os_disk must contain one of 'os_disk_name' and 'os_disk_id'")
+
+ try:
+ os_disk = {}
+ response = self.compute_client.disks.get(resource_group_name, os_disk_name)
+ os_disk['name'] = response.name
+ os_disk['managed_disk'] = dict(id=response.id)
+ os_disk['create_option'] = response.creation_data.create_option
+ return os_disk
+ except Exception as ec:
+ self.fail('Could not find os disk {0} in resource group {1}'.format(os_disk_name, resource_group_name))
+
def get_vm(self):
'''
Get the VM with expanded instanceView
@@ -2189,7 +2384,23 @@ class AzureRMVirtualMachine(AzureRMModuleBase):
:return: VirtualMachine object
'''
try:
+ retry_count = 0
vm = self.compute_client.virtual_machines.get(self.resource_group, self.name, expand='instanceview')
+ while True:
+ if retry_count == 20:
+ self.fail("Error {0} has a provisioning state of Updating. Expecting state to be Successed.".format(self.name))
+
+ if vm.provisioning_state != 'Succeeded':
+ retry_count = retry_count + 1
+ time.sleep(150)
+ vm = self.compute_client.virtual_machines.get(self.resource_group, self.name, expand='instanceview')
+ else:
+ p_state = None
+ for s in vm.instance_view.statuses:
+ if s.code.startswith('PowerState'):
+ p_state = s.code
+ if p_state is not None:
+ break
return vm
except Exception as exc:
self.fail("Error getting virtual machine {0} - {1}".format(self.name, str(exc)))
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine_info.py
index d94f9848e..3df8e2fbc 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachine_info.py
@@ -70,6 +70,12 @@ vms:
returned: always
type: complex
contains:
+ additional_capabilities:
+ description:
+ - Enables or disables a capability on the virtual machine.
+ type: dict
+ returned: always
+ sample: {ultra_ssd_enabled: False}
admin_username:
description:
- Administrator user name.
@@ -244,6 +250,12 @@ vms:
returned: always
type: dict
sample: { "key1":"value1" }
+ vm_agent_version:
+ description:
+ - Version of the Azure VM Agent (waagent) running inside the VM.
+ returned: always
+ type: str
+ sample: '2.9.1.1'
vm_size:
description:
- Virtual machine size.
@@ -459,6 +471,11 @@ class AzureRMVirtualMachineInfo(AzureRMModuleBase):
new_result = {}
+ if instance.get('vm_agent') is not None:
+ new_result['vm_agent_version'] = instance['vm_agent'].get('vm_agent_version')
+ else:
+ new_result['vm_agent_version'] = 'Unknown'
+
if vm.security_profile is not None:
new_result['security_profile'] = dict()
if vm.security_profile.encryption_at_host is not None:
@@ -483,6 +500,7 @@ class AzureRMVirtualMachineInfo(AzureRMModuleBase):
new_result['vm_size'] = result['hardware_profile']['vm_size']
new_result['proximityPlacementGroup'] = result.get('proximity_placement_group')
new_result['zones'] = result.get('zones', None)
+ new_result['additional_capabilities'] = result.get('additional_capabilities')
os_profile = result.get('os_profile')
if os_profile is not None:
new_result['admin_username'] = os_profile.get('admin_username')
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescaleset.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescaleset.py
index 4a64628fb..c4211a971 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescaleset.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescaleset.py
@@ -334,6 +334,7 @@ options:
choices:
- Flexible
- Uniform
+ default: Flexible
security_profile:
description:
- Specifies the Security related profile settings for the virtual machine sclaset.
@@ -646,6 +647,7 @@ azure_vmss:
''' # NOQA
import base64
+import time
try:
from azure.core.exceptions import ResourceNotFoundError
@@ -723,7 +725,9 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase):
scale_in_policy=dict(type='str', choices=['Default', 'OldestVM', 'NewestVM']),
terminate_event_timeout_minutes=dict(type='int'),
ephemeral_os_disk=dict(type='bool'),
- orchestration_mode=dict(type='str', choices=['Uniform', 'Flexible']),
+ orchestration_mode=dict(type='str',
+ choices=['Uniform', 'Flexible'],
+ default='Flexible',),
platform_fault_domain_count=dict(type='int', default=1),
os_disk_size_gb=dict(type='int'),
security_profile=dict(
@@ -1375,7 +1379,18 @@ class AzureRMVirtualMachineScaleSet(AzureRMModuleBase):
:return: VirtualMachineScaleSet object
'''
try:
+ retry_count = 0
vmss = self.compute_client.virtual_machine_scale_sets.get(self.resource_group, self.name)
+ while True:
+ if retry_count == 20:
+ self.fail("Error {0} has a provisioning state of Updating. Expecting state to be Successed.".format(self.name))
+
+ if vmss.provisioning_state != 'Succeeded':
+ retry_count = retry_count + 1
+ time.sleep(150)
+ vmss = self.compute_client.virtual_machine_scale_sets.get(self.resource_group, self.name)
+ else:
+ break
return vmss
except ResourceNotFoundError as exc:
self.fail("Error getting virtual machine scale set {0} - {1}".format(self.name, str(exc)))
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance.py
index ca20000a8..402af0072 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance.py
@@ -165,7 +165,6 @@ class AzureRMVirtualMachineScaleSetInstance(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(ComputeManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-04-01')
instances = self.get()
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance_info.py
index 07b6cf92c..47a3d3318 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualmachinescalesetinstance_info.py
@@ -169,7 +169,6 @@ class AzureRMVirtualMachineScaleSetVMInfo(AzureRMModuleBase):
setattr(self, key, kwargs[key])
self.mgmt_client = self.get_mgmt_svc_client(ComputeManagementClient,
base_url=self._cloud_environment.endpoints.resource_manager,
- is_track2=True,
api_version='2021-04-01')
if (self.instance_id is None):
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualwan.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualwan.py
index 38c542695..53bef71eb 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualwan.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_virtualwan.py
@@ -229,7 +229,7 @@ except ImportError:
class Actions:
- NoAction, Create, Update, Delete = range(4)
+ NoAction, Create, Update, Update_tags, Delete = range(5)
class AzureRMVirtualWan(AzureRMModuleBaseExt):
@@ -251,45 +251,34 @@ class AzureRMVirtualWan(AzureRMModuleBaseExt):
required=True
),
disable_vpn_encryption=dict(
- type='bool',
- disposition='/disable_vpn_encryption'
+ type='bool'
),
virtual_hubs=dict(
type='list',
elements='dict',
- updatable=False,
- disposition='/virtual_hubs',
options=dict(
id=dict(
- type='str',
- disposition='id'
+ type='str'
)
)
),
vpn_sites=dict(
type='list',
elements='dict',
- updatable=False,
- disposition='/vpn_sites',
options=dict(
id=dict(
- type='str',
- disposition='id'
+ type='str'
)
)
),
allow_branch_to_branch_traffic=dict(
- type='bool',
- disposition='/allow_branch_to_branch_traffic'
+ type='bool'
),
allow_vnet_to_vnet_traffic=dict(
- type='bool',
- updatable=False,
- disposition='/allow_vnet_to_vnet_traffic'
+ type='bool'
),
virtual_wan_type=dict(
type='str',
- disposition='/virtual_wan_type',
choices=['Basic', 'Standard']
),
state=dict(
@@ -313,14 +302,12 @@ class AzureRMVirtualWan(AzureRMModuleBaseExt):
supports_tags=True)
def exec_module(self, **kwargs):
- for key in list(self.module_arg_spec.keys()):
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
self.body[key] = kwargs[key]
- self.inflate_parameters(self.module_arg_spec, self.body, 0)
-
resource_group = self.get_resource_group(self.resource_group)
if self.location is None:
# Set default location
@@ -339,18 +326,28 @@ class AzureRMVirtualWan(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
- self.to_do = Actions.Update
+ compare_list = ['disable_vpn_encryption', 'allow_branch_to_branch_traffic']
+ for key in compare_list:
+ if self.body.get(key) is not None and self.body[key] != old_response[key]:
+ self.log('parameter {0} does not match the configuration'.format(key))
+ self.to_do = Actions.Update
+ else:
+ self.body[key] = old_response[key]
+
+ update_tags, self.tags = self.update_tags(old_response.get('tags'))
+ if update_tags:
+ self.to_do = Actions.Update_tags
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
self.results['changed'] = True
if self.check_mode:
return self.results
response = self.create_update_resource()
+ elif self.to_do == Actions.Update_tags:
+ self.results['changed'] = True
+ if self.check_mode:
+ return self.results
+ response = self.update_resource_tags(dict(tags=self.tags))
elif self.to_do == Actions.Delete:
self.results['changed'] = True
if self.check_mode:
@@ -363,6 +360,18 @@ class AzureRMVirtualWan(AzureRMModuleBaseExt):
self.results['state'] = response
return self.results
+ def update_resource_tags(self, tags_parameters):
+ try:
+ response = self.network_client.virtual_wans.update_tags(resource_group_name=self.resource_group,
+ virtual_wan_name=self.name,
+ wan_parameters=tags_parameters)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+ except Exception as exc:
+ self.log('Error attempting to Update the VirtualWan instance.')
+ self.fail('Error Updating the VirtualWan instance tags: {0}'.format(str(exc)))
+ return response.as_dict()
+
def create_update_resource(self):
try:
response = self.network_client.virtual_wans.begin_create_or_update(resource_group_name=self.resource_group,
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy.py
index 5cd10c978..d3584b670 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy.py
@@ -366,7 +366,6 @@ class VMBackupPolicy(AzureRMModuleBaseExt):
response = None
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
old_response = self.get_resource()
@@ -403,10 +402,12 @@ class VMBackupPolicy(AzureRMModuleBaseExt):
self.log('Error in creating Backup Policy.')
self.fail('Error in creating Backup Policy {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy_info.py
index 0aa7b5918..0efe1df6c 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vmbackuppolicy_info.py
@@ -216,7 +216,6 @@ class BackupPolicyVMInfo(AzureRMModuleBaseExt):
self.url = self.get_url()
self.mgmt_client = self.get_mgmt_svc_client(GenericRestClient,
- is_track2=True,
base_url=self._cloud_environment.endpoints.resource_manager)
response = self.get_resource()
@@ -243,10 +242,12 @@ class BackupPolicyVMInfo(AzureRMModuleBaseExt):
except Exception as e:
self.log('Backup policy does not exist.')
self.fail('Error in fetching VM Backup Policy {0}'.format(str(e)))
- try:
+ if hasattr(response, 'body'):
response = json.loads(response.body())
- except Exception:
- response = {'text': response.context['deserialized_data']}
+ elif hasattr(response, 'context'):
+ response = response.context['deserialized_data']
+ else:
+ self.fail("Create or Updating fail, no match message return, return info as {0}".format(response))
return response
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vpnsite.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vpnsite.py
index 32d4fafee..c1b7ff8f8 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vpnsite.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_vpnsite.py
@@ -307,7 +307,7 @@ except ImportError:
class Actions:
- NoAction, Create, Update, Delete = range(4)
+ NoAction, Create, Update, Delete, Update_tags = range(5)
class AzureRMVpnSite(AzureRMModuleBaseExt):
@@ -326,92 +326,71 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
),
virtual_wan=dict(
type='dict',
- disposition='/virtual_wan',
options=dict(
id=dict(
type='str',
- disposition='id'
)
)
),
device_properties=dict(
type='dict',
- disposition='/device_properties',
options=dict(
device_vendor=dict(
type='str',
- disposition='device_vendor'
),
device_model=dict(
type='str',
- disposition='device_model'
),
link_speed_in_mbps=dict(
type='int',
- disposition='link_speed_in_mbps'
)
)
),
ip_address=dict(
type='str',
- disposition='/ip_address'
),
site_key=dict(
type='str',
no_log=True,
- disposition='/site_key'
),
address_space=dict(
type='dict',
- disposition='/address_space',
options=dict(
address_prefixes=dict(
type='list',
- disposition='address_prefixes',
elements='str'
)
)
),
bgp_properties=dict(
type='dict',
- disposition='/bgp_properties',
options=dict(
asn=dict(
type='int',
- disposition='asn'
),
bgp_peering_address=dict(
type='str',
- disposition='bgp_peering_address'
),
peer_weight=dict(
type='int',
- disposition='peer_weight'
),
bgp_peering_addresses=dict(
type='list',
- disposition='bgp_peering_addresses',
elements='dict',
options=dict(
ipconfiguration_id=dict(
type='str',
- disposition='ipconfiguration_id'
),
default_bgp_ip_addresses=dict(
type='list',
- updatable=False,
- disposition='default_bgp_ip_addresses',
elements='str'
),
custom_bgp_ip_addresses=dict(
type='list',
- disposition='custom_bgp_ip_addresses',
elements='str'
),
tunnel_ip_addresses=dict(
type='list',
- updatable=False,
- disposition='tunnel_ip_addresses',
elements='str'
)
)
@@ -420,50 +399,39 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
),
is_security_site=dict(
type='bool',
- disposition='/is_security_site'
),
vpn_site_links=dict(
type='list',
- disposition='/vpn_site_links',
elements='dict',
options=dict(
name=dict(
type='str',
- disposition='name'
),
link_properties=dict(
type='dict',
- disposition='link_properties',
options=dict(
link_provider_name=dict(
type='str',
- disposition='link_provider_name'
),
link_speed_in_mbps=dict(
type='int',
- disposition='link_speed_in_mbps'
)
)
),
ip_address=dict(
type='str',
- disposition='ip_address'
),
fqdn=dict(
type='str',
- disposition='fqdn'
),
bgp_properties=dict(
type='dict',
- disposition='bgp_properties',
options=dict(
asn=dict(
type='int',
- disposition='asn'
),
bgp_peering_address=dict(
type='str',
- disposition='bgp_peering_address'
)
)
)
@@ -471,23 +439,18 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
),
o365_policy=dict(
type='dict',
- disposition='/o365_policy',
options=dict(
break_out_categories=dict(
type='dict',
- disposition='break_out_categories',
options=dict(
allow=dict(
type='bool',
- disposition='allow'
),
optimize=dict(
type='bool',
- disposition='optimize'
),
default=dict(
type='bool',
- disposition='default'
)
)
)
@@ -514,7 +477,7 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
supports_tags=True)
def exec_module(self, **kwargs):
- for key in list(self.module_arg_spec.keys()):
+ for key in list(self.module_arg_spec.keys()) + ['tags']:
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
@@ -540,18 +503,62 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
if self.state == 'absent':
self.to_do = Actions.Delete
else:
- modifiers = {}
- self.create_compare_modifiers(self.module_arg_spec, '', modifiers)
- self.results['modifiers'] = modifiers
- self.results['compare'] = []
- if not self.default_compare(modifiers, self.body, old_response, '', self.results):
+ if self.body.get('virtual_wan') is not None and self.body['virtual_wan'] != old_response.get('virtual_wan'):
self.to_do = Actions.Update
+ for key in self.body.keys():
+ if key == 'address_space':
+ if old_response.get('address_space') is None or\
+ len(self.body['address_space']['address_prefixes']) > len(old_response['address_space']['address_prefixes']) or\
+ not all(key in old_response['address_space']['address_prefixes'] for key in self.body['address_space']['address_prefixes']):
+ self.to_do = Actions.Update
+ elif key == 'device_properties':
+ if old_response.get('device_properties') is None or\
+ not all(self.body['device_properties'][key] == old_response['device_properties'].get(key)
+ for key in self.body['device_properties'].keys()):
+ self.to_do = Actions.Update
+ elif key == 'o365_policy':
+ if old_response.get('o365_policy') is None or\
+ not all(self.body['o365_policy']['break_out_categories'][key] == old_response['o365_policy']['break_out_categories'].get(key)
+ for key in self.body['o365_policy']['break_out_categories'].keys()):
+ self.to_do = Actions.Update
+ elif key == 'vpn_site_links':
+ if old_response.get('vpn_site_links') is None or\
+ not all(self.body['vpn_site_links'][key] == old_response['vpn_site_links'].get(key)
+ for key in self.body['vpn_site_links'].keys()):
+ self.to_do = Actions.Update
+ elif key == 'bgp_properties':
+ if old_response.get('bgp_properties') is None:
+ self.to_do = Actions.Update
+ else:
+ for item in self.body['bgp_properties'].keys():
+ if item != 'bgp_peering_addresses' and item != 'peer_weight':
+ if self.body['bgp_properties'][item] != old_response['bgp_properties'].get(item):
+ self.to_do = Actions.Update
+ else:
+ if self.body['bgp_properties'].get('bgp_peering_addresses') is not None:
+ bgp_address = old_response['bgp_properties']['bgp_peering_addresses']
+ if old_response['bgp_properties'].get('bgp_peering_addresses') is None or\
+ not all(self.body['bgp_properties']['bgp_peering_addresses'][value] == bgp_address.get(value)
+ for value in ['ipconfiguration_id', 'custom_bgp_ip_addresses']):
+ self.to_do = Actions.Update
+
+ elif self.body[key] != old_response.get(key):
+ self.to_do = Actions.Update
+
+ update_tags, self.tags = self.update_tags(old_response.get('tags'))
+ if update_tags:
+ self.to_do = Actions.Update_tags
if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
self.results['changed'] = True
if self.check_mode:
return self.results
response = self.create_update_resource()
+ elif self.to_do == Actions.Update_tags:
+ self.results['changed'] = True
+ if self.check_mode:
+ return self.results
+ response = self.update_resource_tags(dict(tags=self.tags))
elif self.to_do == Actions.Delete:
self.results['changed'] = True
if self.check_mode:
@@ -565,6 +572,18 @@ class AzureRMVpnSite(AzureRMModuleBaseExt):
self.results['state'] = response
return self.results
+ def update_resource_tags(self, tags_parameters):
+ try:
+ response = self.network_client.vpn_sites.update_tags(resource_group_name=self.resource_group,
+ vpn_site_name=self.name,
+ vpn_site_parameters=tags_parameters)
+ if isinstance(response, LROPoller):
+ response = self.get_poller_result(response)
+ except Exception as exc:
+ self.log('Error attempting to update the VpnSite instance tags.')
+ self.fail('Error updating the VpnSite instance: {0}'.format(str(exc)))
+ return response.as_dict()
+
def create_update_resource(self):
try:
response = self.network_client.vpn_sites.begin_create_or_update(resource_group_name=self.resource_group,
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp.py
index 05697176b..e58cbcd43 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp.py
@@ -76,13 +76,13 @@ options:
version:
description:
- Version of the framework. For Linux web app supported value, see U(https://aka.ms/linux-stacks) for more info.
- - C(net_framework) supported value sample, C(v4.0) for .NET 4.6 and C(v3.0) for .NET 3.5.
- - C(php) supported value sample, C(5.5), C(5.6), C(7.0).
- - C(python) supported value sample, C(2.7), C(3.8), C(3.10).
- - C(node) supported value sample, C(6.6), C(6.9).
- - C(dotnetcore) supported value sample, C(1.0), C(1.1), C(1.2).
+ - C(net_framework) supported value sample, C(v4.8) for .NET 4.8 and C(v3.5) for .NET 3.5.
+ - C(php) supported value sample, C(8.1), C(8.2).
+ - C(python) supported value sample, C(3.8), C(3.9), C(3.10), C(3.11), C(3.12).
+ - C(node) supported value sample, C(18), C(20).
+ - C(dotnetcore) supported value sample, C(8), C(7), C(6).
- C(ruby) supported value sample, C(2.3).
- - C(java) supported value sample, C(1.9) for Windows web app. C(1.8) for Linux web app.
+ - C(java) supported value sample, C(21), C(17), C(11) and C(8).
type: str
required: true
settings:
@@ -93,14 +93,14 @@ options:
java_container:
description:
- Name of Java container.
- - Supported only when I(frameworks=java). Sample values C(Tomcat), C(Jetty).
+ - Supported only when I(frameworks=java). Sample values C(Tomcat), C(JavaSE), C(RedHat).
type: str
required: True
java_container_version:
description:
- Version of Java container.
- Supported only when I(frameworks=java).
- - Sample values for C(Tomcat), C(8.0), C(8.5), C(9.0). For C(Jetty,), C(9.1), C(9.3).
+ - Sample values for C(Tomcat), C(8.5), C(9.0), C(10.0), C(10.1).
type: str
required: True
@@ -139,6 +139,11 @@ options:
- Keeps the app loaded even when there's no traffic.
type: bool
+ http20_enabled:
+ description:
+ - Configures a web site to allow clients to connect over HTTP 2.0.
+ type: bool
+
min_tls_version:
description:
- The minimum TLS encryption version required for the app.
@@ -289,7 +294,7 @@ EXAMPLES = '''
testkey: testvalue
frameworks:
- name: "node"
- version: "6.6"
+ version: "18"
- name: Create a windows web app with node, php
azure_rm_webapp:
@@ -302,9 +307,9 @@ EXAMPLES = '''
testkey: testvalue
frameworks:
- name: "node"
- version: 6.6
+ version: 18
- name: "php"
- version: "7.0"
+ version: 8.2
- name: Create a stage deployment slot for an existing web app
azure_rm_webapp:
@@ -494,6 +499,9 @@ class AzureRMWebApps(AzureRMModuleBase):
always_on=dict(
type='bool',
),
+ http20_enabled=dict(
+ type='bool',
+ ),
min_tls_version=dict(
type='str',
choices=['1.0', '1.1', '1.2'],
@@ -583,6 +591,7 @@ class AzureRMWebApps(AzureRMModuleBase):
"python_version",
"scm_type",
"always_on",
+ "http20_enabled",
"min_tls_version",
"ftps_state"]
@@ -591,7 +600,7 @@ class AzureRMWebApps(AzureRMModuleBase):
"https_only"]
self.supported_linux_frameworks = ['ruby', 'php', 'python', 'dotnetcore', 'node', 'java']
- self.supported_windows_frameworks = ['net_framework', 'php', 'python', 'node', 'java']
+ self.supported_windows_frameworks = ['net_framework', 'php', 'python', 'node', 'java', 'dotnetcore']
super(AzureRMWebApps, self).__init__(derived_arg_spec=self.module_arg_spec,
mutually_exclusive=mutually_exclusive,
@@ -605,7 +614,7 @@ class AzureRMWebApps(AzureRMModuleBase):
if hasattr(self, key):
setattr(self, key, kwargs[key])
elif kwargs[key] is not None:
- if key in ["scm_type", "always_on", "min_tls_version", "ftps_state"]:
+ if key in ["scm_type", "always_on", "http20_enabled", "min_tls_version", "ftps_state"]:
self.site_config[key] = kwargs[key]
old_response = None
@@ -655,15 +664,15 @@ class AzureRMWebApps(AzureRMModuleBase):
self.site_config['linux_fx_version'] = (self.frameworks[0]['name'] + '|' + self.frameworks[0]['version']).upper()
if self.frameworks[0]['name'] == 'java':
- if self.frameworks[0]['version'] != '8':
- self.fail("Linux web app only supports java 8.")
+ if self.frameworks[0]['version'] not in ['8', '11', '17', '21']:
+ self.fail("Linux web app only supports java 8, 11, 17 and 21.")
if self.frameworks[0]['settings'] and self.frameworks[0]['settings']['java_container'].lower() != 'tomcat':
self.fail("Linux web app only supports tomcat container.")
if self.frameworks[0]['settings'] and self.frameworks[0]['settings']['java_container'].lower() == 'tomcat':
self.site_config['linux_fx_version'] = 'TOMCAT|' + self.frameworks[0]['settings']['java_container_version'] + '-jre8'
else:
- self.site_config['linux_fx_version'] = 'JAVA|8-jre8'
+ self.site_config['linux_fx_version'] = 'JAVA|{0}-jre{0}'.format(self.frameworks[0]['version'])
else:
for fx in self.frameworks:
if fx.get('name') not in self.supported_windows_frameworks:
@@ -852,7 +861,7 @@ class AzureRMWebApps(AzureRMModuleBase):
# compare xxx_version
def is_site_config_changed(self, existing_config):
for updatable_property in self.site_config_updatable_properties:
- if self.site_config.get(updatable_property):
+ if updatable_property in self.site_config:
if not getattr(existing_config, updatable_property) or \
str(getattr(existing_config, updatable_property)).upper() != str(self.site_config.get(updatable_property)).upper():
return True
diff --git a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp_info.py b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp_info.py
index af035b152..c0ec6b42d 100644
--- a/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp_info.py
+++ b/ansible_collections/azure/azcollection/plugins/modules/azure_rm_webapp_info.py
@@ -138,6 +138,12 @@ webapps:
returned: always
type: bool
sample: true
+ http20_enabled:
+ description:
+ - Configures a web site to allow clients to connect over HTTP 2.0.
+ returned: always
+ type: bool
+ sample: true
min_tls_version:
description:
- The minimum TLS encryption version required for the app.
@@ -486,6 +492,7 @@ class AzureRMWebAppInfo(AzureRMModuleBase):
curated_output['frameworks'].append({'name': tmp[0].lower(), 'version': tmp[1]})
curated_output['always_on'] = configuration.get('always_on')
+ curated_output['http20_enabled'] = configuration.get('http20_enabled')
curated_output['ftps_state'] = configuration.get('ftps_state')
curated_output['min_tls_version'] = configuration.get('min_tls_version')