Repository: clouditor/clouditor
Pull request #931 (2023-01-23 10:38:52)

Add new Azure discoveries
This PR adds missing discovery: 
- [x] Microsoft Defender Information
- [x] Backup Object Storages
- [x] Backup Compute Disks
- [x] VM AutomaticUpdates
- [x] Application Gateway
- [x] L3 Firewall based on NSG

lebogg: Shouldn't we hide new metrics/metric names ? :)
anatheka: The panic error comes from "func (mockSender) Do(req *http.Request)" because no entry for "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances" exists in "func (m mockComputeSender) Do(req *http.Request) ". 

How to solve that? What does the Azure endpoint return? 
anatheka: @oxisto do you have an idea why the test fails here, but not locally? 
oxisto: > @oxisto do you have an idea why the test fails here, but not locally?

Running `go test -timeout 1m clouditor.io/clouditor/service/discovery/azure -json | tparse -follow` in a terminal also panics locally because of this:

```
time="2023-05-03T19:29:42+02:00" level=error msg="could not discover backup vaults: error getting next page: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)" component=azure-discovery
```

In Test_azureComputeDiscovery_List/With_resource_group

If I run it in vscode, it somehow passes and recovery the panic though.
oxisto: > > @oxisto do you have an idea why the test fails here, but not locally?
> 
> Running `go test -timeout 1m clouditor.io/clouditor/service/discovery/azure -json | tparse -follow` in a terminal also panics locally because of this:
> 
> ```
> time="2023-05-03T19:29:42+02:00" level=error msg="could not discover backup vaults: error getting next page: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)" component=azure-discovery
> ```
> 
> In Test_azureComputeDiscovery_List/With_resource_group
> 
> If I run it in vscode, it somehow passes and recovery the panic though.

Seems to be caused by this here:

```
time="2023-05-03T19:34:56+02:00" level=error msg="Not handling mock for /subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.DataProtection/backupVaults yet" component=azure-discovery
```
oxisto: > > > @oxisto do you have an idea why the test fails here, but not locally?
> > 
> > 
> > Running `go test -timeout 1m clouditor.io/clouditor/service/discovery/azure -json | tparse -follow` in a terminal also panics locally because of this:
> > ```
> > time="2023-05-03T19:29:42+02:00" level=error msg="could not discover backup vaults: error getting next page: %!v(PANIC=Error method: runtime error: invalid memory address or nil pointer dereference)" component=azure-discovery
> > ```
> > 
> > 
> >     
> >       
> >     
> > 
> >       
> >     
> > 
> >     
> >   
> > In Test_azureComputeDiscovery_List/With_resource_group
> > If I run it in vscode, it somehow passes and recovery the panic though.
> 
> Seems to be caused by this here:
> 
> ```
> time="2023-05-03T19:34:56+02:00" level=error msg="Not handling mock for /subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.DataProtection/backupVaults yet" component=azure-discovery
> ```

Actually it seems to crash in the "Discovery error" test case, because this intentionally sets a wrong "sender" (the network mock sender) instead of compute. This one doesn't have the mocks for backupVaults and that's why it crashed. Not sure exactly where the issue is because it is perfectly ok for the network mock sender NOT to have the backup vaults endpoint and it should just return a 404, not sure why it panics here.
oxisto: Ok I found it why .... this is caused by the `ResponseError` struct inside the azure SDK, which assumes that certain structs are there to fill out an error message, which are not there in our mock. I will fix this :)

Patch:
diff --git a/go.mod b/go.mod
index 3b0097a8..e62d58d7 100644
--- a/go.mod
+++ b/go.mod
@@ -34,7 +34,9 @@ require (
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.1.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.0
+	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection v1.0.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0
+	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.9.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.1.0
 )
