1#!/usr/local/bin/python3.8
2#
3# Copyright (c) 2017 Zim Kalinowski, <zikalino@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
10
11DOCUMENTATION = '''
12---
13module: azure_rm_appgateway
14version_added: "0.1.2"
15short_description: Manage Application Gateway instance
16description:
17    - Create, update and delete instance of Application Gateway.
18
19options:
20    resource_group:
21        description:
22            - The name of the resource group.
23        required: True
24    name:
25        description:
26            - The name of the application gateway.
27        required: True
28    location:
29        description:
30            - Resource location. If not set, location from the resource group will be used as default.
31    sku:
32        description:
33            - SKU of the application gateway resource.
34        suboptions:
35            name:
36                description:
37                    - Name of an application gateway SKU.
38                choices:
39                    - 'standard_small'
40                    - 'standard_medium'
41                    - 'standard_large'
42                    - 'standard_v2'
43                    - 'waf_medium'
44                    - 'waf_large'
45                    - 'waf_v2'
46            tier:
47                description:
48                    - Tier of an application gateway.
49                choices:
50                    - 'standard'
51                    - 'standard_v2'
52                    - 'waf'
53                    - 'waf_v2'
54            capacity:
55                description:
56                    - Capacity (instance count) of an application gateway.
57    ssl_policy:
58        description:
59            - SSL policy of the application gateway resource.
60        suboptions:
61            disabled_ssl_protocols:
62                description:
63                    - List of SSL protocols to be disabled on application gateway.
64                choices:
65                    - 'tls_v1_0'
66                    - 'tls_v1_1'
67                    - 'tls_v1_2'
68            policy_type:
69                description:
70                    - Type of SSL Policy.
71                choices:
72                    - 'predefined'
73                    - 'custom'
74            policy_name:
75                description:
76                    - Name of Ssl C(predefined) policy.
77                choices:
78                    - 'ssl_policy20150501'
79                    - 'ssl_policy20170401'
80                    - 'ssl_policy20170401_s'
81            cipher_suites:
82                description:
83                    - List of SSL cipher suites to be enabled in the specified order to application gateway.
84                choices:
85                    - tls_ecdhe_rsa_with_aes_256_gcm_sha384
86                    - tls_ecdhe_rsa_with_aes_128_gcm_sha256
87                    - tls_ecdhe_rsa_with_aes_256_cbc_sha384
88                    - tls_ecdhe_rsa_with_aes_128_cbc_sha256
89                    - tls_ecdhe_rsa_with_aes_256_cbc_sha
90                    - tls_ecdhe_rsa_with_aes_128_cbc_sha
91                    - tls_dhe_rsa_with_aes_256_gcm_sha384
92                    - tls_dhe_rsa_with_aes_128_gcm_sha256
93                    - tls_dhe_rsa_with_aes_256_cbc_sha
94                    - tls_dhe_rsa_with_aes_128_cbc_sha
95                    - tls_rsa_with_aes_256_gcm_sha384
96                    - tls_rsa_with_aes_128_gcm_sha256
97                    - tls_rsa_with_aes_256_cbc_sha256
98                    - tls_rsa_with_aes_128_cbc_sha256
99                    - tls_rsa_with_aes_256_cbc_sha
100                    - tls_rsa_with_aes_128_cbc_sha
101                    - tls_ecdhe_ecdsa_with_aes_256_gcm_sha384
102                    - tls_ecdhe_ecdsa_with_aes_128_gcm_sha256
103                    - tls_ecdhe_ecdsa_with_aes_256_cbc_sha384
104                    - tls_ecdhe_ecdsa_with_aes_128_cbc_sha256
105                    - tls_ecdhe_ecdsa_with_aes_256_cbc_sha
106                    - tls_ecdhe_ecdsa_with_aes_128_cbc_sha
107                    - tls_dhe_dss_with_aes_256_cbc_sha256
108                    - tls_dhe_dss_with_aes_128_cbc_sha256
109                    - tls_dhe_dss_with_aes_256_cbc_sha
110                    - tls_dhe_dss_with_aes_128_cbc_sha
111                    - tls_rsa_with_3des_ede_cbc_sha
112                    - tls_dhe_dss_with_3des_ede_cbc_sha
113            min_protocol_version:
114                description:
115                    - Minimum version of Ssl protocol to be supported on application gateway.
116                choices:
117                    - 'tls_v1_0'
118                    - 'tls_v1_1'
119                    - 'tls_v1_2'
120    gateway_ip_configurations:
121        description:
122            - List of subnets used by the application gateway.
123        suboptions:
124            subnet:
125                description:
126                    - Reference of the subnet resource. A subnet from where application gateway gets its private address.
127                suboptions:
128                    id:
129                        description:
130                            - Full ID of the subnet resource. Required if name and virtual_network_name are not provided.
131                    name:
132                        description:
133                            - Name of the subnet. Only used if virtual_network_name is also provided.
134                    virtual_network_name:
135                        description:
136                            - Name of the virtual network. Only used if name is also provided.
137            name:
138                description:
139                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
140    authentication_certificates:
141        description:
142            - Authentication certificates of the application gateway resource.
143        suboptions:
144            data:
145                description:
146                    - Certificate public data - base64 encoded pfx.
147            name:
148                description:
149                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
150    redirect_configurations:
151        description:
152            - Redirect configurations of the application gateway resource.
153        suboptions:
154            redirect_type:
155                description:
156                    - Redirection type.
157                choices:
158                    - 'permanent'
159                    - 'found'
160                    - 'see_other'
161                    - 'temporary'
162            target_listener:
163                description:
164                    - Reference to a listener to redirect the request to.
165            include_path:
166                description:
167                    - Include path in the redirected url.
168            include_query_string:
169                description:
170                    - Include query string in the redirected url.
171            name:
172                description:
173                    - Name of the resource that is unique within a resource group.
174    ssl_certificates:
175        description:
176            - SSL certificates of the application gateway resource.
177        suboptions:
178            data:
179                description:
180                    - Base-64 encoded pfx certificate.
181                    - Only applicable in PUT Request.
182            password:
183                description:
184                    - Password for the pfx file specified in I(data).
185                    - Only applicable in PUT request.
186            name:
187                description:
188                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
189    frontend_ip_configurations:
190        description:
191            - Frontend IP addresses of the application gateway resource.
192        suboptions:
193            private_ip_address:
194                description:
195                    - PrivateIPAddress of the network interface IP Configuration.
196            private_ip_allocation_method:
197                description:
198                    - PrivateIP allocation method.
199                choices:
200                    - 'static'
201                    - 'dynamic'
202            subnet:
203                description:
204                    - Reference of the subnet resource.
205                suboptions:
206                    id:
207                        description:
208                            - Full ID of the subnet resource. Required if name and virtual_network_name are not provided.
209                    name:
210                        description:
211                            - Name of the subnet. Only used if virtual_network_name is also provided.
212                    virtual_network_name:
213                        description:
214                            - Name of the virtual network. Only used if name is also provided.
215            public_ip_address:
216                description:
217                    - Reference of the PublicIP resource.
218            name:
219                description:
220                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
221    frontend_ports:
222        description:
223            - List of frontend ports of the application gateway resource.
224        suboptions:
225            port:
226                description:
227                    - Frontend port.
228            name:
229                description:
230                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
231    backend_address_pools:
232        description:
233            - List of backend address pool of the application gateway resource.
234        suboptions:
235            backend_addresses:
236                description:
237                    - List of backend addresses.
238                suboptions:
239                    fqdn:
240                        description:
241                            - Fully qualified domain name (FQDN).
242                    ip_address:
243                        description:
244                            - IP address.
245            name:
246                description:
247                    - Resource that is unique within a resource group. This name can be used to access the resource.
248    probes:
249        description:
250            - Probes available to the application gateway resource.
251        suboptions:
252            name:
253                description:
254                    - Name of the I(probe) that is unique within an Application Gateway.
255            protocol:
256                description:
257                    - The protocol used for the I(probe).
258                choices:
259                    - 'http'
260                    - 'https'
261            host:
262                description:
263                    - Host name to send the I(probe) to.
264            path:
265                description:
266                    - Relative path of I(probe).
267                    - Valid path starts from '/'.
268                    - Probe is sent to <Protocol>://<host>:<port><path>.
269            timeout:
270                description:
271                    - The probe timeout in seconds.
272                    - Probe marked as failed if valid response is not received with this timeout period.
273                    - Acceptable values are from 1 second to 86400 seconds.
274            interval:
275                description:
276                    - The probing interval in seconds.
277                    - This is the time interval between two consecutive probes.
278                    - Acceptable values are from 1 second to 86400 seconds.
279            unhealthy_threshold:
280                description:
281                    - The I(probe) retry count.
282                    - Backend server is marked down after consecutive probe failure count reaches UnhealthyThreshold.
283                    - Acceptable values are from 1 second to 20.
284            pick_host_name_from_backend_http_settings:
285                description:
286                    - Whether host header should be picked from the host name of the backend HTTP settings. Default value is false.
287                type: bool
288                default: False
289    backend_http_settings_collection:
290        description:
291            - Backend http settings of the application gateway resource.
292        suboptions:
293            probe:
294                description:
295                    - Probe resource of an application gateway.
296            port:
297                description:
298                    - The destination port on the backend.
299            protocol:
300                description:
301                    - The protocol used to communicate with the backend.
302                choices:
303                    - 'http'
304                    - 'https'
305            cookie_based_affinity:
306                description:
307                    - Cookie based affinity.
308                choices:
309                    - 'enabled'
310                    - 'disabled'
311            request_timeout:
312                description:
313                    - Request timeout in seconds.
314                    - Application Gateway will fail the request if response is not received within RequestTimeout.
315                    - Acceptable values are from 1 second to 86400 seconds.
316            authentication_certificates:
317                description:
318                    - List of references to application gateway authentication certificates.
319                    - Applicable only when C(cookie_based_affinity) is enabled, otherwise quietly ignored.
320                suboptions:
321                    id:
322                        description:
323                            - Resource ID.
324            host_name:
325                description:
326                    - Host header to be sent to the backend servers.
327            pick_host_name_from_backend_address:
328                description:
329                    - Whether host header should be picked from the host name of the backend server. Default value is false.
330            affinity_cookie_name:
331                description:
332                    - Cookie name to use for the affinity cookie.
333            path:
334                description:
335                    - Path which should be used as a prefix for all C(http) requests.
336                    - Null means no path will be prefixed. Default value is null.
337            name:
338                description:
339                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
340    http_listeners:
341        description:
342            - List of HTTP listeners of the application gateway resource.
343        suboptions:
344            frontend_ip_configuration:
345                description:
346                    - Frontend IP configuration resource of an application gateway.
347            frontend_port:
348                description:
349                    - Frontend port resource of an application gateway.
350            protocol:
351                description:
352                    - Protocol of the C(http) listener.
353                choices:
354                    - 'http'
355                    - 'https'
356            host_name:
357                description:
358                    - Host name of C(http) listener.
359            ssl_certificate:
360                description:
361                    - SSL certificate resource of an application gateway.
362            require_server_name_indication:
363                description:
364                    - Applicable only if I(protocol) is C(https). Enables SNI for multi-hosting.
365            name:
366                description:
367                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
368    url_path_maps:
369        description:
370            - List of URL path maps of the application gateway resource.
371        suboptions:
372            name:
373                description:
374                    - Name of the resource that is unique within the application gateway. This name can be used to access the resource.
375            default_backend_address_pool:
376                description:
377                    - Backend address pool resource of the application gateway which will be used if no path matches occur.
378            default_backend_http_settings:
379                description:
380                    - Backend http settings resource of the application gateway; used for the I(default_backend_address_pool).
381            path_rules:
382                description:
383                    - List of URL path rules.
384                suboptions:
385                    name:
386                        description:
387                            - Name of the resource that is unique within the path map.
388                    backend_address_pool:
389                        description:
390                            - Backend address pool resource of the application gateway which will be used if the path is matched.
391                    backend_http_settings:
392                        description:
393                            - Backend http settings resource of the application gateway; used for the path's I(backend_address_pool).
394                    paths:
395                        description:
396                            - List of paths.
397    request_routing_rules:
398        description:
399            - List of request routing rules of the application gateway resource.
400        suboptions:
401            rule_type:
402                description:
403                    - Rule type.
404                choices:
405                    - 'basic'
406                    - 'path_based_routing'
407            backend_address_pool:
408                description:
409                    - Backend address pool resource of the application gateway. Not used if I(rule_type) is C(path_based_routing).
410            backend_http_settings:
411                description:
412                    - Backend C(http) settings resource of the application gateway.
413            http_listener:
414                description:
415                    - Http listener resource of the application gateway.
416            name:
417                description:
418                    - Name of the resource that is unique within a resource group. This name can be used to access the resource.
419            redirect_configuration:
420                description:
421                    - Redirect configuration resource of the application gateway.
422            url_path_map:
423                description:
424                    - URL path map resource of the application gateway. Required if I(rule_type) is C(path_based_routing).
425    state:
426        description:
427            - Assert the state of the Public IP. Use C(present) to create or update a and
428              C(absent) to delete.
429        default: present
430        choices:
431            - absent
432            - present
433
434extends_documentation_fragment:
435    - azure.azcollection.azure
436    - azure.azcollection.azure_tags
437
438author:
439    - Zim Kalinowski (@zikalino)
440
441'''
442
443EXAMPLES = '''
444- name: Create instance of Application Gateway
445  azure_rm_appgateway:
446    resource_group: myResourceGroup
447    name: myAppGateway
448    sku:
449      name: standard_small
450      tier: standard
451      capacity: 2
452    gateway_ip_configurations:
453      - subnet:
454          id: "{{ subnet_id }}"
455        name: app_gateway_ip_config
456    frontend_ip_configurations:
457      - subnet:
458          id: "{{ subnet_id }}"
459        name: sample_gateway_frontend_ip_config
460    frontend_ports:
461      - port: 90
462        name: ag_frontend_port
463    backend_address_pools:
464      - backend_addresses:
465          - ip_address: 10.0.0.4
466        name: test_backend_address_pool
467    backend_http_settings_collection:
468      - port: 80
469        protocol: http
470        cookie_based_affinity: enabled
471        name: sample_appgateway_http_settings
472    http_listeners:
473      - frontend_ip_configuration: sample_gateway_frontend_ip_config
474        frontend_port: ag_frontend_port
475        name: sample_http_listener
476    request_routing_rules:
477      - rule_type: Basic
478        backend_address_pool: test_backend_address_pool
479        backend_http_settings: sample_appgateway_http_settings
480        http_listener: sample_http_listener
481        name: rule1
482
483- name: Create instance of Application Gateway by looking up virtual network and subnet
484  azure_rm_appgateway:
485    resource_group: myResourceGroup
486    name: myAppGateway
487    sku:
488      name: standard_small
489      tier: standard
490      capacity: 2
491    gateway_ip_configurations:
492      - subnet:
493          name: default
494          virtual_network_name: my-vnet
495        name: app_gateway_ip_config
496    frontend_ip_configurations:
497      - subnet:
498          name: default
499          virtual_network_name: my-vnet
500        name: sample_gateway_frontend_ip_config
501    frontend_ports:
502      - port: 90
503        name: ag_frontend_port
504    backend_address_pools:
505      - backend_addresses:
506          - ip_address: 10.0.0.4
507        name: test_backend_address_pool
508    backend_http_settings_collection:
509      - port: 80
510        protocol: http
511        cookie_based_affinity: enabled
512        name: sample_appgateway_http_settings
513    http_listeners:
514      - frontend_ip_configuration: sample_gateway_frontend_ip_config
515        frontend_port: ag_frontend_port
516        name: sample_http_listener
517    request_routing_rules:
518      - rule_type: Basic
519        backend_address_pool: test_backend_address_pool
520        backend_http_settings: sample_appgateway_http_settings
521        http_listener: sample_http_listener
522        name: rule1
523
524- name: Create instance of Application Gateway with path based rules
525  azure_rm_appgateway:
526    resource_group: myResourceGroup
527    name: myAppGateway
528    sku:
529      name: standard_small
530      tier: standard
531      capacity: 2
532    gateway_ip_configurations:
533      - subnet:
534          id: "{{ subnet_id }}"
535        name: app_gateway_ip_config
536    frontend_ip_configurations:
537      - subnet:
538          id: "{{ subnet_id }}"
539        name: sample_gateway_frontend_ip_config
540    frontend_ports:
541      - port: 90
542        name: ag_frontend_port
543    backend_address_pools:
544      - backend_addresses:
545          - ip_address: 10.0.0.4
546        name: test_backend_address_pool
547    backend_http_settings_collection:
548      - port: 80
549        protocol: http
550        cookie_based_affinity: enabled
551        name: sample_appgateway_http_settings
552    http_listeners:
553      - frontend_ip_configuration: sample_gateway_frontend_ip_config
554        frontend_port: ag_frontend_port
555        name: sample_http_listener
556    request_routing_rules:
557      - rule_type: path_based_routing
558        http_listener: sample_http_listener
559        name: rule1
560        url_path_map: path_mappings
561    url_path_maps:
562      - name: path_mappings
563        default_backend_address_pool: test_backend_address_pool
564        default_backend_http_settings: sample_appgateway_http_settings
565        path_rules:
566          - name: path_rules
567            backend_address_pool: test_backend_address_pool
568            backend_http_settings: sample_appgateway_http_settings
569            paths:
570              - "/abc"
571              - "/123/*"
572'''
573
574RETURN = '''
575id:
576    description:
577        - Resource ID.
578    returned: always
579    type: str
580    sample: id
581'''
582
583import time
584from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
585from copy import deepcopy
586from ansible.module_utils.common.dict_transformations import (
587    camel_dict_to_snake_dict, snake_dict_to_camel_dict,
588    _camel_to_snake, _snake_to_camel, dict_merge,
589)
590
591try:
592    from msrestazure.azure_exceptions import CloudError
593    from msrest.polling import LROPoller
594    from azure.mgmt.network import NetworkManagementClient
595    from msrest.serialization import Model
596except ImportError:
597    # This is handled in azure_rm_common
598    pass
599
600
601class Actions:
602    NoAction, Create, Update, Delete = range(4)
603
604
605ssl_policy_spec = dict(
606    disabled_ssl_protocols=dict(type='list'),
607    policy_type=dict(type='str', choices=['predefined', 'custom']),
608    policy_name=dict(type='str', choices=['ssl_policy20150501', 'ssl_policy20170401', 'ssl_policy20170401_s']),
609    cipher_suites=dict(type='list'),
610    min_protocol_version=dict(type='str', choices=['tls_v1_0', 'tls_v1_1', 'tls_v1_2'])
611)
612
613
614probe_spec = dict(
615    host=dict(type='str'),
616    interval=dict(type='int'),
617    name=dict(type='str'),
618    path=dict(type='str'),
619    protocol=dict(type='str', choices=['http', 'https']),
620    timeout=dict(type='int'),
621    unhealthy_threshold=dict(type='int'),
622    pick_host_name_from_backend_http_settings=dict(type='bool', default=False)
623)
624
625
626redirect_configuration_spec = dict(
627    include_path=dict(type='bool'),
628    include_query_string=dict(type='bool'),
629    name=dict(type='str'),
630    redirect_type=dict(type='str', choices=['permanent', 'found', 'see_other', 'temporary']),
631    target_listener=dict(type='str')
632)
633
634
635class AzureRMApplicationGateways(AzureRMModuleBase):
636    """Configuration class for an Azure RM Application Gateway resource"""
637
638    def __init__(self):
639        self.module_arg_spec = dict(
640            resource_group=dict(
641                type='str',
642                required=True
643            ),
644            name=dict(
645                type='str',
646                required=True
647            ),
648            location=dict(
649                type='str'
650            ),
651            sku=dict(
652                type='dict'
653            ),
654            ssl_policy=dict(
655                type='dict',
656                options=ssl_policy_spec
657            ),
658            gateway_ip_configurations=dict(
659                type='list'
660            ),
661            authentication_certificates=dict(
662                type='list'
663            ),
664            ssl_certificates=dict(
665                type='list'
666            ),
667            redirect_configurations=dict(
668                type='list',
669                elements='dict',
670                options=redirect_configuration_spec
671            ),
672            frontend_ip_configurations=dict(
673                type='list'
674            ),
675            frontend_ports=dict(
676                type='list'
677            ),
678            backend_address_pools=dict(
679                type='list'
680            ),
681            backend_http_settings_collection=dict(
682                type='list'
683            ),
684            probes=dict(
685                type='list',
686                elements='dict',
687                options=probe_spec
688            ),
689            http_listeners=dict(
690                type='list'
691            ),
692            url_path_maps=dict(
693                type='list'
694            ),
695            request_routing_rules=dict(
696                type='list'
697            ),
698            state=dict(
699                type='str',
700                default='present',
701                choices=['present', 'absent']
702            )
703        )
704
705        self.resource_group = None
706        self.name = None
707        self.parameters = dict()
708
709        self.results = dict(changed=False)
710        self.mgmt_client = None
711        self.state = None
712        self.to_do = Actions.NoAction
713
714        super(AzureRMApplicationGateways, self).__init__(derived_arg_spec=self.module_arg_spec,
715                                                         supports_check_mode=True,
716                                                         supports_tags=True)
717
718    def exec_module(self, **kwargs):
719        """Main module execution method"""
720
721        for key in list(self.module_arg_spec.keys()) + ['tags']:
722            if hasattr(self, key):
723                setattr(self, key, kwargs[key])
724            elif kwargs[key] is not None:
725                if key == "id":
726                    self.parameters["id"] = kwargs[key]
727                elif key == "location":
728                    self.parameters["location"] = kwargs[key]
729                elif key == "sku":
730                    ev = kwargs[key]
731                    if 'name' in ev:
732                        if ev['name'] == 'standard_small':
733                            ev['name'] = 'Standard_Small'
734                        elif ev['name'] == 'standard_medium':
735                            ev['name'] = 'Standard_Medium'
736                        elif ev['name'] == 'standard_large':
737                            ev['name'] = 'Standard_Large'
738                        elif ev['name'] == 'standard_v2':
739                            ev['name'] = 'Standard_v2'
740                        elif ev['name'] == 'waf_medium':
741                            ev['name'] = 'WAF_Medium'
742                        elif ev['name'] == 'waf_large':
743                            ev['name'] = 'WAF_Large'
744                        elif ev['name'] == 'waf_v2':
745                            ev['name'] = 'WAF_v2'
746                    if 'tier' in ev:
747                        if ev['tier'] == 'standard':
748                            ev['tier'] = 'Standard'
749                        if ev['tier'] == 'standard_v2':
750                            ev['tier'] = 'Standard_v2'
751                        elif ev['tier'] == 'waf':
752                            ev['tier'] = 'WAF'
753                        elif ev['tier'] == 'waf_v2':
754                            ev['tier'] = 'WAF_v2'
755                    self.parameters["sku"] = ev
756                elif key == "ssl_policy":
757                    ev = kwargs[key]
758                    if 'policy_type' in ev:
759                        ev['policy_type'] = _snake_to_camel(ev['policy_type'], True)
760                    if 'policy_name' in ev:
761                        if ev['policy_name'] == 'ssl_policy20150501':
762                            ev['policy_name'] = 'AppGwSslPolicy20150501'
763                        elif ev['policy_name'] == 'ssl_policy20170401':
764                            ev['policy_name'] = 'AppGwSslPolicy20170401'
765                        elif ev['policy_name'] == 'ssl_policy20170401_s':
766                            ev['policy_name'] = 'AppGwSslPolicy20170401S'
767                    if 'min_protocol_version' in ev:
768                        if ev['min_protocol_version'] == 'tls_v1_0':
769                            ev['min_protocol_version'] = 'TLSv1_0'
770                        elif ev['min_protocol_version'] == 'tls_v1_1':
771                            ev['min_protocol_version'] = 'TLSv1_1'
772                        elif ev['min_protocol_version'] == 'tls_v1_2':
773                            ev['min_protocol_version'] = 'TLSv1_2'
774                    if 'disabled_ssl_protocols' in ev:
775                        protocols = ev['disabled_ssl_protocols']
776                        if protocols is not None:
777                            for i in range(len(protocols)):
778                                if protocols[i] == 'tls_v1_0':
779                                    protocols[i] = 'TLSv1_0'
780                                elif protocols[i] == 'tls_v1_1':
781                                    protocols[i] = 'TLSv1_1'
782                                elif protocols[i] == 'tls_v1_2':
783                                    protocols[i] = 'TLSv1_2'
784                    if 'cipher_suites' in ev:
785                        suites = ev['cipher_suites']
786                        if suites is not None:
787                            for i in range(len(suites)):
788                                suites[i] = suites[i].upper()
789                elif key == "gateway_ip_configurations":
790                    ev = kwargs[key]
791                    for i in range(len(ev)):
792                        item = ev[i]
793                        if 'subnet' in item and 'name' in item['subnet'] and 'virtual_network_name' in item['subnet']:
794                            id = subnet_id(self.subscription_id,
795                                           kwargs['resource_group'],
796                                           item['subnet']['virtual_network_name'],
797                                           item['subnet']['name'])
798                            item['subnet'] = {'id': id}
799                    self.parameters["gateway_ip_configurations"] = kwargs[key]
800                elif key == "authentication_certificates":
801                    self.parameters["authentication_certificates"] = kwargs[key]
802                elif key == "ssl_certificates":
803                    self.parameters["ssl_certificates"] = kwargs[key]
804                elif key == "redirect_configurations":
805                    ev = kwargs[key]
806                    for i in range(len(ev)):
807                        item = ev[i]
808                        if 'redirect_type' in item:
809                            item['redirect_type'] = _snake_to_camel(item['redirect_type'], True)
810                        if 'target_listener' in item:
811                            id = http_listener_id(self.subscription_id,
812                                                  kwargs['resource_group'],
813                                                  kwargs['name'],
814                                                  item['target_listener'])
815                            item['target_listener'] = {'id': id}
816                    self.parameters["redirect_configurations"] = ev
817                elif key == "frontend_ip_configurations":
818                    ev = kwargs[key]
819                    for i in range(len(ev)):
820                        item = ev[i]
821                        if 'private_ip_allocation_method' in item:
822                            item['private_ip_allocation_method'] = _snake_to_camel(item['private_ip_allocation_method'], True)
823                        if 'public_ip_address' in item:
824                            id = public_ip_id(self.subscription_id,
825                                              kwargs['resource_group'],
826                                              item['public_ip_address'])
827                            item['public_ip_address'] = {'id': id}
828                        if 'subnet' in item and 'name' in item['subnet'] and 'virtual_network_name' in item['subnet']:
829                            id = subnet_id(self.subscription_id,
830                                           kwargs['resource_group'],
831                                           item['subnet']['virtual_network_name'],
832                                           item['subnet']['name'])
833                            item['subnet'] = {'id': id}
834                    self.parameters["frontend_ip_configurations"] = ev
835                elif key == "frontend_ports":
836                    self.parameters["frontend_ports"] = kwargs[key]
837                elif key == "backend_address_pools":
838                    self.parameters["backend_address_pools"] = kwargs[key]
839                elif key == "probes":
840                    ev = kwargs[key]
841                    for i in range(len(ev)):
842                        item = ev[i]
843                        if 'protocol' in item:
844                            item['protocol'] = _snake_to_camel(item['protocol'], True)
845                        if 'pick_host_name_from_backend_http_settings' in item and item['pick_host_name_from_backend_http_settings'] and 'host' in item:
846                            del item['host']
847                    self.parameters["probes"] = ev
848                elif key == "backend_http_settings_collection":
849                    ev = kwargs[key]
850                    for i in range(len(ev)):
851                        item = ev[i]
852                        if 'protocol' in item:
853                            item['protocol'] = _snake_to_camel(item['protocol'], True)
854                        if 'cookie_based_affinity' in item:
855                            item['cookie_based_affinity'] = _snake_to_camel(item['cookie_based_affinity'], True)
856                        if 'probe' in item:
857                            id = probe_id(self.subscription_id,
858                                          kwargs['resource_group'],
859                                          kwargs['name'],
860                                          item['probe'])
861                            item['probe'] = {'id': id}
862                    self.parameters["backend_http_settings_collection"] = ev
863                elif key == "http_listeners":
864                    ev = kwargs[key]
865                    for i in range(len(ev)):
866                        item = ev[i]
867                        if 'frontend_ip_configuration' in item:
868                            id = frontend_ip_configuration_id(self.subscription_id,
869                                                              kwargs['resource_group'],
870                                                              kwargs['name'],
871                                                              item['frontend_ip_configuration'])
872                            item['frontend_ip_configuration'] = {'id': id}
873
874                        if 'frontend_port' in item:
875                            id = frontend_port_id(self.subscription_id,
876                                                  kwargs['resource_group'],
877                                                  kwargs['name'],
878                                                  item['frontend_port'])
879                            item['frontend_port'] = {'id': id}
880                        if 'ssl_certificate' in item:
881                            id = ssl_certificate_id(self.subscription_id,
882                                                    kwargs['resource_group'],
883                                                    kwargs['name'],
884                                                    item['ssl_certificate'])
885                            item['ssl_certificate'] = {'id': id}
886                        if 'protocol' in item:
887                            item['protocol'] = _snake_to_camel(item['protocol'], True)
888                        ev[i] = item
889                    self.parameters["http_listeners"] = ev
890                elif key == "url_path_maps":
891                    ev = kwargs[key]
892                    for i in range(len(ev)):
893                        item = ev[i]
894                        if 'default_backend_address_pool' in item:
895                            id = backend_address_pool_id(self.subscription_id,
896                                                         kwargs['resource_group'],
897                                                         kwargs['name'],
898                                                         item['default_backend_address_pool'])
899                            item['default_backend_address_pool'] = {'id': id}
900                        if 'default_backend_http_settings' in item:
901                            id = backend_http_settings_id(self.subscription_id,
902                                                          kwargs['resource_group'],
903                                                          kwargs['name'],
904                                                          item['default_backend_http_settings'])
905                            item['default_backend_http_settings'] = {'id': id}
906                        if 'path_rules' in item:
907                            ev2 = item['path_rules']
908                            for j in range(len(ev2)):
909                                item2 = ev2[j]
910                                if 'backend_address_pool' in item2:
911                                    id = backend_address_pool_id(self.subscription_id,
912                                                                 kwargs['resource_group'],
913                                                                 kwargs['name'],
914                                                                 item2['backend_address_pool'])
915                                    item2['backend_address_pool'] = {'id': id}
916                                if 'backend_http_settings' in item2:
917                                    id = backend_http_settings_id(self.subscription_id,
918                                                                  kwargs['resource_group'],
919                                                                  kwargs['name'],
920                                                                  item2['backend_http_settings'])
921                                    item2['backend_http_settings'] = {'id': id}
922                                ev2[j] = item2
923                        ev[i] = item
924                    self.parameters["url_path_maps"] = ev
925                elif key == "request_routing_rules":
926                    ev = kwargs[key]
927                    for i in range(len(ev)):
928                        item = ev[i]
929                        if 'rule_type' in item and item['rule_type'] == 'path_based_routing' and 'backend_address_pool' in item:
930                            del item['backend_address_pool']
931                        if 'backend_address_pool' in item:
932                            id = backend_address_pool_id(self.subscription_id,
933                                                         kwargs['resource_group'],
934                                                         kwargs['name'],
935                                                         item['backend_address_pool'])
936                            item['backend_address_pool'] = {'id': id}
937                        if 'backend_http_settings' in item:
938                            id = backend_http_settings_id(self.subscription_id,
939                                                          kwargs['resource_group'],
940                                                          kwargs['name'],
941                                                          item['backend_http_settings'])
942                            item['backend_http_settings'] = {'id': id}
943                        if 'http_listener' in item:
944                            id = http_listener_id(self.subscription_id,
945                                                  kwargs['resource_group'],
946                                                  kwargs['name'],
947                                                  item['http_listener'])
948                            item['http_listener'] = {'id': id}
949                        if 'protocol' in item:
950                            item['protocol'] = _snake_to_camel(item['protocol'], True)
951                        if 'rule_type' in item:
952                            item['rule_type'] = _snake_to_camel(item['rule_type'], True)
953                        if 'redirect_configuration' in item:
954                            id = redirect_configuration_id(self.subscription_id,
955                                                           kwargs['resource_group'],
956                                                           kwargs['name'],
957                                                           item['redirect_configuration'])
958                            item['redirect_configuration'] = {'id': id}
959                        if 'url_path_map' in item:
960                            id = url_path_map_id(self.subscription_id,
961                                                 kwargs['resource_group'],
962                                                 kwargs['name'],
963                                                 item['url_path_map'])
964                            item['url_path_map'] = {'id': id}
965                        ev[i] = item
966                    self.parameters["request_routing_rules"] = ev
967                elif key == "etag":
968                    self.parameters["etag"] = kwargs[key]
969
970        old_response = None
971        response = None
972
973        self.mgmt_client = self.get_mgmt_svc_client(NetworkManagementClient,
974                                                    base_url=self._cloud_environment.endpoints.resource_manager)
975
976        resource_group = self.get_resource_group(self.resource_group)
977
978        if "location" not in self.parameters:
979            self.parameters["location"] = resource_group.location
980
981        old_response = self.get_applicationgateway()
982
983        if not old_response:
984            self.log("Application Gateway instance doesn't exist")
985            if self.state == 'absent':
986                self.log("Old instance didn't exist")
987            else:
988                self.to_do = Actions.Create
989        else:
990            self.log("Application Gateway instance already exists")
991            if self.state == 'absent':
992                self.to_do = Actions.Delete
993            elif self.state == 'present':
994                self.log("Need to check if Application Gateway instance has to be deleted or may be updated")
995                self.to_do = Actions.Update
996
997        if (self.to_do == Actions.Update):
998            if (self.parameters['location'] != old_response['location'] or
999                    self.parameters['sku']['name'] != old_response['sku']['name'] or
1000                    self.parameters['sku']['tier'] != old_response['sku']['tier'] or
1001                    self.parameters['sku']['capacity'] != old_response['sku']['capacity'] or
1002                    not compare_arrays(old_response, self.parameters, 'authentication_certificates') or
1003                    not compare_arrays(old_response, self.parameters, 'gateway_ip_configurations') or
1004                    not compare_arrays(old_response, self.parameters, 'redirect_configurations') or
1005                    not compare_arrays(old_response, self.parameters, 'frontend_ip_configurations') or
1006                    not compare_arrays(old_response, self.parameters, 'frontend_ports') or
1007                    not compare_arrays(old_response, self.parameters, 'backend_address_pools') or
1008                    not compare_arrays(old_response, self.parameters, 'probes') or
1009                    not compare_arrays(old_response, self.parameters, 'backend_http_settings_collection') or
1010                    not compare_arrays(old_response, self.parameters, 'request_routing_rules') or
1011                    not compare_arrays(old_response, self.parameters, 'http_listeners') or
1012                    not compare_arrays(old_response, self.parameters, 'url_path_maps')):
1013
1014                self.to_do = Actions.Update
1015            else:
1016                self.to_do = Actions.NoAction
1017
1018        if (self.to_do == Actions.Create) or (self.to_do == Actions.Update):
1019            self.log("Need to Create / Update the Application Gateway instance")
1020
1021            if self.check_mode:
1022                self.results['changed'] = True
1023                self.results["parameters"] = self.parameters
1024                return self.results
1025
1026            response = self.create_update_applicationgateway()
1027
1028            if not old_response:
1029                self.results['changed'] = True
1030            else:
1031                self.results['changed'] = old_response.__ne__(response)
1032            self.log("Creation / Update done")
1033        elif self.to_do == Actions.Delete:
1034            self.log("Application Gateway instance deleted")
1035            self.results['changed'] = True
1036
1037            if self.check_mode:
1038                return self.results
1039
1040            self.delete_applicationgateway()
1041            # make sure instance is actually deleted, for some Azure resources, instance is hanging around
1042            # for some time after deletion -- this should be really fixed in Azure
1043            while self.get_applicationgateway():
1044                time.sleep(20)
1045        else:
1046            self.log("Application Gateway instance unchanged")
1047            self.results['changed'] = False
1048            response = old_response
1049
1050        if response:
1051            self.results["id"] = response["id"]
1052
1053        return self.results
1054
1055    def create_update_applicationgateway(self):
1056        '''
1057        Creates or updates Application Gateway with the specified configuration.
1058
1059        :return: deserialized Application Gateway instance state dictionary
1060        '''
1061        self.log("Creating / Updating the Application Gateway instance {0}".format(self.name))
1062
1063        try:
1064            response = self.mgmt_client.application_gateways.create_or_update(resource_group_name=self.resource_group,
1065                                                                              application_gateway_name=self.name,
1066                                                                              parameters=self.parameters)
1067            if isinstance(response, LROPoller):
1068                response = self.get_poller_result(response)
1069
1070        except CloudError as exc:
1071            self.log('Error attempting to create the Application Gateway instance.')
1072            self.fail("Error creating the Application Gateway instance: {0}".format(str(exc)))
1073        return response.as_dict()
1074
1075    def delete_applicationgateway(self):
1076        '''
1077        Deletes specified Application Gateway instance in the specified subscription and resource group.
1078
1079        :return: True
1080        '''
1081        self.log("Deleting the Application Gateway instance {0}".format(self.name))
1082        try:
1083            response = self.mgmt_client.application_gateways.delete(resource_group_name=self.resource_group,
1084                                                                    application_gateway_name=self.name)
1085        except CloudError as e:
1086            self.log('Error attempting to delete the Application Gateway instance.')
1087            self.fail("Error deleting the Application Gateway instance: {0}".format(str(e)))
1088
1089        return True
1090
1091    def get_applicationgateway(self):
1092        '''
1093        Gets the properties of the specified Application Gateway.
1094
1095        :return: deserialized Application Gateway instance state dictionary
1096        '''
1097        self.log("Checking if the Application Gateway instance {0} is present".format(self.name))
1098        found = False
1099        try:
1100            response = self.mgmt_client.application_gateways.get(resource_group_name=self.resource_group,
1101                                                                 application_gateway_name=self.name)
1102            found = True
1103            self.log("Response : {0}".format(response))
1104            self.log("Application Gateway instance : {0} found".format(response.name))
1105        except CloudError as e:
1106            self.log('Did not find the Application Gateway instance.')
1107        if found is True:
1108            return response.as_dict()
1109
1110        return False
1111
1112
1113def public_ip_id(subscription_id, resource_group_name, name):
1114    """Generate the id for a frontend ip configuration"""
1115    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/publicIPAddresses/{2}'.format(
1116        subscription_id,
1117        resource_group_name,
1118        name
1119    )
1120
1121
1122def frontend_ip_configuration_id(subscription_id, resource_group_name, appgateway_name, name):
1123    """Generate the id for a frontend ip configuration"""
1124    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/frontendIPConfigurations/{3}'.format(
1125        subscription_id,
1126        resource_group_name,
1127        appgateway_name,
1128        name
1129    )
1130
1131
1132def frontend_port_id(subscription_id, resource_group_name, appgateway_name, name):
1133    """Generate the id for a frontend port"""
1134    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/frontendPorts/{3}'.format(
1135        subscription_id,
1136        resource_group_name,
1137        appgateway_name,
1138        name
1139    )
1140
1141
1142def redirect_configuration_id(subscription_id, resource_group_name, appgateway_name, name):
1143    """Generate the id for a redirect configuration"""
1144    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/redirectConfigurations/{3}'.format(
1145        subscription_id,
1146        resource_group_name,
1147        appgateway_name,
1148        name
1149    )
1150
1151
1152def ssl_certificate_id(subscription_id, resource_group_name, ssl_certificate_name, name):
1153    """Generate the id for a frontend port"""
1154    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/sslCertificates/{3}'.format(
1155        subscription_id,
1156        resource_group_name,
1157        ssl_certificate_name,
1158        name
1159    )
1160
1161
1162def backend_address_pool_id(subscription_id, resource_group_name, appgateway_name, name):
1163    """Generate the id for an address pool"""
1164    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/backendAddressPools/{3}'.format(
1165        subscription_id,
1166        resource_group_name,
1167        appgateway_name,
1168        name
1169    )
1170
1171
1172def probe_id(subscription_id, resource_group_name, appgateway_name, name):
1173    """Generate the id for a probe"""
1174    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/probes/{3}'.format(
1175        subscription_id,
1176        resource_group_name,
1177        appgateway_name,
1178        name
1179    )
1180
1181
1182def backend_http_settings_id(subscription_id, resource_group_name, appgateway_name, name):
1183    """Generate the id for a http settings"""
1184    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/backendHttpSettingsCollection/{3}'.format(
1185        subscription_id,
1186        resource_group_name,
1187        appgateway_name,
1188        name
1189    )
1190
1191
1192def http_listener_id(subscription_id, resource_group_name, appgateway_name, name):
1193    """Generate the id for a http listener"""
1194    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/httpListeners/{3}'.format(
1195        subscription_id,
1196        resource_group_name,
1197        appgateway_name,
1198        name
1199    )
1200
1201
1202def url_path_map_id(subscription_id, resource_group_name, appgateway_name, name):
1203    """Generate the id for a url path map"""
1204    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/applicationGateways/{2}/urlPathMaps/{3}'.format(
1205        subscription_id,
1206        resource_group_name,
1207        appgateway_name,
1208        name
1209    )
1210
1211
1212def subnet_id(subscription_id, resource_group_name, virtual_network_name, name):
1213    """Generate the id for a subnet in a virtual network"""
1214    return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/virtualNetworks/{2}/subnets/{3}'.format(
1215        subscription_id,
1216        resource_group_name,
1217        virtual_network_name,
1218        name
1219    )
1220
1221
1222def compare_arrays(old_params, new_params, param_name):
1223    old = old_params.get(param_name) or []
1224    new = new_params.get(param_name) or []
1225
1226    oldd = array_to_dict(old)
1227    newd = array_to_dict(new)
1228
1229    newd = dict_merge(oldd, newd)
1230    return newd == oldd
1231
1232
1233def array_to_dict(array):
1234    '''Converts list object to dictionary object, including any nested properties on elements.'''
1235    new = {}
1236    for index, item in enumerate(array):
1237        new[index] = deepcopy(item)
1238        if isinstance(item, dict):
1239            for nested in item:
1240                if isinstance(item[nested], list):
1241                    new[index][nested] = array_to_dict(item[nested])
1242    return new
1243
1244
1245def main():
1246    """Main execution"""
1247    AzureRMApplicationGateways()
1248
1249
1250if __name__ == '__main__':
1251    main()
1252