Skip to content

Commit 4125af7

Browse files
[azure][feat] Improve SQL Server (#2151)
Signed-off-by: Lukas Lösche <lukas@some.engineering> Co-authored-by: Lukas Lösche <lukas@some.engineering>
1 parent c8bb791 commit 4125af7

File tree

10 files changed

+197
-45
lines changed

10 files changed

+197
-45
lines changed

plugins/azure/fix_plugin_azure/collector.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
AzureNetworkUsage,
3535
resources as network_resources,
3636
)
37-
from fix_plugin_azure.resource.sql import resources as sql_resources
3837
from fix_plugin_azure.resource.mysql import AzureMysqlCapability, AzureMysqlServerType, resources as mysql_resources
38+
from fix_plugin_azure.resource.sql_server import resources as sql_resources
3939
from fix_plugin_azure.resource.storage import AzureStorageAccountUsage, AzureStorageSku, resources as storage_resources
4040
from fixlib.baseresources import Cloud, GraphRoot, BaseAccount, BaseRegion
4141
from fixlib.core.actions import CoreFeedback, ErrorAccumulator

plugins/azure/fix_plugin_azure/resource/base.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from fixlib.config import current_config
2424
from fixlib.core.actions import CoreFeedback
2525
from fixlib.graph import Graph, EdgeKey, NodeSelector, ByNodeId
26+
from fixlib.json import from_json
2627
from fixlib.json_bender import AsFloat, Bender, bend, S, ForallBend, Bend
2728
from fixlib.lock import RWLock
2829
from fixlib.threading import ExecutorQueue
@@ -50,6 +51,30 @@ def get_client(subscription_id: str) -> MicrosoftClient:
5051
T = TypeVar("T")
5152

5253

54+
def parse_json(
55+
json: Json, clazz: Type[T], builder: GraphBuilder, mapping: Optional[Dict[str, Bender]] = None
56+
) -> Optional[T]:
57+
"""
58+
Use this method to parse json into a class. If the json can not be parsed, the error is reported to the core.
59+
Based on configuration, either the exception is raised or None is returned.
60+
:param json: the json to parse.
61+
:param clazz: the class to parse into.
62+
:param builder: the graph builder.
63+
:param mapping: the optional mapping to apply before parsing.
64+
:return: The parsed object or None.
65+
"""
66+
try:
67+
mapped = bend(mapping, json) if mapping is not None else json
68+
return from_json(mapped, clazz)
69+
except Exception as e:
70+
# report and log the error
71+
builder.core_feedback.error(f"Failed to parse json into {clazz.__name__}: {e}. Source: {json}", log)
72+
# based on the strict flag, either raise the exception or return None
73+
if builder.config.discard_account_on_resource_error:
74+
raise
75+
return None
76+
77+
5378
class MicrosoftResource(BaseResource):
5479
kind: ClassVar[str] = "microsoft_resource"
5580
# The mapping to transform the incoming API json into the internal representation.

plugins/azure/fix_plugin_azure/resource/sql.py renamed to plugins/azure/fix_plugin_azure/resource/sql_server.py

Lines changed: 116 additions & 37 deletions
Large diffs are not rendered by default.

plugins/azure/test/collector_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def test_collect(
5050
)
5151
subscription_collector.collect()
5252
assert len(subscription_collector.graph.nodes) == 407
53-
assert len(subscription_collector.graph.edges) == 625
53+
assert len(subscription_collector.graph.edges) == 627
5454