@@ -142,6 +144,7 @@ require (
 	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/yashtewari/glob-intersection v0.1.0 // indirect
+	go.uber.org/atomic v1.9.0 // indirect
 	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
 	golang.org/x/mod v0.10.0 // indirect
 	golang.org/x/net v0.10.0 // indirect
@@ -168,6 +171,4 @@ require (
 	sigs.k8s.io/yaml v1.3.0 // indirect
 )
 
-require go.uber.org/atomic v1.9.0 // indirect
-
 retract v1.4.7
diff --git a/go.sum b/go.sum
index d5341ef2..516e7a71 100644
--- a/go.sum
+++ b/go.sum
@@ -50,10 +50,14 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 h1:/Di3vB4sNeQ+7A8efjUVENvyB945Wruvstucqp7ZArg=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.0 h1:/CpfzcaYyHdMdx545O0y+oN1jnyePITaZPq9udnw3So=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.0/go.mod h1:LHYqCFeEZ4Cy1FXDxfJ/FwUdbAGaMw4u3Vvhp+xVk4o=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection v1.0.0 h1:VFqjVi532z3gdltbAkYrPl9Ez0czn3ZPM+bjmvLq6fk=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection v1.0.0/go.mod h1:CmZQSRwBPP7KNjDA+PHaoR2m8wgOsbTd9ncqZgSzgHA=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal v1.1.2 h1:mLY+pNLjCUeKhgnAJWAKhEUQM+RJQo2H1fuGSw1Ky1E=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0 h1:QM6sE5k2ZT/vI5BEe0r7mqjsUSnhVBFbOsVkEuaEfiA=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork v1.1.0/go.mod h1:243D9iHbcQXoFUtgHJwL7gl2zx1aDuDMjvBZVGr2uW0=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 h1:ECsQtyERDVz3NP3kvDOTLvbQhqWp/x9EsGKtb4ogUr8=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.9.0 h1:leZbYXt1X1+IXOhinVq/eyCu4J/fR/RcgdF6lWeaa5o=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.9.0/go.mod h1:iwDIDV5jIo+eXQf6RA7PwyJnycTQxX4s4MGKSy+m1LA=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0 h1:LcJtQjCXJUm1s7JpUHZvu+bpgURhCatxVNbGADXniX0=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.3.0/go.mod h1:+OgGVo0Httq7N5oayfvaLQ/Jq+2gJdqfp++Hyyl7Tws=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription v1.1.0 h1:pYhaMoTHP/zYIJGDA1sWsfyTDjdglaoYjIFMOEcL+/U=
diff --git a/internal/constants/constants.go b/internal/constants/constants.go
new file mode 100644
index 00000000..7f18605b
--- /dev/null
+++ b/internal/constants/constants.go
@@ -0,0 +1,33 @@
+// Copyright 2023 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package constants
+
+const (
+	TLS    = "TLS"
+	AES    = "AES"
+	TLS1_2 = "TLS1_2"
+	AES256 = "AES256"
+)
diff --git a/policies/bundles/AutomaticUpdatesInterval/data.json b/policies/bundles/AutomaticUpdatesInterval/data.json
index 053755b8..c47f170e 100644
--- a/policies/bundles/AutomaticUpdatesInterval/data.json
+++ b/policies/bundles/AutomaticUpdatesInterval/data.json
@@ -1,4 +1,4 @@
 {
   "operator": "<=",
-  "target_value": 168
+  "target_value": 720
 }
\ No newline at end of file
diff --git a/policies/bundles/AutomaticUpdatesInterval/input.json b/policies/bundles/AutomaticUpdatesInterval/input.json
index 8c883bd2..97bf2740 100644
--- a/policies/bundles/AutomaticUpdatesInterval/input.json
+++ b/policies/bundles/AutomaticUpdatesInterval/input.json
@@ -1,5 +1,6 @@
 {
     "automaticUpdates": {
+        "_comment": "86400000000000 nanoseconds equals 1 day",
         "interval": 86400000000000
     }
 }
\ No newline at end of file
diff --git a/policies/bundles/AutomaticUpdatesInterval/metric.rego b/policies/bundles/AutomaticUpdatesInterval/metric.rego
index 2f55cb31..6b006a85 100644
--- a/policies/bundles/AutomaticUpdatesInterval/metric.rego
+++ b/policies/bundles/AutomaticUpdatesInterval/metric.rego
@@ -12,6 +12,6 @@ applicable {
 }
 
 compliant {
-	# time.Duration is nanoseconds, we want to convert this to hours
-	compare(data.operator, data.target_value, am.interval / (((1000 * 1000) * 1000) * 3600))
+	# time.Duration is nanoseconds, we want to convert this to days
+	compare(data.operator, data.target_value, am.interval / (1000000000 * 86400)) # nanoseconds to seconds (/1000000000), seconds to days (/(60*60*24) = 86400) 
 }
diff --git a/policies/operators.rego b/policies/operators.rego
index 4ba9c0e8..14ed2f47 100644
--- a/policies/operators.rego
+++ b/policies/operators.rego
@@ -51,6 +51,27 @@ compare(operator, target_values, actual_values) {
     act_val in target_values
 }
 
+# Checks if one element of target_values (array) exists in key of actual_values (object)
+compare(operator, target_values, actual_values) {
+	operator == "isIn"
+    is_object(actual_values)
+    # Get all keys from objects
+    value := object.keys(actual_values)
+    # Check if one the keys is in array of target_values
+    some v in value
+    v in target_values
+}
+
+# Checks if the target_value (string) exists in key of actual_values (object)
+compare(operator, target_value, actual_values) {
+	operator == "isIn"
+    is_object(actual_values)
+    # Get all keys from objects
+    value := object.keys(actual_values)
+    # Check if target_value exists in the set of object's keys 
+    target_value in value
+}
+
 # Checks if the actual_value (string) exists in target_values (array)
 compare(operator, target_values, actual_value) {
 	operator == "allIn"
diff --git a/service/discovery/README.md b/service/discovery/README.md
index 5367016a..8e44a020 100644
--- a/service/discovery/README.md
+++ b/service/discovery/README.md
@@ -13,8 +13,8 @@
 | Evidence        | Azure | AWS |
 |-----------------|-------|-----|
 | Compute         | ✅     | ✅   |
-| RuntimeLanguage | 🚫    | ❌   |
-| RuntimeVersion  | 🚫    | ❌   |
+| RuntimeLanguage | ✅    | ❌   |
+| RuntimeVersion  | ✅    | ❌   |
 
 ### VirtualMachine
 
@@ -25,13 +25,15 @@
 | MalwareProtection | ✅     | ❌   |
 | BootLogging       | ✅     | ✅   |
 | OSLogging         | ✅     | ✅   |
-| AutomaticUpdates  | ❌     | ❌   |
+| AutomaticUpdates  | ✅     | ❌   |
 
 #### Compute
 | Evidence          | Azure | AWS |
 |-------------------|-------|-----|
 | Resource          | ✅     | ✅   |
 | NetworkInterfaces | ✅     | ✅   |
+| ResourceLogging  | ✅     |    |
+| Backup  | ✅    |    |
 
 #### Resource
 | Evidence    | Azure | AWS |
@@ -60,6 +62,13 @@
 | LoggingService  | ✅     | 🚫  |
 | RetentionPeriod | ✅     | 🚫  |
 
+#### ResourceLogging
+| Evidence                  | Azure | AWS |
+|-----------------          |-------|-----|
+| MonitoringLogDataEnabled  | ✅     |   |
+| SecurityAlertsEnabled     | ✅     |   |
+
+
 ### BlockStorage
 
 | Evidence          | Azure | AWS |
@@ -112,7 +121,7 @@
 |-------------------|-------|-----|
 | Networking        | ✅     | ✅   |
 | Networkservice    | ❌     | ❌   |
-| AccessRestriction | ❌     | ❌   |
+| AccessRestriction | partly     | ❌   |
 </details>
 
 ### Storage
@@ -131,6 +140,8 @@
 | Resource         | ✅     | ✅   |
 | AtRestEncryption | ✅     | ✅   |
 | Immutability     | ✅     | ❌   |
+| ResourceLogging  | ✅     |    |
+| Backup  | ✅     |    |
 
 ### ObjectStorageService
 | Evidence       | Azure | AWS |
@@ -159,12 +170,6 @@
 |-------------------|-------|-----|
 | Storage           | ✅     | ❌   |
 
-### Storage
-| Evidence          | Azure | AWS |
-|-------------------|-------|-----|
-| Resource          | ✅     | ✅   |
-| AtRestEncryption  | ✅     | ✅   |
-
 #### ManagedKeyEncryption
 | Evidence  | Azure | AWS |
 |-----------|-------|-----|
@@ -180,3 +185,21 @@
 
 </details>
 
+# Azure Backup
+There are 2 different backup solutions for different resources
+- Backup Vaults and
+- Recovery Services Vault.
+
+| Resource   | Backup Vaults | Recovery Services Vault |
+|------------|-------|-----|
+| Azure Virtual Machine | | ✅ |
+| Azure Storage (Files)| | ✅ |
+| Azure Backup Agent| | ✅ |
+| Azure Backup Server| | ✅ |
+| DPM| | ✅ |
+| SQL in Azure VM | | ✅ |
+| SAP HANA in Azure VM | | ✅ |
+| Azure Storage (Blobs) | ✅ | |
+| Azure disks | ✅ | |
+| Azure Database for PostgreSQL servers | ✅ | |
+| Kubernetes Services | ✅ | |
diff --git a/service/discovery/azure/azure.go b/service/discovery/azure/azure.go
index 5b0adf0a..bbe58721 100644
--- a/service/discovery/azure/azure.go
+++ b/service/discovery/azure/azure.go
@@ -29,7 +29,9 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"strconv"
 	"strings"
+	"time"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
@@ -38,19 +40,32 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"
 
 	"github.com/sirupsen/logrus"
 
+	"clouditor.io/clouditor/internal/constants"
 	"clouditor.io/clouditor/internal/util"
+	"clouditor.io/clouditor/voc"
 )
 
 const (
 	StorageComponent = "storage"
 	ComputeComponent = "compute"
 	NetworkComponent = "network"
+
+	DefenderStorageType        = "StorageAccounts"
+	DefenderVirtualMachineType = "VirtualMachines"
+
+	DataSourceTypeDisc           = "Microsoft.Compute/disks"
+	DataSourceTypeStorageAccount = "Microsoft.Storage/storageAccounts/blobServices"
+
+	Duration30Days = time.Duration(30 * time.Hour * 24)
+	Duration7Days  = time.Duration(7 * time.Hour * 24)
 )
 
 var (
@@ -104,18 +119,35 @@ type azureDiscovery struct {
 	discovererComponent string
 	clients             clients
 	csID                string
+	backupMap           map[string]map[string]*voc.Backup
 }
 
 type clients struct {
-	blobContainerClient     *armstorage.BlobContainersClient
-	fileStorageClient       *armstorage.FileSharesClient
-	accountsClient          *armstorage.AccountsClient
-	networkInterfacesClient *armnetwork.InterfacesClient
-	loadBalancerClient      *armnetwork.LoadBalancersClient
-	functionsClient         *armappservice.WebAppsClient
-	virtualMachinesClient   *armcompute.VirtualMachinesClient
-	blockStorageClient      *armcompute.DisksClient
-	diskEncSetClient        *armcompute.DiskEncryptionSetsClient
+	// Storage
+	blobContainerClient *armstorage.BlobContainersClient
+	fileStorageClient   *armstorage.FileSharesClient
+	accountsClient      *armstorage.AccountsClient
+
+	// Network
+	networkInterfacesClient  *armnetwork.InterfacesClient
+	loadBalancerClient       *armnetwork.LoadBalancersClient
+	applicationGatewayClient *armnetwork.ApplicationGatewaysClient
+
+	// AppService
+	functionsClient *armappservice.WebAppsClient
+
+	// Compute
+	virtualMachinesClient *armcompute.VirtualMachinesClient
+	blockStorageClient    *armcompute.DisksClient
+	diskEncSetClient      *armcompute.DiskEncryptionSetsClient
+
+	// Security
+	defenderClient *armsecurity.PricingsClient
+
+	// Data protection
+	backupPoliciesClient  *armdataprotection.BackupPoliciesClient
+	backupVaultClient     *armdataprotection.BackupVaultsClient
+	backupInstancesClient *armdataprotection.BackupInstancesClient
 }
 
 func (a *azureDiscovery) CloudServiceID() string {
@@ -182,10 +214,181 @@ func NewAuthorizer() (*azidentity.DefaultAzureCredential, error) {
 	return cred, nil
 }
 
+type defenderProperties struct {
+	monitoringLogDataEnabled bool
+	securityAlertsEnabled    bool
+}
+
+// discoverDefender discovers Defender for X services and returns a map with the following properties for each defender type
+// * monitoringLogDataEnabled
+// * securityAlertsEnabled
+// The property will be set to the individual resources, e.g., compute, storage in the corresponding discoverers
+func (d *azureDiscovery) discoverDefender() (map[string]*defenderProperties, error) {
+	var pricings = make(map[string]*defenderProperties)
+
+	if d.clients.defenderClient == nil {
+		return nil, errors.New("defenderClient not set")
+	}
+
+	// List all pricings to get the enabled Defender for X
+	pricingsList, err := d.clients.defenderClient.List(context.Background(), nil)
+	if err != nil {
+		return nil, fmt.Errorf("could not discover pricings")
+	}
+
+	for _, elem := range pricingsList.Value {
+		if util.Deref(elem.Properties.PricingTier) == armsecurity.PricingTierFree {
+			pricings[util.Deref(elem.Name)] = &defenderProperties{
+				monitoringLogDataEnabled: false,
+				securityAlertsEnabled:    false,
+			}
+		} else {
+			pricings[util.Deref(elem.Name)] = &defenderProperties{
+				monitoringLogDataEnabled: true,
+				securityAlertsEnabled:    true,
+			}
+		}
+	}
+
+	return pricings, nil
+}
+
+// discoverBackupVaults receives all backup vaults in the subscription.
+// Since the backups for storage and compute are discovered togehter, the discovery is performed here and results are stored in the azureDiscovery receiver.
+func (d *azureDiscovery) discoverBackupVaults() error {
+
+	if d.backupMap != nil && len(d.backupMap) > 0 {
+		log.Debug("Backup Vaults already discovered.")
+	}
+
+	if d.clients.backupVaultClient == nil || d.clients.backupInstancesClient == nil {
+		return errors.New("backupVaultClient and/or backupInstancesClient missing")
+	}
+
+	// List all backup vaults
+	err := listPager(d,
+		d.clients.backupVaultClient.NewGetInSubscriptionPager,
+		d.clients.backupVaultClient.NewGetInResourceGroupPager,
+		func(res armdataprotection.BackupVaultsClientGetInSubscriptionResponse) []*armdataprotection.BackupVaultResource {
+			return res.Value
+		},
+		func(res armdataprotection.BackupVaultsClientGetInResourceGroupResponse) []*armdataprotection.BackupVaultResource {
+			return res.Value
+		},
+		func(vault *armdataprotection.BackupVaultResource) error {
+			instances, err := d.discoverBackupInstances(resourceGroupName(util.Deref(vault.ID)), util.Deref(vault.Name))
+			if err != nil {
+				err := fmt.Errorf("could not discover backup instances: %v", err)
+				return err
+			}
+
+			for _, instance := range instances {
+				dataSourceType := util.Deref(instance.Properties.DataSourceInfo.DatasourceType)
+
+				// Get retention from backup policy
+				policy, err := d.clients.backupPoliciesClient.Get(context.Background(), resourceGroupName(*vault.ID), *vault.Name, backupPolicyName(*instance.Properties.PolicyInfo.PolicyID), &armdataprotection.BackupPoliciesClientGetOptions{})
+				if err != nil {
+					err := fmt.Errorf("could not get backup policy '%s': %w", *instance.Properties.PolicyInfo.PolicyID, err)
+					log.Error(err)
+					continue
+				}
+
+				// TODO(all):Maybe we should differentiate the backup retention period for different resources, e.g., disk vs blobs (Metrics)
+				retention := policy.BaseBackupPolicyResource.Properties.(*armdataprotection.BackupPolicy).PolicyRules[0].(*armdataprotection.AzureRetentionRule).Lifecycles[0].DeleteAfter.(*armdataprotection.AbsoluteDeleteOption).GetDeleteOption().Duration
+				// Check if map entry already exists
+				_, ok := d.backupMap[dataSourceType]
+				if !ok {
+					d.backupMap[dataSourceType] = make(map[string]*voc.Backup)
+				}
+
+				// Store voc.Backup in backupMap
+				d.backupMap[util.Deref(instance.Properties.DataSourceInfo.DatasourceType)][util.Deref(instance.Properties.DataSourceInfo.ResourceID)] = &voc.Backup{
+					Enabled:         true,
+					Policy:          util.Deref(instance.Properties.PolicyInfo.PolicyID),
+					RetentionPeriod: retentionDuration(util.Deref(retention)),
+					Storage:         voc.ResourceID(util.Deref(instance.ID)),
+					GeoLocation: voc.GeoLocation{
+						Region: util.Deref(vault.Location),
+					},
+					AtRestEncryption: &voc.AtRestEncryption{
+						Algorithm: constants.AES256, // https://learn.microsoft.com/en-us/azure/backup/backup-encryption (Last access: 04/27/2023)
+						Enabled:   true,             // By default, all your data is encrypted using platform-managed keys (https://learn.microsoft.com/en-us/azure/backup/backup-vault-overview#encryption-of-backup-data-using-platform-managed-keys, Last access: 04/27/2023)
+					},
+					TransportEncryption: &voc.TransportEncryption{
+						Enabled:    true,
+						Enforced:   true,
+						Algorithm:  constants.TLS,
+						TlsVersion: constants.TLS1_2, // https://learn.microsoft.com/en-us/azure/backup/transport-layer-security#why-enable-tls-12 (Last access: 04/27/2023)
+					},
+				}
+			}
+
+			return nil
+		})
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// retentionDuration returns the rentention string as time.Duration
+func retentionDuration(retention string) time.Duration {
+	if retention == "" {
+		return time.Duration(0)
+	}
+
+	// Delete first and last character
+	r := retention[1 : len(retention)-1]
+
+	// string to int
+	d, err := strconv.Atoi(r)
+	if err != nil {
+		log.Errorf("could not convert string to int")
+		return time.Duration(0)
+	}
+
+	// Create duration in hours
+	duration := time.Duration(time.Duration(d) * time.Hour * 24)
+
+	return duration
+}
+
+// discoverBackupInstances retrieves the instances in a given backup vault.
+func (d *azureDiscovery) discoverBackupInstances(resourceGroup, vaultName string) ([]*armdataprotection.BackupInstanceResource, error) {
+	var (
+		list armdataprotection.BackupInstancesClientListResponse
+		err  error
+	)
+
+	if resourceGroup == "" || vaultName == "" {
+		return nil, errors.New("missing resource group and/or vault name")
+	}
+
+	// List all instances in the given backupp vault
+	listPager := d.clients.backupInstancesClient.NewListPager(resourceGroup, vaultName, &armdataprotection.BackupInstancesClientListOptions{})
+	for listPager.More() {
+		list, err = listPager.NextPage(context.TODO())
+		if err != nil {
+			err = fmt.Errorf("%s: %v", ErrGettingNextPage, err)
+			return nil, err
+		}
+	}
+
+	return list.Value, nil
+}
+
+// resourceGroupName returns the resource group name of a given Azure ID
 func resourceGroupName(id string) string {
 	return strings.Split(id, "/")[4]
 }
 
+// backupPolicyName returns the backup policy name of a given Azure ID
+func backupPolicyName(id string) string {
+	return strings.Split(id, "/")[10]
+}
+
 // labels converts the resource tags to the vocabulary label
 func labels(tags map[string]*string) map[string]string {
 	l := make(map[string]string)
diff --git a/service/discovery/azure/compute.go b/service/discovery/azure/compute.go
index 6c6fd812..f7285f06 100644
--- a/service/discovery/azure/compute.go
+++ b/service/discovery/azure/compute.go
@@ -29,9 +29,11 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"strings"
 
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection"
 
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/internal/util"
@@ -44,6 +46,7 @@ var (
 
 type azureComputeDiscovery struct {
 	*azureDiscovery
+	defenderProperties map[string]*defenderProperties
 }
 
 func NewAzureComputeDiscovery(opts ...DiscoveryOption) discovery.Discoverer {
@@ -51,7 +54,9 @@ func NewAzureComputeDiscovery(opts ...DiscoveryOption) discovery.Discoverer {
 		&azureDiscovery{
 			discovererComponent: ComputeComponent,
 			csID:                discovery.DefaultCloudServiceID,
+			backupMap:           make(map[string]map[string]*voc.Backup),
 		},
+		make(map[string]*defenderProperties),
 	}
 
 	// Apply options
@@ -76,6 +81,27 @@ func (d *azureComputeDiscovery) List() (list []voc.IsCloudResource, err error) {
 		return nil, fmt.Errorf("%s: %w", ErrCouldNotAuthenticate, err)
 	}
 
+	// initialize backup policies client
+	if err := d.initBackupPoliciesClient(); err != nil {
+		return nil, err
+	}
+
+	// initialize backup vaults client
+	if err := d.initBackupVaultsClient(); err != nil {
+		return nil, err
+	}
+
+	// initialize backup instances client
+	if err := d.initBackupInstancesClient(); err != nil {
+		return nil, err
+	}
+
+	// Discover backup vaults
+	err = d.azureDiscovery.discoverBackupVaults()
+	if err != nil {
+		log.Errorf("could not discover backup vaults: %v", err)
+	}
+
 	log.Info("Discover Azure block storage")
 	// Discover block storage
 	storage, err := d.discoverBlockStorages()
@@ -143,6 +169,8 @@ func (d *azureComputeDiscovery) handleFunction(function *armappservice.Site) voc
 		return nil
 	}
 
+	runtimeLanguage, runtimeVersion := runtimeInfo(*function.Properties.SiteConfig.LinuxFxVersion)
+
 	return &voc.Function{
 		Compute: &voc.Compute{
 			Resource: discovery.NewResource(d,
@@ -158,11 +186,23 @@ func (d *azureComputeDiscovery) handleFunction(function *armappservice.Site) voc
 			),
 			NetworkInterfaces: []voc.ResourceID{},
 		},
-		RuntimeLanguage: "",
-		RuntimeVersion:  "",
+		RuntimeLanguage: runtimeLanguage,
+		RuntimeVersion:  runtimeVersion,
 	}
 }
 
+// runtimeInfo returns the runtime language and version
+func runtimeInfo(runtime string) (runtimeLanguage string, runtimeVersion string) {
+	if runtime == "" || !strings.Contains(runtime, "|") {
+		return "", ""
+	}
+	split := strings.Split(runtime, "|")
+	runtimeLanguage = split[0]
+	runtimeVersion = split[1]
+
+	return
+}
+
 // Discover virtual machines
 func (d *azureComputeDiscovery) discoverVirtualMachines() ([]voc.IsCloudResource, error) {
 	var list []voc.IsCloudResource
@@ -202,8 +242,11 @@ func (d *azureComputeDiscovery) discoverVirtualMachines() ([]voc.IsCloudResource
 }
 
 func (d *azureComputeDiscovery) handleVirtualMachines(vm *armcompute.VirtualMachine) (voc.IsCompute, error) {
-	var bootLogging = []voc.ResourceID{}
-	var osLogging = []voc.ResourceID{}
+	var (
+		bootLogging = []voc.ResourceID{}
+		osLogging   = []voc.ResourceID{}
+		autoUpdates *voc.AutomaticUpdates
+	)
 
 	// If a mandatory field is empty, the whole disk is empty
 	if vm == nil || vm.ID == nil {
@@ -214,6 +257,8 @@ func (d *azureComputeDiscovery) handleVirtualMachines(vm *armcompute.VirtualMach
 		bootLogging = []voc.ResourceID{voc.ResourceID(bootLogOutput(vm))}
 	}
 
+	autoUpdates = automaticUpdates(vm)
+
 	r := &voc.VirtualMachine{
 		Compute: &voc.Compute{
 			Resource: discovery.NewResource(d,
@@ -227,6 +272,7 @@ func (d *azureComputeDiscovery) handleVirtualMachines(vm *armcompute.VirtualMach
 				voc.VirtualMachineType,
 			),
 			NetworkInterfaces: []voc.ResourceID{},
+			ResourceLogging:   d.createResourceLogging(),
 		},
 		BlockStorage:      []voc.ResourceID{},
 		MalwareProtection: &voc.MalwareProtection{},
@@ -250,6 +296,7 @@ func (d *azureComputeDiscovery) handleVirtualMachines(vm *armcompute.VirtualMach
 				},
 			},
 		},
+		AutomaticUpdates: autoUpdates,
 	}
 
 	// Reference to networkInterfaces
@@ -273,6 +320,43 @@ func (d *azureComputeDiscovery) handleVirtualMachines(vm *armcompute.VirtualMach
 	return r, nil
 }
 
+// automaticUpdates returns automaticUpdatesEnabled and automaticUpdatesInterval for a given VM.
+func automaticUpdates(vm *armcompute.VirtualMachine) (automaticUpdates *voc.AutomaticUpdates) {
+	automaticUpdates = &voc.AutomaticUpdates{}
+
+	if vm == nil || vm.Properties == nil || vm.Properties.OSProfile == nil {
+		return
+	}
+
+	// Check if Linux configuration is available
+	if vm.Properties.OSProfile.LinuxConfiguration != nil &&
+		vm.Properties.OSProfile.LinuxConfiguration.PatchSettings != nil {
+		if util.Deref(vm.Properties.OSProfile.LinuxConfiguration.PatchSettings.PatchMode) == armcompute.LinuxVMGuestPatchModeAutomaticByPlatform ||
+			util.Deref(vm.Properties.OSProfile.LinuxConfiguration.PatchSettings.PatchMode) == armcompute.LinuxVMGuestPatchModeImageDefault {
+			automaticUpdates.Enabled = true
+			automaticUpdates.Interval = Duration30Days
+			return
+		}
+	}
+
+	// Check if Windows configuration is available
+	if vm.Properties.OSProfile.WindowsConfiguration != nil &&
+		vm.Properties.OSProfile.WindowsConfiguration.PatchSettings != nil {
+		if util.Deref(vm.Properties.OSProfile.WindowsConfiguration.PatchSettings.PatchMode) == armcompute.WindowsVMGuestPatchModeAutomaticByOS ||
+			util.Deref(vm.Properties.OSProfile.WindowsConfiguration.PatchSettings.PatchMode) == armcompute.WindowsVMGuestPatchModeAutomaticByPlatform {
+			automaticUpdates.Enabled = true
+			automaticUpdates.Interval = Duration30Days
+			return
+
+		} else {
+			return
+
+		}
+	}
+
+	return
+}
+
 func isBootDiagnosticEnabled(vm *armcompute.VirtualMachine) bool {
 	if vm == nil || vm.Properties == nil || vm.Properties.DiagnosticsProfile == nil || vm.Properties.DiagnosticsProfile.BootDiagnostics == nil {
 		return false
@@ -340,6 +424,8 @@ func (d *azureComputeDiscovery) handleBlockStorage(disk *armcompute.Disk) (*voc.
 		return nil, fmt.Errorf("could not get block storage properties for the atRestEncryption: %w", err)
 	}
 
+	backup := d.backupMap[DataSourceTypeDisc][util.Deref(disk.ID)]
+
 	return &voc.BlockStorage{
 		Storage: &voc.Storage{
 			Resource: discovery.NewResource(d,
@@ -353,6 +439,7 @@ func (d *azureComputeDiscovery) handleBlockStorage(disk *armcompute.Disk) (*voc.
 				voc.BlockStorageType,
 			),
 			AtRestEncryption: enc,
+			Backup:           backup,
 		},
 	}, nil
 }
@@ -371,12 +458,12 @@ func (d *azureComputeDiscovery) blockStorageAtRestEncryption(disk *armcompute.Di
 
 	if disk.Properties.Encryption.Type == nil {
 		return enc, errors.New("error getting atRestEncryption properties of blockStorage")
-	} else if *disk.Properties.Encryption.Type == armcompute.EncryptionTypeEncryptionAtRestWithPlatformKey {
+	} else if util.Deref(disk.Properties.Encryption.Type) == armcompute.EncryptionTypeEncryptionAtRestWithPlatformKey {
 		enc = &voc.ManagedKeyEncryption{AtRestEncryption: &voc.AtRestEncryption{
 			Algorithm: "AES256",
 			Enabled:   true,
 		}}
-	} else if *disk.Properties.Encryption.Type == armcompute.EncryptionTypeEncryptionAtRestWithCustomerKey {
+	} else if util.Deref(disk.Properties.Encryption.Type) == armcompute.EncryptionTypeEncryptionAtRestWithCustomerKey {
 		diskEncryptionSetID = util.Deref(disk.Properties.Encryption.DiskEncryptionSetID)
 
 		keyUrl, err = d.keyURL(diskEncryptionSetID)
@@ -386,7 +473,7 @@ func (d *azureComputeDiscovery) blockStorageAtRestEncryption(disk *armcompute.Di
 
 		enc = &voc.CustomerKeyEncryption{
 			AtRestEncryption: &voc.AtRestEncryption{
-				Algorithm: "", // TODO(garuppel): TBD
+				Algorithm: "", // TODO(all): TBD
 				Enabled:   true,
 			},
 			KeyUrl: keyUrl,
@@ -421,6 +508,18 @@ func (d *azureComputeDiscovery) keyURL(diskEncryptionSetID string) (string, erro
 	return util.Deref(keyURL), nil
 }
 
+// TODO(all): Update to generic function or method
+func (d *azureComputeDiscovery) createResourceLogging() (resourceLogging *voc.ResourceLogging) {
+	if d.defenderProperties[DefenderVirtualMachineType] != nil {
+		resourceLogging = &voc.ResourceLogging{
+			MonitoringLogDataEnabled: d.defenderProperties[DefenderVirtualMachineType].monitoringLogDataEnabled,
+			SecurityAlertsEnabled:    d.defenderProperties[DefenderVirtualMachineType].securityAlertsEnabled,
+		}
+	}
+
+	return
+}
+
 // initFunctionsClient creates the client if not already exists
 func (d *azureComputeDiscovery) initFunctionsClient() (err error) {
 	d.clients.functionsClient, err = initClient(d.clients.functionsClient, d.azureDiscovery, armappservice.NewWebAppsClient)
@@ -444,3 +543,24 @@ func (d *azureComputeDiscovery) initDiskEncryptonSetClient() (err error) {
 	d.clients.diskEncSetClient, err = initClient(d.clients.diskEncSetClient, d.azureDiscovery, armcompute.NewDiskEncryptionSetsClient)
 	return
 }
+
+// initBackupPoliciesClient creates the client if not already exists
+func (d *azureComputeDiscovery) initBackupPoliciesClient() (err error) {
+	d.clients.backupPoliciesClient, err = initClient(d.clients.backupPoliciesClient, d.azureDiscovery, armdataprotection.NewBackupPoliciesClient)
+
+	return
+}
+
+// initBackupVaultsClient creates the client if not already exists
+func (d *azureComputeDiscovery) initBackupVaultsClient() (err error) {
+	d.clients.backupVaultClient, err = initClient(d.clients.backupVaultClient, d.azureDiscovery, armdataprotection.NewBackupVaultsClient)
+
+	return
+}
+
+// initBackupInstancesClient creates the client if not already exists
+func (d *azureComputeDiscovery) initBackupInstancesClient() (err error) {
+	d.clients.backupInstancesClient, err = initClient(d.clients.backupInstancesClient, d.azureDiscovery, armdataprotection.NewBackupInstancesClient)
+
+	return
+}
diff --git a/service/discovery/azure/network.go b/service/discovery/azure/network.go
index 602c331e..08b069fb 100644
--- a/service/discovery/azure/network.go
+++ b/service/discovery/azure/network.go
@@ -44,6 +44,7 @@ func NewAzureNetworkDiscovery(opts ...DiscoveryOption) discovery.Discoverer {
 		&azureDiscovery{
 			discovererComponent: NetworkComponent,
 			csID:                discovery.DefaultCloudServiceID,
+			backupMap:           make(map[string]map[string]*voc.Backup),
 		},
 	}
 
@@ -85,6 +86,13 @@ func (d *azureNetworkDiscovery) List() (list []voc.IsCloudResource, err error) {
 	}
 	list = append(list, loadBalancer...)
 
+	// Discover Application Gateway
+	ag, err := d.discoverApplicationGateway()
+	if err != nil {
+		return list, fmt.Errorf("could not discover application gateways: %w", err)
+	}
+	list = append(list, ag...)
+
 	return
 }
 
@@ -123,7 +131,42 @@ func (d *azureNetworkDiscovery) discoverNetworkInterfaces() ([]voc.IsCloudResour
 	return list, nil
 }
 
-// Discover load balancer
+// discoverApplicationGateway discovers application gateways
+func (d *azureNetworkDiscovery) discoverApplicationGateway() ([]voc.IsCloudResource, error) {
+	var list []voc.IsCloudResource
+
+	// initialize application gateway client
+	if err := d.initApplicationGatewayClient(); err != nil {
+		return nil, err
+	}
+
+	// List all application gateways
+	err := listPager(d.azureDiscovery,
+		d.clients.applicationGatewayClient.NewListAllPager,
+		d.clients.applicationGatewayClient.NewListPager,
+		func(res armnetwork.ApplicationGatewaysClientListAllResponse) []*armnetwork.ApplicationGateway {
+			return res.Value
+		},
+		func(res armnetwork.ApplicationGatewaysClientListResponse) []*armnetwork.ApplicationGateway {
+			return res.Value
+		},
+		func(ags *armnetwork.ApplicationGateway) error {
+			s := d.handleApplicationGateway(ags)
+
+			log.Infof("Adding application gateway %+v", s)
+
+			list = append(list, s)
+
+			return nil
+		})
+	if err != nil {
+		return nil, err
+	}
+
+	return list, nil
+}
+
+// discoverLoadBalancer discovers load balancer
 func (d *azureNetworkDiscovery) discoverLoadBalancer() ([]voc.IsCloudResource, error) {
 	var list []voc.IsCloudResource
 
@@ -177,13 +220,34 @@ func (d *azureNetworkDiscovery) handleLoadBalancer(lb *armnetwork.LoadBalancer)
 			Ips:   publicIPAddressFromLoadBalancer(lb),
 			Ports: LoadBalancerPorts(lb),
 		},
-		// TODO(all): do we need the AccessRestriction for load balancers?
-		AccessRestrictions: &[]voc.AccessRestriction{},
 		// TODO(all): do we need the httpEndpoint for load balancers?
 		HttpEndpoints: &[]voc.HttpEndpoint{},
 	}
 }
 
+// handleApplicationGateway returns the application gateway with its properties
+// NOTE: handleApplicationGateway uses the LoadBalancer for now until there is a own resource
+func (d *azureNetworkDiscovery) handleApplicationGateway(ag *armnetwork.ApplicationGateway) voc.IsNetwork {
+	return &voc.LoadBalancer{
+		NetworkService: &voc.NetworkService{
+			Networking: &voc.Networking{
+				Resource: discovery.NewResource(
+					d,
+					voc.ResourceID(util.Deref(ag.ID)),
+					util.Deref(ag.Name),
+					nil,
+					voc.GeoLocation{Region: util.Deref(ag.Location)},
+					labels(ag.Tags),
+					voc.LoadBalancerType,
+				),
+			},
+		},
+		AccessRestriction: voc.WebApplicationFirewall{
+			Enabled: util.Deref(ag.Properties.WebApplicationFirewallConfiguration.Enabled),
+		},
+	}
+}
+
 func (d *azureNetworkDiscovery) handleNetworkInterfaces(ni *armnetwork.Interface) voc.IsNetwork {
 	return &voc.NetworkInterface{
 		Networking: &voc.Networking{
@@ -199,12 +263,21 @@ func (d *azureNetworkDiscovery) handleNetworkInterfaces(ni *armnetwork.Interface
 				voc.NetworkInterfaceType,
 			),
 		},
+		AccessRestriction: &voc.L3Firewall{
+			Enabled: nsgFirewallEnabled(ni),
+			// Inbound: ,
+			// RestrictedPorts: ,
+		},
+	}
+}
 
-		// AccessRestriction: &voc.AccessRestriction{
-		// 	Inbound:         false, // TODO(garuppel): TBD
-		// 	RestrictedPorts: d.getRestrictedPorts(ni),
-		// },
+// nsgFirewallEnabled checks if network security group (NSG) rules are configured. A NSG is a firewall that operates at OSI layers 3 and 4 to filter ingress and egress traffic. (https://learn.microsoft.com/en-us/azure/firewall/firewall-faq#what-is-the-difference-between-network-security-groups--nsgs--and-azure-firewall, Last access: 05/02/2023)
+func nsgFirewallEnabled(ni *armnetwork.Interface) bool {
+	if ni != nil && ni.Properties != nil && ni.Properties.NetworkSecurityGroup != nil && ni.Properties.NetworkSecurityGroup.Properties != nil && ni.Properties.NetworkSecurityGroup.Properties.SecurityRules != nil && len(ni.Properties.NetworkSecurityGroup.Properties.SecurityRules) >= 1 {
+		return true
 	}
+
+	return false
 }
 
 func LoadBalancerPorts(lb *armnetwork.LoadBalancer) (loadBalancerPorts []uint16) {
@@ -303,3 +376,9 @@ func (d *azureNetworkDiscovery) initLoadBalancersClient() (err error) {
 	d.clients.loadBalancerClient, err = initClient(d.clients.loadBalancerClient, d.azureDiscovery, armnetwork.NewLoadBalancersClient)
 	return
 }
+
+// initApplicationGatewayClient creates the client if not already exists
+func (d *azureNetworkDiscovery) initApplicationGatewayClient() (err error) {
+	d.clients.applicationGatewayClient, err = initClient(d.clients.applicationGatewayClient, d.azureDiscovery, armnetwork.NewApplicationGatewaysClient)
+	return
+}
diff --git a/service/discovery/azure/storage.go b/service/discovery/azure/storage.go
index 76ac24e1..2c44be50 100644
--- a/service/discovery/azure/storage.go
+++ b/service/discovery/azure/storage.go
@@ -35,6 +35,8 @@ import (
 	"clouditor.io/clouditor/internal/util"
 	"clouditor.io/clouditor/voc"
 
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
 )
 
@@ -45,6 +47,7 @@ var (
 
 type azureStorageDiscovery struct {
 	*azureDiscovery
+	defenderProperties map[string]*defenderProperties
 }
 
 func NewAzureStorageDiscovery(opts ...DiscoveryOption) discovery.Discoverer {
@@ -52,7 +55,9 @@ func NewAzureStorageDiscovery(opts ...DiscoveryOption) discovery.Discoverer {
 		&azureDiscovery{
 			discovererComponent: StorageComponent,
 			csID:                discovery.DefaultCloudServiceID,
+			backupMap:           make(map[string]map[string]*voc.Backup),
 		},
+		make(map[string]*defenderProperties),
 	}
 
 	// Apply options
@@ -79,6 +84,17 @@ func (d *azureStorageDiscovery) List() (list []voc.IsCloudResource, err error) {
 
 	log.Info("Discover Azure storage resources")
 
+	// initialize defender client
+	if err := d.initDefenderClient(); err != nil {
+		return nil, fmt.Errorf("could not initialize defender client: %w", err)
+	}
+
+	// Discover Defender for X properties to add it to the required resource properties
+	d.defenderProperties, err = d.discoverDefender()
+	if err != nil {
+		return nil, fmt.Errorf("could not discover Defender for X: %w", err)
+	}
+
 	// Discover storage accounts
 	storageAccounts, err := d.discoverStorageAccounts()
 	if err != nil {
@@ -92,6 +108,21 @@ func (d *azureStorageDiscovery) List() (list []voc.IsCloudResource, err error) {
 func (d *azureStorageDiscovery) discoverStorageAccounts() ([]voc.IsCloudResource, error) {
 	var storageResourcesList []voc.IsCloudResource
 
+	// initialize backup policies client
+	if err := d.initBackupPoliciesClient(); err != nil {
+		return nil, err
+	}
+
+	// initialize backup instances client
+	if err := d.initBackupInstancesClient(); err != nil {
+		return nil, err
+	}
+
+	// initialize backup vaults client
+	if err := d.initBackupVaultsClient(); err != nil {
+		return nil, err
+	}
+
 	// initialize storage accounts client
 	if err := d.initAccountsClient(); err != nil {
 		return nil, err
@@ -107,8 +138,14 @@ func (d *azureStorageDiscovery) discoverStorageAccounts() ([]voc.IsCloudResource
 		return nil, err
 	}
 
+	// Discover backup vaults
+	err := d.azureDiscovery.discoverBackupVaults()
+	if err != nil {
+		log.Errorf("could not discover backup vaults: %v", err)
+	}
+
 	// Discover object and file storages
-	err := listPager(d.azureDiscovery,
+	err = listPager(d.azureDiscovery,
 		d.clients.accountsClient.NewListPager,
 		d.clients.accountsClient.NewListByResourceGroupPager,
 		func(res armstorage.AccountsClientListResponse) []*armstorage.Account {
@@ -140,6 +177,7 @@ func (d *azureStorageDiscovery) discoverStorageAccounts() ([]voc.IsCloudResource
 			}
 
 			storageResourcesList = append(storageResourcesList, storageService)
+
 			return nil
 		})
 	if err != nil {
@@ -202,45 +240,6 @@ func (d *azureStorageDiscovery) discoverObjectStorages(account *armstorage.Accou
 	return list, nil
 }
 
-func (d *azureStorageDiscovery) handleObjectStorage(account *armstorage.Account, container *armstorage.ListContainerItem) (*voc.ObjectStorage, error) {
-	if account == nil {
-		return nil, ErrEmptyStorageAccount
-	}
-
-	// It is possible that the container is not empty. In that case we have to check if a mandatory field is empty, so the whole disk is empty
-	if container == nil || container.ID == nil {
-		return nil, fmt.Errorf("container is nil")
-	}
-
-	enc, err := storageAtRestEncryption(account)
-	if err != nil {
-		return nil, fmt.Errorf("could not get object storage properties for the atRestEncryption: %w", err)
-	}
-
-	return &voc.ObjectStorage{
-		Storage: &voc.Storage{
-			Resource: discovery.NewResource(d,
-				voc.ResourceID(util.Deref(container.ID)),
-				util.Deref(container.Name),
-				// We only have the creation time of the storage account the object storage belongs to
-				account.Properties.CreationTime,
-				voc.GeoLocation{
-					// The location is the same as the storage account
-					Region: util.Deref(account.Location),
-				},
-				// The storage account labels the object storage belongs to
-				labels(account.Tags),
-				voc.ObjectStorageType,
-			),
-			AtRestEncryption: enc,
-			Immutability: &voc.Immutability{
-				Enabled: util.Deref(container.Properties.HasImmutabilityPolicy),
-			},
-		},
-		PublicAccess: util.Deref(container.Properties.PublicAccess) != armstorage.PublicAccessNone,
-	}, nil
-}
-
 func (d *azureStorageDiscovery) handleStorageAccount(account *armstorage.Account, storagesList []voc.IsCloudResource) (*voc.ObjectStorageService, error) {
 	var storageResourceIDs []voc.ResourceID
 
@@ -258,7 +257,7 @@ func (d *azureStorageDiscovery) handleStorageAccount(account *armstorage.Account
 	te := &voc.TransportEncryption{
 		Enforced:   util.Deref(account.Properties.EnableHTTPSTrafficOnly),
 		Enabled:    true, // cannot be disabled
-		TlsVersion: string(*account.Properties.MinimumTLSVersion),
+		TlsVersion: string(util.Deref(account.Properties.MinimumTLSVersion)),
 		Algorithm:  "TLS",
 	}
 
@@ -290,19 +289,6 @@ func (d *azureStorageDiscovery) handleStorageAccount(account *armstorage.Account
 	return storageService, nil
 }
 
-// generalizeURL generalizes the URL, because the URL depends on the storage type
-func generalizeURL(url string) string {
-	if url == "" {
-		return ""
-	}
-
-	urlSplit := strings.Split(url, ".")
-	urlSplit[1] = "[file,blob]"
-	newURL := strings.Join(urlSplit, ".")
-
-	return newURL
-}
-
 func (d *azureStorageDiscovery) handleFileStorage(account *armstorage.Account, fileshare *armstorage.FileShareItem) (*voc.FileStorage, error) {
 	if account == nil {
 		return nil, ErrEmptyStorageAccount
@@ -333,11 +319,55 @@ func (d *azureStorageDiscovery) handleFileStorage(account *armstorage.Account, f
 				labels(account.Tags),
 				voc.FileStorageType,
 			),
+			ResourceLogging:  d.createResourceLogging(),
 			AtRestEncryption: enc,
 		},
 	}, nil
 }
 
+func (d *azureStorageDiscovery) handleObjectStorage(account *armstorage.Account, container *armstorage.ListContainerItem) (*voc.ObjectStorage, error) {
+	if account == nil {
+		return nil, ErrEmptyStorageAccount
+	}
+
+	// It is possible that the container is not empty. In that case we have to check if a mandatory field is empty, so the whole disk is empty
+	if container == nil || container.ID == nil {
+		return nil, fmt.Errorf("container is nil")
+	}
+
+	enc, err := storageAtRestEncryption(account)
+	if err != nil {
+		return nil, fmt.Errorf("could not get object storage properties for the atRestEncryption: %w", err)
+	}
+
+	backup := d.backupMap[DataSourceTypeStorageAccount][util.Deref(account.ID)]
+
+	return &voc.ObjectStorage{
+		Storage: &voc.Storage{
+			Resource: discovery.NewResource(d,
+				voc.ResourceID(util.Deref(container.ID)),
+				util.Deref(container.Name),
+				// We only have the creation time of the storage account the object storage belongs to
+				account.Properties.CreationTime,
+				voc.GeoLocation{
+					// The location is the same as the storage account
+					Region: util.Deref(account.Location),
+				},
+				// The storage account labels the object storage belongs to
+				labels(account.Tags),
+				voc.ObjectStorageType,
+			),
+			AtRestEncryption: enc,
+			Immutability: &voc.Immutability{
+				Enabled: util.Deref(container.Properties.HasImmutabilityPolicy),
+			},
+			ResourceLogging: d.createResourceLogging(),
+			Backup:          backup,
+		},
+		PublicAccess: util.Deref(container.Properties.PublicAccess) != armstorage.PublicAccessNone,
+	}, nil
+}
+
 // storageAtRestEncryption takes encryption properties of an armstorage.Account and converts it into our respective
 // ontology object.
 func storageAtRestEncryption(account *armstorage.Account) (enc voc.IsAtRestEncryption, err error) {
@@ -347,17 +377,17 @@ func storageAtRestEncryption(account *armstorage.Account) (enc voc.IsAtRestEncry
 
 	if account.Properties == nil || account.Properties.Encryption.KeySource == nil {
 		return enc, errors.New("keySource is empty")
-	} else if *account.Properties.Encryption.KeySource == armstorage.KeySourceMicrosoftStorage {
+	} else if util.Deref(account.Properties.Encryption.KeySource) == armstorage.KeySourceMicrosoftStorage {
 		enc = &voc.ManagedKeyEncryption{
 			AtRestEncryption: &voc.AtRestEncryption{
 				Algorithm: "AES256",
 				Enabled:   true,
 			},
 		}
-	} else if *account.Properties.Encryption.KeySource == armstorage.KeySourceMicrosoftKeyvault {
+	} else if util.Deref(account.Properties.Encryption.KeySource) == armstorage.KeySourceMicrosoftKeyvault {
 		enc = &voc.CustomerKeyEncryption{
 			AtRestEncryption: &voc.AtRestEncryption{
-				Algorithm: "", // TODO(garuppel): TBD
+				Algorithm: "", // TODO(all): TBD
 				Enabled:   true,
 			},
 			KeyUrl: util.Deref(account.Properties.Encryption.KeyVaultProperties.KeyVaultURI),
@@ -386,6 +416,31 @@ func accountName(id string) string {
 	return splitName[8]
 }
 
+// generalizeURL generalizes the URL, because the URL depends on the storage type
+func generalizeURL(url string) string {
+	if url == "" {
+		return ""
+	}
+
+	urlSplit := strings.Split(url, ".")
+	urlSplit[1] = "[file,blob]"
+	newURL := strings.Join(urlSplit, ".")
+
+	return newURL
+}
+
+// TODO(all): Update to generic function or method
+func (d *azureStorageDiscovery) createResourceLogging() (resourceLogging *voc.ResourceLogging) {
+	if d.defenderProperties[DefenderStorageType] != nil {
+		resourceLogging = &voc.ResourceLogging{
+			MonitoringLogDataEnabled: d.defenderProperties[DefenderStorageType].monitoringLogDataEnabled,
+			SecurityAlertsEnabled:    d.defenderProperties[DefenderStorageType].securityAlertsEnabled,
+		}
+	}
+
+	return
+}
+
 // initAccountsClient creates the client if not already exists
 func (d *azureStorageDiscovery) initAccountsClient() (err error) {
 	d.clients.accountsClient, err = initClient(d.clients.accountsClient, d.azureDiscovery, armstorage.NewAccountsClient)
@@ -403,3 +458,31 @@ func (d *azureStorageDiscovery) initFileStorageClient() (err error) {
 	d.clients.fileStorageClient, err = initClient(d.clients.fileStorageClient, d.azureDiscovery, armstorage.NewFileSharesClient)
 	return
 }
+
+// initDefenderClient creates the client if not already exists
+func (d *azureStorageDiscovery) initDefenderClient() (err error) {
+	d.clients.defenderClient, err = initClient(d.clients.defenderClient, d.azureDiscovery, armsecurity.NewPricingsClient)
+
+	return
+}
+
+// initBackupPoliciesClient creates the client if not already exists
+func (d *azureStorageDiscovery) initBackupPoliciesClient() (err error) {
+	d.clients.backupPoliciesClient, err = initClient(d.clients.backupPoliciesClient, d.azureDiscovery, armdataprotection.NewBackupPoliciesClient)
+
+	return
+}
+
+// initBackupVaultsClient creates the client if not already exists
+func (d *azureStorageDiscovery) initBackupVaultsClient() (err error) {
+	d.clients.backupVaultClient, err = initClient(d.clients.backupVaultClient, d.azureDiscovery, armdataprotection.NewBackupVaultsClient)
+
+	return
+}
+
+// initBackupInstancesClient creates the client if not already exists
+func (d *azureStorageDiscovery) initBackupInstancesClient() (err error) {
+	d.clients.backupInstancesClient, err = initClient(d.clients.backupInstancesClient, d.azureDiscovery, armdataprotection.NewBackupInstancesClient)
+
+	return
+}
diff --git a/service/discovery/k8s/network.go b/service/discovery/k8s/network.go
index 7683fde9..cae023f8 100644
--- a/service/discovery/k8s/network.go
+++ b/service/discovery/k8s/network.go
@@ -130,9 +130,7 @@ func (d *k8sNetworkDiscovery) handleIngress(ingress *v1.Ingress) voc.IsNetwork {
 			Ips:   nil, // TODO (oxisto): fill out IPs
 			Ports: []uint16{80, 443},
 		},
-		// TODO(oxisto): fill out access restrictions
-		AccessRestrictions: &[]voc.AccessRestriction{},
-		HttpEndpoints:      &[]voc.HttpEndpoint{},
+		HttpEndpoints: &[]voc.HttpEndpoint{},
 	}
 
 	for _, rule := range ingress.Spec.Rules {
diff --git a/voc/backup.go b/voc/backup.go
index e2db3e59..8c7617f1 100644
--- a/voc/backup.go
+++ b/voc/backup.go
@@ -29,9 +29,14 @@ package voc
 
 import "time"
 
+// Backup contains the retention period in hours
 type Backup struct {
 	*Availability
-	RetentionPeriod time.Duration `json:"retentionPeriod"`
-	Enabled         bool          `json:"enabled"`
-	Policy          string        `json:"policy"`
+	AtRestEncryption    IsAtRestEncryption   `json:"atRestEncryption"`
+	TransportEncryption *TransportEncryption `json:"transportEncryption"`
+	RetentionPeriod     time.Duration        `json:"retentionPeriod"`
+	Enabled             bool                 `json:"enabled"`
+	Policy              string               `json:"policy"`
+	GeoLocation         GeoLocation          `json:"geoLocation"`
+	Storage             ResourceID           `json:"storage"`
 }
diff --git a/voc/compute.go b/voc/compute.go
index e5fc56e2..4015722f 100644
--- a/voc/compute.go
+++ b/voc/compute.go
@@ -31,5 +31,7 @@ var ComputeType = []string{"Compute", "Resource"}
 
 type Compute struct {
 	*Resource
-	NetworkInterfaces []ResourceID `json:"networkInterfaces"`
+	NetworkInterfaces []ResourceID     `json:"networkInterfaces"`
+	ResourceLogging   *ResourceLogging `json:"resourceLogging"`
+	Backup           *Backup            `json:"backup"`
 }
diff --git a/voc/firewall.go b/voc/firewall.go
new file mode 100644
index 00000000..217cf2ed
--- /dev/null
+++ b/voc/firewall.go
@@ -0,0 +1,32 @@
+// Auto-generated code by owl2java (https://github.com/clouditor/cloud-property-graph)
+
+// Copyright 2023 Fraunhofer AISEC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//           $$\                           $$\ $$\   $$\
+//           $$ |                          $$ |\__|  $$ |
+//  $$$$$$$\ $$ | $$$$$$\  $$\   $$\  $$$$$$$ |$$\ $$$$$$\    $$$$$$\   $$$$$$\
+// $$  _____|$$ |$$  __$$\ $$ |  $$ |$$  __$$ |$$ |\_$$  _|  $$  __$$\ $$  __$$\
+// $$ /      $$ |$$ /  $$ |$$ |  $$ |$$ /  $$ |$$ |  $$ |    $$ /  $$ |$$ | \__|
+// $$ |      $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$ |  $$ |$$\ $$ |  $$ |$$ |
+// \$$$$$$\  $$ |\$$$$$   |\$$$$$   |\$$$$$$  |$$ |  \$$$   |\$$$$$   |$$ |
+//  \_______|\__| \______/  \______/  \_______|\__|   \____/  \______/ \__|
+//
+// This file is part of Clouditor Community Edition.
+
+package voc
+
+type Firewall struct {
+	*AccessRestriction
+}
diff --git a/voc/l3_firewall.go b/voc/l3_firewall.go
index 20463c3b..70b8c338 100644
--- a/voc/l3_firewall.go
+++ b/voc/l3_firewall.go
@@ -28,7 +28,7 @@
 package voc
 
 type L3Firewall struct {
-	*AccessRestriction
+	*Firewall
 	Inbound         bool   `json:"inbound"`
 	Enabled         bool   `json:"enabled"`
 	RestrictedPorts string `json:"restrictedPorts"`
diff --git a/voc/load_balancer.go b/voc/load_balancer.go
index 9e1dc333..635848a1 100644
--- a/voc/load_balancer.go
+++ b/voc/load_balancer.go
@@ -32,8 +32,8 @@ var LoadBalancerType = []string{"LoadBalancer", "NetworkService", "Networking",
 // LoadBalancer is an entity in our Cloud ontology. A Load Balancer may have multiple access restriction features, e.g. a L3 firewall and a WAF
 type LoadBalancer struct {
 	*NetworkService
-	AccessRestrictions *[]AccessRestriction `json:"accessRestrictions"`
-	HttpEndpoints      *[]HttpEndpoint      `json:"httpEndpoints"`
-	NetworkServices    []ResourceID         `json:"networkServices"`
-	Url                string               `json:"url"`
+	AccessRestriction IsAccessRestriction `json:"accessRestriction"`
+	HttpEndpoints     *[]HttpEndpoint     `json:"httpEndpoints"`
+	NetworkServices   []ResourceID        `json:"networkServices"`
+	Url               string              `json:"url"`
 }
diff --git a/voc/network_interface.go b/voc/network_interface.go
index f938fb19..e57d1201 100644
--- a/voc/network_interface.go
+++ b/voc/network_interface.go
@@ -31,6 +31,6 @@ var NetworkInterfaceType = []string{"NetworkInterface", "Networking", "Resource"
 
 type NetworkInterface struct {
 	*Networking
-	NetworkService    ResourceID         `json:"networkService"`
-	AccessRestriction *AccessRestriction `json:"accessRestriction"`
+	NetworkService    ResourceID          `json:"networkService"`
+	AccessRestriction IsAccessRestriction `json:"accessRestriction"`
 }
diff --git a/voc/resource_logging.go b/voc/resource_logging.go
index 9931fdbd..f535c2ca 100644
--- a/voc/resource_logging.go
+++ b/voc/resource_logging.go
@@ -29,4 +29,7 @@ package voc
 
 type ResourceLogging struct {
 	*Logging
+
+	MonitoringLogDataEnabled bool `json:"monitoringLogDataEnabled"`
+	SecurityAlertsEnabled    bool `json:"securityAlertsEnabled"`
 }
diff --git a/voc/storage.go b/voc/storage.go
index 5b38bfbe..2b6c71d0 100644
--- a/voc/storage.go
+++ b/voc/storage.go
@@ -33,4 +33,6 @@ type Storage struct {
 	*Resource
 	AtRestEncryption IsAtRestEncryption `json:"atRestEncryption"`
 	Immutability     *Immutability      `json:"immutability"`
+	ResourceLogging  *ResourceLogging   `json:"resourceLogging"`
+	Backup           *Backup            `json:"backup"`
 }
diff --git a/voc/voc.go b/voc/voc.go
index 42139452..0243f208 100644
--- a/voc/voc.go
+++ b/voc/voc.go
@@ -157,6 +157,13 @@ type IsAuthenticity interface {
 
 func (*Authenticity) authenticity() {}
 
+type IsAccessRestriction interface {
+	IsSecurityFeature
+	accessRestriction()
+}
+
+func (*AccessRestriction) accessRestriction() {}
+
 type HasHttpEndpoint interface {
 	GetHttpEndpoint() *HttpEndpoint
 }
diff --git a/voc/web_application_firewall.go b/voc/web_application_firewall.go
index e536bbd7..26750591 100644
--- a/voc/web_application_firewall.go
+++ b/voc/web_application_firewall.go
@@ -29,6 +29,6 @@ package voc
 
 // WebApplicationFirewall is an entity in our Cloud ontology. A WAF is a L7 firewall that includes L3 capabilities
 type WebApplicationFirewall struct {
-	*AccessRestriction
+	*Firewall
 	Enabled bool `json:"enabled"`
 }


Test patch:
diff --git a/policies/rego_test.go b/policies/rego_test.go
index d0636b06..baccce41 100644
--- a/policies/rego_test.go
+++ b/policies/rego_test.go
@@ -100,7 +100,8 @@ func Test_regoEval_Eval(t *testing.T) {
 				"ResourceInventory":                 true,
 			},
 			wantErr: false,
-		}, {
+		},
+		{
 			name: "ObjectStorage: Non-Compliant Case with no Encryption at rest",
 			fields: fields{
 				qc:      newQueryCache(),
@@ -190,7 +191,7 @@ func Test_regoEval_Eval(t *testing.T) {
 				resource: voc.VirtualMachine{
 					AutomaticUpdates: &voc.AutomaticUpdates{
 						Enabled:      true,
-						Interval:     time.Hour * 24,
+						Interval:     time.Hour * 24 * 30,
 						SecurityOnly: true,
 					},
 					Compute: &voc.Compute{
@@ -324,7 +325,7 @@ func Test_regoEval_Eval(t *testing.T) {
 				}
 			}
 
-			assert.Equal(t, compliants, tt.compliant)
+			assert.Equal(t, tt.compliant, compliants)
 		})
 	}
 }
diff --git a/service/discovery/azure/azure_test.go b/service/discovery/azure/azure_test.go
index 6289daa3..7c7e265c 100644
--- a/service/discovery/azure/azure_test.go
+++ b/service/discovery/azure/azure_test.go
@@ -33,13 +33,19 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"reflect"
 	"testing"
+	"time"
 
+	"clouditor.io/clouditor/internal/constants"
 	"clouditor.io/clouditor/internal/testdata"
-
+	"clouditor.io/clouditor/internal/util"
+	"clouditor.io/clouditor/voc"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dataprotection/armdataprotection"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/subscription/armsubscription"
 	"github.com/stretchr/testify/assert"
@@ -50,7 +56,7 @@ type mockSender struct {
 
 func (mockSender) Do(req *http.Request) (res *http.Response, err error) {
 	if req.URL.Path == "/subscriptions" {
-		res, err = createResponse(map[string]interface{}{
+		res, err = createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":             "/subscriptions/00000000-0000-0000-0000-000000000000",
@@ -60,7 +66,7 @@ func (mockSender) Do(req *http.Request) (res *http.Response, err error) {
 			},
 		}, 200)
 	} else {
-		res, err = createResponse(map[string]interface{}{}, 404)
+		res, err = createResponse(req, map[string]interface{}{}, 404)
 		log.Errorf("Not handling mock for %s yet", req.URL.Path)
 	}
 
@@ -75,7 +81,7 @@ func (*mockAuthorizer) GetToken(_ context.Context, _ policy.TokenRequestOptions)
 	return token, nil
 }
 
-func createResponse(object map[string]interface{}, statusCode int) (res *http.Response, err error) {
+func createResponse(req *http.Request, object map[string]interface{}, statusCode int) (res *http.Response, err error) {
 	buf := new(bytes.Buffer)
 	enc := json.NewEncoder(buf)
 
@@ -88,6 +94,9 @@ func createResponse(object map[string]interface{}, statusCode int) (res *http.Re
 	return &http.Response{
 		StatusCode: statusCode,
 		Body:       body,
+		// We also need to fill out the request because the Azure SDK will
+		// construct the error message out of this
+		Request: req,
 	}, nil
 }
 
@@ -325,7 +334,8 @@ func NewMockAzureDiscovery(transport policy.Transporter, opts ...DiscoveryOption
 				Transport: transport,
 			},
 		},
-		csID: testdata.MockCloudServiceID,
+		csID:      testdata.MockCloudServiceID,
+		backupMap: make(map[string]map[string]*voc.Backup),
 	}
 
 	// Apply options
@@ -335,3 +345,454 @@ func NewMockAzureDiscovery(transport policy.Transporter, opts ...DiscoveryOption
 
 	return d
 }
+
+func Test_azureDiscovery_discoverBackupVaults_Storage(t *testing.T) {
+	type fields struct {
+		azureDiscovery            *azureDiscovery
+		clientBackupVault         bool
+		clientBackupInstance      bool
+		emptyClientBackupInstance bool
+		clientBackupPolicy        bool
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    assert.ValueAssertionFunc
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "all clients missing",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockStorageSender()),
+				clientBackupVault:    false,
+				clientBackupInstance: false,
+				clientBackupPolicy:   false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "backupVaultClient and/or backupInstancesClient missing")
+			},
+		},
+		{
+			name: "backup instance client missing",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockStorageSender()),
+				clientBackupVault:    true,
+				clientBackupInstance: false,
+				clientBackupPolicy:   false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "backupVaultClient and/or backupInstancesClient missing")
+			},
+		},
+		{
+			name: "backup instance client empty",
+			fields: fields{
+				azureDiscovery:            NewMockAzureDiscovery(newMockStorageSender()),
+				clientBackupVault:         true,
+				emptyClientBackupInstance: true,
+				clientBackupPolicy:        false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not discover backup instances:")
+			},
+		},
+
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockStorageSender()),
+				clientBackupVault:    true,
+				clientBackupInstance: true,
+				clientBackupPolicy:   true,
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				d, ok := i1.(*azureStorageDiscovery)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				want := &voc.Backup{
+					RetentionPeriod: Duration7Days,
+					Enabled:         true,
+					GeoLocation:     voc.GeoLocation{Region: "westeurope"},
+					Storage:         voc.ResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/account1-account1-22222222-2222-2222-2222-222222222222"),
+					Policy:          "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer",
+					AtRestEncryption: &voc.AtRestEncryption{
+						Algorithm: "AES256",
+						Enabled:   true,
+					},
+					TransportEncryption: &voc.TransportEncryption{
+						Enforced:   true,
+						Enabled:    true,
+						TlsVersion: constants.TLS1_2,
+						Algorithm:  constants.TLS,
+					},
+				}
+
+				return assert.Equal(t, want, d.backupMap[DataSourceTypeStorageAccount]["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1"])
+
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureStorageDiscovery{
+				azureDiscovery: tt.fields.azureDiscovery,
+			}
+
+			// Set clients if needed
+			if tt.fields.clientBackupVault {
+				// initialize backup vaults client
+				_ = d.initBackupVaultsClient()
+			}
+			if tt.fields.clientBackupInstance {
+				// initialize backup instances client
+				_ = d.initBackupInstancesClient()
+			}
+			if tt.fields.clientBackupPolicy {
+				// initialize backup policies client
+				_ = d.initBackupPoliciesClient()
+			}
+
+			// Set empty client if needed
+			if tt.fields.emptyClientBackupInstance {
+				// empty backup instances client
+				d.clients.backupInstancesClient = &armdataprotection.BackupInstancesClient{}
+			}
+
+			err := d.discoverBackupVaults()
+
+			tt.wantErr(t, err)
+
+			if tt.want != nil {
+				tt.want(t, d)
+			}
+		})
+	}
+}
+
+func Test_azureDiscovery_discoverBackupVaults_Compute(t *testing.T) {
+	type fields struct {
+		azureDiscovery       *azureDiscovery
+		clientBackupVault    bool
+		clientBackupInstance bool
+		clientBackupPolicy   bool
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    assert.ValueAssertionFunc
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "both clients missing",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockComputeSender()),
+				clientBackupVault:    false,
+				clientBackupInstance: false,
+				clientBackupPolicy:   false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "backupVaultClient and/or backupInstancesClient missing")
+			},
+		},
+		{
+			name: "backup instance client missing",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockComputeSender()),
+				clientBackupVault:    true,
+				clientBackupInstance: false,
+				clientBackupPolicy:   false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "backupVaultClient and/or backupInstancesClient missing")
+			},
+		},
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockComputeSender()),
+				clientBackupVault:    true,
+				clientBackupInstance: true,
+				clientBackupPolicy:   true,
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				d, ok := i1.(*azureComputeDiscovery)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				want := &voc.Backup{
+					RetentionPeriod: Duration30Days,
+					Enabled:         true,
+					GeoLocation:     voc.GeoLocation{Region: "westeurope"},
+					Storage:         voc.ResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/disk1-disk1-22222222-2222-2222-2222-222222222222"),
+					Policy:          "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyDisk",
+					AtRestEncryption: &voc.AtRestEncryption{
+						Algorithm: "AES256",
+						Enabled:   true,
+					},
+					TransportEncryption: &voc.TransportEncryption{
+						Enforced:   true,
+						Enabled:    true,
+						TlsVersion: constants.TLS1_2,
+						Algorithm:  constants.TLS,
+					},
+				}
+
+				return assert.Equal(t, want, d.backupMap[DataSourceTypeDisc]["/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/disks/disk1"])
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureComputeDiscovery{
+				azureDiscovery: tt.fields.azureDiscovery,
+			}
+
+			// Set clients if needed
+			if tt.fields.clientBackupVault {
+				// initialize backup vaults client
+				_ = d.initBackupVaultsClient()
+			}
+			if tt.fields.clientBackupInstance {
+				// initialize backup instances client
+				_ = d.initBackupInstancesClient()
+			}
+
+			if tt.fields.clientBackupPolicy {
+				// initialize backup policies client
+				_ = d.initBackupPoliciesClient()
+			}
+
+			err := d.discoverBackupVaults()
+
+			tt.wantErr(t, err)
+
+			if tt.want != nil {
+				tt.want(t, d)
+			}
+		})
+	}
+}
+
+func Test_retentionDuration(t *testing.T) {
+	type args struct {
+		retention string
+	}
+	tests := []struct {
+		name string
+		args args
+		want time.Duration
+	}{
+		{
+			name: "Missing input",
+			args: args{
+				retention: "",
+			},
+			want: time.Duration(0),
+		},
+		{
+			name: "Wrong input",
+			args: args{
+				retention: "TEST",
+			},
+			want: time.Duration(0),
+		},
+		{
+			name: "Happy path",
+			args: args{
+				retention: "P30D",
+			},
+			want: Duration30Days,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := retentionDuration(tt.args.retention); got != tt.want {
+				t.Errorf("retentionDuration() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func Test_azureDiscovery_discoverDefender(t *testing.T) {
+	type fields struct {
+		azureDiscovery      *azureDiscovery
+		clientDefender      bool
+		emptyDefenderClient bool
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    assert.ValueAssertionFunc
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "defenderClient not set",
+			fields: fields{
+				azureDiscovery: NewMockAzureDiscovery(newMockStorageSender()),
+				clientDefender: false,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "defenderClient not set")
+			},
+		},
+		{
+			name: "empty defenderClient",
+			fields: fields{
+				azureDiscovery:      NewMockAzureDiscovery(newMockStorageSender()),
+				clientDefender:      false,
+				emptyDefenderClient: true,
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not discover pricings")
+			},
+		},
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery: NewMockAzureDiscovery(newMockStorageSender()),
+				clientDefender: true,
+			},
+			want: func(tt assert.TestingT, i1 interface{}, i2 ...interface{}) bool {
+				got, ok := i1.(map[string]*defenderProperties)
+				if !assert.True(tt, ok) {
+					return false
+				}
+
+				want := &defenderProperties{
+					monitoringLogDataEnabled: true,
+					securityAlertsEnabled:    true,
+				}
+
+				return assert.Equal(t, want, got[DefenderStorageType])
+			},
+			wantErr: assert.NoError,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureStorageDiscovery{
+				azureDiscovery: tt.fields.azureDiscovery,
+			}
+
+			// Set client if needed
+			if tt.fields.clientDefender {
+				// initialize backup vaults client
+				_ = d.initDefenderClient()
+			}
+
+			// Set empty defender client if needed
+			if tt.fields.emptyDefenderClient {
+				// initialize backup vaults client
+				d.clients.defenderClient = &armsecurity.PricingsClient{}
+			}
+
+			got, err := d.discoverDefender()
+
+			tt.wantErr(t, err)
+
+			if tt.want != nil {
+				tt.want(t, got)
+			}
+		})
+	}
+}
+
+func Test_azureDiscovery_discoverBackupInstances(t *testing.T) {
+	type fields struct {
+		azureDiscovery       *azureDiscovery
+		clientBackupInstance bool
+	}
+	type args struct {
+		resourceGroup string
+		vaultName     string
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		want    []*armdataprotection.BackupInstanceResource
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Input empty",
+			args: args{},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "missing resource group and/or vault name")
+			},
+		},
+		{
+			name: "defenderClient not set",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockNetworkSender()),
+				clientBackupInstance: true,
+			},
+			args: args{
+				resourceGroup: "res1",
+				vaultName:     "backupAccount1",
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "error getting next page: GET")
+			},
+		},
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery:       NewMockAzureDiscovery(newMockStorageSender()),
+				clientBackupInstance: true,
+			},
+			args: args{
+				resourceGroup: "res1",
+				vaultName:     "backupAccount1",
+			},
+			wantErr: assert.NoError,
+			want: []*armdataprotection.BackupInstanceResource{
+				{
+					ID:   util.Ref("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/account1-account1-22222222-2222-2222-2222-222222222222"),
+					Name: util.Ref("account1-account1-22222222-2222-2222-2222-222222222222"),
+					Properties: &armdataprotection.BackupInstance{
+						DataSourceInfo: &armdataprotection.Datasource{
+							ResourceID:     util.Ref("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1"),
+							DatasourceType: util.Ref("Microsoft.Storage/storageAccounts/blobServices"),
+						},
+						PolicyInfo: &armdataprotection.PolicyInfo{
+							PolicyID: util.Ref("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer"),
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureStorageDiscovery{
+				azureDiscovery: tt.fields.azureDiscovery,
+			}
+
+			if tt.fields.clientBackupInstance {
+				// initialize backup instances client
+				_ = d.initBackupInstancesClient()
+			}
+			got, err := d.discoverBackupInstances(tt.args.resourceGroup, tt.args.vaultName)
+
+			tt.wantErr(t, err)
+
+			if !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("azureDiscovery.discoverBackupInstances() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/service/discovery/azure/compute_test.go b/service/discovery/azure/compute_test.go
index 8ab26f06..056ee6f0 100644
--- a/service/discovery/azure/compute_test.go
+++ b/service/discovery/azure/compute_test.go
@@ -30,14 +30,15 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"reflect"
 	"testing"
 	"time"
 
 	"clouditor.io/clouditor/api/discovery"
+	"clouditor.io/clouditor/internal/constants"
 	"clouditor.io/clouditor/internal/testdata"
 	"clouditor.io/clouditor/internal/util"
 	"clouditor.io/clouditor/voc"
-
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2"
@@ -60,7 +61,7 @@ type mockedVirtualMachinesResponse struct {
 
 func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error) {
 	if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Compute/virtualMachines" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/virtualMachines/vm1",
@@ -87,6 +88,13 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 								},
 							},
 						},
+						"osProfile": map[string]interface{}{
+							"linuxConfiguration": map[string]interface{}{
+								"patchSettings": map[string]interface{}{
+									"patchMode": "AutomaticByPlatform",
+								},
+							},
+						},
 						"diagnosticsProfile": map[string]interface{}{
 							"bootDiagnostics": map[string]interface{}{
 								"enabled":    true,
@@ -129,6 +137,13 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 								},
 							},
 						},
+						"osProfile": map[string]interface{}{
+							"windowsConfiguration": map[string]interface{}{
+								"patchSettings": map[string]interface{}{
+									"patchMode": "AutomaticByOS",
+								},
+							},
+						},
 						"diagnosticsProfile": map[string]interface{}{
 							"bootDiagnostics": map[string]interface{}{
 								"enabled":    true,
@@ -160,7 +175,7 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Compute/disks" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/disks/disk1",
@@ -204,7 +219,7 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res2/providers/Microsoft.Compute/disks" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res2/providers/Microsoft.Compute/disks/anotherdisk",
@@ -222,15 +237,15 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res2/providers/Microsoft.Compute/virtualMachines" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res2/providers/Microsoft.Web/sites" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Web/sites" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Web/sites/function1",
@@ -240,12 +255,16 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 						"testKey1": "testTag1",
 						"testKey2": "testTag2",
 					},
-					"properties": map[string]interface{}{},
+					"properties": map[string]interface{}{
+						"siteConfig": map[string]interface{}{
+							"linuxFxVersion": "PYTHON|3.8",
+						},
+					},
 				},
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/diskEncryptionSets/encryptionkeyvault1" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/diskEncryptionSets/encryption-keyvault1",
 			"type":     "Microsoft.Compute/diskEncryptionSets",
 			"name":     "encryptionkeyvault1",
@@ -260,7 +279,7 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/diskEncryptionSets/encryptionkeyvault2" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/diskEncryptionSets/encryption-keyvault2",
 			"type":     "Microsoft.Compute/diskEncryptionSets",
 			"name":     "encryptionkeyvault2",
@@ -273,6 +292,58 @@ func (m mockComputeSender) Do(req *http.Request) (res *http.Response, err error)
 				},
 			},
 		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.DataProtection/backupVaults" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1",
+					"name":     "backupAccount1",
+					"location": "westeurope",
+				},
+			},
+		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/disk1-disk1-22222222-2222-2222-2222-222222222222",
+					"name": "disk1-disk1-22222222-2222-2222-2222-222222222222",
+					"properties": map[string]interface{}{
+						"dataSourceInfo": map[string]interface{}{
+							"resourceID":     "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Compute/disks/disk1",
+							"datasourceType": "Microsoft.Compute/disks",
+						},
+						"policyInfo": map[string]interface{}{
+							"policyId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyDisk",
+						},
+					},
+				},
+			},
+		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyDisk" {
+		return createResponse(req, map[string]interface{}{
+			"properties": map[string]interface{}{
+				"objectType": "BackupPolicy",
+				"policyRules": []map[string]interface{}{
+					{
+						"objectType": "AzureRetentionRule",
+						"lifecycles": []map[string]interface{}{
+							{
+								"deleteAfter": map[string]interface{}{
+									"duration":   "P30D",
+									"objectType": "AbsoluteDeleteOption",
+								},
+								"sourceDataStore": map[string]interface{}{
+									"objectType":    "OperationalStore",
+									"DataStoreType": "DataStoreInfoBase",
+								},
+							},
+						},
+					},
+				},
+			},
+			// },
+		}, 200)
 	}
 
 	return m.mockSender.Do(req)
