Skip to content

Commit 34d50ba

Browse files
authored
[azure][feat] Add flow log resource collection (#2159)
1 parent fdaeb53 commit 34d50ba

File tree

4 files changed

+127
-14
lines changed

4 files changed

+127
-14
lines changed

plugins/azure/fix_plugin_azure/resource/network.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
MicrosoftResource,
1616
)
1717
from fix_plugin_azure.resource.containerservice import AzureManagedCluster
18+
from fix_plugin_azure.resource.storage import AzureStorageAccount
1819
from fix_plugin_azure.utils import rgetattr
1920
from fixlib.baseresources import (
2021
BaseDNSRecordSet,
@@ -1743,19 +1744,24 @@ class AzureTrafficAnalyticsProperties:
17431744

17441745

17451746
@define(eq=False, slots=False)
1746-
class AzureFlowLog:
1747+
class AzureFlowLog(MicrosoftResource):
17471748
kind: ClassVar[str] = "azure_flow_log"
1749+
# Collect via AzureNetworkWatcher()
1750+
reference_kinds: ClassVar[ModelReference] = {
1751+
"predecessors": {"default": ["azure_storage_account"]},
1752+
}
17481753
mapping: ClassVar[Dict[str, Bender]] = {
17491754
"enabled": S("properties", "enabled"),
17501755
"etag": S("etag"),
17511756
"flow_analytics_configuration": S("properties", "flowAnalyticsConfiguration")
17521757
>> Bend(AzureTrafficAnalyticsProperties.mapping),
1753-
"format": S("properties", "format") >> Bend(AzureFlowLogFormatParameters.mapping),
1758+
"flow_log_format": S("properties", "format") >> Bend(AzureFlowLogFormatParameters.mapping),
17541759
"id": S("id"),
17551760
"location": S("location"),
17561761
"name": S("name"),
17571762
"provisioning_state": S("properties", "provisioningState"),
1758-
"retention_policy": S("properties", "retentionPolicy") >> Bend(AzureRetentionPolicyParameters.mapping),
1763+
"retention_policy_parameters": S("properties", "retentionPolicy")
1764+
>> Bend(AzureRetentionPolicyParameters.mapping),
17591765
"storage_id": S("properties", "storageId"),
17601766
"tags": S("tags", default={}),
17611767
"target_resource_guid": S("properties", "targetResourceGuid"),
@@ -1765,18 +1771,18 @@ class AzureFlowLog:
17651771
enabled: Optional[bool] = field(default=None, metadata={"description": "Flag to enable/disable flow logging."})
17661772
etag: Optional[str] = field(default=None, metadata={'description': 'A unique read-only string that changes whenever the resource is updated.'}) # fmt: skip
17671773
flow_analytics_configuration: Optional[AzureTrafficAnalyticsProperties] = field(default=None, metadata={'description': 'Parameters that define the configuration of traffic analytics.'}) # fmt: skip
1768-
format: Optional[AzureFlowLogFormatParameters] = field(default=None, metadata={'description': 'Parameters that define the flow log format.'}) # fmt: skip
1769-
id: Optional[str] = field(default=None, metadata={"description": "Resource ID."})
1774+
flow_log_format: Optional[AzureFlowLogFormatParameters] = field(default=None, metadata={'description': 'Parameters that define the flow log format.'}) # fmt: skip
17701775
location: Optional[str] = field(default=None, metadata={"description": "Resource location."})
1771-
name: Optional[str] = field(default=None, metadata={"description": "Resource name."})
1772-
provisioning_state: Optional[str] = field(default=None, metadata={'description': 'The current provisioning state.'}) # fmt: skip
1773-
retention_policy: Optional[AzureRetentionPolicyParameters] = field(default=None, metadata={'description': 'Parameters that define the retention policy for flow log.'}) # fmt: skip
1776+
retention_policy_parameters: Optional[AzureRetentionPolicyParameters] = field(default=None, metadata={'description': 'Parameters that define the retention policy for flow log.'}) # fmt: skip
17741777
storage_id: Optional[str] = field(default=None, metadata={'description': 'ID of the storage account which is used to store the flow log.'}) # fmt: skip
1775-
tags: Optional[Dict[str, str]] = field(default=None, metadata={"description": "Resource tags."})
17761778
target_resource_guid: Optional[str] = field(default=None, metadata={'description': 'Guid of network security group to which flow log will be applied.'}) # fmt: skip
17771779
target_resource_id: Optional[str] = field(default=None, metadata={'description': 'ID of network security group to which flow log will be applied.'}) # fmt: skip
17781780
type: Optional[str] = field(default=None, metadata={"description": "Resource type."})
17791781

1782+
def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
1783+
if storage_id := self.storage_id:
1784+
builder.add_edge(self, edge_type=EdgeType.default, reverse=True, clazz=AzureStorageAccount, id=storage_id)
1785+
17801786

17811787
@define(eq=False, slots=False)
17821788
class AzureNetworkSecurityGroup(MicrosoftResource, BaseSecurityGroup):
@@ -1790,24 +1796,32 @@ class AzureNetworkSecurityGroup(MicrosoftResource, BaseSecurityGroup):
17901796
access_path="value",
17911797
expect_array=True,
17921798
)
1799+
reference_kinds: ClassVar[ModelReference] = {
1800+
"successors": {"default": ["azure_flow_log"]},
1801+
}
17931802
mapping: ClassVar[Dict[str, Bender]] = {
17941803
"id": S("id"),
17951804
"tags": S("tags", default={}),
17961805
"name": S("name"),
17971806
"default_security_rules": S("properties", "defaultSecurityRules") >> ForallBend(AzureSecurityRule.mapping),
17981807
"etag": S("etag"),
1799-
"flow_logs": S("properties", "flowLogs") >> ForallBend(AzureFlowLog.mapping),
1808+
"flow_log_ids": S("properties", "flowLogs") >> ForallBend(S("id")),
18001809
"flush_connection": S("properties", "flushConnection"),
18011810
"provisioning_state": S("properties", "provisioningState"),
18021811
"resource_guid": S("properties", "resourceGuid"),
18031812
"security_rules": S("properties", "securityRules") >> ForallBend(AzureSecurityRule.mapping),
18041813
}
18051814
default_security_rules: Optional[List[AzureSecurityRule]] = field(default=None, metadata={'description': 'The default security rules of network security group.'}) # fmt: skip
1806-
flow_logs: Optional[List[AzureFlowLog]] = field(default=None, metadata={'description': 'A collection of references to flow log resources.'}) # fmt: skip
1815+
flow_log_ids: Optional[List[str]] = field(default=None, metadata={'description': 'A collection of references to flow log resources.'}) # fmt: skip
18071816
flush_connection: Optional[bool] = field(default=None, metadata={'description': 'When enabled, flows created from Network Security Group connections will be re-evaluated when rules are updates. Initial enablement will trigger re-evaluation.'}) # fmt: skip
18081817
resource_guid: Optional[str] = field(default=None, metadata={'description': 'The resource GUID property of the network security group resource.'}) # fmt: skip
18091818
security_rules: Optional[List[AzureSecurityRule]] = field(default=None, metadata={'description': 'A collection of security rules of the network security group.'}) # fmt: skip
18101819

1820+
def connect_in_graph(self, builder: GraphBuilder, source: Json) -> None:
1821+
if flow_log_ids := self.flow_log_ids:
1822+
for flow_log_id in flow_log_ids:
1823+
builder.add_edge(self, edge_type=EdgeType.default, clazz=AzureFlowLog, id=flow_log_id)
1824+
18111825

18121826
@define(eq=False, slots=False)
18131827
class AzureNetworkInterfaceTapConfiguration(AzureSubResource):
@@ -4411,6 +4425,7 @@ class AzureNetworkWatcher(MicrosoftResource):
44114425
)
44124426
reference_kinds: ClassVar[ModelReference] = {
44134427
"predecessors": {"default": ["azure_virtual_network"]},
4428+
"successors": {"default": ["azure_flow_log"]},
44144429
}
44154430
mapping: ClassVar[Dict[str, Bender]] = {
44164431
"id": S("id"),
@@ -4441,6 +4456,28 @@ def _get_virtual_network_locations_and_ids(self, builder: GraphBuilder) -> List[
44414456
if (vn_location := network.location) and (vn_id := network.id)
44424457
]
44434458

4459+
def post_process(self, graph_builder: GraphBuilder, source: Json) -> None:
4460+
if watcher_id := self.id:
4461+
4462+
def collect_flow_logs() -> None:
4463+
api_spec = AzureResourceSpec(
4464+
service="network",
4465+
version="2024-01-01",
4466+
path=f"{watcher_id}/flowLogs",
4467+
path_parameters=[],
4468+
query_parameters=["api-version"],
4469+
access_path="value",
4470+
expect_array=True,
4471+
)
4472+
items = graph_builder.client.list(api_spec)
4473+
if not items:
4474+
return
4475+
collected = AzureFlowLog.collect(items, graph_builder)
4476+
for resource in collected:
4477+
graph_builder.add_edge(self, node=resource)
4478+
4479+
graph_builder.submit_work(service_name, collect_flow_logs)
4480+
44444481

44454482
@define(eq=False, slots=False)
44464483
class AzureProviderResourceOperationDescription:
@@ -6470,6 +6507,7 @@ def collect_record_sets() -> None:
64706507
AzureNetworkVirtualAppliance,
64716508
AzureNetworkVirtualApplianceSku,
64726509
AzureNetworkWatcher,
6510+
AzureFlowLog,
64736511
AzureP2SVpnGateway,
64746512
AzurePrivateLinkService,
64756513
AzurePublicIPAddress,

plugins/azure/fix_plugin_azure/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def set_bool(val: str) -> bool:
5656
"Enumeration": lambda x: set_bool(x) if x.lower() in ["on", "off"] else str(x),
5757
"Integer": int,
5858
"Numeric": float,
59-
"Set": lambda x: x.split(","),
59+
"Set": lambda x: [s.strip() for s in x.split(",")],
6060
"String": str,
6161
"Boolean": set_bool,
6262
}