5555
graph_collector = MicrosoftGraphOrganizationCollector(
5656
config, Cloud(id="azure"), MicrosoftGraphOrganization(id="test", name="test"), credentials, core_feedback

plugins/azure/test/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def list(self, spec: MicrosoftRestSpec, **kwargs: Any) -> List[Json]:
3636
js = json.load(f)
3737

3838
if spec.expect_array:
39-
js = js[spec.access_path]
39+
js = js[spec.access_path] if spec.access_path else [js]
4040
if spec.expect_array and isinstance(js, list):
4141
return js
4242
else:
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"value": [
3+
{
4+
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/providers/Microsoft.Sql/servers/test-server111/extendedAuditingSettings/Default",
5+
"name": "Default",
6+
"properties": {
7+
"auditActionsAndGroups": [],
8+
"isAzureMonitorTargetEnabled": false,
9+
"isManagedIdentityInUse": false,
10+
"isStorageSecondaryKeyInUse": false,
11+
"predicateExpression": "",
12+
"retentionDays": 0,
13+
"state": "Disabled",
14+
"storageAccountSubscriptionId": "00000000-0000-0000-0000-000000000000",
15+
"storageEndpoint": ""
16+
},
17+
"type": "Microsoft.Sql/servers/extendedAuditingSettings"
18+
}
19+
]
20+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/providers/Microsoft.Sql/servers/test-server111/databases/master/transparentDataEncryption/Current",
3+
"name": "Current",
4+
"properties": {
5+
"state": "Disabled"
6+
},
7+
"type": "Microsoft.Sql/servers/databases/transparentDataEncryption"
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"value": [
3+
{
4+
"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/providers/Microsoft.Sql/servers/test-server111/encryptionProtector/current",
5+
"kind": "servicemanaged",
6+
"name": "current",
7+
"properties": {
8+
"autoRotationEnabled": false,
9+
"serverKeyName": "ServiceManaged",
10+
"serverKeyType": "ServiceManaged"
11+
},
12+
"type": "Microsoft.Sql/servers/encryptionProtector"
13+
}
14+
]
15+
}

plugins/azure/test/sql_test.py renamed to plugins/azure/test/sql_server_test.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from conftest import roundtrip_check
22
from fix_plugin_azure.resource.base import GraphBuilder
3-
from fix_plugin_azure.resource.sql import (
3+
from fix_plugin_azure.resource.sql_server import (
44
AzureSqlServerManagedInstancePool,
55
AzureSqlServerManagedInstance,
66
AzureSqlServer,
@@ -20,7 +20,10 @@ def test_sql_managed_instance(builder: GraphBuilder) -> None:
2020

2121
def test_sql_server(builder: GraphBuilder) -> None:
2222
collected = roundtrip_check(AzureSqlServer, builder)
23+
builder.executor.wait_for_submitted_work()
2324
assert len(collected) == 1
25+
assert collected[0].blob_auditing_policy is not None
26+
assert collected[0].encryption_protector is not None
2427

2528

2629
def test_sql_virtual_cluster(builder: GraphBuilder) -> None:

plugins/azure/tools/azure_model_gen.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ def parse_spec(service: str, version: str, resolved: Json, file: Path) -> Iterat
389389
"subscriptionId",
390390
"location",
391391
"scope",
392+
# "databaseName",
393+
# "serverName",
394+
# "resourceGroupName",
392395
}
393396
if len(param_names) == 0:
394397
schema = method["responses"]["200"]["schema"]
@@ -420,9 +423,8 @@ def parse_spec(service: str, version: str, resolved: Json, file: Path) -> Iterat
420423
)
421424
if ssh := simple_shape(type_definition):
422425
yield AzureRestSpec(ssh, info, type_definition, file)
423-
else:
424-
name = type_definition["ref"] # name is added in the ref parser
425-
yield AzureRestSpec(name, info, type_definition, file)
426+
elif tdef := type_definition.get("ref"):
427+
yield AzureRestSpec(tdef, info, type_definition, file)
426428

427429

428430
class AzureModel:
@@ -617,7 +619,7 @@ def safe_idx(seq, index):
617619
"Checkout https://github.com/Azure/azure-rest-api-specs and set path in env"
618620
)
619621
model = AzureModel(Path(specs_path))
620-
shapes = {spec.name: spec for spec in sorted(model.list_specs({"security"}), key=lambda x: x.name)}
622+
shapes = {spec.name: spec for spec in sorted(model.list_specs({"sql"}), key=lambda x: x.name)}
621623
models = classes_from_model(shapes)
622624
for model in models.values():
623625
if model.name != "Resource":

0 commit comments

Comments
 (0)