@@ -296,7 +367,9 @@ func TestNewAzureComputeDiscovery(t *testing.T) {
 				&azureDiscovery{
 					discovererComponent: ComputeComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				make(map[string]*defenderProperties),
 			},
 		},
 		{
@@ -313,7 +386,9 @@ func TestNewAzureComputeDiscovery(t *testing.T) {
 					},
 					discovererComponent: ComputeComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				make(map[string]*defenderProperties),
 			},
 		},
 		{
@@ -326,7 +401,9 @@ func TestNewAzureComputeDiscovery(t *testing.T) {
 					cred:                &mockAuthorizer{},
 					discovererComponent: ComputeComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				make(map[string]*defenderProperties),
 			},
 		},
 		{
@@ -338,7 +415,9 @@ func TestNewAzureComputeDiscovery(t *testing.T) {
 				&azureDiscovery{
 					discovererComponent: ComputeComponent,
 					csID:                testdata.MockCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				make(map[string]*defenderProperties),
 			},
 		},
 	}
@@ -511,18 +590,18 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 		wantList []voc.IsCloudResource
 		wantErr  assert.ErrorAssertionFunc
 	}{
-		// {
-		// 	name: "Authorize error",
-		// 	fields: fields{
-		// 		azureDiscovery: azureDiscovery{
-		// 			cred: nil,
-		// 		},
-		// 	},
-		// 	wantList: nil,
-		// 	wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-		// 		return assert.ErrorContains(t, err, ErrCouldNotAuthenticate.Error())
-		// 	},
-		// },
+		{
+			name: "Authorize error",
+			fields: fields{
+				azureDiscovery: &azureDiscovery{
+					cred: nil,
+				},
+			},
+			wantList: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, ErrCouldNotAuthenticate.Error())
+			},
+		},
 		{
 			name: "Discovery error",
 			fields: fields{
@@ -553,13 +632,29 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 							Labels: map[string]string{},
 							Type:   voc.BlockStorageType,
 						},
-
 						AtRestEncryption: &voc.ManagedKeyEncryption{
 							AtRestEncryption: &voc.AtRestEncryption{
 								Algorithm: "AES256",
 								Enabled:   true,
 							},
 						},
+						Backup: &voc.Backup{
+							Enabled:         true,
+							RetentionPeriod: Duration30Days,
+							GeoLocation:     voc.GeoLocation{Region: "westeurope"},
+							Policy:          "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyDisk",
+							Storage:         voc.ResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/disk1-disk1-22222222-2222-2222-2222-222222222222"),
+							AtRestEncryption: &voc.AtRestEncryption{
+								Algorithm: "AES256",
+								Enabled:   true,
+							},
+							TransportEncryption: &voc.TransportEncryption{
+								Enforced:   true,
+								Enabled:    true,
+								TlsVersion: constants.TLS1_2,
+								Algorithm:  constants.TLS,
+							},
+						},
 					},
 				},
 				&voc.BlockStorage{
@@ -642,6 +737,10 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  true,
+						Interval: Duration30Days,
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 				&voc.VirtualMachine{
@@ -680,6 +779,10 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  true,
+						Interval: Duration30Days,
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 				&voc.VirtualMachine{
@@ -718,6 +821,10 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  false,
+						Interval: time.Duration(0),
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 				&voc.Function{
@@ -738,6 +845,9 @@ func Test_azureComputeDiscovery_List(t *testing.T) {
 						},
 						NetworkInterfaces: []voc.ResourceID{},
 					},
+
+					RuntimeVersion:  "3.8",
+					RuntimeLanguage: "PYTHON",
 				},
 			},
 			wantErr: assert.NoError,
@@ -836,6 +946,8 @@ func Test_azureComputeDiscovery_discoverFunctions(t *testing.T) {
 						},
 						NetworkInterfaces: []voc.ResourceID{},
 					},
