Repository: aquasecurity/tfsec
Pull request #1085 (2021-09-23 15:44:07)

Adding Azure rules around security center

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/aquasecurity/tfsec?pullRequest=1085) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our [Contributor License Agreement](https://cla-assistant.io/aquasecurity/tfsec?pullRequest=1085) before we can accept your contribution.<br/>**0** out of **2** committers have signed the CLA.<br/><br/>:x: brandon-bailey-rc<br/>:x: brandon-bailey<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/aquasecurity/tfsec?pullRequest=1085) it.</sub>
owenrumney: Thanks @brandon-bailey 


Patch:
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule.go
new file mode 100644
index 000000000..622669170
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule.go
@@ -0,0 +1,60 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-appservices",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure Azure Defender is set to On for container registries",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).`,
+			Impact:      "Azure Defender for App Service detects attacks targeting applications running over App Service.",
+			Resolution:  "Enable AppServices in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,AppServices"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-app-service-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("AppServices", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain AppServices", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule.go
index 5c850f04a..a1fad5a69 100644
--- a/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule.go
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule.go
@@ -16,7 +16,7 @@ import (
 func init() {
 	scanner.RegisterCheckRule(rule.Rule{
 		Service:   "security-center",
-		ShortCode: "defender-on-container",
+		ShortCode: "defender-on-container-registry",
 		Documentation: rule.RuleDocumentation{
 			Summary: "Ensure Azure Defender is set to On for container registries",
 			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule.go
new file mode 100644
index 000000000..229100efc
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule.go
@@ -0,0 +1,62 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-keyvault",
+		Documentation: rule.RuleDocumentation{
+			Summary: "Ensure Azure Defender is set to On for key vaults",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).
+			Azure Defender detects unusual and potentially harmful attempts to access or exploit Key Vault accounts. Resource Manager-based Azure Container Registry registries and provide deeper visibility image vulnerabilities.`,
+			Impact:     "Azure Defender detects unusual and potentially harmful attempts to access or exploit Key Vault accounts.",
+			Resolution: "Enable KeyVaults in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,KeyVaults"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-key-vault-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("KeyVaults", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain KeyVaults", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule.go
new file mode 100644
index 000000000..eee0ed012
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule.go
@@ -0,0 +1,61 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-kubernetes",
+		Documentation: rule.RuleDocumentation{
+			Summary: "Ensure Azure Defender is set to On for Kubernetes",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).
+			Azure Defender for Kubernetes provides cluster-level threat protection by monitoring your AKS-managed services through the logs retrieved by Azure Kubernetes Service (AKS).`,
+			Impact:     "Not enabling defender for container registries could lead to compromised account",
+			Resolution: "Enable KubernetesService in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,KubernetesService"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-kubernetes-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("KubernetesService", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain KubernetesService", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule.go
new file mode 100644
index 000000000..8b376cf47
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule.go
@@ -0,0 +1,60 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-servers",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure Azure Defender is set to On for Servers",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).`,
+			Impact:      "Azure Defender for servers adds threat detection and advanced defenses for Windows and Linux machines.",
+			Resolution:  "Enable VirtualMachines in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "AppServices"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "AppServices,VirtualMachines"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-servers-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("VirtualMachines", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain VirtualMachines", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule.go
new file mode 100644
index 000000000..45954eb8e
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule.go
@@ -0,0 +1,60 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-sql-servers",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure Azure Defender is set to On for SQL Servers",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).`,
+			Impact:      "Azure Defender for SQL servers on machines extends the protections for your Azure-native SQL Servers to fully support hybrid environments and protect SQL servers (all supported version) hosted in Azure",
+			Resolution:  "Enable SqlServers in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,SqlServers"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-sql-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("SqlServers", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain SqlServers", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule.go
new file mode 100644
index 000000000..446516d09
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule.go
@@ -0,0 +1,60 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-sql-servers-vms",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure Azure Defender is set to On for Sql Server on Machines",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).`,
+			Impact:      "Azure Defender for SQL servers on machines extends the protections for your Azure-native SQL Servers to fully support hybrid environments and protect SQL servers (all supported version) hosted in Azure.",
+			Resolution:  "Enable ContainerRegistry in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,SqlServerVirtualMachines"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-sql-usage",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("SqlServerVirtualMachines", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain SqlServerVirtualMachines", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule.go
new file mode 100644
index 000000000..85e4f4e22
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule.go
@@ -0,0 +1,60 @@
+package securitycenter
+
+import (
+	"github.com/aquasecurity/tfsec/pkg/result"
+	"github.com/aquasecurity/tfsec/pkg/severity"
+
+	"github.com/aquasecurity/tfsec/pkg/provider"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/block"
+
+	"github.com/aquasecurity/tfsec/pkg/rule"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+)
+
+func init() {
+	scanner.RegisterCheckRule(rule.Rule{
+		Service:   "security-center",
+		ShortCode: "defender-on-storage",
+		Documentation: rule.RuleDocumentation{
+			Summary:     "Ensure Azure Defender is set to On for storage accounts",
+			Explanation: `Azure Defender is a cloud workload protection service that utilizes and agent-based deployment to analyze signals from Azure network fabric and the service control plane, to detect threats across all Azure resources. It can also analyze non-Azure resources, utilizing Azure Arc, including those on-premises and in both AWS and GCP (once they've been onboarded).`,
+			Impact:      "Azure Defender for Storage detects unusual and potentially harmful attempts to access or exploit storage accounts.",
+			Resolution:  "Enable StorageAccounts in Azure Defender",
+			BadExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "bad_example" {
+  tier          = "Free"
+  resource_type = "VirtualMachines"
+}
+`},
+			GoodExample: []string{`
+resource "azurerm_security_center_subscription_pricing" "good_example" {
+  tier          = "Standard"
+  resource_type = "VirtualMachines,StorageAccounts"
+}
+`},
+			Links: []string{
+				"https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/security_center_subscription_pricing#resource_type",
+				"https://docs.microsoft.com/en-us/azure/security-center/defender-for-storage-introduction",
+			},
+		},
+		Provider:        provider.AzureProvider,
+		RequiredTypes:   []string{"resource"},
+		RequiredLabels:  []string{"azurerm_security_center_subscription_pricing"},
+		DefaultSeverity: severity.High,
+		CheckFunc: func(set result.Set, resourceBlock block.Block, _ block.Module) {
+
+			if resourceBlock.MissingChild("resource_type") {
+				return
+			}
+
+			resourceTypeAttr := resourceBlock.GetAttribute("resource_type")
+			if !resourceTypeAttr.Contains("StorageAccounts", block.IgnoreCase) {
+				set.AddResult().
+					WithDescription("Resource '%s' does not contain StorageAccounts", resourceBlock.FullName()).
+					WithAttribute(resourceTypeAttr)
+			}
+		},
+	})
+}
diff --git a/rules.md b/rules.md
index 4c6210c44..d3448c9f0 100644
--- a/rules.md
+++ b/rules.md
@@ -118,6 +118,14 @@
 | azure-synapse-virtual-network-enabled | azure | synapse | Synapse Workspace should have managed virtual network enabled, the default is disabled. |
 | azure-appservice-enforce-https | azure | appservice | Ensure the Function App can only be accessed via HTTPS. The default is false. |
 | azure-functionapp-authentication-enabled | functionapp | Ensure the Function App has authentication enabled. The default is false. |
+| azure-securitycenter-defender-on-appservices | securitycenter | Ensure Defender is enabled for AppServices. |
+| azure-securitycenter-defender-on-container-registry | securitycenter | Ensure Defender is enabled for ContainerRegistry. |
+| azure-securitycenter-defender-on-keyvault | securitycenter | Ensure Defender is enabled for KeyVault. |
+| azure-securitycenter-defender-on-kubernetes | securitycenter | Ensure Defender is enabled for Kubernetes. |
+| azure-securitycenter-defender-on-servers | securitycenter | Ensure Defender is enabled for VirtualMachines. |
+| azure-securitycenter-defender-on-sql-servers | securitycenter | Ensure Defender is enabled for SqlServers. |
+| azure-securitycenter-defender-on-sql-servers-vms | securitycenter | Ensure Defender is enabled for SqlServersVirtualMachines. |
+| azure-securitycenter-defender-on-storage | securitycenter | Ensure Defender is enabled for StorageAccounts. |
 | digitalocean-compute-no-public-ingress | digitalocean | compute | The firewall has an inbound rule with open access |
 | digitalocean-compute-no-public-egress | digitalocean | compute | The firewall has an outbound rule with open access |
 | digitalocean-droplet-use-ssh-keys | digitalocean | droplet | SSH Keys are the preferred way to connect to your droplet, no keys are supplied |


Test patch:
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule_test.go
new file mode 100644
index 000000000..ae59ab21c
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_appservices_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnAppServices_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-appservices"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnAppServices_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-appservices"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule_test.go
index dcc375649..8f4be03df 100644
--- a/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule_test.go
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_container_registry_rule_test.go
@@ -8,8 +8,8 @@ import (
 	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
 )
 
-func Test_AzureDefenderOnContainer_FailureExamples(t *testing.T) {
-	expectedCode := "azure-security-center-defender-on-container"
+func Test_AzureDefenderOnContainerRegistry_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-container-registry"
 
 	rule, err := scanner.GetRuleById(expectedCode)
 	if err != nil {
@@ -30,8 +30,8 @@ func Test_AzureDefenderOnContainer_FailureExamples(t *testing.T) {
 	}
 }
 
-func Test_AzureDefenderOnContaienr_SuccessExamples(t *testing.T) {
-	expectedCode := "azure-security-center-defender-on-container"
+func Test_AzureDefenderOnContainerRegistry_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-container-registry"
 
 	rule, err := scanner.GetRuleById(expectedCode)
 	if err != nil {
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule_test.go
new file mode 100644
index 000000000..5789034bc
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_keyvault_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnKeyVault_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-keyvault"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnKeyVault_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-keyvault"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule_test.go
new file mode 100644
index 000000000..d2eb6f35f
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_kubernetes_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnKubernetes_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-kubernetes"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnKubernetes_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-kubernetes"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule_test.go
new file mode 100644
index 000000000..59e550c6a
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_servers_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnServers_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-servers"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnServers_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-servers"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule_test.go
new file mode 100644
index 000000000..842c643b3
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnSqlServers_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-sql-servers"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnSqlServers_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-sql-servers"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule_test.go
new file mode 100644
index 000000000..217baae0c
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_sql_servers_vms_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnSqlServersVMS_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-sql-servers-vms"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnSqlServersVMS_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-sql-servers-vms"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}
diff --git a/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule_test.go b/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule_test.go
new file mode 100644
index 000000000..4274d5641
--- /dev/null
+++ b/internal/app/tfsec/rules/azure/securitycenter/defender_on_storage_rule_test.go
@@ -0,0 +1,53 @@
+package securitycenter
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/scanner"
+	"github.com/aquasecurity/tfsec/internal/app/tfsec/testutil"
+)
+
+func Test_AzureDefenderOnStorage_FailureExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-storage"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, badExample := range rule.Documentation.BadExample {
+		t.Logf("Running bad example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(badExample) == "" {
+			t.Fatalf("bad example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (bad) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(badExample, t)
+		testutil.AssertCheckCode(t, rule.ID(), "", results)
+	}
+}
+
+func Test_AzureDefenderOnStorage_SuccessExamples(t *testing.T) {
+	expectedCode := "azure-security-center-defender-on-storage"
+
+	rule, err := scanner.GetRuleById(expectedCode)
+	if err != nil {
+		t.Fatalf("Rule not found: %s", expectedCode)
+	}
+	for i, example := range rule.Documentation.GoodExample {
+		t.Logf("Running good example for '%s' #%d", expectedCode, i+1)
+		if strings.TrimSpace(example) == "" {
+			t.Fatalf("good example code not provided for %s", rule.ID())
+		}
+		defer func() {
+			if err := recover(); err != nil {
+				t.Fatalf("Scan (good) failed: %s", err)
+			}
+		}()
+		results := testutil.ScanHCL(example, t)
+		testutil.AssertCheckCode(t, "", rule.ID(), results)
+	}
+}