1#!/usr/bin/python 2# 3# Copyright (c) 2018 Yunge Zhu <yungez@microsoft.com> 4# 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7from __future__ import absolute_import, division, print_function 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14DOCUMENTATION = ''' 15--- 16module: azure_rm_virtualnetworkpeering 17version_added: "2.8" 18short_description: Manage Azure Virtual Network Peering 19description: 20 - Create, update and delete Azure Virtual Network Peering. 21 22options: 23 resource_group: 24 description: 25 - Name of a resource group where the vnet exists. 26 required: true 27 name: 28 description: 29 - Name of the virtual network peering. 30 required: true 31 virtual_network: 32 description: 33 - Name or resource ID of the virtual network to be peered. 34 required: true 35 remote_virtual_network: 36 description: 37 - Remote virtual network to be peered. 38 - It can be name of remote virtual network in same resource group. 39 - It can be remote virtual network resource ID. 40 - It can be a dict which contains I(name) and I(resource_group) of remote virtual network. 41 - Required when creating. 42 allow_virtual_network_access: 43 description: 44 - Allows VMs in the remote VNet to access all VMs in the local VNet. 45 type: bool 46 default: false 47 allow_forwarded_traffic: 48 description: 49 - Allows forwarded traffic from the VMs in the remote VNet. 50 type: bool 51 default: false 52 use_remote_gateways: 53 description: 54 - If remote gateways can be used on this virtual network. 55 type: bool 56 default: false 57 allow_gateway_transit: 58 description: 59 - Allows VNet to use the remote VNet's gateway. Remote VNet gateway must have --allow-gateway-transit enabled for remote peering. 60 - Only 1 peering can have this flag enabled. Cannot be set if the VNet already has a gateway. 61 type: bool 62 default: false 63 state: 64 description: 65 - State of the virtual network peering. Use C(present) to create or update a peering and C(absent) to delete it. 66 default: present 67 choices: 68 - absent 69 - present 70 71extends_documentation_fragment: 72 - azure 73 74author: 75 - Yunge Zhu (@yungezz) 76''' 77 78EXAMPLES = ''' 79 - name: Create virtual network peering 80 azure_rm_virtualnetworkpeering: 81 resource_group: myResourceGroup 82 virtual_network: myVirtualNetwork 83 name: myPeering 84 remote_virtual_network: 85 resource_group: mySecondResourceGroup 86 name: myRemoteVirtualNetwork 87 allow_virtual_network_access: false 88 allow_forwarded_traffic: true 89 90 - name: Delete the virtual network peering 91 azure_rm_virtualnetworkpeering: 92 resource_group: myResourceGroup 93 virtual_network: myVirtualNetwork 94 name: myPeering 95 state: absent 96''' 97RETURN = ''' 98id: 99 description: 100 - ID of the Azure virtual network peering. 101 returned: always 102 type: str 103 sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Network/virtualNetworks/myVirtualN 104 etwork/virtualNetworkPeerings/myPeering" 105''' 106 107try: 108 from msrestazure.azure_exceptions import CloudError 109 from msrestazure.tools import is_valid_resource_id 110 from msrest.polling import LROPoller 111except ImportError: 112 # This is handled in azure_rm_common 113 pass 114 115from ansible.module_utils.azure_rm_common import AzureRMModuleBase, format_resource_id 116 117 118def virtual_network_to_dict(vnet): 119 ''' 120 Convert a virtual network object to a dict. 121 ''' 122 results = dict( 123 id=vnet.id, 124 name=vnet.name, 125 location=vnet.location, 126 type=vnet.type, 127 tags=vnet.tags, 128 provisioning_state=vnet.provisioning_state, 129 etag=vnet.etag 130 ) 131 if vnet.dhcp_options and len(vnet.dhcp_options.dns_servers) > 0: 132 results['dns_servers'] = [] 133 for server in vnet.dhcp_options.dns_servers: 134 results['dns_servers'].append(server) 135 if vnet.address_space and len(vnet.address_space.address_prefixes) > 0: 136 results['address_prefixes'] = [] 137 for space in vnet.address_space.address_prefixes: 138 results['address_prefixes'].append(space) 139 return results 140 141 142def vnetpeering_to_dict(vnetpeering): 143 ''' 144 Convert a virtual network peering object to a dict. 145 ''' 146 results = dict( 147 id=vnetpeering.id, 148 name=vnetpeering.name, 149 remote_virtual_network=vnetpeering.remote_virtual_network.id, 150 remote_address_space=dict( 151 address_prefixes=vnetpeering.remote_address_space.address_prefixes 152 ), 153 peering_state=vnetpeering.peering_state, 154 provisioning_state=vnetpeering.provisioning_state, 155 use_remote_gateways=vnetpeering.use_remote_gateways, 156 allow_gateway_transit=vnetpeering.allow_gateway_transit, 157 allow_forwarded_traffic=vnetpeering.allow_forwarded_traffic, 158 allow_virtual_network_access=vnetpeering.allow_virtual_network_access, 159 etag=vnetpeering.etag 160 ) 161 return results 162 163 164class AzureRMVirtualNetworkPeering(AzureRMModuleBase): 165 166 def __init__(self): 167 self.module_arg_spec = dict( 168 resource_group=dict( 169 type='str', 170 required=True 171 ), 172 name=dict( 173 type='str', 174 required=True 175 ), 176 virtual_network=dict( 177 type='raw' 178 ), 179 remote_virtual_network=dict( 180 type='raw' 181 ), 182 allow_virtual_network_access=dict( 183 type='bool', 184 default=False 185 ), 186 allow_forwarded_traffic=dict( 187 type='bool', 188 default=False 189 ), 190 allow_gateway_transit=dict( 191 type='bool', 192 default=False 193 ), 194 use_remote_gateways=dict( 195 type='bool', 196 default=False 197 ), 198 state=dict( 199 type='str', 200 default='present', 201 choices=['present', 'absent'] 202 ) 203 ) 204 205 self.resource_group = None 206 self.name = None 207 self.virtual_network = None 208 self.remote_virtual_network = None 209 self.allow_virtual_network_access = None 210 self.allow_forwarded_traffic = None 211 self.allow_gateway_transit = None 212 self.use_remote_gateways = None 213 214 self.results = dict(changed=False) 215 216 super(AzureRMVirtualNetworkPeering, self).__init__(derived_arg_spec=self.module_arg_spec, 217 supports_check_mode=True, 218 supports_tags=False) 219 220 def exec_module(self, **kwargs): 221 """Main module execution method""" 222 223 for key in list(self.module_arg_spec.keys()): 224 setattr(self, key, kwargs[key]) 225 226 to_be_updated = False 227 228 resource_group = self.get_resource_group(self.resource_group) 229 230 # parse virtual_network 231 self.virtual_network = self.parse_resource_to_dict(self.virtual_network) 232 if self.virtual_network['resource_group'] != self.resource_group: 233 self.fail('Resource group of virtual_network is not same as param resource_group') 234 235 # parse remote virtual_network 236 self.remote_virtual_network = self.format_vnet_id(self.remote_virtual_network) 237 238 # get vnet peering 239 response = self.get_vnet_peering() 240 241 if self.state == 'present': 242 if response: 243 # check vnet id not changed 244 existing_vnet = self.parse_resource_to_dict(response['id']) 245 if existing_vnet['resource_group'] != self.virtual_network['resource_group'] or \ 246 existing_vnet['name'] != self.virtual_network['name']: 247 self.fail("Cannot update virtual_network of Virtual Network Peering!") 248 249 # check remote vnet id not changed 250 if response['remote_virtual_network'].lower() != self.remote_virtual_network.lower(): 251 self.fail("Cannot update remote_virtual_network of Virtual Network Peering!") 252 253 # check if update 254 to_be_updated = self.check_update(response) 255 256 else: 257 # not exists, create new vnet peering 258 to_be_updated = True 259 260 # check if vnet exists 261 virtual_network = self.get_vnet(self.virtual_network['resource_group'], self.virtual_network['name']) 262 if not virtual_network: 263 self.fail("Virtual network {0} in resource group {1} does not exist!".format( 264 self.virtual_network['name'], self.virtual_network['resource_group'])) 265 266 elif self.state == 'absent': 267 if response: 268 self.log('Delete Azure Virtual Network Peering') 269 self.results['changed'] = True 270 self.results['id'] = response['id'] 271 272 if self.check_mode: 273 return self.results 274 275 response = self.delete_vnet_peering() 276 277 else: 278 self.fail("Azure Virtual Network Peering {0} does not exist in resource group {1}".format(self.name, self.resource_group)) 279 280 if to_be_updated: 281 self.results['changed'] = True 282 283 if self.check_mode: 284 return self.results 285 286 response = self.create_or_update_vnet_peering() 287 self.results['id'] = response['id'] 288 289 return self.results 290 291 def format_vnet_id(self, vnet): 292 if not vnet: 293 return vnet 294 if isinstance(vnet, dict) and vnet.get('name') and vnet.get('resource_group'): 295 remote_vnet_id = format_resource_id(vnet['name'], 296 self.subscription_id, 297 'Microsoft.Network', 298 'virtualNetworks', 299 vnet['resource_group']) 300 elif isinstance(vnet, str): 301 if is_valid_resource_id(vnet): 302 remote_vnet_id = vnet 303 else: 304 remote_vnet_id = format_resource_id(vnet, 305 self.subscription_id, 306 'Microsoft.Network', 307 'virtualNetworks', 308 self.resource_group) 309 else: 310 self.fail("remote_virtual_network could be a valid resource id, dict of name and resource_group, name of virtual network in same resource group.") 311 return remote_vnet_id 312 313 def check_update(self, exisiting_vnet_peering): 314 if self.allow_forwarded_traffic != exisiting_vnet_peering['allow_forwarded_traffic']: 315 return True 316 if self.allow_gateway_transit != exisiting_vnet_peering['allow_gateway_transit']: 317 return True 318 if self.allow_virtual_network_access != exisiting_vnet_peering['allow_virtual_network_access']: 319 return True 320 if self.use_remote_gateways != exisiting_vnet_peering['use_remote_gateways']: 321 return True 322 return False 323 324 def get_vnet(self, resource_group, vnet_name): 325 ''' 326 Get Azure Virtual Network 327 :return: deserialized Azure Virtual Network 328 ''' 329 self.log("Get the Azure Virtual Network {0}".format(vnet_name)) 330 vnet = self.network_client.virtual_networks.get(resource_group, vnet_name) 331 332 if vnet: 333 results = virtual_network_to_dict(vnet) 334 return results 335 return False 336 337 def create_or_update_vnet_peering(self): 338 ''' 339 Creates or Update Azure Virtual Network Peering. 340 341 :return: deserialized Azure Virtual Network Peering instance state dictionary 342 ''' 343 self.log("Creating or Updating the Azure Virtual Network Peering {0}".format(self.name)) 344 345 vnet_id = format_resource_id(self.virtual_network['name'], 346 self.subscription_id, 347 'Microsoft.Network', 348 'virtualNetworks', 349 self.virtual_network['resource_group']) 350 peering = self.network_models.VirtualNetworkPeering( 351 id=vnet_id, 352 name=self.name, 353 remote_virtual_network=self.network_models.SubResource(id=self.remote_virtual_network), 354 allow_virtual_network_access=self.allow_virtual_network_access, 355 allow_gateway_transit=self.allow_gateway_transit, 356 allow_forwarded_traffic=self.allow_forwarded_traffic, 357 use_remote_gateways=self.use_remote_gateways) 358 359 try: 360 response = self.network_client.virtual_network_peerings.create_or_update(self.resource_group, 361 self.virtual_network['name'], 362 self.name, 363 peering) 364 if isinstance(response, LROPoller): 365 response = self.get_poller_result(response) 366 return vnetpeering_to_dict(response) 367 except CloudError as exc: 368 self.fail("Error creating Azure Virtual Network Peering: {0}.".format(exc.message)) 369 370 def delete_vnet_peering(self): 371 ''' 372 Deletes the specified Azure Virtual Network Peering 373 374 :return: True 375 ''' 376 self.log("Deleting Azure Virtual Network Peering {0}".format(self.name)) 377 try: 378 poller = self.network_client.virtual_network_peerings.delete( 379 self.resource_group, self.virtual_network['name'], self.name) 380 self.get_poller_result(poller) 381 return True 382 except CloudError as e: 383 self.fail("Error deleting the Azure Virtual Network Peering: {0}".format(e.message)) 384 return False 385 386 def get_vnet_peering(self): 387 ''' 388 Gets the Virtual Network Peering. 389 390 :return: deserialized Virtual Network Peering 391 ''' 392 self.log( 393 "Checking if Virtual Network Peering {0} is present".format(self.name)) 394 try: 395 response = self.network_client.virtual_network_peerings.get(self.resource_group, 396 self.virtual_network['name'], 397 self.name) 398 self.log("Response : {0}".format(response)) 399 return vnetpeering_to_dict(response) 400 except CloudError: 401 self.log('Did not find the Virtual Network Peering.') 402 return False 403 404 405def main(): 406 """Main execution""" 407 AzureRMVirtualNetworkPeering() 408 409 410if __name__ == '__main__': 411 main() 412