+					RuntimeVersion:  "3.8",
+					RuntimeLanguage: "PYTHON",
 				},
 			},
 			wantErr: assert.NoError,
@@ -904,6 +1016,11 @@ func Test_azureComputeDiscovery_handleFunction(t *testing.T) {
 						"testKey1": &testTag1,
 						"testKey2": &testTag2,
 					},
+					Properties: &armappservice.SiteProperties{
+						SiteConfig: &armappservice.SiteConfig{
+							LinuxFxVersion: util.Ref("PYTHON|3.8"),
+						},
+					},
 				},
 			},
 			want: &voc.Function{
@@ -924,6 +1041,8 @@ func Test_azureComputeDiscovery_handleFunction(t *testing.T) {
 					},
 					NetworkInterfaces: []voc.ResourceID{},
 				},
+				RuntimeVersion:  "3.8",
+				RuntimeLanguage: "PYTHON",
 			},
 		},
 	}
@@ -949,20 +1068,16 @@ func Test_azureComputeDiscovery_discoverVirtualMachines(t *testing.T) {
 		want    []voc.IsCloudResource
 		wantErr assert.ErrorAssertionFunc
 	}{
-		// TODO(anatheka): Wo kommt die panic her?
-		// {
-		//	name: "Error list pages",
-		//	fields: fields{
-		//		azureDiscovery: azureDiscovery{
-		//			cred: nil,
-		//			sub:  sub,
-		//		},
-		//	},
-		//	want: nil,
-		//	wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-		//		return assert.ErrorContains(t, err, ErrGettingNextPage.Error())
-		//	},
-		// },
+		{
+			name: "Error list pages",
+			fields: fields{
+				azureDiscovery: NewMockAzureDiscovery(nil),
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, ErrGettingNextPage.Error())
+			},
+		},
 		{
 			name: "No error",
 			fields: fields{
@@ -1005,6 +1120,10 @@ func Test_azureComputeDiscovery_discoverVirtualMachines(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  true,
+						Interval: Duration30Days,
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 				&voc.VirtualMachine{
@@ -1043,6 +1162,10 @@ func Test_azureComputeDiscovery_discoverVirtualMachines(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  true,
+						Interval: Duration30Days,
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 				&voc.VirtualMachine{
@@ -1081,6 +1204,10 @@ func Test_azureComputeDiscovery_discoverVirtualMachines(t *testing.T) {
 							},
 						},
 					},
+					AutomaticUpdates: &voc.AutomaticUpdates{
+						Enabled:  false,
+						Interval: time.Duration(0),
+					},
 					MalwareProtection: &voc.MalwareProtection{},
 				},
 			},
@@ -1222,6 +1349,10 @@ func Test_azureComputeDiscovery_handleVirtualMachines(t *testing.T) {
 						},
 					},
 				},
