diff --git a/plugins/module_utils/azure_rm_common.py b/plugins/module_utils/azure_rm_common.py index 9c0e6e839..41f3f9bd0 100644 --- a/plugins/module_utils/azure_rm_common.py +++ b/plugins/module_utils/azure_rm_common.py @@ -245,6 +245,7 @@ def default_api_version(self): 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 @@ -415,6 +416,7 @@ def __init__(self, derived_arg_spec, bypass_checks=False, no_log=False, 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 @@ -1174,6 +1176,14 @@ def sql_client(self): is_track2=True) 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') diff --git a/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py b/plugins/modules/azure_rm_postgresqlflexibleconfiguration_info.py new file mode 100644 index 000000000..52810303b --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexibledatabase.py b/plugins/modules/azure_rm_postgresqlflexibledatabase.py new file mode 100644 index 000000000..0656ddac6 --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py b/plugins/modules/azure_rm_postgresqlflexibledatabase_info.py new file mode 100644 index 000000000..545baf53f --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py b/plugins/modules/azure_rm_postgresqlflexiblefirewallrule.py new file mode 100644 index 000000000..c73843c46 --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py b/plugins/modules/azure_rm_postgresqlflexiblefirewallrule_info.py new file mode 100644 index 000000000..14eb029b4 --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexibleserver.py b/plugins/modules/azure_rm_postgresqlflexibleserver.py new file mode 100644 index 000000000..335dc53c8 --- /dev/null +++ b/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/plugins/modules/azure_rm_postgresqlflexibleserver_info.py b/plugins/modules/azure_rm_postgresqlflexibleserver_info.py new file mode 100644 index 000000000..50fe9adc5 --- /dev/null +++ b/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/pr-pipelines.yml b/pr-pipelines.yml index 6f40e066a..44d46de43 100644 --- a/pr-pipelines.yml +++ b/pr-pipelines.yml @@ -85,6 +85,7 @@ parameters: - "azure_rm_notificationhub" - "azure_rm_openshiftmanagedcluster" - "azure_rm_postgresqlserver" + - "azure_rm_postgresqlflexibleserver" - "azure_rm_privatednsrecordset" - "azure_rm_privatednszone" - "azure_rm_privateendpoint" diff --git a/tests/integration/targets/azure_rm_postgresqlflexibleserver/aliases b/tests/integration/targets/azure_rm_postgresqlflexibleserver/aliases new file mode 100644 index 000000000..5d29c6c4d --- /dev/null +++ b/tests/integration/targets/azure_rm_postgresqlflexibleserver/aliases @@ -0,0 +1,3 @@ +cloud/azure +shippable/azure/group10 +destructive diff --git a/tests/integration/targets/azure_rm_postgresqlflexibleserver/meta/main.yml b/tests/integration/targets/azure_rm_postgresqlflexibleserver/meta/main.yml new file mode 100644 index 000000000..95e1952f9 --- /dev/null +++ b/tests/integration/targets/azure_rm_postgresqlflexibleserver/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/tests/integration/targets/azure_rm_postgresqlflexibleserver/tasks/main.yml b/tests/integration/targets/azure_rm_postgresqlflexibleserver/tasks/main.yml new file mode 100644 index 000000000..2add54e1e --- /dev/null +++ b/tests/integration/targets/azure_rm_postgresqlflexibleserver/tasks/main.yml @@ -0,0 +1,356 @@ +- name: Prepare random number + ansible.builtin.set_fact: + rpfx: "{{ resource_group | hash('md5') | truncate(8, True, '') }}" + new_resource_group: "{{ resource_group }}-02" + run_once: true + +- name: Create a new resource group + azure_rm_resourcegroup: + name: "{{ new_resource_group }}" + location: southeastasia + +- name: Create post gresql flexible server (check mode) + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + 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 + network: + public_network_access: Disabled + maintenance_window: + custom_window: Enabled + start_hour: 8 + start_minute: 4 + day_of_week: 3 + availability_zone: 2 + create_mode: Create + check_mode: true + +- name: Create post gresql flexible server + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + 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 + network: + public_network_access: Disabled + maintenance_window: + custom_window: Enabled + start_hour: 8 + start_minute: 4 + day_of_week: 3 + availability_zone: 2 + create_mode: Create + register: output + +- name: Assert the post grep sql server create success + ansible.builtin.assert: + that: + - output.changed + +- name: Create post gresql flexible server (Idempotent Test) + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + 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 + network: + public_network_access: Disabled + maintenance_window: + custom_window: Enabled + start_hour: 8 + start_minute: 4 + day_of_week: 3 + availability_zone: 2 + create_mode: Create + register: output + +- name: Assert the post grep sql server create success + ansible.builtin.assert: + that: + - not output.changed + +- name: Update post gresql flexible server with multiple parameters + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + sku: + name: Standard_B1ms + tier: Burstable + administrator_login: azureuser + administrator_login_password: Fred@0329 + version: 12 + storage: + storage_size_gb: 256 + fully_qualified_domain_name: st-private-dns-zone.postgres.database.azure.com + backup: + backup_retention_days: 7 + geo_redundant_backup: Disabled + network: + public_network_access: Disabled + maintenance_window: + custom_window: Enabled + start_hour: 10 + start_minute: 6 + day_of_week: 6 + availability_zone: 2 + create_mode: Create + tags: + key1: value1 + key2: value2 + register: output + +- name: Assert the post grep sql server update success + ansible.builtin.assert: + that: + - output.changed + +- name: Gather facts postgresql flexible Server + azure_rm_postgresqlflexibleserver_info: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + register: output + +- name: Assert the post gresql server is well created + ansible.builtin.assert: + that: + - output.servers[0].tags | length == 2 + - output.servers[0].storage.storage_size_gb == 256 + - output.servers[0].maintenance_window.custom_window == 'Enabled' + - output.servers[0].maintenance_window.day_of_week == 6 + - output.servers[0].maintenance_window.start_hour == 10 + - output.servers[0].maintenance_window.start_minute == 6 + +- name: Create a post gresql flexible database(check mode) + azure_rm_postgresqlflexibledatabase: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: database{{ rpfx }} + collation: en_US.utf8 + charset: UTF8 + check_mode: true + +- name: Create a post gresql flexible database + azure_rm_postgresqlflexibledatabase: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: database{{ rpfx }} + collation: en_US.utf8 + charset: UTF8 + register: output + +- name: Assert the post gresql flexible database created success + ansible.builtin.assert: + that: + - output.changed + +- name: Create a post gresql flexible database(Idempotent test) + azure_rm_postgresqlflexibledatabase: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: database{{ rpfx }} + collation: en_US.utf8 + charset: UTF8 + register: output + +- name: Assert the post gresql flexible database no changed + ansible.builtin.assert: + that: + - not output.changed + +- name: Get the post gresql flexibe database facts + azure_rm_postgresqlflexibledatabase_info: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: database{{ rpfx }} + register: output + +- name: Assert the post gresql flexible database facts + ansible.builtin.assert: + that: + - output.databases[0].collation == 'en_US.utf8' + - output.databases[0].charset == 'UTF8' + +- name: Delete the post gresql flexibe database + azure_rm_postgresqlflexibledatabase: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: database{{ rpfx }} + state: absent + register: output + +- name: Assert the post gresql flexible database deleted + ansible.builtin.assert: + that: + - output.changed + +- name: Create a post gresql flexible firwall rule (Check mode) + azure_rm_postgresqlflexiblefirewallrule: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + start_ip_address: 10.0.0.15 + end_ip_address: 10.0.0.20 + check_mode: true + +- name: Create the post gresql flexible firwall rule + azure_rm_postgresqlflexiblefirewallrule: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + start_ip_address: 10.0.0.15 + end_ip_address: 10.0.0.20 + register: output + +- name: Assert the post grepsql flexible firewall rule created well + ansible.builtin.assert: + that: + - output.changed + +- name: Create the post gresql flexible firwall rule (Idempotent test) + azure_rm_postgresqlflexiblefirewallrule: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + start_ip_address: 10.0.0.15 + end_ip_address: 10.0.0.20 + register: output + +- name: Assert the post grepsql flexible firewall rule support idempotent test + ansible.builtin.assert: + that: + - not output.changed + +- name: Update the post gresql flexible firwall rule + azure_rm_postgresqlflexiblefirewallrule: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + start_ip_address: 10.0.0.16 + end_ip_address: 10.0.0.18 + register: output + +- name: Assert the post grepsql flexible server update well + ansible.builtin.assert: + that: + - output.changed + +- name: Get the post gresql flexible firwall rule facts + azure_rm_postgresqlflexiblefirewallrule_info: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + register: output + +- name: Assert the post gresql flexible firewall rule facts + ansible.builtin.assert: + that: + - output.firewall_rules[0].start_ip_address == '10.0.0.16' + - output.firewall_rules[0].end_ip_address == '10.0.0.18' + +- name: Delete the post gresql flexible firwall rule + azure_rm_postgresqlflexiblefirewallrule: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + name: firewall{{ rpfx }} + state: absent + register: output + +- name: Assert the post grepsql flexible server delete well + ansible.builtin.assert: + that: + - output.changed + +- name: List the post gresql flexible config facts + azure_rm_postgresqlflexibleconfiguration_info: + resource_group: "{{ new_resource_group }}" + server_name: postflexible{{ rpfx }} + register: output + +- name: Assert the post gresql flexible server configuration + ansible.builtin.assert: + that: + - output.settings | length > 0 + +- name: Stop the post gresql flexible server + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + is_stop: true + register: output + +- name: Assert the post grep sql server stop success + ansible.builtin.assert: + that: + - output.changed + +- name: Pause for 10 mimutes + ansible.builtin.pause: + minutes: 10 + changed_when: true + +- name: Restart post gresql flexible server + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + is_restart: true + register: output + +- name: Assert the post grep sql server restart success + ansible.builtin.assert: + that: + - output.changed + +- name: Delete post gresql flexible server + azure_rm_postgresqlflexibleserver: + resource_group: "{{ new_resource_group }}" + name: postflexible{{ rpfx }} + state: absent + register: output + +- name: Assert the post gresql server is well deleted + ansible.builtin.assert: + that: + - output.changed + +- name: Delete the new resource group + azure_rm_resourcegroup: + name: "{{ new_resource_group }}" + force_delete_nonempty: true + state: absent + register: output + +- name: Assert the resource group is well deleted + ansible.builtin.assert: + that: + - output.changed