plugins/azure/test/collector_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def test_collect(
4949
config, Cloud(id="azure"), azure_subscription, credentials, core_feedback
5050
)
5151
subscription_collector.collect()
52-
assert len(subscription_collector.graph.nodes) == 446
53-
assert len(subscription_collector.graph.edges) == 681
52+
assert len(subscription_collector.graph.nodes) == 450
53+
assert len(subscription_collector.graph.edges) == 689
5454

5555
graph_collector = MicrosoftGraphOrganizationCollector(
5656
config, Cloud(id="azure"), MicrosoftGraphOrganization(id="test", name="test"), credentials, core_feedback
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
{
2+
"value": [
3+
{
4+
"name": "flowLog1",
5+
"id": "/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.Network/networkWatchers/тц1/FlowLogs/flowLog1",
6+
"etag": "W/\"00000000-0000-0000-0000-000000000000\"",
7+
"properties": {
8+
"provisioningState": "Succeeded",
9+
"targetResourceId": "/subscriptions/subid/resourceGroups/rg/providers/Microsoft.Network/networkSecurityGroups/vm5-nsg",
10+
"targetResourceGuid": "00000000-0000-0000-0000-000000000000",
11+
"storageId": "/subscriptions/subid/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/wzstorage002",
12+
"enabled": true,
13+
"flowAnalyticsConfiguration": {
14+
"networkWatcherFlowAnalyticsConfiguration": {
15+
"enabled": false,
16+
"workspaceId": "-",
17+
"workspaceRegion": "-",
18+
"trafficAnalyticsInterval": 60
19+
}
20+
},
21+
"retentionPolicy": {
22+
"days": 0,
23+
"enabled": false
24+
},
25+
"format": {
26+
"type": "JSON",
27+
"version": 2
28+
}
29+
},
30+
"location": "centraluseuap",
31+
"type": "Microsoft.Network/networkWatchers/FlowLogs",
32+
"identity": {
33+
"type": "UserAssigned",
34+
"userAssignedIdentities": {
35+
"/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id1": {
36+
"clientId": "c16d15e1-f60a-40e4-8a05-df3d3f655c14",
37+
"principalId": "e3858881-e40c-43bd-9cde-88da39c05023"
38+
}
39+
}
40+
}
41+
},
42+
{
43+
"name": "flowLog2",
44+
"id": "/subscriptions/96e68903-0a56-4819-9987-8d08ad6a1f99/resourceGroups/NetworkWatcherRG/providers/Microsoft.Network/networkWatchers/NetworkWatcher_centraluseuap/FlowLogs/flowLog2",
45+
"etag": "W/\"00000000-0000-0000-0000-000000000000\"",
46+
"properties": {
47+
"provisioningState": "Succeeded",
48+
"targetResourceId": "/subscriptions/subid/resourceGroups/rg/providers/Microsoft.Network/networkSecurityGroups/DSCP-test-vm1-nsg",
49+
"targetResourceGuid": "00000000-0000-0000-0000-000000000000",
50+
"storageId": "/subscriptions/subid/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/iraflowlogtest2diag",
51+
"enabled": true,
52+
"flowAnalyticsConfiguration": {},
53+
"retentionPolicy": {
54+
"days": 0,
55+
"enabled": false
56+
},
57+
"format": {
58+
"type": "JSON",
59+
"version": 2
60+
}
61+
},
62+
"type": "Microsoft.Network/networkWatchers/FlowLogs",
63+
"location": "centraluseuap",
64+
"identity": {
65+
"type": "UserAssigned",
66+
"userAssignedIdentities": {
67+
"/subscriptions/subid/resourceGroups/rg1/providers/Microsoft.ManagedIdentity/userAssignedIdentities/id1": {
68+
"clientId": "c16d15e1-f60a-40e4-8a05-df3d3f655c14",
69+
"principalId": "e3858881-e40c-43bd-9cde-88da39c05023"
70+
}
71+
}
72+
}
73+
}
74+
]
75+
}

0 commit comments

Comments
 (0)