+				AutomaticUpdates: &voc.AutomaticUpdates{
+					Enabled:  false,
+					Interval: time.Duration(0),
+				},
 				MalwareProtection: &voc.MalwareProtection{},
 			},
 			wantErr: assert.NoError,
@@ -1859,3 +1990,265 @@ func Test_diskEncryptionSetName(t *testing.T) {
 		})
 	}
 }
+
+func Test_azureComputeDiscovery_createResourceLogging(t *testing.T) {
+	type fields struct {
+		azureDiscovery     *azureDiscovery
+		defenderProperties map[string]*defenderProperties
+	}
+	tests := []struct {
+		name                string
+		fields              fields
+		wantResourceLogging *voc.ResourceLogging
+	}{
+		{
+			name: "Missing defenderProperties",
+			fields: fields{
+				azureDiscovery:     NewMockAzureDiscovery(newMockComputeSender()),
+				defenderProperties: make(map[string]*defenderProperties),
+			},
+			wantResourceLogging: nil,
+		},
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery: NewMockAzureDiscovery(newMockComputeSender()),
+				defenderProperties: map[string]*defenderProperties{
+					DefenderVirtualMachineType: {
+						monitoringLogDataEnabled: true,
+						securityAlertsEnabled:    true,
+					},
+				},
+			},
+			wantResourceLogging: &voc.ResourceLogging{
+				MonitoringLogDataEnabled: true,
+				SecurityAlertsEnabled:    true,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureComputeDiscovery{
+				azureDiscovery:     tt.fields.azureDiscovery,
+				defenderProperties: tt.fields.defenderProperties,
+			}
+			if gotResourceLogging := d.createResourceLogging(); !reflect.DeepEqual(gotResourceLogging, tt.wantResourceLogging) {
+				t.Errorf("azureComputeDiscovery.createResourceLogging() = %v, want %v", gotResourceLogging, tt.wantResourceLogging)
+			}
+		})
+	}
+}
+
+func Test_runtimeInfo(t *testing.T) {
+	type args struct {
+		runtime string
+	}
+	tests := []struct {
+		name                string
+		args                args
+		wantRuntimeLanguage string
+		wantRuntimeVersion  string
+	}{
+		{
+			name: "Empty input",
+			args: args{
+				runtime: "",
+			},
+			wantRuntimeLanguage: "",
+			wantRuntimeVersion:  "",
+		},
+		{
+			name: "Wrong input",
+			args: args{
+				runtime: "TEST",
+			},
+			wantRuntimeLanguage: "",
+			wantRuntimeVersion:  "",
+		},
+		{
+			name: "Happy path",
+			args: args{
+				runtime: "PYTHON|3.8",
+			},
+			wantRuntimeLanguage: "PYTHON",
+			wantRuntimeVersion:  "3.8",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotRuntimeLanguage, gotRuntimeVersion := runtimeInfo(tt.args.runtime)
+			if gotRuntimeLanguage != tt.wantRuntimeLanguage {
+				t.Errorf("runtimeInfo() gotRuntimeLanguage = %v, want %v", gotRuntimeLanguage, tt.wantRuntimeLanguage)
+			}
+			if gotRuntimeVersion != tt.wantRuntimeVersion {
+				t.Errorf("runtimeInfo() gotRuntimeVersion = %v, want %v", gotRuntimeVersion, tt.wantRuntimeVersion)
+			}
+		})
+	}
+}
+
+func Test_automaticUpdatesEnabled(t *testing.T) {
+	type args struct {
+		vm *armcompute.VirtualMachine
+	}
+	tests := []struct {
+		name string
+		args args
+		want *voc.AutomaticUpdates
+	}{
+		{
+			name: "Empty input",
+			args: args{},
+			want: &voc.AutomaticUpdates{
+				Enabled:  false,
+				Interval: time.Duration(0),
+			},
+		},
+		{
+			name: "Happy path: Windows configuration set to manual",
+			args: args{
+				&armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							WindowsConfiguration: &armcompute.WindowsConfiguration{
+								PatchSettings: &armcompute.PatchSettings{
+									PatchMode: util.Ref(armcompute.WindowsVMGuestPatchModeManual),
+								},
+							},
+						},
+					},
+				},
+			},
+			want: &voc.AutomaticUpdates{
+				Enabled:  false,
+				Interval: time.Duration(0),
+			},
+		},
+		{
+			name: "Happy path: Linux configuration",
+			args: args{
+				&armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							LinuxConfiguration: &armcompute.LinuxConfiguration{
+								PatchSettings: &armcompute.LinuxPatchSettings{
+									PatchMode: util.Ref(armcompute.LinuxVMGuestPatchModeAutomaticByPlatform),
+								},
+							},
+						},
+					},
+				},
+			},
+			want: &voc.AutomaticUpdates{
+				Enabled:  true,
+				Interval: Duration30Days,
+			},
+		},
+		{
+			name: "Happy path: Windows configuration",
+			args: args{
+				&armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							WindowsConfiguration: &armcompute.WindowsConfiguration{
+								PatchSettings: &armcompute.PatchSettings{
+									PatchMode: util.Ref(armcompute.WindowsVMGuestPatchModeAutomaticByOS),
+								},
+							},
+						},
+					},
+				},
+			},
+			want: &voc.AutomaticUpdates{
+				Enabled:  true,
+				Interval: Duration30Days,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got := automaticUpdates(tt.args.vm)
+
+			assert.Equal(t, tt.want, got)
+		})
+	}
+}
+
+func Test_automaticUpdates(t *testing.T) {
+	type args struct {
+		vm *armcompute.VirtualMachine
+	}
+	tests := []struct {
+		name                 string
+		args                 args
+		wantAutomaticUpdates *voc.AutomaticUpdates
+	}{
+		{
+			name:                 "Empty input",
+			args:                 args{},
+			wantAutomaticUpdates: &voc.AutomaticUpdates{},
+		},
+		{
+			name: "No automatic update for the given VM",
+			args: args{
+				vm: &armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							LinuxConfiguration: &armcompute.LinuxConfiguration{
+								PatchSettings: &armcompute.LinuxPatchSettings{},
+							},
+						},
+					},
+				},
+			},
+			wantAutomaticUpdates: &voc.AutomaticUpdates{},
+		},
+		{
+			name: "Happy path: Linux",
+			args: args{
+				vm: &armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							LinuxConfiguration: &armcompute.LinuxConfiguration{
+								PatchSettings: &armcompute.LinuxPatchSettings{
+									PatchMode: util.Ref(armcompute.LinuxVMGuestPatchModeAutomaticByPlatform),
+								},
+							},
+						},
+					},
+				},
+			},
+			wantAutomaticUpdates: &voc.AutomaticUpdates{
+				Enabled:  true,
+				Interval: Duration30Days,
+			},
+		},
+		{
+			name: "Happy path: Windows",
+			args: args{
+				vm: &armcompute.VirtualMachine{
+					Properties: &armcompute.VirtualMachineProperties{
+						OSProfile: &armcompute.OSProfile{
+							WindowsConfiguration: &armcompute.WindowsConfiguration{
+								PatchSettings: &armcompute.PatchSettings{
+									PatchMode: util.Ref(armcompute.WindowsVMGuestPatchModeAutomaticByPlatform),
+								},
+							},
+						},
+					},
+				},
+			},
+			wantAutomaticUpdates: &voc.AutomaticUpdates{
+				Enabled:  true,
+				Interval: Duration30Days,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if gotAutomaticUpdates := automaticUpdates(tt.args.vm); !reflect.DeepEqual(gotAutomaticUpdates, tt.wantAutomaticUpdates) {
+				t.Errorf("automaticUpdates() = %v, want %v", gotAutomaticUpdates, tt.wantAutomaticUpdates)
+			}
+		})
+	}
+}
diff --git a/service/discovery/azure/network_test.go b/service/discovery/azure/network_test.go
index 42b895df..46550012 100644
--- a/service/discovery/azure/network_test.go
+++ b/service/discovery/azure/network_test.go
@@ -31,8 +31,8 @@ import (
 
 	"clouditor.io/clouditor/api/discovery"
 	"clouditor.io/clouditor/internal/testdata"
+	"clouditor.io/clouditor/internal/util"
 	"clouditor.io/clouditor/voc"
-
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork"
@@ -50,7 +50,7 @@ func newMockNetworkSender() *mockNetworkSender {
 
 func (m mockNetworkSender) Do(req *http.Request) (res *http.Response, err error) {
 	if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Network/networkInterfaces" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkInterfaces/iface1",
@@ -58,14 +58,32 @@ func (m mockNetworkSender) Do(req *http.Request) (res *http.Response, err error)
 					"location": "eastus",
 					"properties": map[string]interface{}{
 						"networkSecurityGroup": map[string]interface{}{
-							"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkSecurityGroups/nsg1",
+							"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkSecurityGroups/nsg1",
+							"name":     "nsg1",
+							"location": "eastus",
+							"properties": map[string]interface{}{
+								"securityRules": []map[string]interface{}{ // Important: These properties must the same as for the path "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkSecurityGroups/nsg1"
+									{
+										"properties": map[string]interface{}{
+											"access":          "Deny",
+											"sourcePortRange": "*",
+										},
+									},
+									{
+										"properties": map[string]interface{}{
+											"access":          "Deny",
+											"sourcePortRange": "*",
+										},
+									},
+								},
+							},
 						},
 					},
 				},
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkSecurityGroups/nsg1" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/networkSecurityGroups/nsg1",
 			"name":     "nsg1",
 			"location": "eastus",
@@ -87,7 +105,7 @@ func (m mockNetworkSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Network/loadBalancers" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/loadBalancers/lb1",
@@ -183,18 +201,35 @@ func (m mockNetworkSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/publicIPAddresses/test-b9cb3645-25d0-4288-910a-020563f63b1c" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"properties": map[string]interface{}{
 				"ipAddress": "111.222.333.444",
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/publicIPAddresses/test-b9cb3645-25d0-4288-910a-020563f63b1d" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"properties": map[string]interface{}{
 				"ipAddress": nil,
 			},
 		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Network/applicationGateways" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/applicationGateways/appgw1",
+					"name":     "appgw1",
+					"location": "eastus",
+					"properties": map[string]interface{}{
+						"webApplicationFirewallConfiguration": map[string]interface{}{
+							"enabled": true,
+						},
+					},
+				},
+			},
+		}, 200)
 	}
