From 869d5d60882e6a197efe9709ded8d4a993065151 Mon Sep 17 00:00:00 2001 From: 1101-1 <70093559+1101-1@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:29:26 +0500 Subject: [PATCH] [azure][feat] Add new resources for collect to existing base resources (#2127) --- .../azure/fix_plugin_azure/azure_client.py | 2 +- .../azure/fix_plugin_azure/resource/base.py | 64 +- .../fix_plugin_azure/resource/compute.py | 1 - .../resource/containerservice.py | 1 - .../resource/microsoft_graph.py | 7 +- .../fix_plugin_azure/resource/network.py | 731 +++++++++++++++++- .../fix_plugin_azure/resource/storage.py | 1 - plugins/azure/test/collector_test.py | 22 +- plugins/azure/test/conftest.py | 52 +- plugins/azure/test/files/graph/devices.json | 13 + plugins/azure/test/files/graph/groups.json | 72 ++ .../test/files/graph/roleDefinitions.json | 91 +++ .../test/files/graph/serviceprincipals.json | 41 + plugins/azure/test/files/graph/users.json | 33 + .../azure/test/files/network/connections.json | 106 +++ .../azure/test/files/network/dnszones.json | 42 + .../files/network/localNetworkGateways.json | 38 + plugins/azure/test/files/network/probes.json | 24 + .../azure/test/files/network/recordsets.json | 59 ++ .../azure/test/files/network/routeTables.json | 34 + .../files/network/virtualNetworkGateways.json | 213 +++++ .../test/files/network/vpnConnections.json | 97 +++ plugins/azure/test/microsoft_graph_test.py | 34 + plugins/azure/test/network_test.py | 16 +- 24 files changed, 1733 insertions(+), 61 deletions(-) create mode 100644 plugins/azure/test/files/graph/devices.json create mode 100644 plugins/azure/test/files/graph/groups.json create mode 100644 plugins/azure/test/files/graph/roleDefinitions.json create mode 100644 plugins/azure/test/files/graph/serviceprincipals.json create mode 100644 plugins/azure/test/files/graph/users.json create mode 100644 plugins/azure/test/files/network/connections.json create mode 100644 plugins/azure/test/files/network/dnszones.json create mode 100644 plugins/azure/test/files/network/localNetworkGateways.json create mode 100644 plugins/azure/test/files/network/probes.json create mode 100644 plugins/azure/test/files/network/recordsets.json create mode 100644 plugins/azure/test/files/network/routeTables.json create mode 100644 plugins/azure/test/files/network/virtualNetworkGateways.json create mode 100644 plugins/azure/test/files/network/vpnConnections.json create mode 100644 plugins/azure/test/microsoft_graph_test.py diff --git a/plugins/azure/fix_plugin_azure/azure_client.py b/plugins/azure/fix_plugin_azure/azure_client.py index fec2e41f38..064369d849 100644 --- a/plugins/azure/fix_plugin_azure/azure_client.py +++ b/plugins/azure/fix_plugin_azure/azure_client.py @@ -62,8 +62,8 @@ def is_retryable_exception(e: Exception) -> bool: @define class AzureResourceSpec: service: str - version: str path: str + version: str path_parameters: List[str] = [] query_parameters: List[str] = [] access_path: Optional[str] = None diff --git a/plugins/azure/fix_plugin_azure/resource/base.py b/plugins/azure/fix_plugin_azure/resource/base.py index c9067194da..8cb1b22fc9 100644 --- a/plugins/azure/fix_plugin_azure/resource/base.py +++ b/plugins/azure/fix_plugin_azure/resource/base.py @@ -10,7 +10,15 @@ from fix_plugin_azure.azure_client import AzureResourceSpec, MicrosoftClient, MicrosoftRestSpec from fix_plugin_azure.config import AzureConfig -from fixlib.baseresources import BaseGroup, BaseResource, Cloud, EdgeType, BaseAccount, BaseRegion, ModelReference +from fixlib.baseresources import ( + BaseGroup, + BaseResource, + Cloud, + EdgeType, + BaseAccount, + BaseRegion, + ModelReference, +) from fixlib.config import current_config from fixlib.core.actions import CoreFeedback from fixlib.graph import Graph, EdgeKey @@ -324,7 +332,7 @@ def collect_resources_in_group() -> None: resources_api_spec = AzureResourceSpec( service="resources", version="2021-04-01", - path="/subscriptions/{subscriptionId}/resourceGroups/" + f"{self.safe_name}/resources", + path=f"{self.id}/resources", path_parameters=["subscriptionId"], query_parameters=["api-version"], access_path="value", @@ -333,7 +341,55 @@ def collect_resources_in_group() -> None: self._resource_ids_in_group = [r["id"] for r in graph_builder.client.list(resources_api_spec)] + def collect_network_gateways() -> None: + from fix_plugin_azure.resource.network import AzureVirtualNetworkGateway + + api_spec = AzureResourceSpec( + service="network", + version="2023-09-01", + path=f"{self.id}/providers/Microsoft.Network/virtualNetworkGateways", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + AzureVirtualNetworkGateway.collect(items, graph_builder) + + def collect_local_network_gateway() -> None: + from fix_plugin_azure.resource.network import AzureLocalNetworkGateway + + api_spec = AzureResourceSpec( + service="network", + version="2023-09-01", + path=f"{self.id}/providers/Microsoft.Network/localNetworkGateways", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + AzureLocalNetworkGateway.collect(items, graph_builder) + + def collect_network_gateway_connections() -> None: + from fix_plugin_azure.resource.network import AzureVirtualNetworkGatewayConnection + + api_spec = AzureResourceSpec( + service="network", + version="2023-09-01", + path=f"{self.id}/providers/Microsoft.Network/connections", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + AzureVirtualNetworkGatewayConnection.collect(items, graph_builder) + graph_builder.submit_work(service_name, collect_resources_in_group) + graph_builder.submit_work(service_name, collect_network_gateways) + graph_builder.submit_work(service_name, collect_local_network_gateway) + graph_builder.submit_work(service_name, collect_network_gateway_connections) def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: if resource_ids := self._resource_ids_in_group: @@ -740,4 +796,6 @@ def with_location(self, location: BaseRegion) -> GraphBuilder: ) -resources: List[Type[MicrosoftResource]] = [AzureResourceGroup] +resources: List[Type[MicrosoftResource]] = [ + AzureResourceGroup, +] diff --git a/plugins/azure/fix_plugin_azure/resource/compute.py b/plugins/azure/fix_plugin_azure/resource/compute.py index 0d772abff4..269efbbb40 100644 --- a/plugins/azure/fix_plugin_azure/resource/compute.py +++ b/plugins/azure/fix_plugin_azure/resource/compute.py @@ -15,7 +15,6 @@ AzureExtendedLocation, AzurePrincipalClient, AzurePrivateLinkServiceConnectionState, - MicrosoftResource, ) from fix_plugin_azure.resource.metrics import AzureMetricData, AzureMetricQuery, update_resource_metrics from fix_plugin_azure.resource.network import ( diff --git a/plugins/azure/fix_plugin_azure/resource/containerservice.py b/plugins/azure/fix_plugin_azure/resource/containerservice.py index 29e98489da..5474ad5049 100644 --- a/plugins/azure/fix_plugin_azure/resource/containerservice.py +++ b/plugins/azure/fix_plugin_azure/resource/containerservice.py @@ -13,7 +13,6 @@ AzureUserAssignedIdentity, AzurePrincipalClient, AzureManagedServiceIdentity, - MicrosoftResource, ) from fixlib.baseresources import BaseManagedKubernetesClusterProvider, BaseSnapshot, EdgeType, ModelReference from fixlib.json_bender import Bender, S, Bend, ForallBend diff --git a/plugins/azure/fix_plugin_azure/resource/microsoft_graph.py b/plugins/azure/fix_plugin_azure/resource/microsoft_graph.py index a403a298c5..2ade694462 100644 --- a/plugins/azure/fix_plugin_azure/resource/microsoft_graph.py +++ b/plugins/azure/fix_plugin_azure/resource/microsoft_graph.py @@ -7,7 +7,7 @@ from fix_plugin_azure.azure_client import RestApiSpec, MicrosoftRestSpec from fix_plugin_azure.resource.base import GraphBuilder, MicrosoftResource -from fixlib.baseresources import BaseRole, BaseAccount, BaseRegion, ModelReference +from fixlib.baseresources import BaseGroup, BaseRole, BaseAccount, BaseRegion, ModelReference, BaseUser from fixlib.json_bender import Bender, S, ForallBend, Bend, F, MapDict from fixlib.types import Json @@ -573,6 +573,7 @@ class MicrosoftGraphServicePrincipal(MicrosoftGraphEntity): "sign_in_audience": S("signInAudience"), "token_encryption_key_id": S("tokenEncryptionKeyId"), "verified_publisher": S("verifiedPublisher") >> Bend(MicrosoftGraphVerifiedPublisher.mapping), + "access_key_status": S("disabledByMicrosoftStatus"), } account_enabled: Optional[bool] = field(default=None, metadata={'description': 'true if the service principal account is enabled; otherwise, false. If set to false, then no users are able to sign in to this app, even if they re assigned to it. Supports $filter (eq, ne, not, in).'}) # fmt: skip add_ins: Optional[List[MicrosoftGraphAddIn]] = field(default=None, metadata={'description': 'Defines custom behavior that a consuming service can use to call an app in specific contexts. For example, applications that can render file streams may set the addIns property for its FileHandler functionality. This lets services like Microsoft 365 call the application in the context of a document the user is working on.'}) # fmt: skip @@ -717,7 +718,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: @define(eq=False, slots=False) -class MicrosoftGraphUser(MicrosoftGraphEntity): +class MicrosoftGraphUser(MicrosoftGraphEntity, BaseUser): kind: ClassVar[str] = "microsoft_graph_user" api_spec: ClassVar[MicrosoftRestSpec] = RestApiSpec( "graph", @@ -884,7 +885,7 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: @define(eq=False, slots=False) -class MicrosoftGraphGroup(MicrosoftGraphEntity): +class MicrosoftGraphGroup(MicrosoftGraphEntity, BaseGroup): kind: ClassVar[str] = "microsoft_graph_group" api_spec: ClassVar[MicrosoftRestSpec] = RestApiSpec( "graph", diff --git a/plugins/azure/fix_plugin_azure/resource/network.py b/plugins/azure/fix_plugin_azure/resource/network.py index 719c3774de..8b8f004151 100644 --- a/plugins/azure/fix_plugin_azure/resource/network.py +++ b/plugins/azure/fix_plugin_azure/resource/network.py @@ -13,22 +13,26 @@ AzureExtendedLocation, AzurePrivateLinkServiceConnectionState, AzureManagedServiceIdentity, - MicrosoftResource, ) from fix_plugin_azure.resource.containerservice import AzureManagedCluster from fix_plugin_azure.utils import rgetattr from fixlib.baseresources import ( + BaseDNSRecordSet, + BaseDNSZone, BaseGateway, BaseFirewall, BaseIPAddress, BaseNetworkInterface, + BaseHealthCheck, BasePolicy, BaseLoadBalancer, BaseNetwork, BaseNetworkQuota, BasePeeringConnection, + BaseRoutingTable, BaseSecurityGroup, BaseSubnet, + BaseTunnel, ModelReference, EdgeType, ) @@ -1832,8 +1836,17 @@ class AzureRoute(AzureSubResource): @define(eq=False, slots=False) -class AzureRouteTable: +class AzureRouteTable(MicrosoftResource, BaseRoutingTable): kind: ClassVar[str] = "azure_route_table" + api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec( + service="network", + version="2023-09-01", + path="/subscriptions/{subscriptionId}/providers/Microsoft.Network/routeTables", + path_parameters=["subscriptionId"], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) mapping: ClassVar[Dict[str, Bender]] = { "disable_bgp_route_propagation": S("properties", "disableBgpRoutePropagation"), "etag": S("etag"), @@ -1845,16 +1858,14 @@ class AzureRouteTable: "routes": S("properties", "routes") >> ForallBend(AzureRoute.mapping), "tags": S("tags", default={}), "type": S("type"), + "subnets": S("properties", "subnets") >> ForallBend(S("id")), } + subnets: Optional[List[str]] = field(default=None, metadata={'description': 'A collection of references to subnets.'}) # fmt: skip disable_bgp_route_propagation: Optional[bool] = field(default=None, metadata={'description': 'Whether to disable the routes learned by BGP on that route table. True means disable.'}) # fmt: skip etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip - id: Optional[str] = field(default=None, metadata={"description": "Resource ID."}) location: Optional[str] = field(default=None, metadata={"description": "Resource location."}) - name: Optional[str] = field(default=None, metadata={"description": "Resource name."}) - provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the route table.'}) # fmt: skip routes: Optional[List[AzureRoute]] = field(default=None, metadata={'description': 'Collection of routes contained within a route table.'}) # fmt: skip - tags: Optional[Dict[str, str]] = field(default=None, metadata={"description": "Resource tags."}) type: Optional[str] = field(default=None, metadata={"description": "Resource type."}) @@ -2176,6 +2187,7 @@ class AzureSubnet(MicrosoftResource, BaseSubnet): "default": [ "azure_nat_gateway", "azure_network_security_group", + "azure_route_table", ] }, } @@ -2203,7 +2215,7 @@ class AzureSubnet(MicrosoftResource, BaseSubnet): "purpose": S("properties", "purpose"), "resource_navigation_links": S("properties", "resourceNavigationLinks") >> ForallBend(AzureResourceNavigationLink.mapping), - "route_table": S("properties", "routeTable") >> Bend(AzureRouteTable.mapping), + "_route_table_id": S("properties", "routeTable") >> Bend(S("id")), "service_association_links": S("properties", "serviceAssociationLinks") >> ForallBend(AzureServiceAssociationLink.mapping), "service_endpoint_policies": S("properties", "serviceEndpointPolicies") @@ -2227,7 +2239,7 @@ class AzureSubnet(MicrosoftResource, BaseSubnet): private_link_service_network_policies: Optional[str] = field(default=None, metadata={'description': 'Enable or Disable apply network policies on private link service in the subnet.'}) # fmt: skip purpose: Optional[str] = field(default=None, metadata={'description': 'A read-only string identifying the intention of use for this subnet based on delegations and other user-defined properties.'}) # fmt: skip resource_navigation_links: Optional[List[AzureResourceNavigationLink]] = field(default=None, metadata={'description': 'An array of references to the external resources using subnet.'}) # fmt: skip - route_table: Optional[AzureRouteTable] = field(default=None, metadata={"description": "Route table resource."}) + _route_table_id: Optional[str] = field(default=None, metadata={"description": "Route table resource."}) service_association_links: Optional[List[AzureServiceAssociationLink]] = field(default=None, metadata={'description': 'An array of references to services injecting into this subnet.'}) # fmt: skip service_endpoint_policies: Optional[List[AzureServiceEndpointPolicy]] = field(default=None, metadata={'description': 'An array of service endpoint policies.'}) # fmt: skip service_endpoints: Optional[List[AzureServiceEndpointPropertiesFormat]] = field(default=None, metadata={'description': 'An array of service endpoints.'}) # fmt: skip @@ -2238,6 +2250,8 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureNatGateway, id=nat_gateway_id) if nsg_id := self._network_security_group_id: builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureNetworkSecurityGroup, id=nsg_id) + if route_table_id := self._route_table_id: + builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureRouteTable, id=route_table_id) @define(eq=False, slots=False) @@ -3646,6 +3660,38 @@ def _get_virtual_network_ips_and_ids(self, builder: GraphBuilder) -> List[Tuple[ ] +@define(eq=False, slots=False) +class AzureLoadBalancerProbe(MicrosoftResource, BaseHealthCheck): + kind: ClassVar[str] = "azure_load_balancer_probe" + # Collect via AzureLoadBalancer + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("id"), + "tags": S("tags", default={}), + "name": S("name"), + "etag": S("etag"), + "interval_in_seconds": S("properties", "intervalInSeconds"), + "load_balancing_rules_ids": S("properties") >> S("loadBalancingRules", default=[]) >> ForallBend(S("id")), + "no_healthy_backends_behavior": S("properties", "NoHealthyBackendsBehavior"), + "number_of_probes": S("properties", "numberOfProbes"), + "port": S("properties", "port"), + "probe_threshold": S("properties", "probeThreshold"), + "protocol": S("properties", "protocol"), + "provisioning_state": S("properties", "provisioningState"), + "request_path": S("properties", "requestPath"), + "check_interval": S("properties", "intervalInSeconds"), + "healthy_threshold": S("properties", "probeThreshold"), + "unhealthy_threshold": S("properties", "numberOfProbes"), + } + interval_in_seconds: Optional[int] = field(default=None, metadata={'description': 'The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5.'}) # fmt: skip + load_balancing_rules_ids: Optional[List[str]] = field(default=None, metadata={'description': 'The load balancer rules that use this probe.'}) # fmt: skip + no_healthy_backends_behavior: Optional[str] = field(default=None, metadata={'description': 'Determines how new connections are handled by the load balancer when all backend instances are probed down.'}) # fmt: skip + number_of_probes: Optional[int] = field(default=None, metadata={'description': 'The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure.'}) # fmt: skip + port: Optional[int] = field(default=None, metadata={'description': 'The port for communicating the probe. Possible values range from 1 to 65535, inclusive.'}) # fmt: skip + probe_threshold: Optional[int] = field(default=None, metadata={'description': 'The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation.'}) # fmt: skip + protocol: Optional[str] = field(default=None, metadata={'description': 'The protocol of the end point. If Tcp is specified, a received ACK is required for the probe to be successful. If Http or Https is specified, a 200 OK response from the specifies URI is required for the probe to be successful.'}) # fmt: skip + request_path: Optional[str] = field(default=None, metadata={'description': 'The URI used for requesting health status from the VM. Path is required if a protocol is set to http. Otherwise, it is not allowed. There is no default value.'}) # fmt: skip + + @define(eq=False, slots=False) class AzureGatewayLoadBalancerTunnelInterface: kind: ClassVar[str] = "azure_gateway_load_balancer_tunnel_interface" @@ -3881,6 +3927,7 @@ class AzureLoadBalancer(MicrosoftResource, BaseLoadBalancer): ) reference_kinds: ClassVar[ModelReference] = { "predecessors": {"default": ["azure_virtual_network", "azure_subnet", "azure_managed_cluster"]}, + "successors": {"default": ["azure_load_balancer_probe"]}, } mapping: ClassVar[Dict[str, Bender]] = { "id": S("id"), @@ -3895,7 +3942,7 @@ class AzureLoadBalancer(MicrosoftResource, BaseLoadBalancer): "inbound_nat_rules": S("properties", "inboundNatRules") >> ForallBend(AzureInboundNatRule.mapping), "load_balancing_rules": S("properties", "loadBalancingRules") >> ForallBend(AzureLoadBalancingRule.mapping), "outbound_rules": S("properties", "outboundRules") >> ForallBend(AzureOutboundRule.mapping), - "lb_probes": S("properties", "probes") >> ForallBend(AzureProbe.mapping), + "_lb_probes_id": S("properties", "probes") >> ForallBend(S("id")), "provisioning_state": S("properties", "provisioningState"), "resource_guid": S("properties", "resourceGuid"), "azure_sku": S("sku") >> Bend(AzureSku.mapping), @@ -3913,11 +3960,31 @@ class AzureLoadBalancer(MicrosoftResource, BaseLoadBalancer): outbound_rules: Optional[List[AzureOutboundRule]] = field( default=None, metadata={"description": "The outbound rules."} ) - lb_probes: Optional[List[AzureProbe]] = field(default=None, metadata={'description': 'Collection of probe objects used in the load balancer.'}) # fmt: skip + _lb_probes_id: Optional[List[str]] = field(default=None, metadata={'description': 'Collection of probe objects used in the load balancer.'}) # fmt: skip resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the load balancer resource.'}) # fmt: skip azure_sku: Optional[AzureSku] = field(default=None, metadata={"description": "SKU of a load balancer."}) aks_public_ip_address: Optional[str] = field(default=None, metadata={"description": "AKS Load Balancer public IP address."}) # fmt: skip + def post_process(self, graph_builder: GraphBuilder, source: Json) -> None: + def collect_lb_probes() -> None: + api_spec = AzureResourceSpec( + service="network", + version="2024-01-01", + path=f"{self.id}/probes", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + + lb_probes = AzureLoadBalancerProbe.collect(items, graph_builder) + + for lb_probe in lb_probes: + graph_builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureLoadBalancerProbe, id=lb_probe.id) + + graph_builder.submit_work(service_name, collect_lb_probes) + def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: if vns := self.backends: for vn_id in vns: @@ -4714,7 +4781,9 @@ class AzureVirtualHub(MicrosoftResource): expect_array=True, ) reference_kinds: ClassVar[ModelReference] = { - "predecessors": {"default": ["azure_express_route_gateway", "azure_vpn_gateway", "azure_virtual_wan"]}, + "predecessors": { + "default": ["azure_express_route_gateway", "azure_virtual_wan_vpn_gateway", "azure_virtual_wan"] + }, "successors": {"default": ["azure_public_ip_address"]}, } mapping: ClassVar[Dict[str, Bender]] = { @@ -4778,7 +4847,9 @@ def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: self, edge_type=EdgeType.default, reverse=True, clazz=AzureExpressRouteGateway, id=er_gateway_id ) if vpn_gateway_id := self.vpn_gateway: - builder.add_edge(self, edge_type=EdgeType.default, reverse=True, clazz=AzureVpnGateway, id=vpn_gateway_id) + builder.add_edge( + self, edge_type=EdgeType.default, reverse=True, clazz=AzureVirtualWANVpnGateway, id=vpn_gateway_id + ) if vw_id := self.virtual_wan: builder.add_edge(self, edge_type=EdgeType.default, reverse=True, clazz=AzureVirtualWAN, id=vw_id) if ip_config_ids := self.ip_configuration_ids: @@ -5122,9 +5193,11 @@ class AzureVpnSiteLinkConnection(AzureSubResource): @define(eq=False, slots=False) -class AzureVpnConnection(AzureSubResource): - kind: ClassVar[str] = "azure_vpn_connection" - mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { +class AzureVirtualWANVpnConnection(MicrosoftResource, BaseTunnel): + kind: ClassVar[str] = "azure_virtual_wan_vpn_connection" + # Collect via AzureVirtualWANVpnGateway + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("id"), "connection_bandwidth": S("properties", "connectionBandwidth"), "connection_status": S("properties", "connectionStatus"), "dpd_timeout_seconds": S("properties", "dpdTimeoutSeconds"), @@ -5135,6 +5208,7 @@ class AzureVpnConnection(AzureSubResource): "etag": S("etag"), "ingress_bytes_transferred": S("properties", "ingressBytesTransferred"), "ipsec_policies": S("properties", "ipsecPolicies") >> ForallBend(AzureIpsecPolicy.mapping), + "tags": S("tags", default={}), "name": S("name"), "provisioning_state": S("properties", "provisioningState"), "remote_vpn_site": S("properties", "remoteVpnSite", "id"), @@ -5158,8 +5232,6 @@ class AzureVpnConnection(AzureSubResource): etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip ingress_bytes_transferred: Optional[int] = field(default=None, metadata={'description': 'Ingress bytes transferred.'}) # fmt: skip ipsec_policies: Optional[List[AzureIpsecPolicy]] = field(default=None, metadata={'description': 'The IPSec Policies to be considered by this connection.'}) # fmt: skip - name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip - provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip remote_vpn_site: Optional[str] = field(default=None, metadata={"description": "Reference to another subresource."}) routing_configuration: Optional[AzureRoutingConfiguration] = field(default=None, metadata={'description': 'Routing Configuration indicating the associated and propagated route tables for this connection.'}) # fmt: skip routing_weight: Optional[int] = field(default=None, metadata={"description": "Routing weight for vpn connection."}) @@ -5254,8 +5326,8 @@ class AzureVpnGatewayNatRule(AzureSubResource): @define(eq=False, slots=False) -class AzureVpnGateway(MicrosoftResource, BaseGateway): - kind: ClassVar[str] = "azure_vpn_gateway" +class AzureVirtualWANVpnGateway(MicrosoftResource, BaseGateway): + kind: ClassVar[str] = "azure_virtual_wan_vpn_gateway" api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec( service="network", version="2023-05-01", @@ -5265,31 +5337,58 @@ class AzureVpnGateway(MicrosoftResource, BaseGateway): access_path="value", expect_array=True, ) + reference_kinds: ClassVar[ModelReference] = { + "successors": {"default": ["azure_virtual_wan_vpn_connection"]}, + } mapping: ClassVar[Dict[str, Bender]] = { "id": S("id"), "tags": S("tags", default={}), "name": S("name"), "bgp_settings": S("properties", "bgpSettings") >> Bend(AzureBgpSettings.mapping), - "connections": S("properties", "connections") >> ForallBend(AzureVpnConnection.mapping), + "_connections_ids": S("properties", "connections") >> ForallBend(S("id")), "enable_bgp_route_translation_for_nat": S("properties", "enableBgpRouteTranslationForNat"), "etag": S("etag"), "gateway_ip_configurations": S("properties", "ipConfigurations") >> ForallBend(AzureVpnGatewayIpConfiguration.mapping), "is_routing_preference_internet": S("properties", "isRoutingPreferenceInternet"), - "nat_rules": S("properties", "natRules") >> ForallBend(AzureVpnGatewayNatRule.mapping), + "wan_gateway_nat_rules": S("properties", "natRules") >> ForallBend(AzureVpnGatewayNatRule.mapping), "provisioning_state": S("properties", "provisioningState"), "virtual_hub": S("properties", "virtualHub", "id"), "vpn_gateway_scale_unit": S("properties", "vpnGatewayScaleUnit"), } bgp_settings: Optional[AzureBgpSettings] = field(default=None, metadata={"description": "BGP settings details."}) - connections: Optional[List[AzureVpnConnection]] = field(default=None, metadata={'description': 'List of all vpn connections to the gateway.'}) # fmt: skip + _connections_ids: Optional[List[str]] = field(default=None, metadata={'description': 'List of all vpn connections to the gateway.'}) # fmt: skip enable_bgp_route_translation_for_nat: Optional[bool] = field(default=None, metadata={'description': 'Enable BGP routes translation for NAT on this VpnGateway.'}) # fmt: skip gateway_ip_configurations: Optional[List[AzureVpnGatewayIpConfiguration]] = field(default=None, metadata={'description': 'List of all IPs configured on the gateway.'}) # fmt: skip is_routing_preference_internet: Optional[bool] = field(default=None, metadata={'description': 'Enable Routing Preference property for the Public IP Interface of the VpnGateway.'}) # fmt: skip - nat_rules: Optional[List[AzureVpnGatewayNatRule]] = field(default=None, metadata={'description': 'List of all the nat Rules associated with the gateway.'}) # fmt: skip + wan_gateway_nat_rules: Optional[List[AzureVpnGatewayNatRule]] = field(default=None, metadata={'description': 'List of all the nat Rules associated with the gateway.'}) # fmt: skip virtual_hub: Optional[str] = field(default=None, metadata={"description": "Reference to another subresource."}) vpn_gateway_scale_unit: Optional[int] = field(default=None, metadata={'description': 'The scale unit for this vpn gateway.'}) # fmt: skip + def post_process(self, graph_builder: GraphBuilder, source: Json) -> None: + def collect_vpn_connections() -> None: + api_spec = AzureResourceSpec( + service="network", + version="2023-09-01", + path=f"{self.id}/vpnConnections", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + vpn_connections = AzureVirtualWANVpnConnection.collect(items, graph_builder) + + for vpn_connection in vpn_connections: + graph_builder.add_edge( + self, + edge_type=EdgeType.default, + clazz=AzureVirtualWANVpnConnection, + id=vpn_connection.id, + ) + + graph_builder.submit_work(service_name, collect_vpn_connections) + @define(eq=False, slots=False) class AzureVpnServerConfigVpnClientRootCertificate: @@ -5756,6 +5855,582 @@ class AzureWebApplicationFirewallPolicy(MicrosoftResource): resource_state: Optional[str] = field(default=None, metadata={"description": "Resource status of the policy."}) +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayAutoScaleBounds: + kind: ClassVar[str] = "azure_virtual_network_gateway_auto_scale_bounds" + mapping: ClassVar[Dict[str, Bender]] = {"max": S("max"), "min": S("min")} + max: Optional[int] = field(default=None, metadata={'description': 'Maximum Scale Units for Autoscale configuration'}) # fmt: skip + min: Optional[int] = field(default=None, metadata={'description': 'Minimum scale Units for Autoscale configuration'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayAutoScaleConfiguration: + kind: ClassVar[str] = "azure_virtual_network_gateway_auto_scale_configuration" + mapping: ClassVar[Dict[str, Bender]] = { + "bounds": S("bounds") >> Bend(AzureVirtualNetworkGatewayAutoScaleBounds.mapping) + } + bounds: Optional[AzureVirtualNetworkGatewayAutoScaleBounds] = field(default=None, metadata={"description": ""}) + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayIPConfiguration(AzureSubResource): + kind: ClassVar[str] = "azure_virtual_network_gateway_ip_configuration" + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "etag": S("etag"), + "name": S("name"), + "private_ip_address": S("properties", "privateIPAddress"), + "private_ip_allocation_method": S("properties", "privateIPAllocationMethod"), + "provisioning_state": S("properties", "provisioningState"), + "public_ip_address": S("properties", "publicIPAddress", "id"), + "subnet": S("properties", "subnet", "id"), + } + etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip + private_ip_address: Optional[str] = field(default=None, metadata={'description': 'Private IP Address for this gateway.'}) # fmt: skip + private_ip_allocation_method: Optional[str] = field(default=None, metadata={'description': 'IP address allocation method.'}) # fmt: skip + provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip + public_ip_address: Optional[str] = field(default=None, metadata={'description': 'Reference to another subresource.'}) # fmt: skip + subnet: Optional[str] = field(default=None, metadata={"description": "Reference to another subresource."}) + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewaySku: + kind: ClassVar[str] = "azure_virtual_network_gateway_sku" + mapping: ClassVar[Dict[str, Bender]] = {"capacity": S("capacity"), "name": S("name"), "tier": S("tier")} + capacity: Optional[int] = field(default=None, metadata={"description": "The capacity."}) + name: Optional[str] = field(default=None, metadata={"description": "Gateway SKU name."}) + tier: Optional[str] = field(default=None, metadata={"description": "Gateway SKU tier."}) + + +@define(eq=False, slots=False) +class AzureVpnClientRootCertificate(AzureSubResource): + kind: ClassVar[str] = "azure_vpn_client_root_certificate" + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "etag": S("etag"), + "name": S("name"), + "provisioning_state": S("properties", "provisioningState"), + "public_cert_data": S("properties", "publicCertData"), + } + etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip + provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip + public_cert_data: Optional[str] = field(default=None, metadata={"description": "The certificate public data."}) + + +@define(eq=False, slots=False) +class AzureVpnClientRevokedCertificate(AzureSubResource): + kind: ClassVar[str] = "azure_vpn_client_revoked_certificate" + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "etag": S("etag"), + "name": S("name"), + "provisioning_state": S("properties", "provisioningState"), + "thumbprint": S("properties", "thumbprint"), + } + etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip + provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip + thumbprint: Optional[str] = field(default=None, metadata={'description': 'The revoked VPN client certificate thumbprint.'}) # fmt: skip + + +define(eq=False, slots=False) + + +class AzureVngClientConnectionConfiguration(AzureSubResource): + kind: ClassVar[str] = "azure_vng_client_connection_configuration" + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "etag": S("etag"), + "name": S("name"), + "provisioning_state": S("properties", "provisioningState"), + "virtual_network_gateway_policy_groups": S("properties") + >> S("virtualNetworkGatewayPolicyGroups", default=[]) + >> ForallBend(S("id")), + "vpn_client_address_pool": S("properties", "vpnClientAddressPool") >> Bend(AzureAddressSpace.mapping), + } + etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip + provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip + virtual_network_gateway_policy_groups: Optional[List[str]] = field(default=None, metadata={'description': 'List of references to virtualNetworkGatewayPolicyGroups'}) # fmt: skip + vpn_client_address_pool: Optional[AzureAddressSpace] = field(default=None, metadata={'description': 'AddressSpace contains an array of IP address ranges that can be used by subnets of the virtual network.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureVpnClientConfiguration: + kind: ClassVar[str] = "azure_vpn_client_configuration" + mapping: ClassVar[Dict[str, Bender]] = { + "aad_audience": S("aadAudience"), + "aad_issuer": S("aadIssuer"), + "aad_tenant": S("aadTenant"), + "radius_server_address": S("radiusServerAddress"), + "radius_server_secret": S("radiusServerSecret"), + "radius_servers": S("radiusServers") >> ForallBend(AzureRadiusServer.mapping), + "vng_client_connection_configurations": S("vngClientConnectionConfigurations") + >> ForallBend(AzureVngClientConnectionConfiguration.mapping), + "vpn_authentication_types": S("vpnAuthenticationTypes"), + "vpn_client_address_pool": S("vpnClientAddressPool") >> Bend(AzureAddressSpace.mapping), + "vpn_client_ipsec_policies": S("vpnClientIpsecPolicies") >> ForallBend(AzureIpsecPolicy.mapping), + "vpn_client_protocols": S("vpnClientProtocols"), + "vpn_client_revoked_certificates": S("vpnClientRevokedCertificates") + >> ForallBend(AzureVpnClientRevokedCertificate.mapping), + "vpn_client_root_certificates": S("vpnClientRootCertificates") + >> ForallBend(AzureVpnClientRootCertificate.mapping), + } + aad_audience: Optional[str] = field(default=None, metadata={'description': 'The AADAudience property of the VirtualNetworkGateway resource for vpn client connection used for AAD authentication.'}) # fmt: skip + aad_issuer: Optional[str] = field(default=None, metadata={'description': 'The AADIssuer property of the VirtualNetworkGateway resource for vpn client connection used for AAD authentication.'}) # fmt: skip + aad_tenant: Optional[str] = field(default=None, metadata={'description': 'The AADTenant property of the VirtualNetworkGateway resource for vpn client connection used for AAD authentication.'}) # fmt: skip + radius_server_address: Optional[str] = field(default=None, metadata={'description': 'The radius server address property of the VirtualNetworkGateway resource for vpn client connection.'}) # fmt: skip + radius_server_secret: Optional[str] = field(default=None, metadata={'description': 'The radius secret property of the VirtualNetworkGateway resource for vpn client connection.'}) # fmt: skip + radius_servers: Optional[List[AzureRadiusServer]] = field(default=None, metadata={'description': 'The radiusServers property for multiple radius server configuration.'}) # fmt: skip + vng_client_connection_configurations: Optional[List[AzureVngClientConnectionConfiguration]] = field(default=None, metadata={'description': 'per ip address pool connection policy for virtual network gateway P2S client.'}) # fmt: skip + vpn_authentication_types: Optional[List[str]] = field(default=None, metadata={'description': 'VPN authentication types for the virtual network gateway..'}) # fmt: skip + vpn_client_address_pool: Optional[AzureAddressSpace] = field(default=None, metadata={'description': 'AddressSpace contains an array of IP address ranges that can be used by subnets of the virtual network.'}) # fmt: skip + vpn_client_ipsec_policies: Optional[List[AzureIpsecPolicy]] = field(default=None, metadata={'description': 'VpnClientIpsecPolicies for virtual network gateway P2S client.'}) # fmt: skip + vpn_client_protocols: Optional[List[str]] = field(default=None, metadata={'description': 'VpnClientProtocols for Virtual network gateway.'}) # fmt: skip + vpn_client_revoked_certificates: Optional[List[AzureVpnClientRevokedCertificate]] = field(default=None, metadata={'description': 'VpnClientRevokedCertificate for Virtual network gateway.'}) # fmt: skip + vpn_client_root_certificates: Optional[List[AzureVpnClientRootCertificate]] = field(default=None, metadata={'description': 'VpnClientRootCertificate for virtual network gateway.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayPolicyGroupMember: + kind: ClassVar[str] = "azure_virtual_network_gateway_policy_group_member" + mapping: ClassVar[Dict[str, Bender]] = { + "attribute_type": S("attributeType"), + "attribute_value": S("attributeValue"), + "name": S("name"), + } + attribute_type: Optional[str] = field(default=None, metadata={'description': 'The Vpn Policy member attribute type.'}) # fmt: skip + attribute_value: Optional[str] = field(default=None, metadata={'description': 'The value of Attribute used for this VirtualNetworkGatewayPolicyGroupMember.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'Name of the VirtualNetworkGatewayPolicyGroupMember.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayPolicyGroup(AzureSubResource): + kind: ClassVar[str] = "azure_virtual_network_gateway_policy_group" + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "etag": S("etag"), + "is_default": S("properties", "isDefault"), + "name": S("name"), + "policy_members": S("properties", "policyMembers") + >> ForallBend(AzureVirtualNetworkGatewayPolicyGroupMember.mapping), + "priority": S("properties", "priority"), + "provisioning_state": S("properties", "provisioningState"), + "vng_client_connection_configurations": S("properties") + >> S("vngClientConnectionConfigurations", default=[]) + >> ForallBend(S("id")), + } + etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip + is_default: Optional[bool] = field(default=None, metadata={'description': 'Shows if this is a Default VirtualNetworkGatewayPolicyGroup or not.'}) # fmt: skip + name: Optional[str] = field(default=None, metadata={'description': 'The name of the resource that is unique within a resource group. This name can be used to access the resource.'}) # fmt: skip + policy_members: Optional[List[AzureVirtualNetworkGatewayPolicyGroupMember]] = field(default=None, metadata={'description': 'Multiple PolicyMembers for VirtualNetworkGatewayPolicyGroup.'}) # fmt: skip + priority: Optional[int] = field(default=None, metadata={'description': 'Priority for VirtualNetworkGatewayPolicyGroup.'}) # fmt: skip + provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip + vng_client_connection_configurations: Optional[List[str]] = field(default=None, metadata={'description': 'List of references to vngClientConnectionConfigurations.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayNatRule(AzureSubResource): + kind: ClassVar[str] = "azure_virtual_network_gateway_nat_rule" + api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec( + service="network", + version="2024-01-01", + path="/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Network/virtualNetworkGateways/{virtualNetworkGatewayName}/natRules", + path_parameters=["subscriptionId", "resourceGroupName", "virtualNetworkGatewayName"], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + mapping: ClassVar[Dict[str, Bender]] = AzureSubResource.mapping | { + "tags": S("tags", default={}), + "name": S("name"), + "etag": S("etag"), + "external_mappings": S("properties", "externalMappings") >> ForallBend(AzureVpnNatRuleMapping.mapping), + "internal_mappings": S("properties", "internalMappings") >> ForallBend(AzureVpnNatRuleMapping.mapping), + "ip_configuration_id": S("properties", "ipConfigurationId"), + "mode": S("properties", "mode"), + "provisioning_state": S("properties", "provisioningState"), + } + external_mappings: Optional[List[AzureVpnNatRuleMapping]] = field(default=None, metadata={'description': 'The private IP address external mapping for NAT.'}) # fmt: skip + internal_mappings: Optional[List[AzureVpnNatRuleMapping]] = field(default=None, metadata={'description': 'The private IP address internal mapping for NAT.'}) # fmt: skip + ip_configuration_id: Optional[str] = field(default=None, metadata={'description': 'The IP Configuration ID this NAT rule applies to.'}) # fmt: skip + mode: Optional[str] = field(default=None, metadata={"description": "The Source NAT direction of a VPN NAT."}) + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGateway(MicrosoftResource, BaseGateway): + kind: ClassVar[str] = "azure_virtual_network_gateway" + # Collect via AzureResourceGroup + mapping: ClassVar[Dict[str, Bender]] = { + "active_active": S("properties", "activeActive"), + "admin_state": S("properties", "adminState"), + "allow_remote_vnet_traffic": S("properties", "allowRemoteVnetTraffic"), + "allow_virtual_wan_traffic": S("properties", "allowVirtualWanTraffic"), + "gateway_auto_scale_configuration": S("properties", "autoScaleConfiguration") + >> Bend(AzureVirtualNetworkGatewayAutoScaleConfiguration.mapping), + "bgp_settings": S("properties", "bgpSettings") >> Bend(AzureBgpSettings.mapping), + "custom_routes": S("properties", "customRoutes") >> Bend(AzureAddressSpace.mapping), + "disable_ip_sec_replay_protection": S("properties", "disableIPSecReplayProtection"), + "enable_bgp": S("properties", "enableBgp"), + "enable_bgp_route_translation_for_nat": S("properties", "enableBgpRouteTranslationForNat"), + "enable_dns_forwarding": S("properties", "enableDnsForwarding"), + "enable_private_ip_address": S("properties", "enablePrivateIpAddress"), + "etag": S("etag"), + "extended_location": S("extendedLocation") >> Bend(AzureExtendedLocation.mapping), + "gateway_default_site": S("properties", "gatewayDefaultSite", "id"), + "gateway_type": S("properties", "gatewayType"), + "id": S("id"), + "identity": S("identity") >> Bend(AzureManagedServiceIdentity.mapping), + "inbound_dns_forwarding_endpoint": S("properties", "inboundDnsForwardingEndpoint"), + "ip_configurations": S("properties", "ipConfigurations") + >> ForallBend(AzureVirtualNetworkGatewayIPConfiguration.mapping), + "location": S("location"), + "name": S("name"), + "gateway_nat_rules": S("properties", "natRules") >> ForallBend(AzureVirtualNetworkGatewayNatRule.mapping), + "provisioning_state": S("properties", "provisioningState"), + "resource_guid": S("properties", "resourceGuid"), + "network_gateway_sku": S("properties", "sku") >> Bend(AzureVirtualNetworkGatewaySku.mapping), + "tags": S("tags", default={}), + "type": S("type"), + "v_net_extended_location_resource_id": S("properties", "vNetExtendedLocationResourceId"), + "virtual_network_gateway_policy_groups": S("properties", "virtualNetworkGatewayPolicyGroups") + >> ForallBend(AzureVirtualNetworkGatewayPolicyGroup.mapping), + "vpn_client_configuration": S("properties", "vpnClientConfiguration") + >> Bend(AzureVpnClientConfiguration.mapping), + "vpn_gateway_generation": S("properties", "vpnGatewayGeneration"), + "vpn_type": S("properties", "vpnType"), + } + active_active: Optional[bool] = field(default=None, metadata={"description": "ActiveActive flag."}) + admin_state: Optional[str] = field(default=None, metadata={'description': 'Property to indicate if the Express Route Gateway serves traffic when there are multiple Express Route Gateways in the vnet'}) # fmt: skip + allow_remote_vnet_traffic: Optional[bool] = field(default=None, metadata={'description': 'Configure this gateway to accept traffic from other Azure Virtual Networks. This configuration does not support connectivity to Azure Virtual WAN.'}) # fmt: skip + allow_virtual_wan_traffic: Optional[bool] = field(default=None, metadata={'description': 'Configures this gateway to accept traffic from remote Virtual WAN networks.'}) # fmt: skip + gateway_auto_scale_configuration: Optional[AzureVirtualNetworkGatewayAutoScaleConfiguration] = field(default=None, metadata={'description': 'Virtual Network Gateway Autoscale Configuration details'}) # fmt: skip + bgp_settings: Optional[AzureBgpSettings] = field(default=None, metadata={"description": "BGP settings details."}) + custom_routes: Optional[AzureAddressSpace] = field(default=None, metadata={'description': 'AddressSpace contains an array of IP address ranges that can be used by subnets of the virtual network.'}) # fmt: skip + disable_ip_sec_replay_protection: Optional[bool] = field(default=None, metadata={'description': 'disableIPSecReplayProtection flag.'}) # fmt: skip + enable_bgp: Optional[bool] = field(default=None, metadata={'description': 'Whether BGP is enabled for this virtual network gateway or not.'}) # fmt: skip + enable_bgp_route_translation_for_nat: Optional[bool] = field(default=None, metadata={'description': 'EnableBgpRouteTranslationForNat flag.'}) # fmt: skip + enable_dns_forwarding: Optional[bool] = field(default=None, metadata={'description': 'Whether dns forwarding is enabled or not.'}) # fmt: skip + enable_private_ip_address: Optional[bool] = field(default=None, metadata={'description': 'Whether private IP needs to be enabled on this gateway for connections or not.'}) # fmt: skip + extended_location: Optional[AzureExtendedLocation] = field(default=None, metadata={'description': 'ExtendedLocation complex type.'}) # fmt: skip + gateway_default_site: Optional[str] = field(default=None, metadata={'description': 'Reference to another subresource.'}) # fmt: skip + gateway_type: Optional[str] = field(default=None, metadata={'description': 'The type of this virtual network gateway.'}) # fmt: skip + identity: Optional[AzureManagedServiceIdentity] = field(default=None, metadata={'description': 'Identity for the resource.'}) # fmt: skip + inbound_dns_forwarding_endpoint: Optional[str] = field(default=None, metadata={'description': 'The IP address allocated by the gateway to which dns requests can be sent.'}) # fmt: skip + ip_configurations: Optional[List[AzureVirtualNetworkGatewayIPConfiguration]] = field(default=None, metadata={'description': 'IP configurations for virtual network gateway.'}) # fmt: skip + location: Optional[str] = field(default=None, metadata={"description": "Resource location."}) + gateway_nat_rules: Optional[List[AzureVirtualNetworkGatewayNatRule]] = field(default=None, metadata={'description': 'NatRules for virtual network gateway.'}) # fmt: skip + resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the virtual network gateway resource.'}) # fmt: skip + network_gateway_sku: Optional[AzureVirtualNetworkGatewaySku] = field(default=None, metadata={'description': 'VirtualNetworkGatewaySku details.'}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "Resource type."}) + v_net_extended_location_resource_id: Optional[str] = field(default=None, metadata={'description': 'Customer vnet resource id. VirtualNetworkGateway of type local gateway is associated with the customer vnet.'}) # fmt: skip + virtual_network_gateway_policy_groups: Optional[List[AzureVirtualNetworkGatewayPolicyGroup]] = field(default=None, metadata={'description': 'The reference to the VirtualNetworkGatewayPolicyGroup resource which represents the available VirtualNetworkGatewayPolicyGroup for the gateway.'}) # fmt: skip + vpn_client_configuration: Optional[AzureVpnClientConfiguration] = field(default=None, metadata={'description': 'VpnClientConfiguration for P2S client.'}) # fmt: skip + vpn_gateway_generation: Optional[str] = field(default=None, metadata={'description': 'The generation for this VirtualNetworkGateway. Must be None if gatewayType is not VPN.'}) # fmt: skip + vpn_type: Optional[str] = field(default=None, metadata={'description': 'The type of this virtual network gateway.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureLocalNetworkGateway(MicrosoftResource, BaseGateway): + kind: ClassVar[str] = "azure_local_network_gateway" + # Collect via AzureResourceGroup + mapping: ClassVar[Dict[str, Bender]] = { + "bgp_settings": S("properties", "bgpSettings") >> Bend(AzureBgpSettings.mapping), + "etag": S("etag"), + "fqdn": S("properties", "fqdn"), + "gateway_ip_address": S("properties", "gatewayIpAddress"), + "id": S("id"), + "local_network_address_space": S("properties", "localNetworkAddressSpace") >> Bend(AzureAddressSpace.mapping), + "location": S("location"), + "name": S("name"), + "provisioning_state": S("properties", "provisioningState"), + "resource_guid": S("properties", "resourceGuid"), + "tags": S("tags", default={}), + "type": S("type"), + } + bgp_settings: Optional[AzureBgpSettings] = field(default=None, metadata={"description": "BGP settings details."}) + fqdn: Optional[str] = field(default=None, metadata={"description": "FQDN of local network gateway."}) + gateway_ip_address: Optional[str] = field(default=None, metadata={'description': 'IP address of local network gateway.'}) # fmt: skip + local_network_address_space: Optional[AzureAddressSpace] = field(default=None, metadata={'description': 'AddressSpace contains an array of IP address ranges that can be used by subnets of the virtual network.'}) # fmt: skip + location: Optional[str] = field(default=None, metadata={"description": "Resource location."}) + resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the local network gateway resource.'}) # fmt: skip + type: Optional[str] = field(default=None, metadata={"description": "Resource type."}) + + +@define(eq=False, slots=False) +class AzureTunnelConnectionHealth: + kind: ClassVar[str] = "azure_tunnel_connection_health" + mapping: ClassVar[Dict[str, Bender]] = { + "connection_status": S("connectionStatus"), + "egress_bytes_transferred": S("egressBytesTransferred"), + "ingress_bytes_transferred": S("ingressBytesTransferred"), + "last_connection_established_utc_time": S("lastConnectionEstablishedUtcTime"), + "tunnel": S("tunnel"), + } + connection_status: Optional[str] = field(default=None, metadata={'description': 'Virtual Network Gateway connection status.'}) # fmt: skip + egress_bytes_transferred: Optional[int] = field(default=None, metadata={'description': 'The Egress Bytes Transferred in this connection.'}) # fmt: skip + ingress_bytes_transferred: Optional[int] = field(default=None, metadata={'description': 'The Ingress Bytes Transferred in this connection.'}) # fmt: skip + last_connection_established_utc_time: Optional[str] = field(default=None, metadata={'description': 'The time at which connection was established in Utc format.'}) # fmt: skip + tunnel: Optional[str] = field(default=None, metadata={"description": "Tunnel name."}) + + +@define(eq=False, slots=False) +class AzureVirtualNetworkGatewayConnection(MicrosoftResource, BaseTunnel): + kind: ClassVar[str] = "azure_virtual_network_gateway_connection" + # Collect via AzureResourceGroup + reference_kinds: ClassVar[ModelReference] = { + "predecessors": {"default": ["azure_virtual_network_gateway", "azure_local_network_gateway"]}, + } + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("id"), + "tags": S("tags", default={}), + "name": S("name"), + "authorization_key": S("properties", "authorizationKey"), + "connection_mode": S("properties", "connectionMode"), + "connection_protocol": S("properties", "connectionProtocol"), + "connection_status": S("properties", "connectionStatus"), + "connection_type": S("properties", "connectionType"), + "dpd_timeout_seconds": S("properties", "dpdTimeoutSeconds"), + "egress_bytes_transferred": S("properties", "egressBytesTransferred"), + "egress_nat_rules": S("properties") >> S("egressNatRules", default=[]) >> ForallBend(S("id")), + "enable_bgp": S("properties", "enableBgp"), + "enable_private_link_fast_path": S("properties", "enablePrivateLinkFastPath"), + "etag": S("etag"), + "express_route_gateway_bypass": S("properties", "expressRouteGatewayBypass"), + "gateway_custom_bgp_ip_addresses": S("properties", "gatewayCustomBgpIpAddresses") + >> ForallBend(AzureGatewayCustomBgpIpAddressIpConfiguration.mapping), + "ingress_bytes_transferred": S("properties", "ingressBytesTransferred"), + "ingress_nat_rules": S("properties") >> S("ingressNatRules", default=[]) >> ForallBend(S("id")), + "ipsec_policies": S("properties", "ipsecPolicies") >> ForallBend(AzureIpsecPolicy.mapping), + "local_network_gateway2": S("properties", "localNetworkGateway2", "id"), + "peer": S("properties", "peer", "id"), + "provisioning_state": S("properties", "provisioningState"), + "resource_guid": S("properties", "resourceGuid"), + "routing_weight": S("properties", "routingWeight"), + "shared_key": S("properties", "sharedKey"), + "traffic_selector_policies": S("properties", "trafficSelectorPolicies") + >> ForallBend(AzureTrafficSelectorPolicy.mapping), + "tunnel_connection_status": S("properties", "tunnelConnectionStatus") + >> ForallBend(AzureTunnelConnectionHealth.mapping), + "use_local_azure_ip_address": S("properties", "useLocalAzureIpAddress"), + "use_policy_based_traffic_selectors": S("properties", "usePolicyBasedTrafficSelectors"), + "virtual_network_gateway1_id": S("properties", "virtualNetworkGateway1", "id"), + "virtual_network_gateway2_id": S("properties", "virtualNetworkGateway2", "id"), + } + authorization_key: Optional[str] = field(default=None, metadata={"description": "The authorizationKey."}) + connection_mode: Optional[str] = field(default=None, metadata={"description": "Gateway connection type."}) + connection_protocol: Optional[str] = field(default=None, metadata={"description": "Gateway connection protocol."}) + connection_status: Optional[str] = field(default=None, metadata={'description': 'Virtual Network Gateway connection status.'}) # fmt: skip + connection_type: Optional[str] = field(default=None, metadata={"description": "Gateway connection type."}) + dpd_timeout_seconds: Optional[int] = field(default=None, metadata={'description': 'The dead peer detection timeout of this connection in seconds.'}) # fmt: skip + egress_bytes_transferred: Optional[int] = field(default=None, metadata={'description': 'The egress bytes transferred in this connection.'}) # fmt: skip + egress_nat_rules: Optional[List[str]] = field(default=None, metadata={"description": "List of egress NatRules."}) + enable_bgp: Optional[bool] = field(default=None, metadata={"description": "EnableBgp flag."}) + enable_private_link_fast_path: Optional[bool] = field(default=None, metadata={'description': 'Bypass the ExpressRoute gateway when accessing private-links. ExpressRoute FastPath (expressRouteGatewayBypass) must be enabled.'}) # fmt: skip + express_route_gateway_bypass: Optional[bool] = field(default=None, metadata={'description': 'Bypass ExpressRoute Gateway for data forwarding.'}) # fmt: skip + gateway_custom_bgp_ip_addresses: Optional[List[AzureGatewayCustomBgpIpAddressIpConfiguration]] = field(default=None, metadata={'description': 'GatewayCustomBgpIpAddresses to be used for virtual network gateway Connection.'}) # fmt: skip + ingress_bytes_transferred: Optional[int] = field(default=None, metadata={'description': 'The ingress bytes transferred in this connection.'}) # fmt: skip + ingress_nat_rules: Optional[List[str]] = field(default=None, metadata={"description": "List of ingress NatRules."}) + ipsec_policies: Optional[List[AzureIpsecPolicy]] = field(default=None, metadata={'description': 'The IPSec Policies to be considered by this connection.'}) # fmt: skip + local_network_gateway2: Optional[str] = field(default=None, metadata={'description': 'A common class for general resource information.'}) # fmt: skip + peer: Optional[str] = field(default=None, metadata={"description": "Reference to another subresource."}) + resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the virtual network gateway connection resource.'}) # fmt: skip + routing_weight: Optional[int] = field(default=None, metadata={"description": "The routing weight."}) + shared_key: Optional[str] = field(default=None, metadata={"description": "The IPSec shared key."}) + traffic_selector_policies: Optional[List[AzureTrafficSelectorPolicy]] = field(default=None, metadata={'description': 'The Traffic Selector Policies to be considered by this connection.'}) # fmt: skip + tunnel_connection_status: Optional[List[AzureTunnelConnectionHealth]] = field(default=None, metadata={'description': 'Collection of all tunnels connection health status.'}) # fmt: skip + use_local_azure_ip_address: Optional[bool] = field(default=None, metadata={'description': 'Use private local Azure IP for the connection.'}) # fmt: skip + use_policy_based_traffic_selectors: Optional[bool] = field(default=None, metadata={'description': 'Enable policy-based traffic selectors.'}) # fmt: skip + virtual_network_gateway1_id: Optional[str] = field(default=None, metadata={'description': 'A common class for general resource information.'}) # fmt: skip + virtual_network_gateway2_id: Optional[str] = field(default=None, metadata={'description': 'A common class for general resource information.'}) # fmt: skip + + def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None: + if virtual_network_gateway1_id := self.virtual_network_gateway1_id: + builder.add_edge( + self, + edge_type=EdgeType.default, + reverse=True, + clazz=AzureVirtualNetworkGateway, + id=virtual_network_gateway1_id, + ) + if virtual_network_gateway2_id := self.virtual_network_gateway2_id: + builder.add_edge( + self, + edge_type=EdgeType.default, + reverse=True, + clazz=AzureVirtualNetworkGateway, + id=virtual_network_gateway2_id, + ) + if local_network_gateway2_id := self.local_network_gateway2: + builder.add_edge( + self, + edge_type=EdgeType.default, + reverse=True, + clazz=AzureLocalNetworkGateway, + id=local_network_gateway2_id, + ) + + +@define(eq=False, slots=False) +class AzureMxRecord: + kind: ClassVar[str] = "azure_mx_record" + mapping: ClassVar[Dict[str, Bender]] = {"exchange": S("exchange"), "preference": S("preference")} + exchange: Optional[str] = field(default=None, metadata={'description': 'The domain name of the mail host for this MX record.'}) # fmt: skip + preference: Optional[int] = field(default=None, metadata={'description': 'The preference value for this MX record.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureSrvRecord: + kind: ClassVar[str] = "azure_srv_record" + mapping: ClassVar[Dict[str, Bender]] = { + "port": S("port"), + "priority": S("priority"), + "target": S("target"), + "weight": S("weight"), + } + port: Optional[int] = field(default=None, metadata={"description": "The port value for this SRV record."}) + priority: Optional[int] = field(default=None, metadata={"description": "The priority value for this SRV record."}) + target: Optional[str] = field(default=None, metadata={'description': 'The target domain name for this SRV record.'}) # fmt: skip + weight: Optional[int] = field(default=None, metadata={"description": "The weight value for this SRV record."}) + + +@define(eq=False, slots=False) +class AzureTxtRecord: + kind: ClassVar[str] = "azure_txt_record" + mapping: ClassVar[Dict[str, Bender]] = {"value": S("value")} + value: Optional[List[str]] = field(default=None, metadata={"description": "The text value of this TXT record."}) + + +@define(eq=False, slots=False) +class AzureSoaRecord: + kind: ClassVar[str] = "azure_soa_record" + mapping: ClassVar[Dict[str, Bender]] = { + "email": S("email"), + "expire_time": S("expireTime"), + "host": S("host"), + "minimum_ttl": S("minimumTTL"), + "refresh_time": S("refreshTime"), + "retry_time": S("retryTime"), + "serial_number": S("serialNumber"), + } + email: Optional[str] = field(default=None, metadata={"description": "The email contact for this SOA record."}) + expire_time: Optional[int] = field(default=None, metadata={"description": "The expire time for this SOA record."}) + host: Optional[str] = field(default=None, metadata={'description': 'The domain name of the authoritative name server for this SOA record.'}) # fmt: skip + minimum_ttl: Optional[int] = field(default=None, metadata={'description': 'The minimum value for this SOA record. By convention this is used to determine the negative caching duration.'}) # fmt: skip + refresh_time: Optional[int] = field(default=None, metadata={'description': 'The refresh value for this SOA record.'}) # fmt: skip + retry_time: Optional[int] = field(default=None, metadata={"description": "The retry time for this SOA record."}) + serial_number: Optional[int] = field(default=None, metadata={'description': 'The serial number for this SOA record.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureCaaRecord: + kind: ClassVar[str] = "azure_caa_record" + mapping: ClassVar[Dict[str, Bender]] = {"flags": S("flags"), "tag": S("tag"), "value": S("value")} + flags: Optional[int] = field(default=None, metadata={'description': 'The flags for this CAA record as an integer between 0 and 255.'}) # fmt: skip + tag: Optional[str] = field(default=None, metadata={"description": "The tag for this CAA record."}) + value: Optional[str] = field(default=None, metadata={"description": "The value for this CAA record."}) + + +@define(eq=False, slots=False) +class AzureDNSRecordSet(MicrosoftResource, BaseDNSRecordSet): + kind: ClassVar[str] = "azure_dns_record_set" + reference_kinds: ClassVar[ModelReference] = { + "predecessors": {"default": ["azure_dns_zone"]}, + } + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("id"), + "tags": S("tags", default={}), + "name": S("name"), + "a_records": S("properties") >> S("ARecords", default=[]) >> ForallBend(S("ipv4Address")), + "aaaa_records": S("properties") >> S("AAAARecords", default=[]) >> ForallBend(S("ipv6Address")), + "caa_records": S("properties", "caaRecords") >> ForallBend(AzureCaaRecord.mapping), + "cname_record": S("properties", "CNAMERecord", "cname"), + "etag": S("etag"), + "fqdn": S("properties", "fqdn"), + "record_set_metadata": S("properties", "metadata"), + "mx_records": S("properties", "MXRecords") >> ForallBend(AzureMxRecord.mapping), + "ns_records": S("properties") >> S("NSRecords", default=[]) >> ForallBend(S("nsdname")), + "provisioning_state": S("properties", "provisioningState"), + "ptr_records": S("properties") >> S("PTRRecords", default=[]) >> ForallBend(S("ptrdname")), + "soa_record": S("properties", "SOARecord") >> Bend(AzureSoaRecord.mapping), + "srv_records": S("properties", "SRVRecords") >> ForallBend(AzureSrvRecord.mapping), + "target_resource": S("properties", "targetResource", "id"), + "ttl": S("properties", "TTL"), + "txt_records": S("properties", "TXTRecords") >> ForallBend(AzureTxtRecord.mapping), + } + a_records: Optional[List[str]] = field(default=None, metadata={'description': 'The list of A records in the record set.'}) # fmt: skip + aaaa_records: Optional[List[str]] = field(default=None, metadata={'description': 'The list of AAAA records in the record set.'}) # fmt: skip + caa_records: Optional[List[AzureCaaRecord]] = field(default=None, metadata={'description': 'The list of CAA records in the record set.'}) # fmt: skip + cname_record: Optional[str] = field(default=None, metadata={"description": "A CNAME record."}) + fqdn: Optional[str] = field(default=None, metadata={'description': 'Fully qualified domain name of the record set.'}) # fmt: skip + record_set_metadata: Optional[Dict[str, str]] = field(default=None, metadata={'description': 'The metadata attached to the record set.'}) # fmt: skip + mx_records: Optional[List[AzureMxRecord]] = field(default=None, metadata={'description': 'The list of MX records in the record set.'}) # fmt: skip + ns_records: Optional[List[str]] = field(default=None, metadata={'description': 'The list of NS records in the record set.'}) # fmt: skip + ptr_records: Optional[List[str]] = field(default=None, metadata={'description': 'The list of PTR records in the record set.'}) # fmt: skip + soa_record: Optional[AzureSoaRecord] = field(default=None, metadata={"description": "An SOA record."}) + srv_records: Optional[List[AzureSrvRecord]] = field(default=None, metadata={'description': 'The list of SRV records in the record set.'}) # fmt: skip + target_resource: Optional[str] = field(default=None, metadata={"description": "A reference to a another resource"}) + ttl: Optional[int] = field(default=None, metadata={'description': 'The TTL (time-to-live) of the records in the record set.'}) # fmt: skip + txt_records: Optional[List[AzureTxtRecord]] = field(default=None, metadata={'description': 'The list of TXT records in the record set.'}) # fmt: skip + + +@define(eq=False, slots=False) +class AzureDNSZone(MicrosoftResource, BaseDNSZone): + kind: ClassVar[str] = "azure_dns_zone" + api_spec: ClassVar[AzureResourceSpec] = AzureResourceSpec( + service="network", + version="2018-05-01", + path="/subscriptions/{subscriptionId}/providers/Microsoft.Network/dnszones", + path_parameters=["subscriptionId"], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + mapping: ClassVar[Dict[str, Bender]] = { + "id": S("id"), + "tags": S("tags", default={}), + "name": S("name"), + "etag": S("etag"), + "max_number_of_record_sets": S("properties", "maxNumberOfRecordSets"), + "max_number_of_records_per_record_set": S("properties", "maxNumberOfRecordsPerRecordSet"), + "name_servers": S("properties", "nameServers"), + "number_of_record_sets": S("properties", "numberOfRecordSets"), + "registration_virtual_networks": S("properties") + >> S("registrationVirtualNetworks", default=[]) + >> ForallBend(S("id")), + "resolution_virtual_networks": S("properties") + >> S("resolutionVirtualNetworks", default=[]) + >> ForallBend(S("id")), + "zone_type": S("properties", "zoneType"), + } + max_number_of_record_sets: Optional[int] = field(default=None, metadata={'description': 'The maximum number of record sets that can be created in this DNS zone. This is a read-only property and any attempt to set this value will be ignored.'}) # fmt: skip + max_number_of_records_per_record_set: Optional[int] = field(default=None, metadata={'description': 'The maximum number of records per record set that can be created in this DNS zone. This is a read-only property and any attempt to set this value will be ignored.'}) # fmt: skip + name_servers: Optional[List[str]] = field(default=None, metadata={'description': 'The name servers for this DNS zone. This is a read-only property and any attempt to set this value will be ignored.'}) # fmt: skip + number_of_record_sets: Optional[int] = field(default=None, metadata={'description': 'The current number of record sets in this DNS zone. This is a read-only property and any attempt to set this value will be ignored.'}) # fmt: skip + registration_virtual_networks: Optional[List[str]] = field(default=None, metadata={'description': 'A list of references to virtual networks that register hostnames in this DNS zone. This is a only when ZoneType is Private.'}) # fmt: skip + resolution_virtual_networks: Optional[List[str]] = field(default=None, metadata={'description': 'A list of references to virtual networks that resolve records in this DNS zone. This is a only when ZoneType is Private.'}) # fmt: skip + zone_type: Optional[str] = field(default=None, metadata={'description': 'The type of this DNS zone (Public or Private).'}) # fmt: skip + + def post_process(self, graph_builder: GraphBuilder, source: Json) -> None: + def collect_record_sets() -> None: + api_spec = AzureResourceSpec( + service="network", + version="2018-05-01", + path=f"{self.id}/recordsets", + path_parameters=[], + query_parameters=["api-version"], + access_path="value", + expect_array=True, + ) + items = graph_builder.client.list(api_spec) + + record_sets = AzureDNSRecordSet.collect(items, graph_builder) + for record_set in record_sets: + dns_zone_id = "/".join(record_set.id.split("/")[:-2]) + graph_builder.add_edge( + record_set, edge_type=EdgeType.default, reverse=True, clazz=AzureDNSZone, id=dns_zone_id + ) + + graph_builder.submit_work(service_name, collect_record_sets) + + resources: List[Type[MicrosoftResource]] = [ AzureApplicationGateway, AzureApplicationGatewayFirewallRuleSet, @@ -5764,6 +6439,8 @@ class AzureWebApplicationFirewallPolicy(MicrosoftResource): AzureCustomIpPrefix, AzureDdosProtectionPlan, AzureDscpConfiguration, + AzureDNSZone, + AzureDNSRecordSet, AzureExpressRouteCircuit, # AzureExpressRouteCrossConnection, # API is listed but not available AzureExpressRouteGateway, @@ -5773,6 +6450,7 @@ class AzureWebApplicationFirewallPolicy(MicrosoftResource): AzureIpAllocation, AzureIpGroup, AzureLoadBalancer, + AzureLoadBalancerProbe, AzureNatGateway, AzureNetworkInterface, AzureNetworkProfile, @@ -5787,14 +6465,19 @@ class AzureWebApplicationFirewallPolicy(MicrosoftResource): AzureRouteFilter, AzureSecurityPartnerProvider, AzureSubnet, + AzureRouteTable, AzureNetworkUsage, AzureVirtualHub, AzureVirtualNetwork, AzureVirtualNetworkTap, AzureVirtualRouter, AzureVirtualWAN, - AzureVpnGateway, + AzureVirtualWANVpnGateway, + AzureVirtualWANVpnConnection, AzureVpnServerConfiguration, AzureVpnSite, + AzureVirtualNetworkGateway, + AzureLocalNetworkGateway, + AzureVirtualNetworkGatewayConnection, AzureWebApplicationFirewallPolicy, ] diff --git a/plugins/azure/fix_plugin_azure/resource/storage.py b/plugins/azure/fix_plugin_azure/resource/storage.py index f93017b84d..cb14f05766 100644 --- a/plugins/azure/fix_plugin_azure/resource/storage.py +++ b/plugins/azure/fix_plugin_azure/resource/storage.py @@ -14,7 +14,6 @@ AzureUserAssignedIdentity, AzurePrivateLinkServiceConnectionState, AzureSku, - MicrosoftResource, ) from fix_plugin_azure.resource.metrics import AzureMetricData, AzureMetricQuery, update_resource_metrics from fix_plugin_azure.utils import MetricNormalization diff --git a/plugins/azure/test/collector_test.py b/plugins/azure/test/collector_test.py index fdf4e26cbc..bdb03af941 100644 --- a/plugins/azure/test/collector_test.py +++ b/plugins/azure/test/collector_test.py @@ -3,10 +3,12 @@ from queue import Queue from typing import List, Type +from fix_plugin_azure.resource.microsoft_graph import MicrosoftGraphOrganization + from conftest import connect_resources from fix_plugin_azure.azure_client import MicrosoftClient -from fix_plugin_azure.collector import AzureSubscriptionCollector +from fix_plugin_azure.collector import AzureSubscriptionCollector, MicrosoftGraphOrganizationCollector from fix_plugin_azure.config import AzureCredentials, AzureConfig from fix_plugin_azure.resource.base import MicrosoftResource, AzureSubscription, GraphBuilder from fix_plugin_azure.resource.compute import ( @@ -43,10 +45,20 @@ def test_collect( core_feedback: CoreFeedback, azure_client: MicrosoftClient, ) -> None: - collector = AzureSubscriptionCollector(config, Cloud(id="azure"), azure_subscription, credentials, core_feedback) - collector.collect() - assert len(collector.graph.nodes) == 270 - assert len(collector.graph.edges) == 399 + subscription_collector = AzureSubscriptionCollector( + config, Cloud(id="azure"), azure_subscription, credentials, core_feedback + ) + subscription_collector.collect() + assert len(subscription_collector.graph.nodes) == 296 + assert len(subscription_collector.graph.edges) == 443 + + graph_collector = MicrosoftGraphOrganizationCollector( + config, Cloud(id="azure"), MicrosoftGraphOrganization(id="test", name="test"), credentials, core_feedback + ) + graph_collector.collect() + + assert len(graph_collector.graph.nodes) == 7 + assert len(graph_collector.graph.edges) == 6 def test_filter(credentials: AzureCredentials, builder: GraphBuilder) -> None: diff --git a/plugins/azure/test/conftest.py b/plugins/azure/test/conftest.py index dc87dee4c8..1dcac8cc73 100644 --- a/plugins/azure/test/conftest.py +++ b/plugins/azure/test/conftest.py @@ -12,7 +12,7 @@ from pytest import fixture from fix_plugin_azure.config import AzureConfig -from fix_plugin_azure.azure_client import MicrosoftClient, AzureResourceSpec, MicrosoftRestSpec +from fix_plugin_azure.azure_client import MicrosoftClient, AzureResourceSpec, MicrosoftRestSpec, RestApiSpec from fix_plugin_azure.resource.base import GraphBuilder, AzureSubscription, MicrosoftResourceType, AzureLocation from fixlib.baseresources import Cloud from fixlib.core.actions import CoreFeedback @@ -23,23 +23,37 @@ class StaticFileMicrosoftClient(MicrosoftClient): def list(self, spec: MicrosoftRestSpec, **kwargs: Any) -> List[Json]: - assert isinstance(spec, AzureResourceSpec) - query_start_index = spec.path.find("?") - spec_path = spec.path[:query_start_index] if query_start_index != -1 else spec.path - splitted_path = spec_path.rsplit("/") - - last = splitted_path[-1] if splitted_path[-1] != "" else splitted_path[-2] - - path = os.path.dirname(__file__) + f"/files/{spec.service}/{last}.json" - with open(path) as f: - js = json.load(f) - - if spec.expect_array: - js = js[spec.access_path] - if spec.expect_array and isinstance(js, list): - return js - else: - return [js] + assert isinstance(spec, (AzureResourceSpec, RestApiSpec)) + if isinstance(spec, AzureResourceSpec): + query_start_index = spec.path.find("?") + spec_path = spec.path[:query_start_index] if query_start_index != -1 else spec.path + splitted_path = spec_path.rsplit("/") + + last = splitted_path[-1] if splitted_path[-1] != "" else splitted_path[-2] + + path = os.path.dirname(__file__) + f"/files/{spec.service}/{last}.json" + with open(path) as f: + js = json.load(f) + + if spec.expect_array: + js = js[spec.access_path] + if spec.expect_array and isinstance(js, list): + return js + else: + return [js] + else: + last = spec.url.rsplit("/", maxsplit=1)[1] + + path = os.path.dirname(__file__) + f"/files/{spec.service}/{last}.json" + with open(path) as f: + js = json.load(f) + + if spec.expect_array: + js = js[spec.access_path] + if spec.expect_array and isinstance(js, list): + return js + else: + return [js] @staticmethod def create(*args: Any, **kwargs: Any) -> StaticFileMicrosoftClient: @@ -175,3 +189,5 @@ def connect_resources( for node, data in list(builder.graph.nodes(data=True)): if not filter_class or isinstance(node, filter_class): node.connect_in_graph(builder, data.get("source", {})) + + builder.executor.wait_for_submitted_work() diff --git a/plugins/azure/test/files/graph/devices.json b/plugins/azure/test/files/graph/devices.json new file mode 100644 index 0000000000..7d1d1d14a0 --- /dev/null +++ b/plugins/azure/test/files/graph/devices.json @@ -0,0 +1,13 @@ +{ + "value": [ + { + "accountEnabled": true, + "deviceId": "00000000-0000-0000-0000-000000000000", + "deviceVersion": 1, + "displayName": "contoso_Android", + "Manufacturer": "Google", + "Model": "Pixel 3a", + "operatingSystemVersion": "10.0" + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/graph/groups.json b/plugins/azure/test/files/graph/groups.json new file mode 100644 index 0000000000..41d400f266 --- /dev/null +++ b/plugins/azure/test/files/graph/groups.json @@ -0,0 +1,72 @@ +{ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#groups", + "value": [ + { + "id": "45b7d2e7-b882-4a80-ba97-10b7a63b8fa4", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2018-12-22T02:21:05Z", + "description": "Self help community for golf", + "displayName": "Golf Assist", + "expirationDateTime": null, + "groupTypes": [ + "Unified" + ], + "isAssignableToRole": null, + "mail": "golfassist@contoso.com", + "mailEnabled": true, + "mailNickname": "golfassist", + "membershipRule": null, + "membershipRuleProcessingState": null, + "onPremisesLastSyncDateTime": null, + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": "CAN", + "preferredLanguage": null, + "proxyAddresses": [ + "smtp:golfassist@contoso.com", + "SMTP:golfassist@contoso.com" + ], + "renewedDateTime": "2018-12-22T02:21:05Z", + "resourceBehaviorOptions": [], + "resourceProvisioningOptions": [], + "securityEnabled": false, + "theme": null, + "visibility": "Public", + "onPremisesProvisioningErrors": [] + }, + { + "id": "d7797254-3084-44d0-99c9-a3b5ab149538", + "deletedDateTime": null, + "classification": null, + "createdDateTime": "2018-11-19T20:29:40Z", + "description": "Talk about golf", + "displayName": "Golf Discussion", + "expirationDateTime": null, + "groupTypes": [], + "isAssignableToRole": null, + "mail": "golftalk@contoso.com", + "mailEnabled": true, + "mailNickname": "golftalk", + "membershipRule": null, + "membershipRuleProcessingState": null, + "onPremisesLastSyncDateTime": null, + "onPremisesSecurityIdentifier": null, + "onPremisesSyncEnabled": null, + "preferredDataLocation": "CAN", + "preferredLanguage": null, + "proxyAddresses": [ + "smtp:golftalk@contoso.com", + "SMTP:golftalk@contoso.com" + ], + "renewedDateTime": "2018-11-19T20:29:40Z", + "resourceBehaviorOptions": [], + "resourceProvisioningOptions": [], + "securityEnabled": false, + "serviceProvisioningErrors": [], + "theme": null, + "visibility": null, + "onPremisesProvisioningErrors": [] + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/graph/roleDefinitions.json b/plugins/azure/test/files/graph/roleDefinitions.json new file mode 100644 index 0000000000..e5956cee61 --- /dev/null +++ b/plugins/azure/test/files/graph/roleDefinitions.json @@ -0,0 +1,91 @@ +{ + "@odata.context": "https://graph.microsoft.com/beta/$metadata#roleManagement/directory/roleDefinitions", + "value": [ + { + "id": "729827e3-9c14-49f7-bb1b-9608f156bbb8", + "description": "Can reset passwords for non-administrators and Helpdesk Administrators.", + "displayName": "Helpdesk Administrator", + "isBuiltIn": true, + "isEnabled": true, + "isPrivileged": true, + "templateId": "729827e3-9c14-49f7-bb1b-9608f156bbb8", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.directory/users/invalidateAllRefreshTokens", + "microsoft.directory/users/bitLockerRecoveryKeys/read", + "microsoft.directory/users/password/update", + "microsoft.azure.serviceHealth/allEntities/allTasks", + "microsoft.azure.supportTickets/allEntities/allTasks", + "microsoft.office365.webPortal/allEntities/standard/read", + "microsoft.office365.serviceHealth/allEntities/allTasks", + "microsoft.office365.supportTickets/allEntities/allTasks" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + { + "id": "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + } + ] + }, + { + "id": "f023fd81-a637-4b56-95fd-791ac0226033", + "description": "Can read service health information and manage support tickets.", + "displayName": "Service Support Administrator", + "isBuiltIn": true, + "isEnabled": true, + "isPrivileged": false, + "templateId": "f023fd81-a637-4b56-95fd-791ac0226033", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.azure.serviceHealth/allEntities/allTasks", + "microsoft.azure.supportTickets/allEntities/allTasks", + "microsoft.office365.webPortal/allEntities/standard/read", + "microsoft.office365.serviceHealth/allEntities/allTasks", + "microsoft.office365.supportTickets/allEntities/allTasks" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + { + "id": "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + } + ] + }, + { + "id": "b0f54661-2d74-4c50-afa3-1ec803f12efe", + "description": "Can perform common billing related tasks like updating payment information.", + "displayName": "Billing Administrator", + "isBuiltIn": true, + "isEnabled": true, + "isPrivileged": false, + "templateId": "b0f54661-2d74-4c50-afa3-1ec803f12efe", + "version": "1", + "rolePermissions": [ + { + "allowedResourceActions": [ + "microsoft.directory/organization/basic/update", + "microsoft.azure.serviceHealth/allEntities/allTasks", + "microsoft.azure.supportTickets/allEntities/allTasks", + "microsoft.commerce.billing/allEntities/allTasks", + "microsoft.office365.webPortal/allEntities/standard/read", + "microsoft.office365.serviceHealth/allEntities/allTasks", + "microsoft.office365.supportTickets/allEntities/allTasks" + ], + "condition": null + } + ], + "inheritsPermissionsFrom": [ + { + "id": "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + } + ] + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/graph/serviceprincipals.json b/plugins/azure/test/files/graph/serviceprincipals.json new file mode 100644 index 0000000000..d4b7a9517a --- /dev/null +++ b/plugins/azure/test/files/graph/serviceprincipals.json @@ -0,0 +1,41 @@ +{ + "accountEnabled": true, + "addIns": [], + "alternativeNames": [ + "http://contoso/a7770d29-4321-41a6-b863-ca11d6639448" + ], + "appDisplayName": "My app", + "appId": "appId-value", + "appOwnerOrganizationId": "65415bb1-9267-4313-bbf5-ae259732ee12", + "appRoleAssignmentRequired": true, + "appRoles": [], + "disabledByMicrosoftStatus": null, + "displayName": "My app instance in tenant", + "endpoints": [], + "homepage": null, + "id": "00af5dfb-85da-4b41-a677-0c6b86dd34f8", + "verifiedPublisher": { + "displayName": "publisher_contoso", + "verifiedPublisherId": "9999999", + "addedDateTime": "2021-04-24T17:49:44Z" + }, + "info": { + "termsOfServiceUrl": null, + "supportUrl": null, + "privacyStatementUrl": null, + "marketingUrl": null, + "logoUrl": null + }, + "keyCredentials": [], + "logoutUrl": null, + "oauth2PermissionScopes": [], + "passwordCredentials": [], + "publisherName": null, + "replyUrls": [], + "resourceSpecificApplicationPermissions": [], + "servicePrincipalNames": [], + "servicePrincipalType": null, + "signInAudience": "AzureADandPersonalMicrosoftAccount", + "tags": [], + "tokenEncryptionKeyId": null +} \ No newline at end of file diff --git a/plugins/azure/test/files/graph/users.json b/plugins/azure/test/files/graph/users.json new file mode 100644 index 0000000000..26239ca583 --- /dev/null +++ b/plugins/azure/test/files/graph/users.json @@ -0,0 +1,33 @@ +{ + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users", + "value": [ + { + "businessPhones": [], + "displayName": "Conf Room Adams", + "givenName": null, + "jobTitle": null, + "mail": "Adams@contoso.com", + "mobilePhone": null, + "officeLocation": null, + "preferredLanguage": null, + "surname": null, + "userPrincipalName": "Adams@contoso.com", + "id": "6ea91a8d-e32e-41a1-b7bd-d2d185eed0e0" + }, + { + "businessPhones": [ + "425-555-0100" + ], + "displayName": "MOD Administrator", + "givenName": "MOD", + "jobTitle": null, + "mail": null, + "mobilePhone": "425-555-0101", + "officeLocation": null, + "preferredLanguage": "en-US", + "surname": "Administrator", + "userPrincipalName": "admin@contoso.com", + "id": "4562bcc8-c436-4f95-b7c0-4f8ce89dca5e" + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/connections.json b/plugins/azure/test/files/network/connections.json new file mode 100644 index 0000000000..9ee8006998 --- /dev/null +++ b/plugins/azure/test/files/network/connections.json @@ -0,0 +1,106 @@ +{ + "value": [ + { + "name": "conn1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/connections/conn1", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/connections", + "location": "centralus", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "virtualNetworkGateway1": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1", + "properties": {} + }, + "localNetworkGateway2": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/localNetworkGateways/localgw1", + "properties": {} + }, + "ingressNatRules": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1/natRules/natRule1" + } + ], + "egressNatRules": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1/natRules/natRule2" + } + ], + "connectionType": "IPsec", + "connectionProtocol": "IKEv1", + "routingWeight": 0, + "dpdTimeoutSeconds": 30, + "enableBgp": false, + "gatewayCustomBgpIpAddresses": [ + { + "ipConfigurationId": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw/ipConfigurations/default", + "customBgpIpAddress": "169.254.21.1" + }, + { + "ipConfigurationId": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw/ipConfigurations/ActiveActive", + "customBgpIpAddress": "169.254.21.3" + } + ], + "useLocalAzureIpAddress": false, + "usePolicyBasedTrafficSelectors": false, + "ipsecPolicies": [], + "trafficSelectorPolicies": [], + "ingressBytesTransferred": 0, + "egressBytesTransferred": 0, + "connectionMode": "Default" + } + }, + { + "name": "conn2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/connections/conn2", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/connections", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "virtualNetworkGateway1": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2", + "properties": {} + }, + "localNetworkGateway2": { + "properties": {}, + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/localNetworkGateways/localgw2" + }, + "ingressNatRules": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/natRules/natRule1" + } + ], + "egressNatRules": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/natRules/natRule2" + } + ], + "connectionType": "IPsec", + "connectionProtocol": "IKEv2", + "routingWeight": 0, + "dpdTimeoutSeconds": 20, + "enableBgp": false, + "gatewayCustomBgpIpAddresses": [ + { + "ipConfigurationId": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/ipConfigurations/default", + "customBgpIpAddress": "169.254.21.4" + }, + { + "ipConfigurationId": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/ipConfigurations/ActiveActive", + "customBgpIpAddress": "169.254.21.6" + } + ], + "useLocalAzureIpAddress": true, + "usePolicyBasedTrafficSelectors": false, + "ipsecPolicies": [], + "trafficSelectorPolicies": [], + "ingressBytesTransferred": 0, + "egressBytesTransferred": 0, + "connectionMode": "Default" + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/dnszones.json b/plugins/azure/test/files/network/dnszones.json new file mode 100644 index 0000000000..f930983023 --- /dev/null +++ b/plugins/azure/test/files/network/dnszones.json @@ -0,0 +1,42 @@ +{ + "nextLink": "https://servicehost/subscriptions/subid/providers/Microsoft.Network/dnsZones?api-version=2018-05-01&$skipToken=skipToken", + "value": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/dnsZones/zone1", + "etag": "00000000-0000-0000-0000-000000000000", + "location": "global", + "name": "zone1", + "type": "Microsoft.Network/dnsZones", + "properties": { + "maxNumberOfRecordSets": 5000, + "numberOfRecordSets": 2, + "nameServers": [ + "ns1-01.azure-dns.com", + "ns2-01.azure-dns.net", + "ns3-01.azure-dns.org", + "ns4-01.azure-dns.info" + ] + }, + "tags": { + "key1": "value1" + } + }, + { + "id": "/subscriptions/subid/resourceGroups/rg2/providers/Microsoft.Network/dnsZones/zone2", + "etag": "00000000-0000-0000-0000-000000000000", + "location": "global", + "name": "zone2", + "type": "Microsoft.Network/dnsZones", + "properties": { + "maxNumberOfRecordSets": 5000, + "numberOfRecordSets": 300, + "nameServers": [ + "ns1-02.azure-dns.com", + "ns2-02.azure-dns.net", + "ns3-02.azure-dns.org", + "ns4-02.azure-dns.info" + ] + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/localNetworkGateways.json b/plugins/azure/test/files/network/localNetworkGateways.json new file mode 100644 index 0000000000..f2c8258b09 --- /dev/null +++ b/plugins/azure/test/files/network/localNetworkGateways.json @@ -0,0 +1,38 @@ +{ + "value": [ + { + "name": "localgw1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/localNetworkGateways/localgw1", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/localNetworkGateways", + "location": "centralus", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "localNetworkAddressSpace": { + "addressPrefixes": [ + "10.1.0.0/16" + ] + }, + "gatewayIpAddress": "x.x.x.x" + } + }, + { + "name": "localgw2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/localNetworkGateways/localgw2", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/localNetworkGateways", + "location": "eastus", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "localNetworkAddressSpace": { + "addressPrefixes": [ + "10.2.0.0/16" + ] + }, + "gatewayIpAddress": "x.x.x.x" + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/probes.json b/plugins/azure/test/files/network/probes.json new file mode 100644 index 0000000000..e203df0ad5 --- /dev/null +++ b/plugins/azure/test/files/network/probes.json @@ -0,0 +1,24 @@ +{ + "value": [ + { + "name": "prlb", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/lb/probes/prlb", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/loadBalancers/probes", + "properties": { + "provisioningState": "Succeeded", + "protocol": "Http", + "port": 80, + "requestPath": "healthcheck.aspx", + "intervalInSeconds": 15, + "numberOfProbes": 2, + "probeThreshold": 1, + "loadBalancingRules": [ + { + "id": "/subscriptions/subid/resourceGroups/testrg/providers/Microsoft.Network/loadBalancers/lb/loadBalancingRules/rulelb" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/recordsets.json b/plugins/azure/test/files/network/recordsets.json new file mode 100644 index 0000000000..94c249d37c --- /dev/null +++ b/plugins/azure/test/files/network/recordsets.json @@ -0,0 +1,59 @@ +{ + "nextLink": "https://servicehost/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/dnsZones/zone1/CAA?api-version=2018-05-01&$skipToken=skipToken", + "value": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/dnsZones/zone1/CAA/record1", + "etag": "00000000-0000-0000-0000-000000000000", + "name": "record1", + "type": "Microsoft.Network/dnsZones/CAA", + "properties": { + "metadata": { + "key1": "value1" + }, + "TTL": 3600, + "fqdn": "record1.zone1", + "caaRecords": [ + { + "flags": 0, + "tag": "issue", + "value": "ca.contoso.com" + } + ] + } + }, + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/dnsZones/zone1/A/record1", + "etag": "00000000-0000-0000-0000-000000000000", + "name": "record1", + "type": "Microsoft.Network/dnsZones/A", + "properties": { + "metadata": { + "key1": "value1" + }, + "TTL": 3600, + "fqdn": "record1.zone1", + "ARecords": [ + { + "ipv4Address": "127.0.0.1" + } + ] + } + }, + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/dnsZones/zone1/CNAME/record2", + "etag": "00000000-0000-0000-0000-000000000000", + "name": "record2", + "type": "Microsoft.Network/dnsZones/CNAME", + "properties": { + "metadata": { + "key1": "value1" + }, + "TTL": 3600, + "fqdn": "record2.zone1", + "CNAMERecord": { + "cname": "contoso.com" + } + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/routeTables.json b/plugins/azure/test/files/network/routeTables.json new file mode 100644 index 0000000000..48e20d8d84 --- /dev/null +++ b/plugins/azure/test/files/network/routeTables.json @@ -0,0 +1,34 @@ +{ + "value": [ + { + "name": "testrt", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/routeTables/testrt", + "type": "Microsoft.Network/routeTables", + "location": "westus", + "properties": { + "provisioningState": "Succeeded", + "routes": [ + { + "name": "route1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/routeTables/testrt/routes/route1", + "properties": { + "provisioningState": "Succeeded", + "addressPrefix": "10.0.3.0/24", + "nextHopType": "VirtualNetworkGateway" + } + } + ] + } + }, + { + "name": "testrt3", + "id": "/subscriptions/subid/resourceGroups/rg2/providers/Microsoft.Network/routeTables/testrt3", + "type": "Microsoft.Network/routeTables", + "location": "westus", + "properties": { + "provisioningState": "Succeeded", + "routes": [] + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/virtualNetworkGateways.json b/plugins/azure/test/files/network/virtualNetworkGateways.json new file mode 100644 index 0000000000..a8a4765aec --- /dev/null +++ b/plugins/azure/test/files/network/virtualNetworkGateways.json @@ -0,0 +1,213 @@ +{ + "value": [ + { + "name": "vpngw1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/virtualNetworkGateways", + "location": "loc1", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "ipConfigurations": [ + { + "name": "default", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1/ipConfigurations/default", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "properties": { + "provisioningState": "Succeeded", + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/publicIPAddresses/vpngw1-ip" + }, + "subnet": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet1/subnets/GatewaySubnet" + } + } + } + ], + "natRules": [ + { + "name": "natRule1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1/natRules/natRule1", + "etag": "W/\"00ae2b69-88e7-4b3a-b66a-cfa2244e0801\"", + "properties": { + "provisioningState": "Succeeded", + "type": "Static", + "mode": "EgressSnat", + "internalMappings": [ + { + "addressSpace": "10.10.0.0/24" + } + ], + "externalMappings": [ + { + "addressSpace": "50.0.0.0/24" + } + ] + } + }, + { + "name": "natRule2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw1/natRules/natRule2", + "etag": "W/\"00ae2b69-88e7-4b3a-b66a-cfa2244e0801\"", + "properties": { + "provisioningState": "Succeeded", + "type": "Static", + "mode": "IngressSnat", + "internalMappings": [ + { + "addressSpace": "20.10.0.0/24" + } + ], + "externalMappings": [ + { + "addressSpace": "30.0.0.0/24" + } + ] + } + } + ], + "enableBgpRouteTranslationForNat": false, + "sku": { + "name": "VpnGw1", + "tier": "VpnGw1", + "capacity": 2 + }, + "gatewayType": "Vpn", + "vpnType": "RouteBased", + "vpnGatewayGeneration": "None", + "enableBgp": false, + "enablePrivateIpAddress": false, + "activeActive": false, + "disableIPSecReplayProtection": false, + "vpnClientConfiguration": { + "vpnClientProtocols": [], + "vpnClientRootCertificates": [], + "vpnClientRevokedCertificates": [] + }, + "bgpSettings": { + "asn": 65515, + "bgpPeeringAddress": "10.0.0.14", + "peerWeight": 0 + }, + "customRoutes": { + "addressPrefixes": [ + "101.168.0.6/32" + ] + }, + "allowVirtualWanTraffic": false, + "allowRemoteVnetTraffic": false + } + }, + { + "name": "vpngw2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "type": "Microsoft.Network/virtualNetworkGateways", + "location": "loc2", + "properties": { + "provisioningState": "Succeeded", + "resourceGuid": "00000000-0000-0000-0000-000000000000", + "ipConfigurations": [ + { + "name": "default", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/ipConfigurations/default", + "etag": "W/\"00000000-0000-0000-0000-000000000000\"", + "properties": { + "provisioningState": "Succeeded", + "privateIPAllocationMethod": "Dynamic", + "privateIPAddress": "10.1.0.7", + "publicIPAddress": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/publicIPAddresses/vpngw2-ip" + }, + "subnet": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworks/vnet2/subnets/GatewaySubnet" + } + } + } + ], + "natRules": [ + { + "name": "natRule1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/natRules/natRule1", + "etag": "W/\"00ae2b69-88e7-4b3a-b66a-cfa2244e0801\"", + "properties": { + "provisioningState": "Succeeded", + "type": "Static", + "mode": "EgressSnat", + "internalMappings": [ + { + "addressSpace": "10.10.0.0/24" + } + ], + "externalMappings": [ + { + "addressSpace": "50.0.0.0/24" + } + ] + } + }, + { + "name": "natRule2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualNetworkGateways/vpngw2/natRules/natRule2", + "etag": "W/\"00ae2b69-88e7-4b3a-b66a-cfa2244e0801\"", + "properties": { + "provisioningState": "Succeeded", + "type": "Static", + "mode": "IngressSnat", + "internalMappings": [ + { + "addressSpace": "20.10.0.0/24" + } + ], + "externalMappings": [ + { + "addressSpace": "30.0.0.0/24" + } + ] + } + } + ], + "enableBgpRouteTranslationForNat": false, + "sku": { + "name": "VpnGw1", + "tier": "VpnGw1", + "capacity": 2 + }, + "gatewayType": "Vpn", + "vpnType": "RouteBased", + "vpnGatewayGeneration": "None", + "enableBgp": false, + "enablePrivateIpAddress": true, + "activeActive": false, + "disableIPSecReplayProtection": false, + "vpnClientConfiguration": { + "vpnClientProtocols": [ + "OpenVPN" + ], + "vpnClientRootCertificates": [], + "vpnClientRevokedCertificates": [], + "radiusServers": [ + { + "radiusServerAddress": "10.2.0.0", + "radiusServerScore": 20 + } + ] + }, + "bgpSettings": { + "asn": 65515, + "bgpPeeringAddress": "10.1.0.46", + "peerWeight": 0 + }, + "customRoutes": { + "addressPrefixes": [ + "101.168.0.6/32" + ] + }, + "allowVirtualWanTraffic": false, + "allowRemoteVnetTraffic": false + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/files/network/vpnConnections.json b/plugins/azure/test/files/network/vpnConnections.json new file mode 100644 index 0000000000..0452a13a11 --- /dev/null +++ b/plugins/azure/test/files/network/vpnConnections.json @@ -0,0 +1,97 @@ +{ + "value": [ + { + "name": "vpnConnection1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnGateways/gateway1/vpnConnections/vpnConnection1", + "etag": "w/\\00000000-0000-0000-0000-000000000000\\", + "properties": { + "provisioningState": "Succeeded", + "remoteVpnSite": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnSites/vpnSite1" + }, + "enableInternetSecurity": false, + "ingressBytesTransferred": 0, + "egressBytesTransferred": 0, + "trafficSelectorPolicies": [], + "vpnLinkConnections": [ + { + "name": "Connection-Link1", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnGateways/gateway1/vpnConnections/vpnConnection1/VpnSiteLinkConnections/Connection-Link1", + "type": "Microsoft.Network/vpnGateways/vpnConnections/VpnSiteLinkConnections", + "etag": "w/\\00000000-0000-0000-0000-000000000000\\", + "properties": { + "provisioningState": "Succeeded", + "vpnSiteLink": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnSites/vpnSite1/vpnSiteLinks/siteLink1" + }, + "connectionBandwidth": 200, + "ipsecPolicies": [], + "vpnConnectionProtocolType": "IKEv2", + "sharedKey": "key", + "ingressBytesTransferred": 0, + "egressBytesTransferred": 0, + "enableBgp": false, + "enableRateLimiting": false, + "useLocalAzureIpAddress": false, + "usePolicyBasedTrafficSelectors": false, + "routingWeight": 0, + "vpnLinkConnectionMode": "Default" + } + }, + { + "name": "Connection-Link2", + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnGateways/gateway1/vpnConnections/vpnConnection1/VpnSiteLinkConnections/Connection-Link2", + "type": "Microsoft.Network/vpnGateways/vpnConnections/VpnSiteLinkConnections", + "etag": "w/\\00000000-0000-0000-0000-000000000000\\", + "properties": { + "provisioningState": "Succeeded", + "vpnSiteLink": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/vpnSites/vpnSite1/vpnSiteLinks/siteLink2" + }, + "connectionBandwidth": 200, + "ipsecPolicies": [], + "vpnConnectionProtocolType": "IKEv2", + "sharedKey": "key", + "ingressBytesTransferred": 0, + "egressBytesTransferred": 0, + "enableBgp": false, + "enableRateLimiting": false, + "useLocalAzureIpAddress": false, + "usePolicyBasedTrafficSelectors": false, + "routingWeight": 0, + "vpnLinkConnectionMode": "Default" + } + } + ], + "routingConfiguration": { + "associatedRouteTable": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/hub1/hubRouteTables/hubRouteTable1" + }, + "propagatedRouteTables": { + "labels": [ + "label1", + "label2" + ], + "ids": [ + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/hub1/hubRouteTables/hubRouteTable1" + }, + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/hub1/hubRouteTables/hubRouteTable2" + }, + { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/hub1/hubRouteTables/hubRouteTable3" + } + ] + }, + "inboundRouteMap": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/virtualHub1/routeMaps/routeMap1" + }, + "outboundRouteMap": { + "id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/virtualHubs/virtualHub1/routeMaps/routeMap2" + } + } + } + } + ] +} \ No newline at end of file diff --git a/plugins/azure/test/microsoft_graph_test.py b/plugins/azure/test/microsoft_graph_test.py new file mode 100644 index 0000000000..d7755f33c9 --- /dev/null +++ b/plugins/azure/test/microsoft_graph_test.py @@ -0,0 +1,34 @@ +from conftest import roundtrip_check +from fix_plugin_azure.resource.base import GraphBuilder +from fix_plugin_azure.resource.microsoft_graph import ( + MicrosoftGraphDevice, + MicrosoftGraphServicePrincipal, + MicrosoftGraphGroup, + MicrosoftGraphRole, + MicrosoftGraphUser, +) + + +def test_microsoft_graph_device(builder: GraphBuilder) -> None: + collected = roundtrip_check(MicrosoftGraphDevice, builder) + assert len(collected) == 1 + + +def test_microsoft_graph_service_principal(builder: GraphBuilder) -> None: + collected = roundtrip_check(MicrosoftGraphServicePrincipal, builder) + assert len(collected) == 1 + + +def test_microsoft_graph_group(builder: GraphBuilder) -> None: + collected = roundtrip_check(MicrosoftGraphGroup, builder) + assert len(collected) == 1 + + +def test_microsoft_graph_role(builder: GraphBuilder) -> None: + collected = roundtrip_check(MicrosoftGraphRole, builder) + assert len(collected) == 1 + + +def test_microsoft_graph_user(builder: GraphBuilder) -> None: + collected = roundtrip_check(MicrosoftGraphUser, builder) + assert len(collected) == 1 diff --git a/plugins/azure/test/network_test.py b/plugins/azure/test/network_test.py index 65e19d52a1..7aec480d0f 100644 --- a/plugins/azure/test/network_test.py +++ b/plugins/azure/test/network_test.py @@ -131,13 +131,14 @@ def test_load_balancer(builder: GraphBuilder) -> None: ] assert len(collected) == 2 - resource_types: List[Type[MicrosoftResource]] = [AzureVirtualNetwork, AzureManagedCluster] + resource_types: List[Type[MicrosoftResource]] = [AzureVirtualNetwork, AzureManagedCluster, AzureLoadBalancerProbe] roundtrip_check(AzurePublicIPAddress, builder) connect_resources(builder, resource_types) assert collected[0].aks_public_ip_address == "41.85.154.247" assert len(builder.edges_of(AzureVirtualNetwork, AzureLoadBalancer)) == 1 assert len(builder.edges_of(AzureManagedCluster, AzureLoadBalancer)) == 1 + assert len(builder.edges_of(AzureLoadBalancer, AzureLoadBalancerProbe)) == 2 def test_network_profile(builder: GraphBuilder) -> None: @@ -217,7 +218,7 @@ def test_virtual_hub(builder: GraphBuilder) -> None: resource_types: List[Type[MicrosoftResource]] = [ AzureExpressRouteGateway, - AzureVpnGateway, + AzureVirtualWANVpnGateway, AzureVirtualWAN, AzurePublicIPAddress, ] @@ -225,7 +226,7 @@ def test_virtual_hub(builder: GraphBuilder) -> None: connect_resources(builder, resource_types) assert len(builder.edges_of(AzureExpressRouteGateway, AzureVirtualHub)) == 1 - assert len(builder.edges_of(AzureVpnGateway, AzureVirtualHub)) == 1 + assert len(builder.edges_of(AzureVirtualWANVpnGateway, AzureVirtualHub)) == 1 assert len(builder.edges_of(AzureVirtualWAN, AzureVirtualHub)) == 1 assert len(builder.edges_of(AzureVirtualHub, AzurePublicIPAddress)) == 1 @@ -246,9 +247,16 @@ def test_virtual_wan(builder: GraphBuilder) -> None: def test_vpn_gateway(builder: GraphBuilder) -> None: - collected = roundtrip_check(AzureVpnGateway, builder) + collected = roundtrip_check(AzureVirtualWANVpnGateway, builder) assert len(collected) == 2 + resource_types: List[Type[MicrosoftResource]] = [ + AzureVirtualWANVpnConnection, + ] + connect_resources(builder, resource_types) + + assert len(builder.edges_of(AzureVirtualWANVpnGateway, AzureVirtualWANVpnConnection)) == 2 + def test_vpn_server_configuration(builder: GraphBuilder) -> None: collected = roundtrip_check(AzureVpnServerConfiguration, builder)