#!/usr/bin/python # # Copyright (c) 2016 Matt Davis, # Chris Houseknecht, # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # DOCUMENTATION = ''' --- module: azure_rm_subnet version_added: "2.1" short_description: Manage Azure subnets. description: - Create, update or delete a subnet within a given virtual network. Allows setting and updating the address prefix CIDR, which must be valid within the context of the virtual network. Use the azure_rm_networkinterface module to associate interfaces with the subnet and assign specific IP addresses. options: resource_group: description: - Name of resource group. required: true name: description: - Name of the subnet. required: true address_prefix_cidr: description: - CIDR defining the IPv4 address space of the subnet. Must be valid within the context of the virtual network. required: true aliases: - address_prefix security_group_name: description: - Name of an existing security group with which to associate the subnet. required: false default: null aliases: - security_group state: description: - Assert the state of the subnet. Use 'present' to create or update a subnet and 'absent' to delete a subnet. required: true default: present choices: - absent - present virtual_network_name: description: - Name of an existing virtual network with which the subnet is or will be associated. required: true aliases: - virtual_network extends_documentation_fragment: - azure author: - "Chris Houseknecht (@chouseknecht)" - "Matt Davis (@nitzmahone)" ''' EXAMPLES = ''' - name: Create a subnet azure_rm_subnet: name: foobar virtual_network_name: My_Virtual_Network resource_group: Testing address_prefix_cidr: "10.1.0.0/24" - name: Delete a subnet azure_rm_subnet: name: foobar virtual_network_name: My_Virtual_Network resource_group: Testing state: absent ''' RETURN = ''' state: description: Current state of the subnet. returned: always type: dict sample: { "address_prefix": "10.1.0.0/16", "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/virtualNetworks/My_Virtual_Network/subnets/foobar", "name": "foobar", "network_security_group": { "id": "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/Testing/providers/Microsoft.Network/networkSecurityGroups/secgroupfoo", "name": "secgroupfoo" }, "provisioning_state": "Succeeded" } ''' from ansible.module_utils.basic import * from ansible.module_utils.azure_rm_common import * try: from msrestazure.azure_exceptions import CloudError from azure.mgmt.network.models import Subnet, NetworkSecurityGroup except ImportError: # This is handled in azure_rm_common pass NAME_PATTERN = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9.-_]+[a-zA-Z0-9_]$") def subnet_to_dict(subnet): result = dict( id=subnet.id, name=subnet.name, provisioning_state=subnet.provisioning_state, address_prefix=subnet.address_prefix, network_security_group=dict(), ) if subnet.network_security_group: id_keys = azure_id_to_dict(subnet.network_security_group.id) result['network_security_group']['id'] = subnet.network_security_group.id result['network_security_group']['name'] = id_keys['networkSecurityGroups'] return result class AzureRMSubnet(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']), virtual_network_name=dict(type='str', required=True, aliases=['virtual_network']), address_prefix_cidr=dict(type='str', aliases=['address_prefix']), security_group_name=dict(type='str', aliases=['security_group']), ) required_if = [ ('state', 'present', ['address_prefix_cidr']) ] self.results = dict( changed=False, state=dict() ) self.resource_group = None self.name = None self.state = None self.virtual_etwork_name = None self.address_prefix_cidr = None self.security_group_name = None super(AzureRMSubnet, self).__init__(self.module_arg_spec, supports_check_mode=True, required_if=required_if) def exec_module(self, **kwargs): nsg = None subnet = None for key in self.module_arg_spec: setattr(self, key, kwargs[key]) if not NAME_PATTERN.match(self.name): self.fail("Parameter error: name must begin with a letter or number, end with a letter, number " "or underscore and may contain only letters, numbers, periods, underscores or hyphens.") if self.state == 'present' and not CIDR_PATTERN.match(self.address_prefix_cidr): self.fail("Invalid address_prefix_cidr value {0}".format(self.address_prefix_cidr)) if self.security_group_name: nsg = self.get_security_group(self.security_group_name) results = dict() changed = False try: self.log('Fetching subnet {0}'.format(self.name)) subnet = self.network_client.subnets.get(self.resource_group, self.virtual_network_name, self.name) self.check_provisioning_state(subnet, self.state) results = subnet_to_dict(subnet) if self.state == 'present': if self.address_prefix_cidr: if results['address_prefix'] != self.address_prefix_cidr: self.log("CHANGED: subnet {0} address_prefix_cidr".format(self.name)) changed = True results['address_prefix'] = self.address_prefix_cidr if self.security_group_name: if results['network_security_group'].get('id') != nsg.id: self.log("CHANGED: subnet {0} network security group".format(self.name)) changed = True results['network_security_group']['id'] = nsg.id results['network_security_group']['name'] = nsg.name elif self.state == 'absent': changed = True except CloudError: # the subnet does not exist if self.state == 'present': changed = True self.results['changed'] = changed self.results['state'] = results if not self.check_mode: if self.state == 'present' and changed: if not subnet: # create new subnet self.log('Creating subnet {0}'.format(self.name)) subnet = Subnet( address_prefix=self.address_prefix_cidr ) if nsg: subnet.network_security_group = NetworkSecurityGroup(id=nsg.id, name=nsg.name, location=nsg.location, resource_guid=nsg.resource_guid) else: # update subnet self.log('Updating subnet {0}'.format(self.name)) subnet = Subnet( address_prefix=results['address_prefix'] ) if results['network_security_group'].get('id'): nsg = self.get_security_group(results['network_security_group']['name']) subnet.network_security_group = NetworkSecurityGroup(id=nsg.id, name=nsg.name, location=nsg.location, resource_guid=nsg.resource_guid) self.results['state'] = self.create_or_update_subnet(subnet) elif self.state == 'absent': # delete subnet self.delete_subnet() # the delete does not actually return anything. if no exception, then we'll assume # it worked. self.results['state']['status'] = 'Deleted' return self.results def create_or_update_subnet(self, subnet): try: poller = self.network_client.subnets.create_or_update(self.resource_group, self.virtual_network_name, self.name, subnet) new_subnet = self.get_poller_result(poller) except Exception as exc: self.fail("Error creating or updateing subnet {0} - {1}".format(self.name, str(exc))) self.check_provisioning_state(new_subnet) return subnet_to_dict(new_subnet) def delete_subnet(self): self.log('Deleting subnet {0}'.format(self.name)) try: poller = self.network_client.subnets.delete(self.resource_group, self.virtual_network_name, self.name) result = self.get_poller_results(poller) except Exception as exc: self.fail("Error deleting subnet {0} - {1}".format(self.name, str(exc))) return result def get_security_group(self, name): self.log("Fetching security group {0}".format(name)) nsg = None try: nsg = self.network_client.network_security_groups.get(self.resource_group, name) except Exception as exc: self.fail("Error: fetching network security group {0} - {1}.".format(name, str(exc))) return nsg def main(): AzureRMSubnet() if __name__ == '__main__': main()