+	// return createResponse(req, map[string]interface{}{
+	// 	"value": &[]map[string]interface{}{
 
 	return m.mockSender.Do(req)
 }
@@ -261,7 +296,9 @@ func Test_azureNetworkDiscovery_List(t *testing.T) {
 							Labels: map[string]string{},
 						},
 					},
-					AccessRestriction: nil,
+					AccessRestriction: &voc.L3Firewall{
+						Enabled: true,
+					},
 				},
 				&voc.LoadBalancer{
 					NetworkService: &voc.NetworkService{
@@ -280,8 +317,7 @@ func Test_azureNetworkDiscovery_List(t *testing.T) {
 						Ips:   []string{"111.222.333.444"},
 						Ports: []uint16{1234, 5678},
 					},
-					AccessRestrictions: &[]voc.AccessRestriction{},
-					HttpEndpoints:      &[]voc.HttpEndpoint{},
+					HttpEndpoints: &[]voc.HttpEndpoint{},
 				},
 				&voc.LoadBalancer{
 					NetworkService: &voc.NetworkService{
@@ -300,8 +336,7 @@ func Test_azureNetworkDiscovery_List(t *testing.T) {
 						Ports: []uint16{1234, 5678},
 						Ips:   []string{},
 					},
-					AccessRestrictions: &[]voc.AccessRestriction{},
-					HttpEndpoints:      &[]voc.HttpEndpoint{},
+					HttpEndpoints: &[]voc.HttpEndpoint{},
 				},
 				&voc.LoadBalancer{
 					NetworkService: &voc.NetworkService{
@@ -320,8 +355,26 @@ func Test_azureNetworkDiscovery_List(t *testing.T) {
 						Ports: []uint16{1234, 5678},
 						Ips:   []string{},
 					},
-					AccessRestrictions: &[]voc.AccessRestriction{},
-					HttpEndpoints:      &[]voc.HttpEndpoint{},
+					HttpEndpoints: &[]voc.HttpEndpoint{},
+				},
+				&voc.LoadBalancer{
+					NetworkService: &voc.NetworkService{
+						Networking: &voc.Networking{
+							Resource: &voc.Resource{
+								ID:        "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Network/applicationGateways/appgw1",
+								ServiceID: testdata.MockCloudServiceID,
+								Name:      "appgw1",
+								GeoLocation: voc.GeoLocation{
+									Region: "eastus",
+								},
+								Type:   voc.LoadBalancerType,
+								Labels: map[string]string{},
+							},
+						},
+					},
+					AccessRestriction: voc.WebApplicationFirewall{
+						Enabled: true,
+					},
 				},
 			},
 			wantErr: assert.NoError,
@@ -333,6 +386,8 @@ func Test_azureNetworkDiscovery_List(t *testing.T) {
 				azureDiscovery: tt.fields.azureDiscovery,
 			}
 			gotList, err := d.List()
+
+			assert.Equal(t, len(tt.wantList), len(gotList))
 			if !tt.wantErr(t, err) {
 				return
 			}
@@ -362,6 +417,7 @@ func TestNewAzureNetworkDiscovery(t *testing.T) {
 				&azureDiscovery{
 					discovererComponent: NetworkComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
 			},
 		},
@@ -379,6 +435,7 @@ func TestNewAzureNetworkDiscovery(t *testing.T) {
 					},
 					discovererComponent: NetworkComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
 			},
 		},
