Skip to content

Commit

Permalink
New modules for webapp vnet connection (#590)
Browse files Browse the repository at this point in the history
* info module for webapp vnetintegration

* add module for configuring vnet connection

* add coverage for webapp vnetconnection module

* add doc/examples

* add delete idempotent test for webpp vnetconnection

* formatting

* formatting and logging

* Apply suggestions from code review

Co-authored-by: Fred-sun <[email protected]>

* rename module for collection consistency

* correct import location and line lengths

* __future__ imports needed at top

* add tests for _info module; add to asserts on main module tests

* add new module to build/test pipeline

* clearer test names

* Apply suggestions from code review

Co-authored-by: Fred-sun <[email protected]>

* Update plugins/modules/azure_rm_webappvnetconnection_info.py

Co-authored-by: Fred-sun <[email protected]>

Co-authored-by: Fred-sun <[email protected]>
  • Loading branch information
l3ender and Fred-sun authored Aug 13, 2021
1 parent 2a8915e commit 45b239d
Show file tree
Hide file tree
Showing 7 changed files with 566 additions and 0 deletions.
8 changes: 8 additions & 0 deletions plugins/module_utils/azure_rm_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,14 @@ def get_data_svc_client(self, **kwags):
config = self.add_user_agent(config)
return ServiceClient(creds=config.credentials, config=config)

def get_subnet_detail(self, subnet_id):
vnet_detail = subnet_id.split('/Microsoft.Network/virtualNetworks/')[1].split('/subnets/')
return dict(
resource_group=subnet_id.split('resourceGroups/')[1].split('/')[0],
vnet_name=vnet_detail[0],
subnet_name=vnet_detail[1],
)

# passthru methods to AzureAuth instance for backcompat
@property
def credentials(self):
Expand Down
259 changes: 259 additions & 0 deletions plugins/modules/azure_rm_webappvnetconnection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
#!/usr/bin/python
#
# Copyright (c) 2021 Ross Bender (@l3ender)
#
# 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_webappvnetconnection
version_added: "1.8.0"
short_description: Manage web app virtual network connection
description:
- Add, remove, or update the virtual network connection for a web app.
options:
name:
description:
- Name of the web app.
required: true
type: str
resource_group:
description:
- Resource group of the web app.
required: true
type: str
state:
description:
- State of the virtual network connection. Use C(present) to create or update and C(absent) to delete.
type: str
default: present
choices:
- absent
- present
vnet_name:
description:
- Name of the virtual network. Required if adding or updating.
type: str
subnet:
description:
- Name of the virtual network's subnet. Required if adding or updating.
type: str
vnet_resource_group:
description:
- Name of the resource group for the virtual network. Defaults to main C(resource_group) value.
type: str
extends_documentation_fragment:
- azure.azcollection.azure
author:
- Ross Bender (@l3ender)
'''

EXAMPLES = '''
- name: Configure web app with virtual network
azure.azcollection.azure_rm_webappvnetconnection:
name: "MyWebapp"
resource_group: "MyResourceGroup"
vnet_name: "MyVnetName"
subnet: "MySubnetName"
- name: Configure web app with virtual network in different resource group
azure.azcollection.azure_rm_webappvnetconnection:
name: "MyWebapp"
resource_group: "MyResourceGroup"
vnet_name: "MyVnetName"
subnet: "MySubnetName"
vnet_resource_group: "MyOtherResourceGroup"
- name: Delete web app virtual network
azure.azcollection.azure_rm_webappvnetconnection:
name: "MyWebapp"
resource_group: "MyResourceGroup"
state: "absent"
'''

RETURN = '''
connection:
description:
- The web app's virtual network connection.
returned: always
type: complex
contains:
id:
description:
- ID of the web app virtual network connection.
returned: always
type: str
sample: /subscriptions/xxx-xxx/resourceGroups/myResourceGroup/providers/Microsoft.Web/sites/myWebApp/virtualNetworkConnections/yyy-yyy_subnet
name:
description:
- Name of the web app virtual network connection.
returned: always
type: str
sample: yyy-yyy_subnet
subnet_name:
description:
- Name of the subnet connected to the web app.
returned: always
type: str
sample: mySubnet
vnet_name:
description:
- Name of the virtual network connected to the web app.
returned: always
type: str
sample: myVnet
vnet_resource_group:
description:
- Name of the resource group the virtual network is in.
returned: always
type: str
sample: myResourceGroup
vnet_resource_id:
description:
- ID of the virtual network/subnet connected to the web app.
returned: always
type: str
sample: /subscriptions/xxx-xxx/resourceGroups/myResourceGroup/providers/Microsoft.Network/virtualNetworks/myVnet/subnets/mySubnet
'''

from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase

try:
from azure.mgmt.web.models import SwiftVirtualNetwork
except Exception:
# This is handled in azure_rm_common
pass


class AzureRMWebAppVnetConnection(AzureRMModuleBase):

def __init__(self):

self.module_arg_spec = dict(
name=dict(type='str', required=True),
resource_group=dict(type='str', required=True),
state=dict(type='str', default='present', choices=['present', 'absent']),
vnet_name=dict(type='str'),
subnet=dict(type='str'),
vnet_resource_group=dict(type='str'),
)

self.results = dict(
changed=False,
connection=dict(),
)

self.state = None
self.name = None
self.resource_group = None
self.vnet_name = None
self.subnet = None
self.vnet_resource_group = None

super(AzureRMWebAppVnetConnection, 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])

changed = False
vnet = self.get_vnet_connection()
if vnet:
self.results['connection'] = self.set_results(vnet)

if self.state == 'absent' and vnet:
changed = True
if not self.check_mode:
self.log('Deleting vnet connection for webapp {0}'.format(self.name))
self.delete_vnet_connection()
self.results['connection'] = dict()
elif self.state == 'present':
self.vnet_resource_group = self.vnet_resource_group or self.resource_group

if not vnet:
self.log('Adding vnet connection for webapp {0}'.format(self.name))
changed = True
else:
subnet_detail = self.get_subnet_detail(vnet.vnet_resource_id)
if (subnet_detail['resource_group'] != self.vnet_resource_group
or subnet_detail['vnet_name'] != self.vnet_name
or subnet_detail['subnet_name'] != self.subnet):
self.log('Detected change in vnet connection for webapp {0}'.format(self.name))
changed = True

if changed:
if not self.check_mode:
self.log('Updating vnet connection for webapp {0}'.format(self.name))
subnet = self.get_subnet()
param = SwiftVirtualNetwork(subnet_resource_id=subnet.id)
self.create_or_update_vnet_connection(param)
vnet = self.get_vnet_connection()
self.results['connection'] = self.set_results(vnet)

self.results['changed'] = changed
return self.results

def get_vnet_connection(self):
connections = self.list_vnet_connections()
for connection in connections:
if connection.is_swift:
return connection

return None

def list_vnet_connections(self):
try:
return self.web_client.web_apps.list_vnet_connections(resource_group_name=self.resource_group, name=self.name)
except Exception as exc:
self.fail("Error getting webapp vnet connections {0} (rg={1}) - {2}".format(self.name, self.resource_group, str(exc)))

def delete_vnet_connection(self):
try:
return self.web_client.web_apps.delete_swift_virtual_network(resource_group_name=self.resource_group, name=self.name)
except Exception as exc:
self.fail("Error deleting webapp vnet connection {0} (rg={1}) - {3}".format(self.name, self.resource_group, str(exc)))

def create_or_update_vnet_connection(self, vnet):
try:
return self.web_client.web_apps.create_or_update_swift_virtual_network_connection(
resource_group_name=self.resource_group, name=self.name, connection_envelope=vnet)
except Exception as exc:
self.fail("Error creating/updating webapp vnet connection {0} (vnet={1}, rg={2}) - {3}".format(
self.name, self.vnet_name, self.resource_group, str(exc)))

def get_subnet(self):
try:
return self.network_client.subnets.get(resource_group_name=self.vnet_resource_group, virtual_network_name=self.vnet_name, subnet_name=self.subnet)
except Exception as exc:
self.fail("Error getting subnet {0} in vnet={1} (rg={2}) - {3}".format(self.subnet, self.vnet_name, self.vnet_resource_group, str(exc)))

def set_results(self, vnet):
vnet_dict = vnet.as_dict()

output = dict()
output['id'] = vnet_dict['id']
output['name'] = vnet_dict['name']
subnet_id = vnet_dict.get('subnet_resource_id', vnet_dict.get('vnet_resource_id'))
output['vnet_resource_id'] = subnet_id
subnet_detail = self.get_subnet_detail(subnet_id)
output['vnet_resource_group'] = subnet_detail['resource_group']
output['vnet_name'] = subnet_detail['vnet_name']
output['subnet_name'] = subnet_detail['subnet_name']

return output


def main():
AzureRMWebAppVnetConnection()


if __name__ == '__main__':
main()
Loading

0 comments on commit 45b239d

Please sign in to comment.