@@ -392,6 +449,7 @@ func TestNewAzureNetworkDiscovery(t *testing.T) {
 					cred:                &mockAuthorizer{},
 					discovererComponent: NetworkComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
 			},
 		},
@@ -625,3 +683,83 @@ func Test_publicIPAddressFromLoadBalancer(t *testing.T) {
 		})
 	}
 }
+
+func Test_azureNetworkDiscovery_discoverApplicationGateway(t *testing.T) {
+	type fields struct {
+		azureDiscovery *azureDiscovery
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		want    []voc.IsCloudResource
+		wantErr assert.ErrorAssertionFunc
+	}{
+		{
+			name: "Error list pages",
+			fields: fields{
+				azureDiscovery: &azureDiscovery{
+					cred: nil,
+				},
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, ErrGettingNextPage.Error())
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureNetworkDiscovery{
+				azureDiscovery: tt.fields.azureDiscovery,
+			}
+			got, err := d.discoverApplicationGateway()
+			if !tt.wantErr(t, err) {
+				return
+			}
+			assert.Equalf(t, tt.want, got, "discoverApplicationGateway()")
+		})
+	}
+}
+
+func Test_nsgFirewallEnabled(t *testing.T) {
+	type args struct {
+		ni *armnetwork.Interface
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		{
+			name: "Empty input",
+			args: args{},
+			want: false,
+		},
+		{
+			name: "Happy path",
+			args: args{
+				ni: &armnetwork.Interface{
+					Properties: &armnetwork.InterfacePropertiesFormat{
+						NetworkSecurityGroup: &armnetwork.SecurityGroup{
+							Properties: &armnetwork.SecurityGroupPropertiesFormat{
+								SecurityRules: []*armnetwork.SecurityRule{
+									{
+										ID: util.Ref("test security rule"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			want: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := nsgFirewallEnabled(tt.args.ni); got != tt.want {
+				t.Errorf("nsgFirewallEnabled() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/service/discovery/azure/storage_test.go b/service/discovery/azure/storage_test.go
index 00543a89..4b18c47c 100644
--- a/service/discovery/azure/storage_test.go
+++ b/service/discovery/azure/storage_test.go
@@ -29,16 +29,18 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"reflect"
 	"testing"
 	"time"
 
 	"clouditor.io/clouditor/api/discovery"
+	"clouditor.io/clouditor/internal/constants"
 	"clouditor.io/clouditor/internal/testdata"
 	"clouditor.io/clouditor/internal/util"
 	"clouditor.io/clouditor/voc"
-
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
 	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
+	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity"
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage"
 	"github.com/stretchr/testify/assert"
 	"k8s.io/apimachinery/pkg/util/json"
@@ -59,7 +61,7 @@ type responseStorageAccount struct {
 
 func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error) {
 	if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Storage/storageAccounts" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1",
@@ -127,7 +129,7 @@ func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Storage/storageAccounts/account3" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &map[string]interface{}{
 				"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account3",
 				"name":     "account3",
@@ -160,7 +162,7 @@ func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1/blobServices/default/containers" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1/blobServices/default/containers/container1",
@@ -183,7 +185,7 @@ func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account2/blobServices/default/containers" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account2/blobServices/default/containers/container3",
@@ -206,7 +208,7 @@ func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1/fileServices/default/shares" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{
 				{
 					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1/fileServices/default/shares/fileshare1",
@@ -221,9 +223,82 @@ func (m mockStorageSender) Do(req *http.Request) (res *http.Response, err error)
 			},
 		}, 200)
 	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account2/fileServices/default/shares" {
-		return createResponse(map[string]interface{}{
+		return createResponse(req, map[string]interface{}{
 			"value": &[]map[string]interface{}{},
 		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.DataProtection/backupVaults" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":       "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1",
+					"name":     "backupAccount1",
+					"location": "westeurope",
+				},
+			},
+		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/account1-account1-22222222-2222-2222-2222-222222222222",
+					"name": "account1-account1-22222222-2222-2222-2222-222222222222",
+					"properties": map[string]interface{}{
+						"dataSourceInfo": map[string]interface{}{
+							"resourceID":     "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1",
+							"datasourceType": "Microsoft.Storage/storageAccounts/blobServices",
+						},
+						"policyInfo": map[string]interface{}{
+							"policyId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer",
+						},
+					},
+				},
+			},
+		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer" {
+		return createResponse(req, map[string]interface{}{
+			"properties": map[string]interface{}{
+				"objectType": "BackupPolicy",
+				"policyRules": []map[string]interface{}{
+					{
+						"objectType": "AzureRetentionRule",
+						"lifecycles": []map[string]interface{}{
+							{
+								"deleteAfter": map[string]interface{}{
+									"duration":   "P7D",
+									"objectType": "AbsoluteDeleteOption",
+								},
+								"sourceDataStore": map[string]interface{}{
+									"objectType":    "OperationalStore",
+									"DataStoreType": "DataStoreInfoBase",
+								},
+							},
+						},
+					},
+				},
+			},
+			// },
+		}, 200)
+	} else if req.URL.Path == "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Security/pricings" {
+		return createResponse(req, map[string]interface{}{
+			"value": &[]map[string]interface{}{
+				{
+					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Security/pricings/VirtualMachines",
+					"name": "VirtualMachines",
+					"type": "Microsoft.Security/pricings",
+					"properties": map[string]interface{}{
+						"pricingTier": armsecurity.PricingTierStandard,
+					},
+				},
+				{
+					"id":   "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Security/pricings/StorageAccounts",
+					"name": "StorageAccounts",
+					"type": "Microsoft.Security/pricings",
+					"properties": map[string]interface{}{
+						"pricingTier": armsecurity.PricingTierStandard,
+					},
+				},
+			},
+		}, 200)
 	}
 
 	return m.mockSender.Do(req)
@@ -247,7 +322,9 @@ func TestNewAzureStorageDiscovery(t *testing.T) {
 				azureDiscovery: &azureDiscovery{
 					discovererComponent: StorageComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				defenderProperties: make(map[string]*defenderProperties),
 			},
 		},
 		{
@@ -264,7 +341,9 @@ func TestNewAzureStorageDiscovery(t *testing.T) {
 					},
 					discovererComponent: StorageComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				defenderProperties: make(map[string]*defenderProperties),
 			},
 		},
 		{
@@ -277,7 +356,9 @@ func TestNewAzureStorageDiscovery(t *testing.T) {
 					cred:                &mockAuthorizer{},
 					discovererComponent: StorageComponent,
 					csID:                discovery.DefaultCloudServiceID,
+					backupMap:           make(map[string]map[string]*voc.Backup),
 				},
+				defenderProperties: make(map[string]*defenderProperties),
 			},
 		},
 	}
@@ -338,16 +419,6 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 				return assert.ErrorContains(t, err, ErrCouldNotAuthenticate.Error())
 			},
 		},
-		{
-			name: "Discovery error",
-			fields: fields{
-				azureDiscovery: NewMockAzureDiscovery(newMockNetworkSender()),
-			},
-			wantList: nil,
-			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-				return assert.ErrorContains(t, err, "could not discover storage accounts:")
-			},
-		},
 		{
 			name: "Without errors",
 			fields: fields{
@@ -374,6 +445,27 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 							},
 						},
 						Immutability: &voc.Immutability{Enabled: false},
+						Backup: &voc.Backup{
+							Enabled:         true,
+							RetentionPeriod: Duration7Days,
+							GeoLocation:     voc.GeoLocation{Region: "westeurope"},
+							Policy:          "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer",
+							Storage:         voc.ResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/account1-account1-22222222-2222-2222-2222-222222222222"),
+							AtRestEncryption: &voc.AtRestEncryption{
+								Algorithm: "AES256",
+								Enabled:   true,
+							},
+							TransportEncryption: &voc.TransportEncryption{
+								Enforced:   true,
+								Enabled:    true,
+								TlsVersion: constants.TLS1_2,
+								Algorithm:  constants.TLS,
+							},
+						},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 					PublicAccess: true,
 				},
@@ -397,6 +489,27 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 							},
 						},
 						Immutability: &voc.Immutability{Enabled: false},
+						Backup: &voc.Backup{
+							Enabled:         true,
+							RetentionPeriod: Duration7Days,
+							GeoLocation:     voc.GeoLocation{Region: "westeurope"},
+							Policy:          "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupPolicies/backupPolicyContainer",
+							Storage:         voc.ResourceID("/subscriptions/00000000-0000-0000-0000-000000000000/resourcegroups/res1/providers/Microsoft.DataProtection/backupVaults/backupAccount1/backupInstances/account1-account1-22222222-2222-2222-2222-222222222222"),
+							AtRestEncryption: &voc.AtRestEncryption{
+								Algorithm: "AES256",
+								Enabled:   true,
+							},
+							TransportEncryption: &voc.TransportEncryption{
+								Enforced:   true,
+								Enabled:    true,
+								TlsVersion: constants.TLS1_2,
+								Algorithm:  constants.TLS,
+							},
+						},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 					PublicAccess: true,
 				},
@@ -419,6 +532,10 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 								Enabled:   true,
 							},
 						},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 				},
 				&voc.FileStorage{
@@ -440,6 +557,10 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 								Enabled:   true,
 							},
 						},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 				},
 				&voc.ObjectStorageService{
@@ -503,6 +624,10 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 							KeyUrl: "https://testvault.vault.azure.net/keys/testkey/123456",
 						},
 						Immutability: &voc.Immutability{Enabled: false},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 					PublicAccess: false,
 				},
@@ -527,6 +652,10 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 							KeyUrl: "https://testvault.vault.azure.net/keys/testkey/123456",
 						},
 						Immutability: &voc.Immutability{Enabled: false},
+						ResourceLogging: &voc.ResourceLogging{
+							MonitoringLogDataEnabled: true,
+							SecurityAlertsEnabled:    true,
+						},
 					},
 					PublicAccess: false,
 				},
@@ -588,7 +717,7 @@ func Test_azureStorageDiscovery_List(t *testing.T) {
 }
 
 func TestStorageHandleMethodsWhenInputIsInvalid(t *testing.T) {
-	d := azureStorageDiscovery{&azureDiscovery{csID: testdata.MockCloudServiceID}}
+	d := azureStorageDiscovery{&azureDiscovery{csID: testdata.MockCloudServiceID}, make(map[string]*defenderProperties)}
 
 	// Get mocked armstorage.Account
 	reqURL := "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Storage/storageAccounts/account3"
@@ -627,7 +756,7 @@ func TestStorageMethodsWhenInputIsInvalid(t *testing.T) {
 }
 
 func TestStorageDiscoverMethodsWhenInputIsInvalid(t *testing.T) {
-	d := azureStorageDiscovery{&azureDiscovery{}}
+	d := azureStorageDiscovery{&azureDiscovery{}, make(map[string]*defenderProperties)}
 
 	// Get mocked armstorage.Account
 	reqURL := "/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Storage/storageAccounts/account3"
@@ -1082,7 +1211,7 @@ func Test_azureStorageDiscovery_handleStorageAccount(t *testing.T) {
 }
 
 func Test_handleObjectStorage(t *testing.T) {
-	// accountID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1"
+	accountID := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/res1/providers/Microsoft.Storage/storageAccounts/account1"
 	accountRegion := "eastus"
 	creationTime := time.Date(2017, 05, 24, 13, 28, 53, 4540398, time.UTC)
 	keySource := armstorage.KeySourceMicrosoftStorage
@@ -1105,40 +1234,40 @@ func Test_handleObjectStorage(t *testing.T) {
 		want    *voc.ObjectStorage
 		wantErr assert.ErrorAssertionFunc
 	}{
-		// {
-		// 	name: "Account is empty",
-		// 	want: nil,
-		// 	wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-		// 		return assert.ErrorIs(t, err, ErrEmptyStorageAccount)
-		// 	},
-		// },
-		// {
-		// 	name: "Container is empty",
-		// 	args: args{
-		// 		account: &armstorage.Account{
-		// 			ID: &accountID,
-		// 		},
-		// 	},
-		// 	want: nil,
-		// 	wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-		// 		return assert.ErrorContains(t, err, "container is nil")
-		// 	},
-		// },
-		// {
-		// 	name: "Error getting atRestEncryption properties",
-		// 	args: args{
-		// 		account: &armstorage.Account{
-		// 			ID: &accountID,
-		// 		},
-		// 		container: &armstorage.ListContainerItem{
-		// 			ID: &containerID,
-		// 		},
-		// 	},
-		// 	want: nil,
-		// 	wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
-		// 		return assert.ErrorContains(t, err, "could not get object storage properties for the atRestEncryption:")
-		// 	},
-		// },
+		{
+			name: "Account is empty",
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorIs(t, err, ErrEmptyStorageAccount)
+			},
+		},
+		{
+			name: "Container is empty",
+			args: args{
+				account: &armstorage.Account{
+					ID: &accountID,
+				},
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "container is nil")
+			},
+		},
+		{
+			name: "Error getting atRestEncryption properties",
+			args: args{
+				account: &armstorage.Account{
+					ID: &accountID,
+				},
+				container: &armstorage.ListContainerItem{
+					ID: &containerID,
+				},
+			},
+			want: nil,
+			wantErr: func(t assert.TestingT, err error, i ...interface{}) bool {
+				return assert.ErrorContains(t, err, "could not get object storage properties for the atRestEncryption:")
+			},
+		},
 		{
 			name: "No error",
 			fields: fields{
@@ -1146,6 +1275,7 @@ func Test_handleObjectStorage(t *testing.T) {
 			},
 			args: args{
 				account: &armstorage.Account{
+					ID: &accountID,
 					Properties: &armstorage.AccountProperties{
 						Encryption: &armstorage.Encryption{
 							KeySource: &keySource,
@@ -1446,3 +1576,51 @@ func Test_azureStorageDiscovery_discoverObjectStorages(t *testing.T) {
 		})
 	}
 }
+
+func Test_azureStorageDiscovery_createResourceLogging(t *testing.T) {
+	type fields struct {
+		azureDiscovery     *azureDiscovery
+		defenderProperties map[string]*defenderProperties
+	}
+	tests := []struct {
+		name                string
+		fields              fields
+		wantResourceLogging *voc.ResourceLogging
+	}{
+		{
+			name: "Missing defenderProperties",
+			fields: fields{
+				azureDiscovery:     NewMockAzureDiscovery(newMockStorageSender()),
+				defenderProperties: make(map[string]*defenderProperties),
+			},
+			wantResourceLogging: nil,
+		},
+		{
+			name: "Happy path",
+			fields: fields{
+				azureDiscovery: NewMockAzureDiscovery(newMockStorageSender()),
+				defenderProperties: map[string]*defenderProperties{
+					DefenderStorageType: {
+						monitoringLogDataEnabled: true,
+						securityAlertsEnabled:    true,
+					},
+				},
+			},
+			wantResourceLogging: &voc.ResourceLogging{
+				MonitoringLogDataEnabled: true,
+				SecurityAlertsEnabled:    true,
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			d := &azureStorageDiscovery{
+				azureDiscovery:     tt.fields.azureDiscovery,
+				defenderProperties: tt.fields.defenderProperties,
+			}
+			if gotResourceLogging := d.createResourceLogging(); !reflect.DeepEqual(gotResourceLogging, tt.wantResourceLogging) {
+				t.Errorf("azureStorageDiscovery.createResourceLogging() = %v, want %v", gotResourceLogging, tt.wantResourceLogging)
+			}
+		})
+	}
+}