From a59d4007cc703bf02ca9f57fa39756147da74d2f Mon Sep 17 00:00:00 2001 From: cmendible <266546+cmendible@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:22:50 +0100 Subject: [PATCH] added csv reports and fixed #198 y #199 --- README.md | 58 ++- cmd/azqr/pbi.go | 12 +- cmd/azqr/rules.go | 6 +- cmd/azqr/scan.go | 5 +- docs/content/en/docs/Install/_index.md | 1 - docs/content/en/docs/Overview/_index.md | 60 ++- .../content/en/docs/Recommendations/_index.md | 318 ++++++++++++++ docs/content/en/docs/Results/_index.md | 91 ---- docs/content/en/docs/Rules/_index.md | 316 -------------- docs/content/en/docs/Usage/_index.md | 2 +- docs/static/img/defender.png | Bin 20282 -> 0 bytes docs/static/img/overview.png | Bin 92244 -> 0 bytes docs/static/img/recommendations.png | Bin 107664 -> 0 bytes docs/static/img/services.png | Bin 80153 -> 0 bytes go.mod | 2 +- internal/embeded/azqr.pbit | Bin 109799 -> 102486 bytes internal/graph/graph.go | 4 +- internal/renderers/csv/csv.go | 45 ++ internal/renderers/{ => excel}/advisor.go | 16 +- internal/renderers/{ => excel}/cost.go | 30 +- internal/renderers/{ => excel}/defender.go | 16 +- internal/renderers/{ => excel}/excel.go | 6 +- .../renderers/{ => excel}/recommendations.go | 21 +- internal/renderers/excel/services.go | 45 ++ internal/renderers/overview.go | 46 -- internal/renderers/pbi.go | 233 ---------- internal/renderers/pbi/pbi.go | 32 ++ internal/renderers/report_data.go | 106 ++++- internal/renderers/services.go | 72 --- internal/scan.go | 19 +- internal/scanners/adf/rules.go | 55 +-- internal/scanners/adf/rules_test.go | 10 +- internal/scanners/advisor.go | 28 -- internal/scanners/afd/rules.go | 57 +-- internal/scanners/afd/rules_test.go | 8 +- internal/scanners/afw/rules.go | 69 ++- internal/scanners/afw/rules_test.go | 14 +- internal/scanners/agw/rules.go | 114 +++-- internal/scanners/agw/rules_test.go | 12 +- internal/scanners/aks/rules.go | 166 +++---- internal/scanners/aks/rules_test.go | 54 +-- internal/scanners/apim/rules.go | 126 +++--- internal/scanners/apim/rules_test.go | 58 +-- internal/scanners/appcs/rules.go | 87 ++-- internal/scanners/appcs/rules_test.go | 18 +- internal/scanners/appi/rules.go | 42 +- internal/scanners/appi/rules_test.go | 4 +- internal/scanners/asp/rules.go | 409 ++++++++---------- internal/scanners/asp/rules_test.go | 84 ++-- internal/scanners/ca/rules.go | 69 ++- internal/scanners/ca/rules_test.go | 14 +- internal/scanners/cae/rules.go | 69 ++- internal/scanners/cae/rules_test.go | 10 +- internal/scanners/ci/rules.go | 67 ++- internal/scanners/ci/rules_test.go | 10 +- internal/scanners/cog/rules.go | 78 ++-- internal/scanners/cog/rules_test.go | 12 +- internal/scanners/cosmos/rules.go | 99 ++--- internal/scanners/cosmos/rules_test.go | 24 +- internal/scanners/cost.go | 26 +- internal/scanners/cr/rules.go | 108 ++--- internal/scanners/cr/rules_test.go | 18 +- internal/scanners/dbw/rules.go | 73 ++-- internal/scanners/dbw/rules_test.go | 12 +- internal/scanners/dec/rules.go | 87 ++-- internal/scanners/dec/rules_test.go | 18 +- internal/scanners/defender.go | 21 - internal/scanners/evgd/rules.go | 78 ++-- internal/scanners/evgd/rules_test.go | 10 +- internal/scanners/evh/rules.go | 90 ++-- internal/scanners/evh/rules_test.go | 18 +- internal/scanners/kv/rules.go | 93 ++-- internal/scanners/kv/rules_test.go | 14 +- internal/scanners/lb/rules.go | 69 ++- internal/scanners/lb/rules_test.go | 25 +- internal/scanners/logic/rules.go | 54 +-- internal/scanners/logic/rules_test.go | 14 +- internal/scanners/maria/rules.go | 67 ++- internal/scanners/maria/rules_test.go | 12 +- internal/scanners/mysql/rules.go | 159 +++---- internal/scanners/mysql/rules_test.go | 32 +- internal/scanners/psql/rules.go | 146 +++---- internal/scanners/psql/rules_test.go | 36 +- internal/scanners/redis/rules.go | 99 ++--- internal/scanners/redis/rules_test.go | 16 +- internal/scanners/sb/rules.go | 78 ++-- internal/scanners/sb/rules_test.go | 20 +- internal/scanners/scanner.go | 183 ++------ internal/scanners/sigr/rules.go | 71 ++- internal/scanners/sigr/rules_test.go | 12 +- internal/scanners/sql/rules.go | 113 ++--- internal/scanners/sql/rules_test.go | 22 +- internal/scanners/st/rules.go | 125 +++--- internal/scanners/st/rules_test.go | 80 +++- internal/scanners/st/st.go | 15 +- internal/scanners/traf/rules.go | 79 ++-- internal/scanners/traf/rules_test.go | 22 +- internal/scanners/vm/rules.go | 75 ++-- internal/scanners/vm/rules_test.go | 6 +- internal/scanners/vnet/rules.go | 63 ++- internal/scanners/vnet/rules_test.go | 14 +- internal/scanners/vwan/rules.go | 59 +-- internal/scanners/vwan/rules_test.go | 8 +- internal/scanners/wps/rules.go | 81 ++-- internal/scanners/wps/rules_test.go | 16 +- internal/{ref/ref.go => to/Ptr.go} | 4 +- 106 files changed, 2593 insertions(+), 3377 deletions(-) create mode 100644 docs/content/en/docs/Recommendations/_index.md delete mode 100644 docs/content/en/docs/Results/_index.md delete mode 100644 docs/content/en/docs/Rules/_index.md delete mode 100644 docs/static/img/defender.png delete mode 100644 docs/static/img/overview.png delete mode 100644 docs/static/img/recommendations.png delete mode 100644 docs/static/img/services.png create mode 100644 internal/renderers/csv/csv.go rename internal/renderers/{ => excel}/advisor.go (73%) rename internal/renderers/{ => excel}/cost.go (55%) rename internal/renderers/{ => excel}/defender.go (73%) rename internal/renderers/{ => excel}/excel.go (96%) rename internal/renderers/{ => excel}/recommendations.go (71%) create mode 100644 internal/renderers/excel/services.go delete mode 100644 internal/renderers/overview.go delete mode 100644 internal/renderers/pbi.go create mode 100644 internal/renderers/pbi/pbi.go delete mode 100644 internal/renderers/services.go rename internal/{ref/ref.go => to/Ptr.go} (70%) diff --git a/README.md b/README.md index dbc1423c..bb14fb15 100644 --- a/README.md +++ b/README.md @@ -13,29 +13,51 @@ ## Scan Results -The output generated by **Azure Quick Review (azqr)** is presented in the form of an Excel file, which consists of several sheets including **Overview**, **Recommendations**, **Services**, **Defender**, **Advisor** and **Costs**. Additionally, when running the tool on a Windows system, it also generates a Power BI Desktop Template for further analysis and visualization of the Azure resource data. - -The **Overview** sheet provides a summary of the Azure resources scanned by the tool, including the following information: - -* **SubscriptionID**: This is the unique identifier for the Azure subscription under which the resource is deployed. -* **ResourceGroup**: The resource group where the resource is deployed. -* **Location**: The geographical region where the resource is deployed. -* **Type**: The specific type or category of the Azure resource. -* **Name**: The name assigned to the resource, providing a human-readable identifier for easy reference and management. -* **SKU**: The SKU represents the specific variant or configuration of the Azure resource. It defines the characteristics and capabilities of the resource. -* **SLA**: The Service Level Agreement (SLA) represents the agreed-upon performance and availability guarantees for the Azure service based on its current configuration. -* **AZ**: A Boolean value indicating whether the service is "Availability Zone aware." Availability Zones are physically separate datacenters within an Azure region, providing increased resiliency and fault tolerance for critical services. -* **PVT**: A Boolean value indicating whether the service has a private IP address. Private IP addresses are used for internal communication within Azure Virtual Networks. -* **DS**: A Boolean value indicating whether diagnostic settings are enabled for the service. Diagnostic settings allow you to collect logs, metrics, and other monitoring data for Azure resources. -* **CAF**: A Boolean value indicating whether the service is compliant with the [Cloud Adoption Framework](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) (CAF) naming convention. The CAF provides best practices and guidance for organizations adopting Azure. +The output generated by **Azure Quick Review (azqr)** is presented by default in four `csv` files: + +* **azqr-YYYY-MM-DD-HH-MM-SS.services.csv:** This file contains the details of the Azure services scanned by the tool, including: + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Resource Group: The resource group where the resource is deployed. + * Location: The geographical region where the resource is deployed. + * Type: The specific type or category of the Azure resource. + * Service Name: The name assigned to the service, providing a human-readable identifier for easy reference and management. + * Compliant: A Boolean value indicating whether the service is compliant with Azure's best practices and recommendations. + * Impact: The potential impact of non-compliance on the service. + * Category: The category or type of recommendation. + * Recommendation: The specific recommendation or best practice. + * Result: The result or value resulting from the evaluation of the recommendation (i.e. Service SLA or SKU). + * Learn: A link to additional information or documentation related to the recommendation. +* **azqr-YYYY-MM-DD-HH-MM-SS.defender.csv:** + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Name: Microsoft Defender for Cloud plan name. + * Tier: The tier of the plan. + * Deprecated: a Boolean value indicating whether the plan is deprecated. +* **azqr-YYYY-MM-DD-HH-MM-SS.advisor.csv:** + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Name: The name of the resource identified by Advisor. + * Type: The resource type of the resource identified by Advisor. + * Category: The category of the recommendation. + * Description: The description of the recommendation. + * PotentialBenefits: The potential benefits of the recommendation. + * Risk: Risk related to the recommendation. + * LearnMoreLink: A link to additional information or documentation related to the recommendation. +* **azqr-YYYY-MM-DD-HH-MM-SS.costs.csv:** + * From: the start date of the cost analysis period. + * To: the end date of the cost analysis period. + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * ServiceName: The type of the Azure service for which the cost is calculated. + * Value: The cost value associated with the service. + * Currency: The currency in which the cost is calculated. > By default, Azure Quick Review (azqr) masks the Subscription Ids in the spreadsheet, ensuring that they are not directly visible in the output. This helps protect sensitive information and maintain data privacy and security. To view the Subscription Ids, you can use the `--mask=false` flag when running the tool. -To learn more about the **Recommendations**, **Services**, **Defender**, **Advisor** and **Costs** sheets, check the [Scan Results](https://azure.github.io/azqr/docs/results) documentation. +> Azure Quick Review can also generate an Excel file with the same information as the CSV files. To generate the Excel file, you can use the `--excel` (or `-x`) flag when running the tool. -## Azure Quick Review Rules +> A Power BI template is also available to help you visualize the results generated by Azure Quick Review. You can create the template running Azure Quick Review with the `pbi` command. -To learn more about the rules used by **Azure Quick Review (azqr)** for generating recommendations, you can refer to the documentation available [here](https://azure.github.io/azqr/docs/rules). +## Azure Quick Review Recommendations + +To learn more about the recommendations used by **Azure Quick Review (azqr)**, you can refer to the documentation available [here](https://azure.github.io/azqr/recommendations). ## Supported Azure Services diff --git a/cmd/azqr/pbi.go b/cmd/azqr/pbi.go index 56c5e509..92ffed09 100644 --- a/cmd/azqr/pbi.go +++ b/cmd/azqr/pbi.go @@ -4,22 +4,22 @@ package azqr import ( - "github.com/Azure/azqr/internal/renderers" + "github.com/Azure/azqr/internal/renderers/pbi" "github.com/spf13/cobra" ) func init() { - pbiCmd.PersistentFlags().StringP("excel-report", "x", "", "Path to azqr Excel report file") + pbiCmd.PersistentFlags().StringP("template-path", "p", "", "Path were the PowerBI template will be created") rootCmd.AddCommand(pbiCmd) } var pbiCmd = &cobra.Command{ Use: "pbi", - Short: "Creates PowerBI desktop dashboard template", - Long: "Creates PowerBI desktop dashboard template", + Short: "Creates Power BI Desktop dashboard template", + Long: "Creates Power BI Desktop dashboard template", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - xlsx, _ := cmd.Flags().GetString("excel-report") - renderers.CreatePBIReport(xlsx) + path, _ := cmd.Flags().GetString("template-path") + pbi.CreatePBIReport(path) }, } diff --git a/cmd/azqr/rules.go b/cmd/azqr/rules.go index 8067b841..3b185108 100644 --- a/cmd/azqr/rules.go +++ b/cmd/azqr/rules.go @@ -24,8 +24,8 @@ var rulesCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { serviceScanners := internal.GetScanners() - fmt.Println("# | Id | Category | Subcategory | Name | Severity | More Info") - fmt.Println("---|---|---|---|---|---|---") + fmt.Println("# | Category | Impact | Recommendation | More Info") + fmt.Println("---|---|---|---|---") i := 0 for _, scanner := range serviceScanners { @@ -45,7 +45,7 @@ var rulesCmd = &cobra.Command{ for _, k := range keys { rule := rules[k] i++ - fmt.Printf("%s | %s | %s | %s | %s | %s | [Learn](%s)", fmt.Sprint(i), rule.Id, rule.Category, rule.Subcategory, rule.Description, rule.Severity, rule.Url) + fmt.Printf("%s | %s | %s | %s | [Learn](%s)", fmt.Sprint(i), rule.Category, rule.Impact, rule.Recommendation, rule.Url) fmt.Println() } } diff --git a/cmd/azqr/scan.go b/cmd/azqr/scan.go index 0178664e..1a4348a3 100644 --- a/cmd/azqr/scan.go +++ b/cmd/azqr/scan.go @@ -16,7 +16,8 @@ func init() { scanCmd.PersistentFlags().BoolP("defender", "d", true, "Scan Defender Status") scanCmd.PersistentFlags().BoolP("advisor", "a", true, "Scan Azure Advisor Recommendations") scanCmd.PersistentFlags().BoolP("costs", "c", false, "Scan Azure Costs") - scanCmd.PersistentFlags().StringP("output-name", "o", "", "Output file name") + scanCmd.PersistentFlags().BoolP("excel", "x", false, "Create excel report") + scanCmd.PersistentFlags().StringP("output-name", "o", "", "Output file name without extension") scanCmd.PersistentFlags().BoolP("mask", "m", true, "Mask the subscription id in the report") scanCmd.PersistentFlags().BoolP("azure-cli-credential", "f", false, "Force the use of Azure CLI Credential") scanCmd.PersistentFlags().BoolP("debug", "", false, "Set log level to debug") @@ -42,6 +43,7 @@ func scan(cmd *cobra.Command, serviceScanners []scanners.IAzureScanner) { defender, _ := cmd.Flags().GetBool("defender") advisor, _ := cmd.Flags().GetBool("advisor") cost, _ := cmd.Flags().GetBool("costs") + xlsx, _ := cmd.Flags().GetBool("excel") mask, _ := cmd.Flags().GetBool("mask") debug, _ := cmd.Flags().GetBool("debug") forceAzureCliCredential, _ := cmd.Flags().GetBool("azure-cli-credential") @@ -53,6 +55,7 @@ func scan(cmd *cobra.Command, serviceScanners []scanners.IAzureScanner) { Defender: defender, Advisor: advisor, Cost: cost, + Xlsx: xlsx, Mask: mask, Debug: debug, ServiceScanners: serviceScanners, diff --git a/docs/content/en/docs/Install/_index.md b/docs/content/en/docs/Install/_index.md index e3e3bffd..4d736809 100644 --- a/docs/content/en/docs/Install/_index.md +++ b/docs/content/en/docs/Install/_index.md @@ -27,7 +27,6 @@ $latest_azqr=$(iwr https://api.github.com/repos/Azure/azqr/releases/latest).cont iwr https://github.com/Azure/azqr/releases/download/$latest_azqr/azqr-windows-latest-amd64.exe -OutFile azqr.exe ``` - ## Install on Mac Download the latest release from [here](https://github.com/Azure/azqr/releases). \ No newline at end of file diff --git a/docs/content/en/docs/Overview/_index.md b/docs/content/en/docs/Overview/_index.md index 5816f726..988db5c4 100644 --- a/docs/content/en/docs/Overview/_index.md +++ b/docs/content/en/docs/Overview/_index.md @@ -8,29 +8,51 @@ weight: 1 ## Scan Results -The output generated by **Azure Quick Review (azqr)** is presented in the form of an Excel file, which consists of several sheets including **Overview**, **Recommendations**, **Services**, **Defender**, **Advisor** and **Costs**. Additionally, when running the tool on a Windows system, it also generates a Power BI Desktop Template for further analysis and visualization of the Azure resource data. - -The **Overview** sheet provides a summary of the Azure resources scanned by the tool, including the following information: - -* **SubscriptionID**: This is the unique identifier for the Azure subscription under which the resource is deployed. -* **ResourceGroup**: The resource group where the resource is deployed. -* **Location**: The geographical region where the resource is deployed. -* **Type**: The specific type or category of the Azure resource. -* **Name**: The name assigned to the resource, providing a human-readable identifier for easy reference and management. -* **SKU**: The SKU represents the specific variant or configuration of the Azure resource. It defines the characteristics and capabilities of the resource. -* **SLA**: The Service Level Agreement (SLA) represents the agreed-upon performance and availability guarantees for the Azure service based on its current configuration. -* **AZ**: A Boolean value indicating whether the service is "Availability Zone aware." Availability Zones are physically separate datacenters within an Azure region, providing increased resiliency and fault tolerance for critical services. -* **PVT**: A Boolean value indicating whether the service has a private IP address. Private IP addresses are used for internal communication within Azure Virtual Networks. -* **DS**: A Boolean value indicating whether diagnostic settings are enabled for the service. Diagnostic settings allow you to collect logs, metrics, and other monitoring data for Azure resources. -* **CAF**: A Boolean value indicating whether the service is compliant with the [Cloud Adoption Framework](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) (CAF) naming convention. The CAF provides best practices and guidance for organizations adopting Azure. +The output generated by **Azure Quick Review (azqr)** is presented by default in four `csv` files: + +* **azqr-YYYY-MM-DD-HH-MM-SS.services.csv:** This file contains the details of the Azure services scanned by the tool, including: + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Resource Group: The resource group where the resource is deployed. + * Location: The geographical region where the resource is deployed. + * Type: The specific type or category of the Azure resource. + * Service Name: The name assigned to the service, providing a human-readable identifier for easy reference and management. + * Compliant: A Boolean value indicating whether the service is compliant with Azure's best practices and recommendations. + * Impact: The potential impact of non-compliance on the service. + * Category: The category or type of recommendation. + * Recommendation: The specific recommendation or best practice. + * Result: The result or value resulting from the evaluation of the recommendation (i.e. Service SLA or SKU). + * Learn: A link to additional information or documentation related to the recommendation. +* **azqr-YYYY-MM-DD-HH-MM-SS.defender.csv:** + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Name: Microsoft Defender for Cloud plan name. + * Tier: The tier of the plan. + * Deprecated: a Boolean value indicating whether the plan is deprecated. +* **azqr-YYYY-MM-DD-HH-MM-SS.advisor.csv:** + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * Name: The name of the resource identified by Advisor. + * Type: The resource type of the resource identified by Advisor. + * Category: The category of the recommendation. + * Description: The description of the recommendation. + * PotentialBenefits: The potential benefits of the recommendation. + * Risk: Risk related to the recommendation. + * LearnMoreLink: A link to additional information or documentation related to the recommendation. +* **azqr-YYYY-MM-DD-HH-MM-SS.costs.csv:** + * From: the start date of the cost analysis period. + * To: the end date of the cost analysis period. + * Subscription: The unique identifier for the Azure subscription under which the resource is deployed. + * ServiceName: The type of the Azure service for which the cost is calculated. + * Value: The cost value associated with the service. + * Currency: The currency in which the cost is calculated. > By default, Azure Quick Review (azqr) masks the Subscription Ids in the spreadsheet, ensuring that they are not directly visible in the output. This helps protect sensitive information and maintain data privacy and security. To view the Subscription Ids, you can use the `--mask=false` flag when running the tool. -To learn more about the **Recommendations**, **Services**, **Defender**, **Advisor** and **Costs** sheets, check the [Scan Results](https://azure.github.io/azqr/results) documentation. +> Azure Quick Review can also generate an Excel file with the same information as the CSV files. To generate the Excel file, you can use the `--excel` (or `-x`) flag when running the tool. -## Azure Quick Review Rules +> A Power BI template is also available to help you visualize the results generated by Azure Quick Review. You can create the template running Azure Quick Review with the `pbi` command. -To learn more about the rules used by **Azure Quick Review (azqr)** for generating recommendations, you can refer to the documentation available [here](https://azure.github.io/azqr/rules). +## Azure Quick Review Recommendations + +To learn more about the recommendations used by **Azure Quick Review (azqr)**, you can refer to the documentation available [here](https://azure.github.io/azqr/recommendations). ## Supported Azure Services @@ -43,6 +65,7 @@ To learn more about the rules used by **Azure Quick Review (azqr)** for generati * Azure Application Insights * Azure Cache for Redis * Azure Cognitive Services Account +* Azure Container Apps Environment * Azure Container Apps * Azure Container Instances * Azure Container Registry @@ -68,6 +91,7 @@ To learn more about the rules used by **Azure Quick Review (azqr)** for generati * Azure SignalR Service * Azure SQL Database * Azure Storage Account +* Azure Traffic Manager * Azure Virtual Machine * Azure Virtual Network * Azure Virtual WAN diff --git a/docs/content/en/docs/Recommendations/_index.md b/docs/content/en/docs/Recommendations/_index.md new file mode 100644 index 00000000..079f2c63 --- /dev/null +++ b/docs/content/en/docs/Recommendations/_index.md @@ -0,0 +1,318 @@ +--- +title: Recommendations +description: Recommendations +weight: 3 +--- + +Azure Quick Review checks the following recommendations for Azure resources. The recommendations are categorized based on their impact and category: + +\# | Category | Impact | Recommendation | More Info +---|---|---|---|--- +1 | Monitoring and Alerting | Low | Azure Databricks should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/databricks/administration-guide/account-settings/audit-log-delivery) +2 | High Availability | High | Azure Databricks should have a SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) +3 | Security | High | Azure Databricks should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/databricks/administration-guide/cloud-configurations/azure/private-link) +4 | High Availability | High | Azure Databricks SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/databricks/) +5 | Governance | Low | Azure Databricks Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +6 | Security | Medium | Azure Databricks should have the Public IP disabled | [Learn](https://learn.microsoft.com/en-us/azure/databricks/security/network/secure-cluster-connectivity) +7 | Monitoring and Alerting | Low | Azure Data Factory should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/data-factory/monitor-configure-diagnostics) +8 | Security | High | Azure Data Factory should have private endpoints enabled | [Learn]() +9 | High Availability | High | Azure Data Factory SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) +10 | Governance | Low | Azure Data Factory Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +11 | Governance | Low | Azure Data Factory should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +12 | Monitoring and Alerting | Low | Azure FrontDoor should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-logs) +13 | High Availability | High | Azure FrontDoor SLA | [Learn](https://www.azure.cn/en-us/support/sla/cdn/) +14 | High Availability | High | Azure FrontDoor SKU | [Learn](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/tier-comparison) +15 | Governance | Low | Azure FrontDoor Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +16 | Governance | Low | Azure FrontDoor should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +17 | Monitoring and Alerting | Low | Azure Firewall should have diagnostic settings enabled | [Learn](https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics) +18 | High Availability | High | Azure Firewall should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/firewall/features#availability-zones) +19 | High Availability | High | Azure Firewall SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) +20 | High Availability | High | Azure Firewall SKU | [Learn](https://learn.microsoft.com/en-us/azure/firewall/choose-firewall-sku) +21 | Governance | Low | Azure Firewall Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +22 | Governance | Low | Azure Firewall should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +23 | Scalability | High | Application Gateway: Ensure autoscaling is used with a minimum of 2 instances | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant) +24 | Security | High | Application Gateway: Secure all incoming connections with SSL | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/services/networking/azure-application-gateway#security) +25 | Security | High | Application Gateway: Enable WAF policies | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/features#web-application-firewall) +26 | High Availability | High | Application Gateway: Use Application GW V2 instead of V1 | [Learn](https://azure.microsoft.com/en-us/updates/application-gateway-v1-will-be-retired-on-28-april-2026-transition-to-application-gateway-v2/) +27 | Monitoring and Alerting | Low | Application Gateway: Monitor and Log the configurations and traffic | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-diagnostics#diagnostic-logging) +28 | High Availability | Medium | Application Gateway should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant) +29 | High Availability | Medium | Application Gateway: Plan for backend maintenance by using connection draining | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/features#connection-draining) +30 | High Availability | High | Application Gateway SLA | [Learn](https://www.azure.cn/en-us/support/sla/application-gateway/) +31 | High Availability | High | Application Gateway SKU | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/understanding-pricing) +32 | Governance | Low | Application Gateway Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +33 | Governance | Low | Application Gateway should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +34 | Monitoring and Alerting | Low | AKS Cluster should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/aks/monitor-aks#collect-resource-logs) +35 | High Availability | High | AKS Cluster should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/aks/availability-zones) +36 | High Availability | High | AKS Cluster should have an SLA | [Learn](https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers#uptime-sla-terms-and-conditions) +37 | Security | High | AKS Cluster should be private | [Learn](https://learn.microsoft.com/en-us/azure/aks/private-clusters) +38 | High Availability | High | AKS Production Cluster should use Standard SKU | [Learn](https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers) +39 | Governance | Low | AKS Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +40 | Security | Medium | AKS should integrate authentication with AAD (Managed) | [Learn](https://learn.microsoft.com/en-us/azure/aks/managed-azure-ad) +41 | Security | Medium | AKS should be RBAC enabled. | [Learn](https://learn.microsoft.com/azure/aks/manage-azure-rbac) +42 | Security | Medium | AKS should have local accounts disabled | [Learn](https://learn.microsoft.com/azure/aks/managed-aad#disable-local-accounts) +43 | Security | Medium | AKS should have httpApplicationRouting disabled | [Learn](https://learn.microsoft.com/azure/aks/http-application-routing) +44 | Monitoring and Alerting | High | AKS should have Container Insights enabled | [Learn](https://learn.microsoft.com/azure/azure-monitor/insights/container-insights-overview) +45 | Security | High | AKS should have outbound type set to user defined routing | [Learn](https://learn.microsoft.com/azure/aks/limit-egress-traffic) +46 | Scalability | Medium | AKS should avoid using kubenet network plugin | [Learn](https://learn.microsoft.com/azure/aks/operator-best-practices-network) +47 | Scalability | Medium | AKS should have autoscaler enabled | [Learn](https://learn.microsoft.com/azure/aks/concepts-scale) +48 | Governance | Low | AKS should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +49 | Scalability | Low | AKS Node Pools should have MaxSurge set | [Learn](https://learn.microsoft.com/en-us/azure/aks/operator-best-practices-run-at-scale#cluster-upgrade-considerations-and-best-practices) +50 | Monitoring and Alerting | Low | APIM should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-azure-monitor#resource-logs) +51 | High Availability | High | APIM should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt) +52 | High Availability | High | APIM should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/api-management/) +53 | Security | High | APIM should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/api-management/private-endpoint) +54 | High Availability | High | Azure APIM SKU | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-features) +55 | Governance | Low | APIM should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +56 | Governance | Low | APIM should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +57 | Security | Medium | APIM should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity) +58 | Security | High | APIM should only accept a minimum of TLS 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers) +59 | Security | High | APIM should should not accept weak or deprecated ciphers. | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers) +60 | Security | High | APIM: Renew expiring certificates | [Learn](https://learn.microsoft.com/en-us/azure/api-management/configure-custom-domain?tabs=custom) +61 | High Availability | High | APIM: Migrate instance hosted on the stv1 platform to stv2 | [Learn](https://learn.microsoft.com/en-us/azure/api-management/migrate-stv1-to-stv2?tabs=portal) +62 | Monitoring and Alerting | Low | AppConfiguration should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal) +63 | High Availability | High | AppConfiguration should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/app-configuration/) +64 | Security | High | AppConfiguration should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-private-endpoint) +65 | High Availability | High | AppConfiguration SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/app-configuration/) +66 | Governance | Low | AppConfiguration Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +67 | Governance | Low | AppConfiguration should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +68 | Security | Medium | AppConfiguration should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-disable-access-key-authentication?tabs=portal#disable-access-key-authentication) +69 | Disaster Recovery | Medium | AppConfiguration should have purge protection enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-soft-delete#purge-protection) +70 | High Availability | High | Azure Application Insights SLA | [Learn](https://www.azure.cn/en-us/support/sla/application-insights/index.html) +71 | Governance | Low | Azure Application Insights Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +72 | Governance | Low | Azure Application Insights should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +73 | Scalability | Low | Azure Application Insights should store data in a Log Analytics Workspace | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/app/create-workspace-resource) +74 | Monitoring and Alerting | Low | Container Apps Environment should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/log-options#diagnostic-settings) +75 | High Availability | High | Container Apps Environment should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/disaster-recovery?tabs=bash#set-up-zone-redundancy-in-your-container-apps-environment) +76 | High Availability | High | Container Apps Environment should have a SLA | [Learn](https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/) +77 | Security | High | Container Apps Environment should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/vnet-custom-internal?tabs=bash&pivots=azure-portal) +78 | Governance | Low | Container Apps Environment Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +79 | Governance | Low | Container Apps Environment should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +80 | High Availability | High | ContainerApp should have a SLA | [Learn](https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/) +81 | Governance | Low | ContainerApp Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +82 | Governance | Low | ContainerApp should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +83 | Security | Low | ContainerApp should not allow insecure ingress traffic | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/ingress-how-to?pivots=azure-cli) +84 | Security | Low | ContainerApp should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=portal%2Cdotnet) +85 | High Availability | Low | ContainerApp should use Azure Files to persist container data | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/storage-mounts?pivots=azure-cli) +86 | High Availability | Low | ContainerApp should avoid using session affinity | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/sticky-sessions?pivots=azure-portal) +87 | High Availability | High | ContainerInstance should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-instances/availability-zones) +88 | High Availability | High | ContainerInstance should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/container-instances/v1_0/index.html) +89 | Security | High | ContainerInstance should use private IP addresses | [Learn]() +90 | High Availability | High | ContainerInstance SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/container-instances/) +91 | Governance | Low | ContainerInstance Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +92 | Governance | Low | ContainerInstance should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +93 | Monitoring and Alerting | Low | Cognitive Service Account should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing) +94 | High Availability | High | Cognitive Service Account should have a SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +95 | Security | High | Cognitive Service Account should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-virtual-networks) +96 | High Availability | High | Cognitive Service Account SKU | [Learn](https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts?pivots=deployment-language-bicep#sku) +97 | Governance | Low | Cognitive Service Account Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +98 | Governance | Low | Cognitive Service Account should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +99 | Security | Medium | Cognitive Service Account should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/ai-services/policy-reference#azure-ai-services) +100 | Monitoring and Alerting | Low | CosmosDB should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/monitor-resource-logs) +101 | High Availability | High | CosmosDB should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability) +102 | High Availability | High | CosmosDB should have a SLA | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability#slas) +103 | Security | High | CosmosDB should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-configure-private-endpoints) +104 | High Availability | High | CosmosDB SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/cosmos-db/autoscale-provisioned/) +105 | Governance | Low | CosmosDB Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +106 | Governance | Low | CosmosDB should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +107 | Security | High | CosmosDB should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#disable-local-auth) +108 | Security | High | CosmosDB: disable write operations on metadata resources (databases, containers, throughput) via account keys | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/role-based-access-control#set-via-arm-template) +109 | Monitoring and Alerting | Low | ContainerRegistry should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/monitor-service) +110 | High Availability | High | ContainerRegistry should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/zone-redundancy) +111 | High Availability | High | ContainerRegistry should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/container-registry/) +112 | Security | High | ContainerRegistry should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-private-link) +113 | High Availability | High | ContainerRegistry SKU | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-skus) +114 | Governance | Low | ContainerRegistry Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +115 | Security | Medium | ContainerRegistry should have anonymous pull access disabled | [Learn](https://learn.microsoft.com/azure/container-registry/anonymous-pull-access#configure-anonymous-pull-access) +116 | Security | Medium | ContainerRegistry should have the Administrator account disabled | [Learn](https://learn.microsoft.com/azure/container-registry/container-registry-authentication-managed-identity) +117 | Governance | Low | ContainerRegistry should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +118 | Governance | Medium | ContainerRegistry should use retention policies | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-retention-policy) +119 | Monitoring and Alerting | Low | Azure Data Explorer should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/using-diagnostic-logs) +120 | High Availability | High | Azure Data Explorer SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) +121 | High Availability | High | Azure Data Explorer Production Cluster should not use Dev SKU | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/manage-cluster-choose-sku) +122 | Governance | Low | Azure Data Explorer Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +123 | Governance | Low | Azure Data Explorer should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +124 | Security | High | Azure Data Explorer should use Disk Encryption | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/cluster-encryption-overview) +125 | Security | Low | Azure Data Explorer should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/configure-managed-identities-cluster?tabs=portal) +126 | Monitoring and Alerting | Low | Event Grid Domain should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/diagnostic-logs) +127 | High Availability | High | Event Grid Domain should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/event-grid/) +128 | Security | High | Event Grid Domain should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/configure-private-endpoints) +129 | High Availability | High | Event Grid Domain SKU | [Learn](https://azure.microsoft.com/en-gb/pricing/details/event-grid/) +130 | Governance | Low | Event Grid Domain Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +131 | Governance | Low | Event Grid Domain should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +132 | Security | Medium | Event Grid Domain should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/authenticate-with-access-keys-shared-access-signatures) +133 | Monitoring and Alerting | Low | Event Hub Namespace should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing) +134 | High Availability | High | Event Hub Namespace should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-premium-overview#high-availability-with-availability-zones) +135 | High Availability | High | Event Hub Namespace should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/event-hubs/) +136 | Security | High | Event Hub Namespace should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/network-security) +137 | High Availability | High | Event Hub Namespace SKU | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/compare-tiers) +138 | Governance | Low | Event Hub Namespace Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +139 | Governance | Low | Event Hub should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +140 | Security | Medium | Event Hub should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/authorize-access-event-hubs#shared-access-signatures) +141 | Monitoring and Alerting | Low | Key Vault should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/monitor-key-vault) +142 | High Availability | High | Key Vault should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/key-vault/) +143 | Security | High | Key Vault should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service) +144 | High Availability | High | Key Vault SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/key-vault/) +145 | Governance | Low | Key Vault Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +146 | Governance | Low | Key Vault should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +147 | Disaster Recovery | Medium | Key Vault should have soft delete enabled | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview) +148 | Disaster Recovery | Medium | Key Vault should have purge protection enabled | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview#purge-protection) +149 | Monitoring and Alerting | Low | Load Balancer should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/monitor-load-balancer#creating-a-diagnostic-setting) +150 | High Availability | High | Load Balancer should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant) +151 | High Availability | High | Load Balancer should have a SLA | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/skus) +152 | High Availability | High | Load Balancer SKU | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/skus) +153 | Governance | Low | Load Balancer Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +154 | Governance | Low | Load Balancer should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +155 | Monitoring and Alerting | Low | Logic App should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data) +156 | High Availability | High | Logic App should have a SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +157 | Security | High | Logic App should limit access to Http Triggers | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-securing-a-logic-app?tabs=azure-portal#restrict-access-by-ip-address-range) +158 | Governance | Low | Logic App Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +159 | Governance | Low | Logic App should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +160 | Monitoring and Alerting | Low | MariaDB should have diagnostic settings enabled | [Learn]() +161 | Security | High | MariaDB should have private endpoints enabled | [Learn]() +162 | Governance | Low | MariaDB server Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +163 | High Availability | High | MariaDB server should have a SLA | [Learn]() +164 | Governance | Low | MariaDB should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +165 | Security | Low | MariaDB should enforce TLS >= 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/mariadb/howto-tls-configurations) +166 | Monitoring and Alerting | Low | Azure Database for MySQL - Flexible Server should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/tutorial-query-performance-insights#set-up-diagnostics) +167 | High Availability | High | Azure Database for MySQL - Flexible Server should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-configure-high-availability-cli) +168 | High Availability | High | Azure Database for MySQL - Flexible Server should have a SLA | [Learn](hhttps://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +169 | Security | High | Azure Database for MySQL - Flexible Server should have private access enabled | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-virtual-network-cli) +170 | High Availability | High | Azure Database for MySQL - Flexible Server SKU | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage) +171 | Governance | Low | Azure Database for MySQL - Flexible Server Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +172 | Governance | Low | Azure Database for MySQL - Flexible Server should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +173 | Monitoring and Alerting | Low | Azure Database for MySQL - Flexible Server should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-monitoring#server-logs) +174 | High Availability | High | Azure Database for MySQL - Flexible Server should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/mysql/) +175 | Security | High | Azure Database for MySQL - Flexible Server should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-data-access-security-private-link) +176 | High Availability | High | Azure Database for MySQL - Flexible Server SKU | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-pricing-tiers) +177 | Governance | Low | Azure Database for MySQL - Flexible Server Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +178 | High Availability | High | Azure Database for MySQL - Single Server is on the retirement path | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/whats-happening-to-mysql-single-server) +179 | Governance | Low | Azure Database for MySQL - Single Server should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +180 | Monitoring and Alerting | Low | App Service should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#send-logs-to-azure-monitor) +181 | Security | High | App Service should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/app-service/networking/private-endpoint) +182 | Governance | Low | App Service Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +183 | Security | High | App Service should use HTTPS only | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) +184 | Governance | Low | App Service should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +185 | Security | Medium | App Service should use VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +186 | Security | Medium | App Service should have VNET Route all enabled for VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +187 | Security | High | App Service should use TLS 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) +188 | Security | High | App Service remote debugging should be disabled | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) +189 | Security | High | App Service should not allow insecure FTP | [Learn](https://learn.microsoft.com/en-us/azure/app-service/deploy-ftp?tabs=portal) +190 | Scalability | High | App Service should have Always On enabled | [Learn](https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal) +191 | High Availability | Medium | App Service should avoid using Client Affinity | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) +192 | Security | Medium | App Service should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) +193 | Monitoring and Alerting | Low | Plan should have diagnostic settings enabled | [Learn]() +194 | High Availability | High | Plan should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/reliability/migrate-app-service) +195 | High Availability | High | Plan should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/app-service/) +196 | High Availability | High | Plan SKU | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans) +197 | Governance | Low | Plan Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +198 | Governance | Low | Plan should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +199 | Monitoring and Alerting | Low | Function should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitor-log-analytics?tabs=csharp) +200 | Security | High | Function should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-vnet) +201 | Governance | Low | Function Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +202 | Security | High | Function should use HTTPS only | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) +203 | Governance | Low | Function should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +204 | Security | Medium | Function should use VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +205 | Security | Medium | Function should have VNET Route all enabled for VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +206 | Security | Medium | Function should use TLS 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) +207 | Security | Medium | Function remote debugging should be disabled | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) +208 | High Availability | Medium | Function should avoid using Client Affinity | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) +209 | Security | Medium | Function should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) +210 | Monitoring and Alerting | Low | Logic App should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data) +211 | Security | High | Logic App should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/secure-single-tenant-workflow-virtual-network-private-endpoint) +212 | Governance | Low | Logic App Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +213 | Security | High | Logic App should use HTTPS only | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) +214 | Governance | Low | Logic App should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +215 | Security | Medium | Logic App should use VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +216 | Security | Medium | Logic App should have VNET Route all enabled for VNET integration | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) +217 | Security | Medium | Logic App should use TLS 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) +218 | Security | Medium | Logic App remote debugging should be disabled | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) +219 | High Availability | Medium | Logic App should avoid using Client Affinity | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) +220 | Security | Medium | Logic App should use Managed Identities | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) +221 | Monitoring and Alerting | Low | PostgreSQL should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/howto-configure-and-access-logs) +222 | High Availability | High | PostgreSQL should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/overview#architecture-and-high-availability) +223 | High Availability | High | PostgreSQL should have a SLA | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-compare-single-server-flexible-server) +224 | Security | High | PostgreSQL should have private access enabled | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-networking#private-access-vnet-integration) +225 | High Availability | High | PostgreSQL SKU | [Learn](https://azure.microsoft.com/en-gb/pricing/details/postgresql/flexible-server/) +226 | Governance | Low | PostgreSQL Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +227 | Governance | Low | PostgreSQL should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +228 | Monitoring and Alerting | Low | PostgreSQL should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-server-logs#resource-logs) +229 | High Availability | High | PostgreSQL should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/postgresql/) +230 | Security | High | PostgreSQL should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-data-access-and-security-private-link) +231 | High Availability | High | PostgreSQL SKU | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-pricing-tiers) +232 | Governance | Low | PostgreSQL Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +233 | Governance | Low | PostgreSQL should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +234 | Security | High | PostgreSQL should enforce SSL | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-ssl-connection-security#enforcing-tls-connections) +235 | Security | Low | PostgreSQL should enforce TLS >= 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/how-to-tls-configurations) +236 | Monitoring and Alerting | Low | Redis should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-monitor-diagnostic-settings) +237 | High Availability | High | Redis should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-high-availability) +238 | High Availability | High | Redis should have a SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +239 | Security | High | Redis should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-private-link) +240 | High Availability | High | Redis SKU | [Learn](https://azure.microsoft.com/en-gb/pricing/details/cache/) +241 | Governance | Low | Redis Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +242 | Governance | Low | Redis should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +243 | Security | High | Redis should not enable non SSL ports | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-configure#access-ports) +244 | Security | Low | Redis should enforce TLS >= 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-remove-tls-10-11) +245 | Monitoring and Alerting | Low | Service Bus should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/monitor-service-bus#collection-and-routing) +246 | High Availability | High | Service Bus should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-outages-disasters#availability-zones) +247 | High Availability | High | Service Bus should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/service-bus/) +248 | Security | High | Service Bus should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/network-security) +249 | High Availability | High | Service Bus SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/service-bus/) +250 | Governance | Low | Service Bus Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +251 | Governance | Low | Service Bus should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +252 | Security | Medium | Service Bus should have local authentication disabled | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-sas) +253 | Monitoring and Alerting | Low | SignalR should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-howto-diagnostic-logs) +254 | High Availability | High | SignalR should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/availability-zones) +255 | High Availability | High | SignalR should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/signalr-service/) +256 | Security | High | SignalR should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/howto-private-endpoints) +257 | High Availability | High | SignalR SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/signalr-service/) +258 | Governance | Low | SignalR Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +259 | Governance | Low | SignalR should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +260 | Monitoring and Alerting | Low | SQL should have diagnostic settings enabled | [Learn]() +261 | Security | High | SQL should have private endpoints enabled | [Learn]() +262 | Governance | Low | SQL Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +263 | Governance | Low | SQL should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +264 | Security | Low | SQL should enforce TLS >= 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/azure-sql/database/connectivity-settings?view=azuresql&tabs=azure-portal#minimal-tls-version) +265 | Monitoring and Alerting | Low | SQL Database should have diagnostic settings enabled | [Learn]() +266 | High Availability | High | SQL Database should have availability zones enabled | [Learn]() +267 | High Availability | High | SQL Database should have a SLA | [Learn]() +268 | High Availability | High | SQL Database SKU | [Learn](https://docs.microsoft.com/en-us/azure/azure-sql/database/service-tiers-vcore?tabs=azure-portal) +269 | Governance | Low | SQL Database Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +270 | Governance | Low | SQL Database should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +271 | Monitoring and Alerting | Low | Traffic Manager should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs) +272 | High Availability | High | Traffic Manager should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway) +273 | High Availability | High | Traffic Manager should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/traffic-manager/) +274 | Governance | Low | Traffic Manager Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +275 | Governance | Low | Traffic Manager should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +276 | High Availability | High | Traffic Manager should use at least 2 endpoints | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-endpoint-types) +277 | Security | High | Traffic Manager: HTTP endpoints should be monitored using HTTPS | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring) +278 | Monitoring and Alerting | Low | Storage should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage) +279 | High Availability | High | Storage should have availability zones enabled | [Learn](https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage) +280 | High Availability | High | Storage should have a SLA | [Learn](https://www.azure.cn/en-us/support/sla/storage/) +281 | Security | High | Storage should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints) +282 | High Availability | High | Storage SKU | [Learn](https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types) +283 | Governance | Low | Storage Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +284 | Security | High | Storage Account should use HTTPS only | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-require-secure-transfer) +285 | Governance | Low | Storage Account should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +286 | Security | Low | Storage Account should enforce TLS >= 1.2 | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/transport-layer-security-configure-minimum-version?tabs=portal) +287 | Disaster Recovery | Low | Storage Account should have inmutable storage versioning enabled | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/storage-accounts/reliability) +288 | Disaster Recovery | Medium | Storage Account should have soft delete enabled | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/storage-accounts/reliability) +289 | Monitoring and Alerting | Low | Virtual Machine should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install) +290 | High Availability | High | Virtual Machine should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones) +291 | High Availability | High | Virtual Machine should have a SLA | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) +292 | Governance | Low | Virtual Machine Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +293 | Governance | Low | Virtual Machine should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +294 | High Availability | High | Virtual Machine should use managed disks | [Learn](https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#virtual-machines) +295 | Scalability | Low | Virtual Machine should host application or database data on a data disk | [Learn](https://learn.microsoft.com/azure/virtual-machines/managed-disks-overview#data-disk) +296 | Monitoring and Alerting | Low | Virtual Network should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing) +297 | High Availability | High | Virtual Network should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones) +298 | Governance | Low | Virtual Network Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +299 | Governance | Low | Virtual Network should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) +300 | Security | High | Virtual Network: All Subnets should have a Network Security Group associated | [Learn](https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices) +301 | High Availability | High | Virtual Network should have at least two DNS servers assigned | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances?tabs=redhat#specify-dns-servers) +302 | Monitoring and Alerting | Low | Web Pub Sub should have diagnostic settings enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs) +303 | High Availability | High | Web Pub Sub should have availability zones enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones) +304 | High Availability | High | Web Pub Sub should have a SLA | [Learn](https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/) +305 | Security | High | Web Pub Sub should have private endpoints enabled | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints) +306 | High Availability | High | Web Pub Sub SKU | [Learn](https://azure.microsoft.com/en-us/pricing/details/web-pubsub/) +307 | Governance | Low | Web Pub Sub Name should comply with naming conventions | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) +308 | Governance | Low | Web Pub Sub should have tags | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) diff --git a/docs/content/en/docs/Results/_index.md b/docs/content/en/docs/Results/_index.md deleted file mode 100644 index d1b02412..00000000 --- a/docs/content/en/docs/Results/_index.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: Scan Results -description: Scan Results -weight: 5 ---- - -Azure Quick Review (azqr) creates an excel spreadsheet with the following sections: - -* [Overview](#overview) -* [Recommendations](#recommendations) -* [Services](#services) -* [Defender](#defender) -* [Advisor](#advisor) -* [Costs](#costs) (Disabled by default) - -## Overview - -The overview section contains the following information: - -* **SubscriptionID**: This is the unique identifier for the Azure subscription under which the resource is deployed. -* **ResourceGroup**: The resource group where the resource is deployed. -* **Location**: The geographical region where the resource is deployed. -* **Type**: The specific type or category of the Azure resource. -* **Name**: The name assigned to the resource, providing a human-readable identifier for easy reference and management. -* **SKU**: The SKU represents the specific variant or configuration of the Azure resource. It defines the characteristics and capabilities of the resource. -* **SLA**: The Service Level Agreement (SLA) represents the agreed-upon performance and availability guarantees for the Azure service based on its current configuration. -* **AZ**: A Boolean value indicating whether the service is "Availability Zone aware." Availability Zones are physically separate datacenters within an Azure region, providing increased resiliency and fault tolerance for critical services. -* **PVT**: A Boolean value indicating whether the service has a private IP address. Private IP addresses are used for internal communication within Azure Virtual Networks. -* **DS**: A Boolean value indicating whether diagnostic settings are enabled for the service. Diagnostic settings allow you to collect logs, metrics, and other monitoring data for Azure resources. -* **CAF**: A Boolean value indicating whether the service is compliant with the [Cloud Adoption Framework](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) (CAF) naming convention. The CAF provides best practices and guidance for organizations adopting Azure. - -![overview](/azqr/img/overview.png) - -## Recommendations - -The recommendations section contains a summary of the recommendations for the scanned services: - -* **Id**: The unique identifier for the rule. -* **Category**: The category of the rule. -* **Subcategory**: The subcategory of the rule. -* **Description**: The description of the rule. -* **Severity**: The severity of the rule (High, Medium, Low). -* **Learn**: Link to relevant documentation. - -![recommendations](/azqr/img/recommendations.png) - -## Services - -The services section contains the following information: - -* **Subscription**: This is the unique identifier for the Azure subscription under which the resource is deployed. -* **Resource Group**: The resource group where the resource is deployed. -* **Location**: The geographical region where the resource is deployed. -* **Type**: The specific type or category of the Azure resource. -* **Service Name**: The name assigned to the resource. -* **Category**: The category of the rule. -* **Subcategory**: The subcategory of the rule. -* **Severity**: The severity of the rule (High, Medium, Low). -* **Description**: The description of the rule. -* **Result**: The result of the rule evaluation. -* **Broken**: True if the rule is broken. -* **Learn**: Link to relevant documentation. - -![services](/azqr/img/services.png) - -## Defender - -The defender section contains the following information: - -* **Name**: Microsoft Defender for Cloud plan name. -* **Tier**: The tier of the plan. -* **Deprecated**: True if the plan is deprecated. - -![defender](/azqr/img/defender.png) - -## Advisor - -This section shows the Azure Advisor Recommendations with the following information: - -* **Subscription Id**: This is the unique identifier for the Azure subscription under which the resource is deployed. -* **Name**: The name of the resource identified by Advisor. -* **Type**: The resource type of the resource identified by Advisor. -* **Category**: The category of the recommendation. -* **Description**: The description of the recommendation. -* **PotentialBenefits**: The potential benefits of the recommendation. -* **Risk**: Risk related to the recommendation. -* **LearnMoreLink** Link to relevant documentation. - -## Costs - -Displays the Azure Actual Costs for the period from the first day of the current month until the day Azure Quick Review (azqr) is used. diff --git a/docs/content/en/docs/Rules/_index.md b/docs/content/en/docs/Rules/_index.md deleted file mode 100644 index d094b988..00000000 --- a/docs/content/en/docs/Rules/_index.md +++ /dev/null @@ -1,316 +0,0 @@ ---- -title: Rules -description: Rules -weight: 3 ---- - -Azure Quick Review uses the following rules to identify Azure resources that may be or not be compliant with Azure best practices and recommendations: - -\# | Id | Category | Subcategory | Name | Severity | More Info ----|---|---|---|---|---|--- -1 | dbw-001 | Reliability | Diagnostic Logs | Azure Databricks should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/databricks/administration-guide/account-settings/audit-log-delivery) -2 | dbw-003 | Reliability | SLA | Azure Databricks should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) -3 | dbw-004 | Security | Private Endpoint | Azure Databricks should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/databricks/administration-guide/cloud-configurations/azure/private-link) -4 | dbw-005 | Reliability | SKU | Azure Databricks SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/databricks/) -5 | dbw-006 | Operational Excellence | Naming Convention (CAF) | Azure Databricks Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -6 | dbw-007 | Security | Identity and Access Control | Azure Databricks should have the Public IP disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/databricks/security/network/secure-cluster-connectivity) -7 | adf-001 | Reliability | Diagnostic Logs | Azure Data Factory should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/data-factory/monitor-configure-diagnostics) -8 | adf-002 | Security | Private Endpoint | Azure Data Factory should have private endpoints enabled | High | [Learn]() -9 | adf-003 | Reliability | SLA | Azure Data Factory SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) -10 | adf-004 | Operational Excellence | Naming Convention (CAF) | Azure Data Factory Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -11 | adf-005 | Operational Excellence | Tags | Azure Data Factory should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -12 | afd-001 | Reliability | Diagnostic Logs | Azure FrontDoor should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-logs) -13 | afd-003 | Reliability | SLA | Azure FrontDoor SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/cdn/) -14 | afd-005 | Reliability | SKU | Azure FrontDoor SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/tier-comparison) -15 | afd-006 | Operational Excellence | Naming Convention (CAF) | Azure FrontDoor Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -16 | afd-007 | Operational Excellence | Tags | Azure FrontDoor should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -17 | afw-001 | Reliability | Diagnostic Logs | Azure Firewall should have diagnostic settings enabled | Medium | [Learn](https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics) -18 | afw-002 | Reliability | Availability Zones | Azure Firewall should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/firewall/features#availability-zones) -19 | afw-003 | Reliability | SLA | Azure Firewall SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) -20 | afw-005 | Reliability | SKU | Azure Firewall SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/firewall/choose-firewall-sku) -21 | afw-006 | Operational Excellence | Naming Convention (CAF) | Azure Firewall Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -22 | afw-007 | Operational Excellence | Tags | Azure Firewall should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -23 | agw-001 | Reliability | Scaling | Application Gateway: Ensure autoscaling is used with a minimum of 2 instances | High | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant) -24 | agw-002 | Security | SSL | Application Gateway: Secure all incoming connections with SSL | High | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/services/networking/azure-application-gateway#security) -25 | agw-003 | Security | Firewall | Application Gateway: Enable WAF policies | High | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/features#web-application-firewall) -26 | agw-004 | Reliability | SKU | Application Gateway: Use Application GW V2 instead of V1 | High | [Learn](https://azure.microsoft.com/en-us/updates/application-gateway-v1-will-be-retired-on-28-april-2026-transition-to-application-gateway-v2/) -27 | agw-005 | Reliability | Diagnostic Logs | Application Gateway: Monitor and Log the configurations and traffic | Medium | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-diagnostics#diagnostic-logging) -28 | agw-007 | Reliability | Availability Zones | Application Gateway should have availability zones enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant) -29 | agw-008 | Reliability | Maintenance | Application Gateway: Plan for backend maintenance by using connection draining | Medium | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/features#connection-draining) -30 | agw-103 | Reliability | SLA | Application Gateway SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/application-gateway/) -31 | agw-104 | Reliability | SKU | Application Gateway SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/application-gateway/understanding-pricing) -32 | agw-105 | Operational Excellence | Naming Convention (CAF) | Application Gateway Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -33 | agw-106 | Operational Excellence | Tags | Application Gateway should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -34 | aks-001 | Reliability | Diagnostic Logs | AKS Cluster should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/aks/monitor-aks#collect-resource-logs) -35 | aks-002 | Reliability | Availability Zones | AKS Cluster should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/aks/availability-zones) -36 | aks-003 | Reliability | SLA | AKS Cluster should have an SLA | High | [Learn](https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers#uptime-sla-terms-and-conditions) -37 | aks-004 | Security | Private Endpoint | AKS Cluster should be private | High | [Learn](https://learn.microsoft.com/en-us/azure/aks/private-clusters) -38 | aks-005 | Reliability | SKU | AKS Production Cluster should use Standard SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers) -39 | aks-006 | Operational Excellence | Naming Convention (CAF) | AKS Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -40 | aks-007 | Security | Identity and Access Control | AKS should integrate authentication with AAD (Managed) | Medium | [Learn](https://learn.microsoft.com/en-us/azure/aks/managed-azure-ad) -41 | aks-008 | Security | Identity and Access Control | AKS should be RBAC enabled. | Medium | [Learn](https://learn.microsoft.com/azure/aks/manage-azure-rbac) -42 | aks-009 | Security | Identity and Access Control | AKS should have local accounts disabled | Medium | [Learn](https://learn.microsoft.com/azure/aks/managed-aad#disable-local-accounts) -43 | aks-010 | Security | Best Practices | AKS should have httpApplicationRouting disabled | Medium | [Learn](https://learn.microsoft.com/azure/aks/http-application-routing) -44 | aks-011 | Reliability | Monitoring | AKS should have Container Insights enabled | Medium | [Learn](https://learn.microsoft.com/azure/azure-monitor/insights/container-insights-overview) -45 | aks-012 | Security | Networking | AKS should have outbound type set to user defined routing | High | [Learn](https://learn.microsoft.com/azure/aks/limit-egress-traffic) -46 | aks-013 | Performance Efficiency | Networking | AKS should avoid using kubenet network plugin | Medium | [Learn](https://learn.microsoft.com/azure/aks/operator-best-practices-network) -47 | aks-014 | Operational Excellence | Scaling | AKS should have autoscaler enabled | Medium | [Learn](https://learn.microsoft.com/azure/aks/concepts-scale) -48 | aks-015 | Operational Excellence | Tags | AKS should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -49 | aks-016 | Operational Excellence | Tags | AKS Node Pools should have MaxSurge set | Low | [Learn](https://learn.microsoft.com/en-us/azure/aks/operator-best-practices-run-at-scale#cluster-upgrade-considerations-and-best-practices) -50 | apim-001 | Reliability | Diagnostic Logs | APIM should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-azure-monitor#resource-logs) -51 | apim-002 | Reliability | Availability Zones | APIM should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt) -52 | apim-003 | Reliability | SLA | APIM should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/api-management/) -53 | apim-004 | Security | Private Endpoint | APIM should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/private-endpoint) -54 | apim-005 | Reliability | SKU | Azure APIM SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-features) -55 | apim-006 | Operational Excellence | Naming Convention (CAF) | APIM should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -56 | apim-007 | Operational Excellence | Tags | APIM should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -57 | apim-008 | Security | Identity and Access Control | APIM should use Managed Identities | Medium | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity) -58 | apim-009 | Security | TLS | APIM should only accept a minimum of TLS 1.2 | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers) -59 | apim-010 | Security | Cyphers | APIM should should not accept weak or deprecated ciphers. | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers) -60 | apim-011 | Security | Certificates | APIM: Renew expiring certificates | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/configure-custom-domain?tabs=custom) -61 | apim-012 | Reliability | Reliability | APIM: Migrate instance hosted on the stv1 platform to stv2 | High | [Learn](https://learn.microsoft.com/en-us/azure/api-management/migrate-stv1-to-stv2?tabs=portal) -62 | appcs-001 | Reliability | Diagnostic Logs | AppConfiguration should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal) -63 | appcs-003 | Reliability | SLA | AppConfiguration should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/app-configuration/) -64 | appcs-004 | Security | Private Endpoint | AppConfiguration should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-private-endpoint) -65 | appcs-005 | Reliability | SKU | AppConfiguration SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/app-configuration/) -66 | appcs-006 | Operational Excellence | Naming Convention (CAF) | AppConfiguration Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -67 | appcs-007 | Operational Excellence | Tags | AppConfiguration should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -68 | appcs-008 | Security | Identity and Access Control | AppConfiguration should have local authentication disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-disable-access-key-authentication?tabs=portal#disable-access-key-authentication) -69 | appcs-009 | Reliability | Reliability | AppConfiguration should have purge protection enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-soft-delete#purge-protection) -70 | appi-001 | Reliability | SLA | Azure Application Insights SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/application-insights/index.html) -71 | appi-002 | Operational Excellence | Naming Convention (CAF) | Azure Application Insights Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -72 | appi-003 | Operational Excellence | Tags | Azure Application Insights should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -73 | appi-004 | Reliability | Reliability | Azure Application Insights should store data in a Log Analytics Workspace | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/app/create-workspace-resource) -74 | cae-001 | Reliability | Diagnostic Logs | Container Apps Environment should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/log-options#diagnostic-settings) -75 | cae-002 | Reliability | Availability Zones | Container Apps Environment should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/disaster-recovery?tabs=bash#set-up-zone-redundancy-in-your-container-apps-environment) -76 | cae-003 | Reliability | SLA | Container Apps Environment should have a SLA | High | [Learn](https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/) -77 | cae-004 | Security | Private Endpoint | Container Apps Environment should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/vnet-custom-internal?tabs=bash&pivots=azure-portal) -78 | cae-006 | Operational Excellence | Naming Convention (CAF) | Container Apps Environment Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -79 | cae-007 | Operational Excellence | Tags | Container Apps Environment should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -80 | ca-003 | Reliability | SLA | ContainerApp should have a SLA | High | [Learn](https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/) -81 | ca-006 | Operational Excellence | Naming Convention (CAF) | ContainerApp Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -82 | ca-007 | Operational Excellence | Tags | ContainerApp should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -83 | ca-008 | Security | HTTPS Only | ContainerApp should not allow insecure ingress traffic | Low | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/ingress-how-to?pivots=azure-cli) -84 | ca-009 | Security | Identity and Access Control | ContainerApp should use Managed Identities | Low | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=portal%2Cdotnet) -85 | ca-010 | Reliability | Reliability | ContainerApp should use Azure Files to persist container data | Low | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/storage-mounts?pivots=azure-cli) -86 | ca-011 | Reliability | Reliability | ContainerApp should avoid using session affinity | Low | [Learn](https://learn.microsoft.com/en-us/azure/container-apps/sticky-sessions?pivots=azure-portal) -87 | ci-002 | Reliability | Availability Zones | ContainerInstance should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/container-instances/availability-zones) -88 | ci-003 | Reliability | SLA | ContainerInstance should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/container-instances/v1_0/index.html) -89 | ci-004 | Security | Private IP Address | ContainerInstance should use private IP addresses | High | [Learn]() -90 | ci-005 | Reliability | SKU | ContainerInstance SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/container-instances/) -91 | ci-006 | Operational Excellence | Naming Convention (CAF) | ContainerInstance Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -92 | ci-007 | Operational Excellence | Tags | ContainerInstance should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -93 | cog-001 | Reliability | Diagnostic Logs | Cognitive Service Account should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing) -94 | cog-003 | Reliability | SLA | Cognitive Service Account should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -95 | cog-004 | Security | Private Endpoint | Cognitive Service Account should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-virtual-networks) -96 | cog-005 | Reliability | SKU | Cognitive Service Account SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts?pivots=deployment-language-bicep#sku) -97 | cog-006 | Operational Excellence | Naming Convention (CAF) | Cognitive Service Account Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -98 | cog-007 | Operational Excellence | Tags | Cognitive Service Account should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -99 | cog-008 | Security | Identity and Access Control | Cognitive Service Account should have local authentication disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/ai-services/policy-reference#azure-ai-services) -100 | cosmos-001 | Reliability | Diagnostic Logs | CosmosDB should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/monitor-resource-logs) -101 | cosmos-002 | Reliability | Availability Zones | CosmosDB should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability) -102 | cosmos-003 | Reliability | SLA | CosmosDB should have a SLA | High | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability#slas) -103 | cosmos-004 | Security | Private Endpoint | CosmosDB should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-configure-private-endpoints) -104 | cosmos-005 | Reliability | SKU | CosmosDB SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/cosmos-db/autoscale-provisioned/) -105 | cosmos-006 | Operational Excellence | Naming Convention (CAF) | CosmosDB Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -106 | cosmos-007 | Operational Excellence | Tags | CosmosDB should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -107 | cosmos-008 | Security | Security | CosmosDB should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#disable-local-auth) -108 | cosmos-009 | Security | Security | CosmosDB: disable write operations on metadata resources (databases, containers, throughput) via account keys | Low | [Learn](https://learn.microsoft.com/en-us/azure/cosmos-db/role-based-access-control#set-via-arm-template) -109 | cr-001 | Reliability | Diagnostic Logs | ContainerRegistry should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/monitor-service) -110 | cr-002 | Reliability | Availability Zones | ContainerRegistry should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/zone-redundancy) -111 | cr-003 | Reliability | SLA | ContainerRegistry should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/container-registry/) -112 | cr-004 | Security | Private Endpoint | ContainerRegistry should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-private-link) -113 | cr-005 | Reliability | SKU | ContainerRegistry SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-skus) -114 | cr-006 | Operational Excellence | Naming Convention (CAF) | ContainerRegistry Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -115 | cr-007 | Security | Identity and Access Control | ContainerRegistry should have anonymous pull access disabled | Medium | [Learn](https://learn.microsoft.com/azure/container-registry/anonymous-pull-access#configure-anonymous-pull-access) -116 | cr-008 | Security | Identity and Access Control | ContainerRegistry should have the Administrator account disabled | Medium | [Learn](https://learn.microsoft.com/azure/container-registry/container-registry-authentication-managed-identity) -117 | cr-009 | Operational Excellence | Tags | ContainerRegistry should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -118 | cr-010 | Operational Excellence | Retention Policies | ContainerRegistry should use retention policies | Medium | [Learn](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-retention-policy) -119 | dec-001 | Reliability | Diagnostic Logs | Azure Data Explorer should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/using-diagnostic-logs) -120 | dec-002 | Reliability | SLA | Azure Data Explorer SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services) -121 | dec-003 | Reliability | SKU | Azure Data Explorer Production Cluster should not use Dev SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/manage-cluster-choose-sku) -122 | dec-004 | Operational Excellence | Naming Convention (CAF) | Azure Data Explorer Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -123 | dec-005 | Operational Excellence | Tags | Azure Data Explorer should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -124 | dec-008 | Security | Disk Encryption | Azure Data Explorer should use Disk Encryption | High | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/cluster-encryption-overview) -125 | dec-009 | Security | Identity and Access Control | Azure Data Explorer should use Managed Identities | Low | [Learn](https://learn.microsoft.com/en-us/azure/data-explorer/configure-managed-identities-cluster?tabs=portal) -126 | evgd-001 | Reliability | Diagnostic Logs | Event Grid Domain should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/diagnostic-logs) -127 | evgd-003 | Reliability | SLA | Event Grid Domain should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/event-grid/) -128 | evgd-004 | Security | Private Endpoint | Event Grid Domain should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/configure-private-endpoints) -129 | evgd-005 | Reliability | SKU | Event Grid Domain SKU | High | [Learn](https://azure.microsoft.com/en-gb/pricing/details/event-grid/) -130 | evgd-006 | Operational Excellence | Naming Convention (CAF) | Event Grid Domain Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -131 | evgd-007 | Operational Excellence | Tags | Event Grid Domain should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -132 | evgd-008 | Security | Identity and Access Control | Event Grid Domain should have local authentication disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/event-grid/authenticate-with-access-keys-shared-access-signatures) -133 | evh-001 | Reliability | Diagnostic Logs | Event Hub Namespace should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing) -134 | evh-002 | Reliability | Availability Zones | Event Hub Namespace should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-premium-overview#high-availability-with-availability-zones) -135 | evh-003 | Reliability | SLA | Event Hub Namespace should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/event-hubs/) -136 | evh-004 | Security | Private Endpoint | Event Hub Namespace should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/network-security) -137 | evh-005 | Reliability | SKU | Event Hub Namespace SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/compare-tiers) -138 | evh-006 | Operational Excellence | Naming Convention (CAF) | Event Hub Namespace Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -139 | evh-007 | Operational Excellence | Tags | Event Hub should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -140 | evh-008 | Security | Identity and Access Control | Event Hub should have local authentication disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/event-hubs/authorize-access-event-hubs#shared-access-signatures) -141 | kv-001 | Reliability | Diagnostic Logs | Key Vault should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/monitor-key-vault) -142 | kv-003 | Reliability | SLA | Key Vault should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/key-vault/) -143 | kv-004 | Security | Private Endpoint | Key Vault should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service) -144 | kv-005 | Reliability | SKU | Key Vault SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/key-vault/) -145 | kv-006 | Operational Excellence | Naming Convention (CAF) | Key Vault Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -146 | kv-007 | Operational Excellence | Tags | Key Vault should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -147 | kv-008 | Reliability | Reliability | Key Vault should have soft delete enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview) -148 | kv-009 | Reliability | Reliability | Key Vault should have purge protection enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview#purge-protection) -149 | lb-001 | Reliability | Diagnostic Logs | Load Balancer should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/monitor-load-balancer#creating-a-diagnostic-setting) -150 | lb-002 | Reliability | Availability Zones | Load Balancer should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant) -151 | lb-003 | Reliability | SLA | Load Balancer should have a SLA | High | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/skus) -152 | lb-005 | Reliability | SKU | Load Balancer SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/load-balancer/skus) -153 | lb-006 | Operational Excellence | Naming Convention (CAF) | Load Balancer Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -154 | lb-007 | Operational Excellence | Tags | Load Balancer should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -155 | logic-001 | Reliability | Diagnostic Logs | Logic App should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data) -156 | logic-003 | Reliability | SLA | Logic App should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -157 | logic-004 | Security | Firewall | Logic App should limit access to Http Triggers | High | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-securing-a-logic-app?tabs=azure-portal#restrict-access-by-ip-address-range) -158 | logic-006 | Operational Excellence | Naming Convention (CAF) | Logic App Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -159 | logic-007 | Operational Excellence | Tags | Logic App should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -160 | maria-001 | Reliability | Diagnostic Logs | MariaDB should have diagnostic settings enabled | Medium | [Learn]() -161 | maria-002 | Security | Private Endpoint | MariaDB should have private endpoints enabled | High | [Learn]() -162 | maria-003 | Operational Excellence | Naming Convention (CAF) | MariaDB server Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -163 | maria-004 | Reliability | SLA | MariaDB server should have a SLA | High | [Learn]() -164 | maria-005 | Operational Excellence | Tags | MariaDB should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -165 | maria-006 | Security | TLS | MariaDB should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/mariadb/howto-tls-configurations) -166 | mysqlf-001 | Reliability | Diagnostic Logs | Azure Database for MySQL - Flexible Server should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/tutorial-query-performance-insights#set-up-diagnostics) -167 | mysqlf-002 | Reliability | Availability Zones | Azure Database for MySQL - Flexible Server should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-configure-high-availability-cli) -168 | mysqlf-003 | Reliability | SLA | Azure Database for MySQL - Flexible Server should have a SLA | High | [Learn](hhttps://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -169 | mysqlf-004 | Security | Private IP Address | Azure Database for MySQL - Flexible Server should have private access enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-virtual-network-cli) -170 | mysqlf-005 | Reliability | SKU | Azure Database for MySQL - Flexible Server SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage) -171 | mysqlf-006 | Operational Excellence | Naming Convention (CAF) | Azure Database for MySQL - Flexible Server Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -172 | mysqlf-007 | Operational Excellence | Tags | Azure Database for MySQL - Flexible Server should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -173 | mysql-001 | Reliability | Diagnostic Logs | Azure Database for MySQL - Flexible Server should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-monitoring#server-logs) -174 | mysql-003 | Reliability | SLA | Azure Database for MySQL - Flexible Server should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/mysql/) -175 | mysql-004 | Security | Private Endpoint | Azure Database for MySQL - Flexible Server should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-data-access-security-private-link) -176 | mysql-005 | Reliability | SKU | Azure Database for MySQL - Flexible Server SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-pricing-tiers) -177 | mysql-006 | Operational Excellence | Naming Convention (CAF) | Azure Database for MySQL - Flexible Server Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -178 | mysql-007 | Reliability | SKU | Azure Database for MySQL - Single Server is on the retirement path | High | [Learn](https://learn.microsoft.com/en-us/azure/mysql/single-server/whats-happening-to-mysql-single-server) -179 | mysql-008 | Operational Excellence | Tags | Azure Database for MySQL - Single Server should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -180 | app-001 | Reliability | Diagnostic Logs | App Service should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#send-logs-to-azure-monitor) -181 | app-004 | Security | Private Endpoint | App Service should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/app-service/networking/private-endpoint) -182 | app-006 | Operational Excellence | Naming Convention (CAF) | App Service Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -183 | app-007 | Security | HTTPS Only | App Service should use HTTPS only | High | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) -184 | app-008 | Operational Excellence | Tags | App Service should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -185 | app-009 | Security | Networking | App Service should use VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -186 | app-010 | Security | Networking | App Service should have VNET Route all enabled for VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -187 | app-011 | Security | TLS | App Service should use TLS 1.2 | High | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) -188 | app-012 | Security | Security | App Service remote debugging should be disabled | High | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) -189 | app-013 | Security | Security | App Service should not allow insecure FTP | High | [Learn](https://learn.microsoft.com/en-us/azure/app-service/deploy-ftp?tabs=portal) -190 | app-014 | Security | Security | App Service should have Always On enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal) -191 | app-015 | Reliability | Reliability | App Service should avoid using Client Affinity | Medium | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) -192 | app-016 | Security | Identity and Access Control | App Service should use Managed Identities | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) -193 | asp-001 | Reliability | Diagnostic Logs | Plan should have diagnostic settings enabled | Medium | [Learn]() -194 | asp-002 | Reliability | Availability Zones | Plan should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/reliability/migrate-app-service) -195 | asp-003 | Reliability | SLA | Plan should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/app-service/) -196 | asp-005 | Reliability | SKU | Plan SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans) -197 | asp-006 | Operational Excellence | Naming Convention (CAF) | Plan Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -198 | asp-007 | Operational Excellence | Tags | Plan should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -199 | func-001 | Reliability | Diagnostic Logs | Function should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitor-log-analytics?tabs=csharp) -200 | func-004 | Security | Private Endpoint | Function should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-vnet) -201 | func-006 | Operational Excellence | Naming Convention (CAF) | Function Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -202 | func-007 | Security | HTTPS Only | Function should use HTTPS only | High | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) -203 | func-008 | Operational Excellence | Tags | Function should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -204 | func-009 | Security | Networking | Function should use VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -205 | func-010 | Security | Networking | Function should have VNET Route all enabled for VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -206 | func-011 | Security | TLS | Function should use TLS 1.2 | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) -207 | func-012 | Security | Security | Function remote debugging should be disabled | Medium | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) -208 | func-013 | Reliability | Reliability | Function should avoid using Client Affinity | Medium | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) -209 | func-014 | Security | Identity and Access Control | Function should use Managed Identities | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) -210 | logics-001 | Reliability | Diagnostic Logs | Logic App should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data) -211 | logics-004 | Security | Private Endpoint | Logic App should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/logic-apps/secure-single-tenant-workflow-virtual-network-private-endpoint) -212 | logics-006 | Operational Excellence | Naming Convention (CAF) | Logic App Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -213 | logics-007 | Security | HTTPS Only | Logic App should use HTTPS only | High | [Learn](https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https) -214 | logics-008 | Operational Excellence | Tags | Logic App should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -215 | logics-009 | Security | Networking | Logic App should use VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -216 | logics-010 | Security | Networking | Logic App should have VNET Route all enabled for VNET integration | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration) -217 | logics-011 | Security | TLS | Logic App should use TLS 1.2 | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-tls) -218 | logics-012 | Security | Security | Logic App remote debugging should be disabled | Medium | [Learn](https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging) -219 | logics-013 | Reliability | Reliability | Logic App should avoid using Client Affinity | Medium | [Learn](https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist) -220 | logics-014 | Security | Identity and Access Control | Logic App should use Managed Identities | Medium | [Learn](https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=portal%2Chttp) -221 | psqlf-001 | Reliability | Diagnostic Logs | PostgreSQL should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/howto-configure-and-access-logs) -222 | psqlf-002 | Reliability | Availability Zones | PostgreSQL should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/overview#architecture-and-high-availability) -223 | psqlf-003 | Reliability | SLA | PostgreSQL should have a SLA | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-compare-single-server-flexible-server) -224 | psqlf-004 | Security | Private IP Address | PostgreSQL should have private access enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-networking#private-access-vnet-integration) -225 | psqlf-005 | Reliability | SKU | PostgreSQL SKU | High | [Learn](https://azure.microsoft.com/en-gb/pricing/details/postgresql/flexible-server/) -226 | psqlf-006 | Operational Excellence | Naming Convention (CAF) | PostgreSQL Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -227 | psqlf-007 | Operational Excellence | Tags | PostgreSQL should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -228 | psql-001 | Reliability | Diagnostic Logs | PostgreSQL should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-server-logs#resource-logs) -229 | psql-003 | Reliability | SLA | PostgreSQL should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/postgresql/) -230 | psql-004 | Security | Private Endpoint | PostgreSQL should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-data-access-and-security-private-link) -231 | psql-005 | Reliability | SKU | PostgreSQL SKU | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-pricing-tiers) -232 | psql-006 | Operational Excellence | Naming Convention (CAF) | PostgreSQL Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -233 | psql-007 | Operational Excellence | Tags | PostgreSQL should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -234 | psql-008 | Security | SSL | PostgreSQL should enforce SSL | High | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-ssl-connection-security#enforcing-tls-connections) -235 | psql-009 | Security | TLS | PostgreSQL should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/postgresql/single-server/how-to-tls-configurations) -236 | redis-001 | Reliability | Diagnostic Logs | Redis should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-monitor-diagnostic-settings) -237 | redis-002 | Reliability | Availability Zones | Redis should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-high-availability) -238 | redis-003 | Reliability | SLA | Redis should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -239 | redis-004 | Security | Private Endpoint | Redis should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-private-link) -240 | redis-005 | Reliability | SKU | Redis SKU | High | [Learn](https://azure.microsoft.com/en-gb/pricing/details/cache/) -241 | redis-006 | Operational Excellence | Naming Convention (CAF) | Redis Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -242 | redis-007 | Operational Excellence | Tags | Redis should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -243 | redis-008 | Security | SSL | Redis should not enable non SSL ports | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-configure#access-ports) -244 | redis-009 | Security | TLS | Redis should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-remove-tls-10-11) -245 | sb-001 | Reliability | Diagnostic Logs | Service Bus should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/monitor-service-bus#collection-and-routing) -246 | sb-002 | Reliability | Availability Zones | Service Bus should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-outages-disasters#availability-zones) -247 | sb-003 | Reliability | SLA | Service Bus should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/service-bus/) -248 | sb-004 | Security | Private Endpoint | Service Bus should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/network-security) -249 | sb-005 | Reliability | SKU | Service Bus SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/service-bus/) -250 | sb-006 | Operational Excellence | Naming Convention (CAF) | Service Bus Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -251 | sb-007 | Operational Excellence | Tags | Service Bus should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -252 | sb-008 | Security | Identity and Access Control | Service Bus should have local authentication disabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-sas) -253 | sigr-001 | Reliability | Diagnostic Logs | SignalR should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-howto-diagnostic-logs) -254 | sigr-002 | Reliability | Availability Zones | SignalR should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/availability-zones) -255 | sigr-003 | Reliability | SLA | SignalR should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/signalr-service/) -256 | sigr-004 | Security | Private Endpoint | SignalR should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-signalr/howto-private-endpoints) -257 | sigr-005 | Reliability | SKU | SignalR SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/signalr-service/) -258 | sigr-006 | Operational Excellence | Naming Convention (CAF) | SignalR Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -259 | sigr-007 | Operational Excellence | Tags | SignalR should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -260 | sql-001 | Reliability | Diagnostic Logs | SQL should have diagnostic settings enabled | Medium | [Learn]() -261 | sql-004 | Security | Private Endpoint | SQL should have private endpoints enabled | High | [Learn]() -262 | sql-006 | Operational Excellence | Naming Convention (CAF) | SQL Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -263 | sql-007 | Operational Excellence | Tags | SQL should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -264 | sql-008 | Security | TLS | SQL should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-sql/database/connectivity-settings?view=azuresql&tabs=azure-portal#minimal-tls-version) -265 | sqldb-001 | Reliability | Diagnostic Logs | SQL Database should have diagnostic settings enabled | Medium | [Learn]() -266 | sqldb-002 | Reliability | Availability Zones | SQL Database should have availability zones enabled | High | [Learn]() -267 | sqldb-003 | Reliability | SLA | SQL Database should have a SLA | High | [Learn]() -268 | sqldb-005 | Reliability | SKU | SQL Database SKU | High | [Learn](https://docs.microsoft.com/en-us/azure/azure-sql/database/service-tiers-vcore?tabs=azure-portal) -269 | sqldb-006 | Operational Excellence | Naming Convention (CAF) | SQL Database Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -270 | sqldb-007 | Operational Excellence | Tags | SQL Database should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -271 | traf-001 | Reliability | Diagnostic Logs | Traffic Manager should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs) -272 | traf-002 | Reliability | Availability Zones | Traffic Manager should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway) -273 | traf-003 | Reliability | SLA | Traffic Manager should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/traffic-manager/) -274 | traf-006 | Operational Excellence | Naming Convention (CAF) | Traffic Manager Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -275 | traf-007 | Operational Excellence | Tags | Traffic Manager should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -276 | traf-008 | Reliability | Reliability | Traffic Manager should use at least 2 endpoints | High | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-endpoint-types) -277 | traf-009 | Security | HTTPS Only | Traffic Manager: HTTP endpoints should be monitored using HTTPS | High | [Learn](https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring) -278 | st-001 | Reliability | Diagnostic Logs | Storage should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage) -279 | st-002 | Reliability | Availability Zones | Storage should have availability zones enabled | High | [Learn](https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage) -280 | st-003 | Reliability | SLA | Storage should have a SLA | High | [Learn](https://www.azure.cn/en-us/support/sla/storage/) -281 | st-004 | Security | Private Endpoint | Storage should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints) -282 | st-005 | Reliability | SKU | Storage SKU | High | [Learn](https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types) -283 | st-006 | Operational Excellence | Naming Convention (CAF) | Storage Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -284 | st-007 | Security | HTTPS Only | Storage Account should use HTTPS only | High | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/storage-require-secure-transfer) -285 | st-008 | Operational Excellence | Tags | Storage Account should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -286 | st-009 | Security | TLS | Storage Account should enforce TLS >= 1.2 | Low | [Learn](https://learn.microsoft.com/en-us/azure/storage/common/transport-layer-security-configure-minimum-version?tabs=portal) -287 | vm-001 | Reliability | Diagnostic Logs | Virtual Machine should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install) -288 | vm-002 | Reliability | Availability Zones | Virtual Machine should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones) -289 | vm-003 | Reliability | SLA | Virtual Machine should have a SLA | High | [Learn](https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1) -290 | vm-006 | Operational Excellence | Naming Convention (CAF) | Virtual Machine Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -291 | vm-007 | Operational Excellence | Tags | Virtual Machine should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -292 | vm-008 | Reliability | Reliability | Virtual Machine should use managed disks | High | [Learn](https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#virtual-machines) -293 | vm-009 | Reliability | Reliability | Virtual Machine should host application or database data on a data disk | Low | [Learn](https://learn.microsoft.com/azure/virtual-machines/managed-disks-overview#data-disk) -294 | vnet-001 | Reliability | Diagnostic Logs | Virtual Network should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing) -295 | vnet-002 | Reliability | Availability Zones | Virtual Network should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones) -296 | vnet-006 | Operational Excellence | Naming Convention (CAF) | Virtual Network Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -297 | vnet-007 | Operational Excellence | Tags | Virtual Network should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) -298 | vnet-008 | Security | Networking | Virtual Network: All Subnets should have a Network Security Group associated | High | [Learn](https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices) -299 | vnet-009 | Reliability | Reliability | Virtual NetworK should have at least two DNS servers assigned | High | [Learn](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-name-resolution-for-vms-and-role-instances?tabs=redhat#specify-dns-servers) -300 | wps-001 | Reliability | Diagnostic Logs | Web Pub Sub should have diagnostic settings enabled | Medium | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs) -301 | wps-002 | Reliability | Availability Zones | Web Pub Sub should have availability zones enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones) -302 | wps-003 | Reliability | SLA | Web Pub Sub should have a SLA | High | [Learn](https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/) -303 | wps-004 | Security | Private Endpoint | Web Pub Sub should have private endpoints enabled | High | [Learn](https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints) -304 | wps-005 | Reliability | SKU | Web Pub Sub SKU | High | [Learn](https://azure.microsoft.com/en-us/pricing/details/web-pubsub/) -305 | wps-006 | Operational Excellence | Naming Convention (CAF) | Web Pub Sub Name should comply with naming conventions | Low | [Learn](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations) -306 | wps-007 | Operational Excellence | Tags | Web Pub Sub should have tags | Low | [Learn](https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json) diff --git a/docs/content/en/docs/Usage/_index.md b/docs/content/en/docs/Usage/_index.md index fa5a5574..0aadd29f 100644 --- a/docs/content/en/docs/Usage/_index.md +++ b/docs/content/en/docs/Usage/_index.md @@ -44,4 +44,4 @@ For information on available commands and help run: ```bash ./azqr -h -``` +``` \ No newline at end of file diff --git a/docs/static/img/defender.png b/docs/static/img/defender.png deleted file mode 100644 index 06aa83d0a4e6245f13b9f4168c7d4ba5de74782f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20282 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 P-7a$z#F+|Xm)m~>bRXgD diff --git a/docs/static/img/overview.png b/docs/static/img/overview.png deleted file mode 100644 index bc81bf4a804d6847c1836dd499adb0ada3519485..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92244 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

7(gdB_YLY1%ArUaSL;~pC4c1O8nS2{gO7} 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 TGUP>rI^oGaGd> zicvL(F?HC=UtLc3Q`Dn#9$4kp6K$9IZmz2c^~F z75A?3ic_Zx4fj-2^gBn*CIgttnW_nK7hR~}wV5G^P<3T%6&Uh0U#AnPaM8xz#1`Krm z?QuQF;;rhoFYT!tS>NhSE}L{MYCo&zmd_kXA?-OcV=;Dd?yAj!*!u&XsFM)Lsk!IH zG#Gn>7*0rLGLX|gGB6m1Ao@?x4Ohl>&QS!J*31i&Ukg*8<+T|8Tv?v=gYWyRWpRW6jk~XwGX$e2k z8Ov)dJTn=?(^Nq?N$8UDaCYHja7=+}9U^c?lBI6ns8jLc?A+f}NTUlDTFpXJsLLjK zLYYQwxnswYg%S)QPOW6L$+9Z$}EI1Ts0nWU_{;%rJGODdMT+_uhNN{&| zcMYz=p#=)XwG=2$8!R}*9ZGO_x8m+vq%AHLinY)}>1^8X*gEIT%$k`$!ymGem1Jip zYbV!x-S@K>dgWM=#h+e?vQU{eV2#s6pCKr*gMM0L{+LPbmm4v(@>F;mr90}(9wo}k z2K;iq_A!#%XxAM~!{N=}GFw+X;=mc_7__AE!rS4;V&Q$0&JgccSZH*O@#a&)NdUbC z^!8Z`52IZ5_d@%qK88p5otfD&<`Z#RnGhjnCwX@~q2a+HJ`dI8 zA{jCGrWn1K)HVrakY{F+A7n46F`)N{i)lNNNnflpi4X_v6!Ycd(*1bOz8B06`HXO= zr0Q0SvjG8nA1xiW;PQ&u$D<=Y_n=+!w%sJu8vFGxQ;#@H*`Cj4parV;rky!ezi1x! zpbLh;Oq0R+RAhE(`lG^P4`Wf7ot-r55J`^EN z#A{E0u^@{S*jFVVnP$+@?9*?? z1ade7BeT}asrF8lN1~!~&jj2^h>UH|jmOgl>dIG=OJq%%Y(uPOGnzbV4O=B4gF%M_ z+In)fJ>V2I1%h!7@ua}n_}fYxh1PBp5mO?8U-F4N zs8D8@A01%Y6!z4~TSPAw3c>^VyV+I783C@W$QUTPY{r^a4ZzxEXr4mq%AP4^qO?t; z$9UfntJsHdn3E(HqXwSA`M$o#<{qTX1>xka<9QAWhh^vE}(*(NNh zSf5x--by4j>!-Ab7el=hdb%jiaQITfx;w!kS96!IU+*`XZli3SN<_G?-?f9nHP>D- zq&I8Fzslpwas;c z$0LZmY(DNzmg};Ck+8H+MJ~Nn)?Oxg5!?QV==_7xyiyVBJZZsv|6zj8(rQb7p)=oT zsF7(66MKV7wy`GLf^)eZM6Zo%{CKw8#}Yz;mz~I{?mJB*GcFN{3_`jK(r8aPN^NLz`-z}&nBAbT=Nj>$bn-~&?-mBqv&`6<-fjbLIZmq=TQ}X zP(+g8H}_Vd0?=*f5tJStNQtNj(xE{@=PQi1J~7;!CoB^ocC|hCQHzd8a|R@Z8EE*; zm2JSU@$0P_s6>|R4woI!Cp}W}hT4abrJci@_9&lh5C;XOKE<*L*zK??pt}Ud=S`$D zv(k)*=03wCTbqOykJ1QbDF;El#A8styS<>;JK(MqSK$)Z@e_Msio5RDo&`l;pR@j| zWmsc|guEd1wmv}nemS-{fme#+Gya_+-YhlDW|v3&nuk0SW$t*75_@y`39AAaYClGV z?@2h}e0HJr;uSg;wm*`$GJ@IZoSq6WxrVjFohv@~Vl^5iG4Y=j6LuDi)){TQD{cWBvXNp%+lX63`H0Ow^XM6Pv?(&!1F1=eopgV9lGS zCi$0w?ZejSZxrL=bU#3Y{z8fpGu`*jwf%$8%n!L>x(OqK*E|CRnXxcWM=#1!2M%*F zLU+%ZyoGftWH9nG16L}cTs{>$nRCGnAACsfU4^N*Nc8`xHqm0V!ns-OBK=?KsB6>OI`>HdfY|_VCoVG>Okx1}G`#lz zGn{=5>G;2+*)ONAm9WOZgEw^{dHz0Ch^6OlR9`*=M9S)*`e6nCAIWRK4)5CrcCvKt zgx1zy(au?cSfc56{bP4CoZS5Rsu0|xb1MA6WS|?*EGNKQdi`JEEI%qH@>NDo&SX!2 zqX2-zV(6QtE=2c08*uhN16u9h9X)|XI%$jcZW4pnH=b)gpPI9>@0Ty$-U@ComLE*} zfRJj!8__F|1#+&YFTvFj4E59@U#Jm9ou;kS78A+6lLFa71E!FyMW(Wv*roE#d>m}! zXOc&&@#-5CnW%eN4rI>k__}m*p0C+v@A_w(*nW|{dM#T}H7M8lL3Bny-^B)VO8&uO zoI`tUIrxNQ;9sTdw3^3S1P#BU8S^?^J77H4y^7Q~7a`)Zj*k3q&h=Z|d<7yl32beD)A-%Y;iOr6%Nh7 z{dnVK(a~HbM66Ug#7ah!P(I;7vKAe7rIQ!dD$ntw7%ek7fNB?z$24+xL7iOgU0XNp zE=~ejYSE}Y-0seveQQ-~`T_F>v1$6#Q$MaOq^c0!B*Z366fqnHF$+deS~>sox6bTY z*)8_5gs|NAgkemr3?<&UB^SL+9ErCuxf=!cuF!kuy3z#7A^vZ;woD-d&G(R$lZ40+ zSEgGNC0tB5G@isT9mk!+_FYYXdh%5aoo8_3t3!ux7gP4}D*&)l7M_UMH%O59Zn~5> z_TsW8=%%_M|g5Hmi7hw5q%u<6WcGpWC)~-T$ zpi)*L`iNMA35V$l5UB{#b(WphQ`?{AcK%n3rNFmD`M7ug8=Dij6XX1 zv`sozG|e+OU9QvhCwFN+(V4>MU%6L4aEAWC{5Q=iAPaL(;h-g6pJe4mC0crSXydgVd% zyXR}e^^K&2#?q83zJxzCC?u53HD#6R10CdEeD9NnKEx`dz9SY?e2vYGyHC>6s@DRtpn`JnjO5xPOD^^+(Ul5cZa-K zc>I;#onA=Z<>?7;o*~ifM`oQ~gtznz%k$S1WQ~|hlr7N@W;VLD$I*>l6+Km-1eL>) zpH|^p&z6vE(b;{w}6xBefvaW+BkO|uwg zjg52-&8wZm<>wQBQucO*h~MWz;1FtRfjnP2gzyYY z!Wba6QbF5)^ZU|^y-*a1Ons)R*od?Crx`ATRsmIK?uP+VS(A9TCFmxNM8KQu4+{8V zVIxDXyz2xRY9O>!O>;DsCb(eC(2Sv&UsC4S{YM*zTcP&V7PFZL7? zx-!WqI7?evQ?YZ1YO;U6tM2B}z&I*yLv5>5-zQ4Yyl2g3y~nMOEeeqkAZ7K%s)gBb zqiKTpL$Zr}vCbp9c(q4e*y))&6P|#S)oM>!w53nXJ}Nf4u&=vI5HZ%vFOT+ah)^%!0?l%T2c|Pr)8~<8rgW$~FGVQR1HpL1de{mI1`sJDFI;_@!R~vQwPA~IZ&sgOm#R}R_lE-4S%4HGaAej#9|~t z=iG|FkLy-g%y77%jAPkBG_)A9wtM_kK86edJwqq5LZM)kIsP;ekgkf`?nyEOzYx##SLTQVn~D`@=py)vj~F-|XfkUWaQHPp!2hOA9?qgbxVd~Y>4%9<$Fc_> zx+vqv-wPr|S%T&%r>z{R-Lq@G{aW9cCYRniff$2T_6%|fa)Il&_PbXaLDB-$x2oAX zfQ;=)c}>Qzekv7W=@FwY<}c5QtXa&Gul!uz4N{R%%?zp5i4n+QoB6fFnV7@`>X^8+j)}Q7-NRN3^f)1rinmu zZ`E&Z*$O!y!;VKox8gvl2kKAobS}83%F^(|ri5ie0ru&*aqL-lh4C2h9r+YQI3tXqm<%po}rwd_b!F5~y4p32@bh}!ug-ScdOFOI=1xX`20eKMRr$vvk>-{q}1 z@;`=_VRo!Z#w~KzSA<&ya~2`Nma&)W%)XfSxeo+mY1S_1yTAxJ^8QIGy#V%INHW%! zd2S1`4kDvVzz%>-PKZ|)7|vWm`*n%Ak|U-zgtP)*WO1UMVpg=`@ik#j2RZPCd(XKA zB2BmW;wd*1F2L= zjJVh%3Y_IAbmzaP;4z>Izod)0C43b25TYJwb@0wf7LmMEj+hGY#=`QrrPTPb?hmrN z7NI>mjJ+`#v+u!2Lnpny;MmqFB=Y-^-rcQdp_uaotN0pNwfYf42eR&#Yi6Pnx_X*B z7>03MmUFq&MPZ-xCR615VgjT0%klT?e7l~<`FqC>pB`B-vJmTU9h*?g2!%nOkDq0m zk(=(a67+@exC67L?ok$9l92(tL+3q5?E!FPeR9L@Ki~KN=2-$9{#f<&lGsWr_ ziLVgCuPYcRiz|%nO|}aGG+{>>Bkp^5Sq!G-i17SjX>$XY{hzJ**Z{*)SK)toKxLQc zgHER?msk<)hniHdj{m;S4RfKC##5?NqrgjIfeC-kOJV68R%Iq-Zm$DkaYenR*nCkP zB&)s*9Z0_P%Zq!k_RzHo-uMNe*a$jFq&M!lMvxHBi=nwK<&y4gZnI6p*=|RJ(>>u2 z;1TlOU)J~Vn#HVP`3GL5-;(pfwTN}}jhzYTmZ%eUUONm_Q)EeO>-o_S z4h&z9DM;Bb7K=zz{v}tQ%_RIg5lZ89EF@LMS9muwr)8dwN~&I4J*d|W9zxw0M9k=rn#kGk>SiVtIH%9@6Qp2o=j?~G zPg!xdqzpIeQP>$tI&V6RMwzy43SXI+EGRt{V}Xk?c`U z@q7=ogeDKtS}=_$ldA1kpte!PNnU-`;0Z5Q&Lzb2D%b-o{p*fZ>sw)`!_)0ku2-k- z{i)x6-lqI8=YO=+Hjuqa&A5fNiI>ureD+FD_p`DOH4NA5J93hPKbS%MwXV0Ca;e>o zk>c5&7;cLaZ_SvGGyB_Dj0}zI>qT!SQC@b5e-nF=&Iami;YY0nhluA~7Z$QLQTz02 z6u9cSFx5Y`dekcU?1l^9f6tB=WjsKYFw+|1pyY|;*Lpt9v*M{j)gWS`XvL*k&g_0M zDEv3q8@N`z4E?V7{-8$(b;lh3sj#@vGY|H*^1Gtdhx$&}Wg$`JKfqe;iyK10>a9WF z*5|Zmy8Sj$)5)srZ-iE09z)Gv+x%{Z!AjzC)tzO=3hqy zB!oXp3bYuJF#ILgSgk%u3gkdWfWp~+iuwux5#c2x<$tGzyMEkUeFYN0FMyFlfOZ8g z-CdvU0@r;2h7SRWVZgD<0-E-C5m0h+K9oOCw+HWU>?x%GCAdg8c30j9jxG6>kUA3j@@>Wo({266kV@%29>N1h~Pe;)orh)>{}CHPRYvv|}LVkQ{6w5sep zB-2q~MN$^_;$3EQUWj@h0Qm^``RU~u)0O}E=MSeLtF$tnCZk_Rbgwo>T0rX~QZn~} z;lcaBuvP!Ru&cE_I&YUWP8>8nz-^FqSi=6<=;FrC*@M5fvVLkDaeU(9?R>Oj*K1*q zoIAwW1sQu*qIkewPFCq1gbXp`&1ABrc&}KuDJe=c- zvQQFOd8@I#VjRh#&R+9aS(nFHLqzECgYTDW_=1rptz0l>L`s9@!YZ_QB6=Ar*y1`J zL-3!V)i>ndL2Ld06k30|tJ;LdAR@)0V5bLJ%OlvFlW1+kz&V?&G{*yn@jGP4MlYIv z`r9|DeA36RjC^MCt}Y}g-X9q?lqqqKw}AD&*#4b;pmQx7}71uU6doytQ`eJa4qxyD@^_9Vqo;z#{{de(6VF%*wl~jg@^slC|4&}EyGAx# zCc_x^GH%=BoZ?@-Y8a3K{$;gI`1-D{Ar+=k8|oKI=7f{wjF&%Th!OW9DLiTbS+f-x zr++{&G@G9*TZ}*RJAa9wVRn>K6G?S@*X=&6vVs{Kdo6sBSkmL5$#f*moHk* z`*L%MX5QZ@JF{;SW6R_)%B+Nah!nKt{F@0@{+CSfPfVTUebJCmH4Pb&$PVF&!m_Dm z@w4Q$p(f^H&F%(o-6!AM!oN|D+3G|+Z_!4vTz%W*3=bMGKT^|VrOM0Y)!uT#gGb z@VM=bu-vd!*Gtmr^)T*@s1Q6%T|}q;-5V(TnfAc4g0IAsItSb{OKwTbn!YsbZsx}? zJ89IgS71+wq7r7WPg8%VSj@6>`AngPi#Ic>TE4*Ba969nk~H=n8z-gwFC2*MJr8fP z{lK#!k5jtwr3lKH*TaE1#t;N=0-F9XPBLtef`4<{9BZmjvY-_)6|Po?u$p3J5W=Ag z=|PU2szYd%#E}!KD{`_9g(-$UK>FGJbobP=3e!Z;&FDgDc$nFaq=~i?QSb4QtPeSt z^U=CH)|2CRs%JaZMGT3tu=eqAb=HP!{~$HX45cQym#v}8P`ppv6Qvfog%54Nb=rU& z(esrVlPEPfQJcIO!SlqiReF&A`4=OCwLc#l{#!vxVID5x07Dxr3EKQTH*Z`4+c9e>PFty2Z~{s&TQ40gR;vc?1( zL&m2A5_@CqV40G3TF@t z4O8;566FP@NUGT@UWTNE#}JOkQgsO2I@x~ZWCz`WH!bJb)d=w%iWn!IR&>7ebCvd0 zGj-Q{7HpRl%p@uk9Jv|$Jl2bOc)CSWYX2I^omTi8$-P2osc`*`7OVrz_Hg9e^xo{LhJ!w5J0r!plTfkoq^@<(_vxi>T zXJR~j@`JOwjG>+Y=__W!a`3Pvw(D>ZCdiUDyk2Z;5F!!V>=e0~PzgRVPq_35KYttN zKrZ4EX`EyL(NnC?)tQ=DbOHfJfA_f{vMp8cBOr>q3RH`N<56FBeh`L}?PCPpMNneb zbVheAqVsa@$KN&T%9GX(mu`(a$rjrT_umZ4%N^|ssVj+sQxa@L=;R}2(flkUQl<*| z>zvONrnlMg#Ff8hguKeyjj_4R+j3VvZy6f`h5e;`K+p-TzzoDd;Cx5VgURzse^_1}rkNBgC%K4I>0 zCJv-_mXUPO$U`b}f@WQnBJTEI@=d&StWEnJ)(<_tu$57 zbQl}`3(u#DGuNuTgn>yk81#eG*wNCHDL@cv-`Oec^JB)vMmJG}-#Hch!am%y*#Oc% zuFI@s{3!6!*Z-97>(K9r*ZTsLBB(ks^10euUleBEvPDd#_uwJlptst&fZN_stPM7r z)m(`%@DIXkoz^;B){yg=44}LTD>DRYK;bem+|fAfubT``|7GavDv}ywwGM^L?_?}I zvq&`r)B~h|^-mX+E32Vj>Rd1*#I^^JH~a_#|DnbI+yXt^ z_e{VnEo&x)!@~cxK;1^rzm841nxV07JZIt`9F7U$|G*8Ok+dYbH=HeN|E|bc4NrcR z)*_6$a328uUv?@LSO1-8^|=6m4dA+4pxFr!+x?}p-O>ATv41@S{4$FHp`)?ole%un+rHMU5=E5+$cnMG{geudwf_{Mbntwq-M zI=28-P9A1AYDj_VeR!3I9GUi?DkuCuS2=zBM2+OvWG+6e_RUClEki$cw#NJTpnkVY zX)1d93FE##soSU}%E+#VBWvI(}SzPGk4kkv7&tQ?~;3sA!ecZ z-z=y?#C+StejR>4iI+6e$!Xcg-5u?yUWXS`vBR;a)+j51G4{ z!VfamNEN1+VhQ`S`l==5txBKH;2EK}Lg?{3DV)L-9ub9PCDdDeWZ|@2@C_M5vlyk+ zP#AS2BdgHs&HWc=SuHAMJ8+%fz-60OZvJl7|kug!9ok`-6YL0A&2zGUe&?@x&yq(GH8^WYn|NROj z>z^2I%=W*;a6KiWok+ChWqZeG994Z{s5iTm!!s8(V4xiZK>`?_$c|MI~Q5xrcY*p;U#8;LhVMK4uNi^ zZ%epk^Q7GYVV8NHUCYMvA7n%K|bFls~J@3#Rt}<-zW_$ix8W-XEu7;#gl-Twm1T`7t)H+`y&9S`9@8* zioAmJ$d&BXfJ0Lxg#>1h!h9i9TH+cJT!ONUmrl0%%mOgaM6f5{i!xo!yVQx zOtFVLRJjR$`Kdq=6@qO;xFhIef_=eC{J7&xt5WSQYAUaj4K*ymT(GwCb(Rb%3pVUQ zEP+fuW(6`ySE>Im*Bn3BOn^5Ny@= zqvymFJ=hf@PR)w-Gw62LQUlrXKPOP``Ee1vv`0NBFJy#Ha(oDm!~P>Y87~H%3{+h@ zyKaaT8<(V3&aU)?Kl|gUOo=1+Ofgy}$)PRt?@W6yy10|Rf_o`0?^X3T=-GPu&|9KD z=o&(@IjJL!m+G7_s32!lkC?_m+~RMU;6ET$aoN7 za z&uaWn@B^edk2t8XJ$s&G6PNIeE6MP~-}vLj5mC`^!-|Rc>mPyaFvPCE%6;#1%02wb z$3cQ(GcO2vM!?W#mxFO-DlT$qB9U&SMEe>Na&-s~()D5K-;dOEtlM&5Q%Bw{xGy(Yyd0f=5ybcc+=1rnR()<=iLIGvFJ^5B_%pU_h zndG&Vo+9-rpt!)Lt9AZu5#k5kC`9#2^>Y2-GZt;TO+&-|gH@=xXyh6OsJlY;%A+zrg>64Dq*7i*tq^8t2fO+QsjDWAw*Ka3CpgF z;TcwEZW;xAz6goen$~=zKk^;$EAg6=s}j7!;Rli&{cIjx7M-%E80J*r9xM^*+?ZdA zM1Zb6b{w9Y>q5T%Ss#Q}&_a0*h!ly`2%frH+U(PJ-jqKHLehA3Z!t%$Y1lo_=Un&lM z0@bK}DW}(RX6K}r{za21#OPotsCXV{t!EMy4TYSI1xg`ng$5-Cup1{=HWtwIJDeWC zKcg<2XN3nI#&un%K0;C{1HJre3rPh+yt@&8Xe_K8J1k|N`e24>xUZ$fdJc@w9LKJW ztrd-H3PiWK6%)QKH9-ZD7qJ=wgQjeK(?*DHj8tXxg{UctL88p^j$fkH>(+>`ET{mH zHGmsHJlhDaie7=HPR+&Z=5t1)w}<VBzxsTMf_g5l<%_POlP z=}9N~w7q1A;K<-fvLEVWYF0IJi=?kPCJ+QHaQ8qelA&&5%}td@AG2yTD%uc&#fO9~ zg6C(uX7bT7kB+LWQt(xm3!PK6I=5~WV%btQ4T3K?exzJr$Ec zR!Xne9eMK9ifo#2Zu+$o4@FY9SN)E3PuXX`vCp2%Ltt{R!6&r~VuHA4^9N-9{zaYP zqQhXdGoH^ zi*;K0SpSZ%sn26a6lh=Tq`hw|T!H@N8^J5sK`Jmpv&a7ER(Law_gdsqZW7GRD52x3 z%=qzb^$?285`PGR+Voz4r1z(l=!>|jVWnzBJC#3uxyNrxZW(j?!$nrzuwk)E{5`v! zjR;`mU-Bz&l!!@rUe{`NXlPyKjUn^dyNwUWGtUj5zh&@<*x{mt?;$&09||6ub1{>7Q0Lay4B%2GNw-s@Z;%}mW? z3^vZ}A#na zA+V~`H#dvH$9NElW`2|(zQy!|-AqS^gsHgwJc|M-vrhbS{q6^{liB;H!n#Xk8@VuX zon*;z!zVq7lDE`ldLQvc$6mo1ZsoYHWu#k3dLWa8s{@n0Y4~+E>5sk> z=5VG4v}AT!ErH`wsS?rnh-U5Yx$7vvQPMP(OXhu>dvBS*T7C0gm|-N(NYP>fpBoKQO007St;I zY>w4EbCsBsl4r_QT1@_q{PK`e@tD0VAmmnAqU^0e6neDDp>8?l8gpatHw~(hLIic? z$ZLLDx%bYFpI^su5w7%LY2=rb5M|lSe5_Rl36c>IxxF6Z4h@(~B*2{OcToJ+ysa@^ zmuHtmzpqc8c34u(uC3HF37djahR-|b7-NA+5<`3kZa|h}Js4$1Pt0V3QKgmZk}R;4 z9!;-81E|kiay?b{oG3+xL1{13HLEI0aSNw;m!R?kGSVOU&czsQBnY3nNaAZlC^IjG&h?i zwQQg6TG9Xc1`fnQUDYU9Ctlnm*KqHDpSh($p!J$4`k1Pk_DNH$t~b!G5j$9U?%B-< zrLxo4X%p}!Jgo}=;03_#*+N`}Ode2ke7XN!(PMAl|N3+UbwBrgU2hrB81M4|GQ!ASS$pm^=lrezoQwC5AF0z^V!K2{L`3sI zKq$I_+*9mQr2Li!SX--9mJN4%-<;24ES)f6kP zPv*OdDhSN%V&=_MF{XSLvbdg2p6Qqv5yV?|lT21S-Z@&X$-LnAwqdE+=*yRgEskEv z@lBHIVkTmmsV5x?5~WH;TpnAx43EiV!&-9kD~i!M$1fuYzxxwYsnRc!gL)+exU&tO zMh1~@y)<2-)&mtI)n8`uW>Vfzw;0i%9P=5$szeYvBUuK|;AnREup-kH$$5=;bwnSh zI;|)jeu$GFF30>*|CBWn0nEuwk;_(z;M&t9r+{2^k7vtr+uBNl1Oxc&t~UmdlBhaim}S%h1`^z zsZH0Wy_l-fPm@wVr+)WRS9w<&2(0nDAQeiC}P zd)s$A2^()kvSa)?zPCUAt8m4LV-Rmc_g% zWoDqPEYQS9?7q~a-~lEo9%p);-TXc}b&iS&`qvyWy9+8Vq6O5fFD!fEtCEqDlP@jZ zeI8wa2C8ohI2?;89xY{`q6@{uqk@F-p~U0cm*|tCg%f?gx>cYt9E1++OwO@M=TKi@ z9%cbf)hL(m&>q75kk;d}Y-%A>##Mu1^U3$Xq|Su#XP7+q-IpHye3=nY-+}6G6EeH( zzH3Jq0d{lSSmZ&*H_Wv)3ZGqiGCUn zo*Sv&+5Wi71`8dPX`Lg^=0bZNrIb6F)gitNEooXgGmWN>8xhJR!IOFJ7dyd|7~u$D zW{+{N>gbpW{yR;zT6yjb0sUJzG1&pz784bTgBU~y<4Y0N-j~`F&wqRgpX_HSOFwa_ z_E4eeS5}4<_YpmayrF6#Tk!RXP0Wn+p8>h_a1YK0+?uXsztHk!maA8irBh!a^(x@7 z$;2f;8c*{`!l&K>9=OT*k88ZYgU8L+z20~VS=dTY{AlSiuJY119B|pwY}NnZNux}J zXm+`jur?BMARMqLKWTVI4UFEInK2W0>LBfDbRG_3DE=88_;_b$r&PyG9Q8h{iLavd zuS`>T>8Sd~ahy>er637{Vd92C&CR(3$Qm$d*SSXDv(v*uAmB6yycpaDL-DxDN_!oc zQsCZl?^1Uxf^I+M_=qr7dX^$z?P|8|u`_pPzhx&~!FwQ%+aq=!2yAw<7Hp^Er@6r3 zKnTeEua}LxnNwWwt5dFLXhM4xVU7SllY<|>r)whMvEb8A!U8D_Q?P@pBfM;bBGKig zrwis7SNPs^td_$5S#tbPK(u3jL<zOokuPL7Sj_{9n5jWR2b>NmWAZ%|2Pc2r!D$d5h`jjVs3&`1PfaVow)QNp|~ z0W#j)T$qbioPZp|&!*wda|fr4U^F9P7ldzj94|TBhvJ8zzYQV3y`jh6wcv^_K8Jqk zXbO1T_NY4htIsLtI?aL}@8C^i#A|7>eTL0e zH88>)66;?Q3c@dd@Z}(^)PAb1cnNeZ5wd37{M)z)az;Uz2jOxF2e5)r+%}su`1esd z@+4ht(YSbri9m;Ip-X~=P0zX|ay>RqcIe+P=WqrP9@=8d1Gh+zKw5fmPNk>py_C zmwDx1-zztL0zDeSKhMJyuM%cfl#a1*0v7%K#)mWDezPX$<$haIsiC18wUCX@&d_2! z@KB+WR74)b1<&~G5vHL89E{*moQc#^0AVM(XDqo$*I@V&=v3O091Ecnj$r{G>VXea z;U}qHWhmMKz+0iGxzN+$Z1t`S{;C$+(}s|C8noa1i6k z@Ier+6@)uC>DNUAMmPd;3+XuPJ-7Tt+vwWFBMQEW=ve>-=HODQo7@%3|D#ZGklKrYeN7dCg{5Zb@ zPQUz*2S7YObgVw|FcHYdK+ndZXY2rI^oo&u!D#crcd6jJ+Qut} zkqg&Qb-w`V=eR?Ziv5$HPA3fw*H9qy)1Qg@=#~9!Q)%Y-@irjU{2TfdVB!|#UheUM zPBd^9L$eLui0f6epWZ>U7UdXwHxE+x!Kl1g-<@R?YE+v1Zl|j!+?VwfK&7va9*ixs zV!EB+Rte|lc5hnMpWP4ieEVpeM+ES8p+C>Jn;ClS^$5cPwH|sm*FBW6RGX`bpEAcf zqDPKULCr$ewP}1Sn1>-4hkm#Zx^0H2+#iP`pezh9xC8K6E`doHvhP#1jb^!^Wt<=+ zeds_{7#lNoc-swdr=CABhDo~v*?)9W1Q!#A@J*hthJ7S{3%O19t2puhedc_jxy5L_`&-N1GPIH?tGPG{)vmEhKV({ZRe;~Nu9O79X5FZKkeL40vJl_GYMOA9n zq^UIN#PwFvTlVAZPG?CQ=EhFu!=uV%>$m;zPEQwvXD9F$;vsyQAO*bP%S-DwQC!LKj+dKGe@bL;h zt%1|hPs+7fj_r1HnN(Qfz?$#AKgl++t<1~)XKlz61!(s{o4Cb*j>Flxt(?7GbZyKU z_c@VhPLv^)hPT}<6bm)=E$D{pnb`3hzxCIvkyo?VI;ltS$;2rTPU)N|l`fMT=E>{o zgMrEIuKm)|l5J+6UPT+bs6xY3#4EcgVq_pA+5@(ZZRIlJ~CDJ%nV)3Q;QMfeCM|F}k6WN#3 z91i87(N?mNjQ-%(1BcU_5}2?Z)|=*m_3V;kU( zb5RcKt5SJ__e1XBe6Z&shv~C-*|6;hE_Aos6|3?OCY&7qt5&6LLe9G)O{Jy*{b?%O!K6*tiY3~vIHhL z!}l7M21QA@%msmhI4sJG*mr^MS2apRq|aH*qXZ>!_6@#l_T2iY4q?fEJS?s{px2hO z3}DFu$#vdr?PB;@EkTy)Osx;P$OtQwlbVR=;;-Ziia>HKNi`e}ezfsMg(sP|Ot4(l zetc5a7w2J)+MTY@%864q(+c!lCeCa(Bx4HZ__H*3fa!#zo;$OCLxHkEONC z?)}=dXBTp!{hUh5ifVZ$^zzaB8Gu~pn*32cVe*bHbm{DCc1JykgstEe3=EHnO%lh~ znq#a%m=43hm7z03Mm#nqHgFVqwoE{eZ$rka7ztQ80t%T_9Ed&g=CcIvCl4issUT?U z^>8SMh^u@rI--UnoQyX>XW3AjJ|N5Cg7k1|ajEuioW)2#Galqbyuc+wPZBLda(HpQ zX8s~An?!O)rjkc8HD@8DHj*#e1ViP+@fI$@Lp*;VGmHsxVJ#WTWVhJi5{L*BTN0A zF!@Sus6VATA#O+QG?o3rCKtTvwZ)~Rr5mVD0^sZ(+;gJs5Ht&v0sl0}V8X80>Zxar zZDpQ}0Cb??-OQEdJFgBN!Veziuy`0Rtw8ZBTT|^gU!wL35Ixe`+~K zr?#ey=A6l{If_}(OO_YhD&inM9PZ=%iV`A;Xkq)~(Q^l8dgxLfHg_jrO|HHc;Xs^S z^6@znfGi5kw{m$%&z;eJ9xg@2OFt#kd5lvNPnD5(vz`v1hHczzN{vKaiMSb(wl1}Z zam6FxFI?c?(QGu%n^G+4jk3a%ic%c-h_brEeN*J2>EqDpg(T~TC?31x{aQP^xilAE zpOk&Y9F^lI+#UfvG#xh_4(gwmd=OSUW-pv~rWi?AV6kx#Nsis#N<#&GkczFc;Ia#% zqh}X%Z!EIqAaQ=K#u*n9bSg-3O)MLrNcEV1IJm)Z`&p;!K?y_7BRB+Lm%_TmoCM$2 zZ+Aki@B<|^ld*EIJFTn65b; z9}^}+%*>z*&;Y_p^ZMBa;qWs->D0jdWB_`c8k*~uwkVgLO5u?%c{6nt#Tpy^kS}y- zXlORlyTq*N<#AT75M)1vv)0;~3GQpEv*&!4Baw&Ee(mw&8qV3s`m6CnR!4+h=K?vL zGfqBsd7GTI*M)Wlx?bT$m4fx7mmL0yMc+H*Y!s!h8rV($3FO%1g0d>rYoTIEvf#fv9@-pQHwU zg1deRp;kT8fO-a(4{;Gskvo~N4Vr~{fv?|GL9^#O&-pJsYD>~SNaMTHMGj=P=r?YI zNq2W$>a5f(w{jU5lGqI|9B16w zklI;K_WIiW%cwV>i_sY+*ob`~%4ZMVUV#jTTUSo*)U+caZD&$nUKfPuu_&21nx(wp zcYX9(09e)2D28Ww7=%iCIGJ4OsCNW%6P?PaJx1*@zmf}C&z%6)>Tg7Y-;8D82xkCp z+*5#4M><<}R=(5qWipnu0ONr(`vxFjzVzE!xi3mnO_8sIfbEOhtv}OLXNC`B4NFW9 zZo?Dt7ZGr}PFJt&*=hc*e*37FYWQ_Wr4+-L^?Y}(y9o$FD!N_Y-V8uKvXSQhYhu68 zP9l(7zf)hSbh2_q!Z$V70A1!z_I-WAP}PW0vhoYQGP|ALQPZ8IUMVK3;B-+n%-^ zN^Cy3rZ<7(D$~OqLBfBf`vy(C4Qu#jQ7;n~0IW7llV&oo?Xsn8aJ?5;<-6a2*J9H&rm+~aOb@3)W!v$Webb#NYm zq04+ADfbz1fz5u8fg#E=#(_q~ZpEw(%@k-VY&bsWi*L3f!u%X z{{wOshb92bb&>PO**#4P!S1qDY{qd_jyfOJ&>7`H5m`uAF3bqCK4kAwqYSiPnN6^b(7H3#ww|5+x{1~VAhx}~)q#g-Z!Gpb zodL=NUwZV@cVA`%2ccM3LUFQwmuYOA-VGcK#Smy#6kuyi9emviQT0H8#%k0w8bRtw@ zj_WkP0Ty#=zKQ*Pq+)YypP^q91|9J0iW;W$+h=S_jfgaw=_rg62CQPm0a2Fn)|^w^)V)fbELw5BX8u?E(mp)sg^Pw zp@vRH_PRf07-_g#E#FTDyDiBAou`1b;PjJIOVk2BjYc|~ug56nr)6VkP^jd!-C~uT z#NMJ;`pm!tbye>8maS*^{&;c!Jk@gj@w`}Jyo*1Ga348TC)4SAuXs95_TzYui9&XP zAY>#)KG90{@O{KJI=ZB5Dr;Qu7n6-)VH^Phi27JHwRUq{wLh*Xz*~-YppLUvwJa!U zH1b4V%$-trb2#c3$7z`B?TS$kCb*Cqm$%mzhz`Li{j)~YBm8%_o?)P;V)Izs`9iSy zGZ7uw`UBkN6^!3qAQmGV^mx?}4ags%JPx$^gq~PjxIqGHwN9RyQ5fR!YfeyC#7kS@ zd~@YwZv5gxldnRvKj_<}5FONb+`-mE{P#4lW8{s%eS+b>I{)BmuASbp!_J)972j|k zn3hGf@_)ZW=P%VR=lGsQ!mMTesQ4sD^E+*MnD0Qv3Q&1)iiKfkRseyi`Jqt@D-m=x z46+Jn9^>uxs1@3QcA_hH_o4f3AglOdD_W93xnd`OLkNyi7a!LWu9X(Wi3e$=RZ) zTBk`fEoQwgj!r4CJ@y0_6`$xhJ9*0#=}_%hdHD3;Fs39#0Z{#Ikv3ors)rJ2q6w4Y zOv-^eEdx(Sp3IIXR1cihfvD*9_xGeP$_ac$ln$g&7$6E+%>e~Gra}>lpGhl$oF3%M zS2;g|rIPZa^nqPrOnwp{2<4qPFihofxg9t6$u~Gke$z50Db?^Amro2~b6-cB#Tg9#|$~uQm|C<6epzn3N7>6;Z7BVvdsA?Qed@VuD=YjJ%^L71~b-n(vZr&nNm0Gae zsrBK@Y+&DbL+vjK64Ms}f5k2OGtf9pO#0rIjqABMkIY`rKE!H>oezcjGZgGGj%B>W zgbQC{rqeXi-B_6GD=2EA3|u9tyk!Ob>#E#;y9#6oV1QYj*D%%DhM|P{qZbeZ9K!w$ zTP9^0B_LY7)}$p_&*u1C6$MO)L;eqHtz@jP$zs(HCAJO=7r}eJdF|$6z&^;Xx~!;A z@^H9dCaVi5V6@!+J09zyy^V1~t%OI+gxk4cM&HiI{f}aZUIhHp>6!VuM(iZUuoJqx z|2Pb8L^f41@Y3>myybefLEYVGUEqda^ZsE3O?0e)&#Af(V}beq+t41&godaPTmdFF zfNOo%Fj5(i!No)irL_X9guU~m&SmgN|3qg<_FlI$AXY}TO8oLiXCeR@lJb051 z2?Z0t%sR%)pVKFcR2m>*1+im0eASPEIFa&4R>xJ%#hRfBmaQ;hJc=xf+;hKiStrEn zBncQbw(jV>u9tfEU!yiG5<k!n>-}aGgxj&W@)Ny80iBuj-d$_d;AT3Q76zj8aC!{gf@r!P2x=J zycnRibUuc$pIOK9Fz~&2a1SB&!GKrMkwi^GxwS-JX}9e0>&EAlN!(HQHfy0P=4T16 z*g;pk2olxzBeOa#iF#r%fs~nm?h2Su&%&0)a_4^axh7&Wp(?+Z?jxDo=NVF!bMEf! zBw@P0u{$hZEAM88{#tpCmRzFKDxFnO+^a4WdQqvRiDqwt{$3vyX)wUSkK>U;gxK(0 zo(Z+9u1b9=1fS6%o_(#2cjxm^-!#YMxW2pa_{W#~wI}rEZ@4rbbhGb205Q->M?k+` zR+a7_3|~5&uejyt8qpHR>qc*o@U7B_lCKk1%B9^)Iiw@$!shv^6Z&ea?|0#N13?G5 zA}!Z0ty!$@c;TH@qWA&q= zp-N+1KNQyf9tCK(XF%5(4(95Q=zlHd1#S#6ViRM)Dp<`XvAQTtoHRw^N)*=U?KK4!>^z|qo6cJVg^*l+&=Eo+ zL}O#Srlmt1ujM3GNXY>rq0s&Isar#Tk6(!LYtm)XRFeQRg&rR@bbr^|j}?YE#^u5y zjCvFF!=n06Mv2O&^fTlYguQ9FIv(o*#i!!OOYu{LaROH7t+@*N2AT1}GcwJNT@G;V z^Ra3hxt>#jR4lKOYyF2K?kBebU4=1*^d=t3E#zWRql7gm-h95hM;^~Jwip2dNnFj= z4{*3|J$DOFZ)M2^b4%*-AqA^5Jt6uQsf-SbQ9CA{DN+6W*^Nz)Hzo2ymFay87D^7a zc+8`(wvzApiHZHp`UGd%MTPObBp(-ids*il98PYm@^|hW;&~!K_Q^Me@e+hYBozS* z#eoR|M}A{mgzwd%pxBN{dMy* zmwjYyz=k(u2}XEmzW*B9XGT~8?pz_ttN`O|y|k^tVt6eA>9QI|m$ej*$~#W7;yR@~ z<&AvC>yNwqF5Khx{XtB)nWi9q{t=(1e{us&0Vci?h?LxKS}gVDWvNG!H9p3m+d>4k z^@n_XZgwT$8``7jCZrn(y{~J>55M{ccf!N)86RzBH@Iv z*KJ8`hGzdXlN@=ubB8vlooFOUfa|)FrZn|_5dEEQB=nJxgFr_zB3`wVE@tnO0BOe^ z{BGpuCIf+YvN`8r`|iAC!Pf@lY(8bOC;25s4{U*Sww(x`zqY?F$q<+2zBJ+pEwESJ z$@f0=EL~Qd=Kv2VwqLTlZ}dy~DAgRFjw6Kou#qocUiA6u`)fLcs5aNL%s+ifl~I2p zEzBVJxHGV^BT}`t>*7m^*(Cu6ESMlcfN4;F%64!$4W_5YUmV(Kue6t>HJ%Z@guPfb3p+d0-wOwdleuH5#qXJFG|(gsbxdFQ$NJ6PFo_ne z-U?5(+eZooCTwp*0mpT$i%>**XM7IjZaRv9l6cC9K;$CKLP>1PBA#1tV|558-g^Y9 z+J|-8zED@x!7o_NBR`b;^N{gI&iIQT%L;aB5xQ?mXSjoxB~%r3Y}g{wP6Z@$*Tm zCiLp!(6BkS<_o!g1?rt9G;BR{XJf7_1OcuOc{bdYbu3Uzzll=Rgl6+IXBkJTDbnC6 z+W}XEdLJ8wTKUs+2}g+1DOz}K@KLkhjcXg1dS3kCY*>V1Q{m|ZbJMU3`yuih=BFY} zIOi-O8QqN{CSu07Ck&37gB(>9U#fTZ(6p&z6fo zLX3u?DEo-qj@K@n0ol2pA<*x!=0;-Ux5R46$s*cDuVlzO(A~2QlY-v_+8Mx-@2cy8 zM5d);;i&(B8Jd5_?D6Ww_|uCV)r&CtEM`vGEbrnklv>KbHh{!|%JAR8;t?7S;0+>G zuGAb3Ib0t-Z#HT4LE}#L?T0e%1fCuMJJ(nc7;>;W+#W`!JxoEz^>@k3duOf*C?}sc z2F=#L-bW$Ws2=zrY7R~WcVUo%QBHZo#ZJts~mci#WR zm&S~Bp_sWES`k@85kZmyOf5aBrl|4m$EkC0iT(pm=UqCR|3|uXa6cp9{#*N9=2rkx zj?PW-f@_oAa2VgIKEPxQ`R;NmyNlRMHXZ`V+ z1Hh6(WL7GXD`ePI1V*4D?{Ouv&8{Fku4MAk{%fIdx^0S6jZ^K4_7jim_VKaJno*v! z+s5ej=RrJ$>ci3Q&jArL_b;6HS`rz z9O1@pV5ZCMWmmJjrDY?``#I!keL#=dX35i0?5c*RBB2cdm9=c|q}2Nk`q?}A93C?D zE#PVzFMnuTpoCxIV#`|N&-EbNFDj0?4?oZWT~V7Ht3P?3P>5oDLU58DULc65VNVUEgJ#@B1)YmR|z(tH*D`nIzYeWtQ#%aL~Fh#Sunqk#zP5q}uBbx&2d$ zTUB58=k=spVLF@l8`_au;4!_*IZLT6G79GhS?WYjiLa=OhZO5w1n#+^L~TASLUpIV z1AbINMxx|U?#3G!dpkn&6Llwnt1*@Q_ItA2-*1D{&nQ6Y;zz%G!uTuE4VS(wuDt)^ zFy;@YE8$PuSbe^{9AN*d)*w0WQ-H9ffZ6@FbPi>6Nf-Zgb!KP}q2!i&^aE+n*ZF-s3xnL0>TF zw^nvJ-QxqA3Hqn*Z(MtehzyzJTP-XwUwp@wAS)={+=bx;wM4*A%PO`Gnr<==Ojyau^*Vg zPan*r(tWJtN|KGN{K)a9qO4lYLMBfB{!#nqzMS+|I7Mu0JTJTP#8?!5LJ-xFCF0P$ zx$QhHufE83@iFcfn~c!+swq=zfu1%!cVT=B<_no{7d1L`^$GG7zllhA#NE23p7ri~ z9!7reJ)EyH>2RK< zcj|$(;6+A89T%U^Q>qfdEoANN=rTK$ACnyY-H!`_<;S**=Kjw~f;l>4?dUTey^^PC z=KLjOGi&4a$PiaV|BPht`6)eY=Gw#3IMAn-3SQSM_P^bFo{_t>NWL+!8KmyiS%0V7 zZ>I7Farma5QC0s}^bN%8rDZbs_`*u=(ahtpm>WDAjJ({|o@$3KH}(f_Udvq^n*G`P zAXArY;tqaq*Q48yHt|a3k3KH;==Z3+gY`iE!D`z~O(95GEqP3xyA!OCZsEK8F$VcM z756^mj;%kALnwkB+`;Vdb*acsLQQ`_|F|pWUb`6kXAPp!=l=QEt#tU8eXA&Y55)WR zO{(T17U7Te{$KPTB_S)4kM(4nq3Av0y%TRLVy^w0eoC zM&|DbxFT*Dt1dD~!7e9uEoHL7YiZ9Y!?8c}iIpOYiPF9O10F5|EnwHVehpIF4!hk8 zQs@R{ATEq~5jdWS)wxylP&VUfJc}8- z)}rFeC3g4n7Yl3i^}uAzxLJZSH>D9%%xsv2R|}>1W1LHWWcufs8!&N+4>ycRx}IY{ zz-A6-DGs-dS5~PS80N4Ov!g0vdZC*G@(AN^%C9k~_o4SD^awFDKU=s`Kj}f~tev zv&<@MUq5(h*3(EnyXmAUtNgfOK>43;$*btl$g00Pgj?)8yhA9)bniJixqb#PW&e4} zBABgpsBxmXO@694G;~Tq?vJ8axF|t8_z%@67~q!qd&jCnFZYkxz06qzbG5p1uyV1^ zq!^CQjs7j;XhlE|c|3xG0eL8B!RXNEoR{D`|JIxhJQ_gV3IzBIP`AvR{0{ei_XG6@ zK$`yV&Do$keh0e?cl8chLnr`s3l88Lpl(vWcA&9b{;=Qs*Q{*s3;;0b%`2=?N83fX)0{Mcv5H zSX}+5#kp+S1J~eQe7V15CO_m{Hg z=|B;t)nu1oBXI=B%B4P4eqT3a#2uJ2WbknAf{-~DMzJlq>uN5y!RgUl`v_z03H`{iKoQ95Bq{<79(Zb2 z0kB*^ngPO5B!!Mc(|Bnqf^a$-&_vkge1aPoYly$%lHL28W9>HimGZS)+7pqpPy3@T z(%ce-^fS5O>GH1_22nBQxWVl`-M#FN4YjGfe(?mY#&jk9ilmBF_+D=%@1|B-?&tDj zDxc9Nt7;Lf5EgOy=7*d&JT+L#0~r>rEAmOL%{M(vvJyU)aZw zW4%gGc9gSP+A;Cj!OpG!{qAMPoWyhs+^sOmR=bV67Eb!;V+EYyOjbtW_!79|0uvm0 zm*jm{y`@DnK17@5=j*pZkDj#)9DSKx*g4|ZGIzeP!Fh!dXA#g&>91hHY!SFD%}PXa zN&i81^d*0ToBB+CFs>0J4`)hDW~Y1TgnR*uFr5q&!GCv7I( zl_aTB{+$sPFJ4{T8;R?cf(5JZ5;_%MjIC?>zz+$U6@8M*A9-fxdL~RhsO+`BYnTL> zvZIhB%-WawI8mdcO_`Pn8vd+EAx)$mu*{FgWwAmm5-LQZJL%~uoAMZ9gbgfGGU9TO zc;9uKk^qVR=)2`Ay;)2I;u8K0)rm;@IG?Xy!=4UBneF|~?r4^bN6eb)-=NwT+dQUOxGz zX(E@6QpXlCBn+XdF}M7&w!+Vtk;O%H%O*#1Lg;F+l|jBeY2S?yO}Zg2$tS<=R7w00)o;I!b~m@%li3SfOnfs+!vCP zhQ-AnfsQHbF8q};1KMYg(K1A*7lR`cZA$8TEQRshA0nUBg$Lzf?qB4&(Mfic6;(BL zDm9#zU}p7JLT2tWT1W~-q4DOHb;99B^!G(?UU1-7kT4`OXjb@Mgfss3@Q(NQ%OF|wQ!CS54k^NV}_mD^hCsS-=nT3 zEY3{Qem8hubA2X^w{s{5d2K%J8(kj8*8Fq1prZqLX||7A^f%5bXm1?zoZiARz^1HE z)bi7CsX_s1Z76H2u>1F<=Ycd&5)E)~!}jYLIk?x=Cf1*YzvN}-&yN0!8SOKEu*+ZI zM;@@Y>)aDfuH&a_`Tn)*&T3Yp{nJcej7P1f`x9R#;<6u%cY8uaWtqv2L~m5TQ?vde zFWmI5AyySILDlC)`-@l8B{)VLN|fF%X~Nd8B8T+8VB}M>M0uQ(HF9@{SGzLpCDcAY zq)7zmQNdp@pQNwWyp=?e)cEo{_JTJ3Ar*FENQJt{kr}C|vo$BBP1;iIi*HVy;Ev7b zprw!)3t4jA0d1Z2p5r>Fp8cq0PD%PDTNh+nI+nyv$ViWy zEYpjV^vxpXh2ELhEy65@=C$pDD`Q0U5fMOi*olvhG?H&CoMds%>%f%rKysGX?mB7V zdQCI;7A}GoZoz?KT7oCx^WX;ESDSrVOv_I1CpA))1vte=c*8UANDx;$+Z8hop`(yD z(`j=NbBfR>?iN;qeTuPt4j$e{JiM|;!qIP`FJs&Q8JiT^Zeg zp!9k}BAi#&9ZS~R7CyE^iwsilbxCwBN&_-`PNfV}&8^kjoslT3w{ID#GFl>mEX02A^(ExU(?`%?hDihgb^O@+BEr$^VzeRYDNi)4*2-`NBh-kmFC zKe_ZC)6v-%hN1C0h-aH4@=^8bDWnbEPN!dj6$f|s)_mO1z3N=1=J~3F`}+SRQA|_= z9sF`9SSbSB8$zhZ)Y<~Sz8LAP1hbuD~x<;$A} zZBh8ZmmMyIam8~@*k!V?_8dWoB$PB=vSTZ1S@I(EOIf8}T^^=amqQW#6Ft9WN#g&4 z+_(tLn-ap&vWP-evM%&bz|Luv1B66GZElm5abdEkh%Xa0Tvus`knBJ484uulF@AMAeaJJefdY74~n1 z)qvV=^SirX{Z3>6TOM&l&|9Fyt_xr0&Q$7CdC-4e|BCzPU=x0^Zsech_R#Kn(hW=JYYQ=agcjP}oN z29vi)z|>BzylRCak0jo%1gw2(@X%wz_(qTHOLe>VQsLy}o^ot?TFumQTe69*WQ(rZuf(Mmd^t+dI*m3esY1_`=N#xD`Jg_jf>(?J7%ucoyyjp#;*&>!;j{5;R;{zX9 zu5~I!EyARXNx%Hz+(yIE%lsLqo)lysPKr_pEmyNfH~8quieZ=S-UOWtJ6mUDWPA{e zadq)RW~8?7G0iOi)aSr1Rj4eL5eNHS&8_q;OEWMpQC3d{mYrVAD z=>pGjUnirDMD_$pQuh;?5R0oNwVm0G^iC$vn%@*-YBezNnQ0wm?hlsniyrj|8GDq- z9r%rzFj<$}?~)$GW`U|Owf+7c45*jEkeWBWycGLdsS9EONr329A!ubA>8_zpe6gn| zmj19@7l~aR6RnLV>9SU5l!~`Cyr@GRSvsZnDoG2oOS=>HyF*c^ec*z8h$mk8Z?3ge zhISk@9cYQag-$1-Oq>jRCgXWB!pDMn>woc_+W^{i>8djp=OdV@@7vUGyNd^UZ zSbO71i%8xv)KN#(yi%mT+lSKAuSYQ8IfOibSIat%8K4mFzCe7 zRBPqnt5Bl9ysrLy(p7>_t z^VV9{M0lwHe`ei^j}X3fwNJX@Ez!m6Y^s2|Yd1z4zUd~cRZR~lm}CPFm;QIuSuN89Yw+mJ=V<@7SM2Ylt|92ti`DP-{b1!MW4bt! z-Ytv|Zc8U!ZM5w|(OfxXCMJ^{ny`#UHjcvo(C{xv!(~_ml`EIVIS}V zP3mN-oCG=qAIl<;n{{&Ctl!f-eIaAE8Jm8EW3VLiq@p2^))hMOAxZ=y-eMw{8$wJ? z&79#FqtgkR!FJCb`KajZKYB|+S%4_g6#kY5snO@Vo-*?DVH#V%@(1Y*8byl~kVT!+ zCUPnfkC=rK&|tKB^9BV}>2$g=o*DYxgV<~B)ooU#a+Y@Xss+=@Cvx2#pFD_dzx;T} zEs1>)L-^jXy_dt}!d*b8YRP|Ob}1^sT!KVC0)*gJOkZ?o3s7)?^wjsxrqTstK{(1l2LTzq@Zi|E%pripZ2@c%*j4`g*^|l9%lcm9O1!%baL7 z!p=wQhWasP-!LQWxUjVK-ucxQ20+Ffu>3C>lk-Xk zHYmBMNuRNNC3;V>Pw%_3;F@+^{&$BaX1))vV!+ocDwqjnp*~~X-y5^T-+0t7s$UC57TZXK`Zef(1Ci@&$7e^tn&|eXk z?|IF)Z#T!|P?t3;ar?3iG`ERhae4u9_fbB#({1m$tEh<^;zsPB-m1wtsLB}x$`DPx zvV*UDUy90fTwYR-^odT)r9FD2QiMEq78G~9UO^hkP>?~h+fzTr;Y*$3by zoIj^a`p30s9V#>Ypp*2Mdh|7BXl*N@8-lAG23pw@^J_hh*5)cc4?U=OpKw|aUSOp) z=6+S)7t+9KBPbn^+hHPSbDOgM(Q1mdajzcE%ltJ_f8!%w$U2MH?=t4@vH^}CW+=5F zGO@j5w_KTk5>BxkzV3xpKcen)x-2B7KVc|HVxKnJ5?}_iZtGv+#If;=6FQMy$fGNj8voV%@IcP4{Y49Z`i+kaopfeq-L3--PCT=tBo8+%S8awq zDQ(K}=Y1{hBoSM|@Z)+Fa1}D{M&%s0E0s5Y4U1H-zR=#HAEq8`^}#$pV}${cT*_8a zqU*a*!nrp8F#;@f7)AncBdJ-WS>B?nWyn!JB@<6sf96&#>Xb$12&(UQg}CZXTf@Qd zCAZlR7bmCd`Nv%y!b5YmonlyG%p&*y)xGKS1c11|{vX2X&hITca#8xP5A|@9*zZ-Ha)KE|`KY2B{v;fi_mr6QX=g-fOG&@7X>--Gj5Y zzmI~%W@4R;YFdnnYtYa>S<(H$PBs4;`%=( z^xx6?vhn8t=fDmz$kg=-sLwYg4GP)D)5l26M;^5lXFFQ6p;XBU*Y!-{P3Y@C=%ALj;{`gFE7 z^pHkpdgq5*S|v>b;D1_UM^=*t-}J0bc`z=WSQ*~bIv792*NWmUP{)KMHc-hgg**U| zi@j^5zQ@wIH~9t)Y9zWV*tD;N9~eSMNN#w;|2u?Nx@u7Y2w)^4Cd1(_D^^!U#l&bd z$u}U!K!hDK5q>h~5;zuTa3y{yh$BxaEJ3k8PyY3*PzNdQoz6WMLJr#__@@3F>eo=` z8hFEABS*Q*5NI>McqLTiebCR_!n2Uu8Q@oMukKVWU|G_Ar(nYOhdg+B8z@M?M-4mo z4QOH=km&};5tW_adDUBc1$}KcNpbBAk}T&Il!^T>zUKkKPV*n%la+wK52^fMMD7B6 zH4x$UGZL4``Q%r+G zV$@?*@MjmvFdNL&_?nd_r?xu!S@uOEo~{$=92$4P@??LJRQOlQUB~4^3#0FyQI@ph z(`g}a>vibC(O2;DUBq|>e`;2wX_GXRyXPtC4*xp+WO*u5TlcW@7NU$K@w!E`_maG; z!0PYAE%&>X5tF~NK0fb-KikH_hqq)#fBw2B-Tv9v5CK~MXQ#c#*LWvy(L4{I%+A2c zguNTAdQ@UB!0h~xunmi^fp`XoxiJyZT}jG{+Rcggdu0L_*@T}(Ek_e=i$s`RY$FYw z!Y)K97>2grc8YjuIf&R=ECo4WCuu&UB$pw$W(jE=VJn4ER6fMi&k|8FYYPGX z&$5BlNOGnjA*uN`g&`udwM#h=r^uyEiABqM9^S}aK$hA*ONm}Y{D_Wf z``54awc{P8PVC%|T>4|Fs{J?;f?3gqj($sLF8it0&qtn1b)C>q9$FJeX76m8xlU}t zE+{zbA}TQ5$&sujzN8jR@s!&e@`z8ePIUoi;c?_Ix4jeCj;R3=3{1mdTI6?I4vdJ z6oIk``wq2t1ZY=50t9~!;}YNT*-vb-qz$%ralploSQ?1@`zpHGLnITy3iBv4&l7#R zmN!?WF45A~PsHeS@;PvpEP4=fo35k^YQ0v{yOU^m(0@g|t!;nZe<~DeNg-}(i6k0V z;tq|n!jnpf$&5anTBPTq+nW9R`ZGLz#-)!EM|SyxqZJt8As+UBDAuf9m|mw zU>Fz8;67zI@5}ngXD8y}mg6bfv(>TfXDz<>h z)am=~lW~&?9#!!Dg5=-El0XG(pu)q=1Q3Xs_Ka~4g$|KWb~bG3z$l$!a6X1-VvE9RXCMd6e>cyTP?-dDzv5h z)w>!Tb*UwfQR%8e$}U{r2C2kNz>bjGa6?qEzCt5+9#ogODl~!gIMomAH+(C=Y?62t(how`hQG)A22}> zuO6yEV~2#AcyRn^*GlHM8+(;;VU#e2qrs1rfyE(AWZhIDPD*wxtKX~-y%HIY-&i- z(VRf&zHa1#lbS7dITlA~LXmd~0ei3Ngn-ZdHUS|mD|umMDtqL;A7Jrmbr$vfbq@z>sl?FOm#BC4ORgm4E48Jm$rCPzjgCx?X_gd$nF zHTQvBg5oUFqCceREgGF|cs;|BO_E{$KH<=Xgb~$2$SxCW%)LjANUxM% z)a#?mFJiI5QI|`hn8_Gkv|EiFh@u2F=8voO@W|?ohqE2i=%pR8=`en^c(>ldpiyeB ztm#+8f;*XEd9Fk;*Rq9=3lkd(B6W@*|AzcVn!Y0#M#nQdUcPl-o!NcK?ZkR$V zq(Fyjsr)~hl(xwUREH>uXw$^4TNSk=BJ*X!2s}@^^U;)`IfK`a^d~RSvBCKE?MXMe zF8^)TnY6|R>DFqSEx#A>^GCUb5SK(w5uQcxsW5+da50=u%Nc6?ndTQjgew{mY&*_A&c^X2i<< zTK=Enk^%n1oINf}zr(Hes^W-Z2U;)MBQ-02#DFYzjZ`$BCk zM82y*{4g2v3U>x5(>d&yOw4Omtv*s^xY_4f< zA^D93r$v}Dy;knyzdFAy7kg3Az%>*OZ*5GC{zt(%p5MT&>oJwCI^`%r2nf!^S;A#T zIN@46Awk*agMlP+n3p3eQrb?hKc!e%bhsU1R%vQN2A5->Lik5LZy5@N&h>b{T_e3NeNeRZI$pCk`EU1d3KNR*5NvJNkINC zemi9dqgB+|RYPy28LOwf&P z-c*T(DJ%ksB^~LT$_++QF(g)aNMW595WF-0KJIq15IA11%}A~ZjqG?g4uF?xf56Lt zI0UB}j3>Ezgjwd|9GEm%f5f`ow>HYmA$q!b7;AaqpzkavnKD=SiI;AS*LFC_3salb zE*MK~J|Sn0amSE`T{X(&U6?TFmuWYqDf?io1U>VJYp_%!LGJ-ci)=T13^MMyBo>_r z{#@KI4n6z&_(gLLq=)A)_ zLpylqCgiM#67j!Lx0$kf;>=jAySVzwUnu}=rVxwDRdvdw3VO~iID|-9%EURAyrF?* zct%Bq?|B5V@v{^_u%;y^yR?WSz$>3ZIs+fB7<+Z^Rjy&%D+-d-+~ChkdAan(9DxpS_#W3VHAGwBl&fflG zY{FE%1yvY6WUWnyVI8ET0;Kg`v-JdF>1-u4I1EG)OjcqkCrF^(D<=JzU!@;q4jS|s=lj3iEF1Mt zxP2KSi}lgZ08_=+vd<~YrS6-6y0JN87D6u-%2feHCF4JFQ>Fkx#Z7M%(%0&`I6WM|3eYj2x!Y?}T%2Jo8L{^1z zjifFuy*t<4;Io1hgUyDiR1Ij&gr*<#F>T3z714UC3{%n16w7kbV*5G42y;?)iK&?A zs&Oj|5d9IkBj-FaiX<~iQt9C@Gp!~|9;201AYtxz4F31dg?hgu#(OpN(!*vyttn)r=isgRU-x<p|12 zt$>LmwM?aAr|_D@2&g3K^A{4I6wXIhF>}8&PNaRn#z9lL;!( z6QS>gF**%ad%OU~F^`wN_CcmiqEg+KN*MOwL_BvXl$(=>iEX_H^c3oU*15Os|DEXX zmVt6dvmO5HRK^&5wuE*8j}hb$*G-%vKq8(*5#zP4xq=|DT4!@>*18Y)sdEDB_-+sL zH`I=KmM}$fEn$))!jGAxzz>sY6gx5}w^t^%weeHYy&u~d81y~5q^N?z+HEZmV7{6w zLX6$`G^B;3;~;zmewR2)oeT*yluc)f2iFn3CV6 z3aDO$?}iI0)2WF#+o&SEa~W(PO%wV>UG#+NBs4t2Qd!Uq;4M};)ac&RHB!JQbH3vhNzRg#BS@VxoTroUnn(&2A<*iUjM2)T zM8W?-aGOYI_qyQz%~-$oT4pRkkZwr#$^IZE=F){XH8M}gtl6p*;8jpL3HX=LJ&9S6w4bDlX?XaSY;9X~pajg&B`ttDh}y=GFzap&mmzLC7`5 z{_wC&ml5m1c~$edVKPpJs}!WK0a0v_Qn6NqpR(neZ<=Hovy5%!Q$VAT4tUGc;5RCT zLj5Mmw(qF^04fH8eV*-p z^5@N?ey{bvIL1gkh59gf++FAhFSkip#%3Z!1EUoP*0;}$zok70JbIfX_0CXUwJ)Wp zcXYx1Ab*1~0qt7+T@~ag1Ab}nmLfR4O)r2j*lGCYdaGiUMi-0Goa&2b=)5jwGZ#&i z`+F0i+#7!9>zMpQl2Y;)wCEe9{c9#~t!QmHd1)m>35SoLT-b1mnw%T$Yi6anR-qmW zlB>GBN=bR^hJS%EFkuPx^>$Ac1eODuNNMnAfQHDSnHSt+sAT<)>v0IJ{7F z#Wb?eL1n$K&EMoNjjt#sm6?m`+R#@SL+K z!RqJCaPEIh+h4QCXaPt9N$kpd(k;fFc!3Md&R@w|TlfUw0OwX#deTw1^V(aU;w0E% z{!+Hm!JPf%Qw?N!AIiJX%lhak`%b?IxjN3$>H_H-6Mf9g!hZZz%M`*Byy+A#`3G55 z{~pueduT)cHqN{EpLtrFf7s1nkbslAOL%#jfanW`bPN&auVvP(kJ~u+BL=Gw`E4Muk=~oMAPvkc*!(?e=2uxRo-|Y3mM=Zw81TW**xfMNaasJp~`lKXoIZ=gzjs zaWCR`T>5x+=(+b*Y}H2>3%dIh?w0}KzU~XP>F;EyS^&R#(YCX?KvMM=zj`}+I(hP8 z@IS@-DbI|WmRi2{3CGdgh-a7H>6K(|YxpGB5kKazh<=v(i2Ht<+%yJ66iYQNg8qgd z<}$FA443*Z6SrFL$;8bRb)j}l0Cd97s=RN#P6*-}_t$29Q1y8o_j!AH+qo8sg}MHTPvbfC8&6@nS2MvBQesyPBt=VX zJ-tWwKJT-)$lbc`mH@C#Hd!mYhGEva=!n~ZI&{LComxw~E>A8W?@3sQNdd$3E-fmVOR>u6c#CX+4Hb~xd`AZ(9TC?l9X zY2*p*d%eeP^=)wpje8@}BnJ=B%0tz!eq!*WOyP5R%2qj~nHwyblA9VDCU7_(P-I!5 zcy!wS@)(l9c2=LcNyHwmEjgMpQRo~u0t5Te42KdDiro-VVUK-RFYKR{1+)aBg0b|i zMsA!Gv}H=DWgx38n4!*@JSm&;Gp@wxZachU(^25A=hjf>f?w=rpY-@F5EUfO+44mr zH?z1!+wL07tWa*WBnc+W+Yn{H6^qZO3_2QhS(FdZEL*mrkKAWwN%yN=z<$}0X%yl| zamp-%;mD5v$b|vxacL$UnVo~61i_NmO>bmCP~<|~Ayg`%p@j11ziM%HRh^;0GuMOGtxG00$uM3 z71Wbbo+lKbc>Megm3Op+XzLd3h$^W#8%qE9jBC|g?eCW{!?q5gq7jdx+JxWRxdv1$1G=1j# zx-MXDjk}UF7+U~4>;Cl$ou3E=5WMFoBlFwCjH);xv+gEh(=jBr{o@E;yA}N+L$Mq?$7eKUAXxB1*rJ`RTz=fS6v~q zOLRQ1j-IeRKPkMSIbsZR_iMDCzgW|l{svl6Sng6tbUsHWYKi>RRYi`Ve21dUX<(F#_-ag+F|d#8sNS-bi8=sA%0! zDHck`(y!;GH4e*1^A2HOB*&??^V&>a_z3`lc946H{JpW5nHlixN8W*vcxLknj)#c_ z!`yyuy%T$Uv|T@Tzehx~w?B>2a!NKeXFHaA^Ei~adJ)zqlH6iY8MDbM zF*Ib1!yHU^S3O`@^|!T2Vm)L|%4z_?s)Fz`;q&}O2Q=~8m}X)fa-5XFeV38*4M#JO zxftjqV0$#-edhkQ`+V~&dt9&NXO1#C@>Ja9Mt1j-3TgtYws>K(0l>-CLPr?E4EW2* zl~(GxpbV111@yX`rPODD57^xje8Ee;Ds|sf^cnb{{+*ZP41OY|7afja#J%OfF$m(BZm+OJ&B;zz>!U#_9ZQ#rwv4&Ku@!g((A)Li&T?xl6ut;Y`XS z!Y=i0`%k}oh4IX#oCxE_{(h(ed!kpXN!H8nT^ZCinnGSwF3CwbsHa?@W3enBF|N-p z2K;nFObtgs3z>AV7Gi!FehyH63N&;I%?AL~NzK4S-bLW~q|^89 zi*WGAnFY21cK{koVyT9n1|N2F=a z#l8KkUo2DecoqVOcb@;D6{ZJICgJ-Z{)m4}?-5e{rle}`L<)-6#}Xu7*uzc9#zpcUmuW3I<)Rhx3^3 zuno@<_?oJ$#0dxOGcvv``Y};+UP>Q`LfhTq&&vK&(`o3Rkof`c_AZP@biDpx&=z0~ z9*b)aqTL97&mn{MhOcuNL(1kpWlak{9kY4TRJ;FZswd2}J+a2xkAX0+g5@vc76h;7 zBg-984C;0LYmMgV6?gx7MHSS0SDzu>s9g)`X{7e@?=kflg2`IRW2j1?Cr;P#ycfYG2TZ2($Kf;i(PB(gkfNncbj{4s2zJCoEzfS8N zzV%sGFJbIui*U8Y?j#9 zrvJBBCjOla_4H@CG=d&TMLSP8|Eijc>+0(z4YrN`G*tU{1ON4(UQ|!O&F4>O(9e|D z#cU?ur-gMhL#Up}01;EpVyd0YT|rGV=X8qvZ4FO zqhj)^vnj8(oB!f5J8v6tw#g9Zt?(yr zmhf|6(P2a0hUa?flS6Ays`NyVe&x#npp22GC-+$G=&fKj6*K6X)-P<|A(kq<^*57$ z!|#XgENwWY$-ozIaAN^FQ$d^Yvxh%%kVC(z|h;*Sbabvmo7ZHg}a z<fIZm~z5hz=-b=7{&|^;7%>HBK+lXLJnR;0Zj! zD3H(-ED&rydU|cpuSnwXA3IVnOtNfdS5WXjWIWLVg8c{IP(H$n3#kfnq(&{B(#Y?~;wcexD}Jaz z)Il;ij6qzFsE#R3NiG`KhmY!(G5o3gmMaitjS?D4JwDBX;|Q<=MgM^vxc|cr>{ZJN zeE<}u2$9wOS`4|s#4vmvVQ8PR`9SRIJKQPsH@o4dJd!G0pj`5>v{Ckq8UPuvdUbCk znYiW&=*o%5pysupC~Y{V#UhTD4_x&4&8o}Zi$TjL0@GQK-GgQ;6-n)LyiZ)|Gs1TC z*YOG4rb~sWaW#gS9c8^gQB?B+=b9%czZka#B38>F%a^eCyVv##Q=-If8EBw*;G(FJ zxx~DjRX_6&=BQ8ZS9F#^_f{{Z#XXC5YhoV*noP~Cx&4g{-lcvs>@9r=d8%{{g( zIdWo~rcT5St@PF`zpwGfn8KD%LQfJB8)fU>jjU@+{sro3ekQb;7J-R1p0Kf&jV5zz z7g}uL+Q;OPd7U!GO3NF4lb2zGlUsyux#U1xM=V`r7DBd<`FtdNA3p$b|xHVuyGg$ zVdk6jhXYLplr@|V`Qv}8)-{advV(Z+9OeD3Mr&!j6q7@vY@r9s*;7@@eZtCAwp20I zsciT{ZzaFQN9#*|e>^9!PFB`q4wtU6{l!pvM7i~*m@VpRgvZ4_VXkCX4Z^Ru?vmZ0 zJF7~7WReI+IJal<;=S58h7!4_Ce*Bt^ZFmqbc99=66GzYAnC>fI+Z1q8B=F&VDWzD z*!kXtp+Kf3}0fR`N!twM~F@-bWq{(28;_K~ ziTVBmW2%n|nYlzzm<**;ArYP+dJ``5F#z59ZQ1@N*J*Y9+gj*^u<0b%*3#a%7;ken z?$ohry{p~lB9X9Cl{7$00=jc%K58l4_Zm6}$)+?hG59qTf}1RzvBmfvo&~5hVR#Yk zW8ZJKowT)2HyG6wKnEe+DUZlI)Dc1km!hoZ6YDe)ZA(itR|7orQvO+puu#fOalSbj zE}%Hy^H*`6H#D!z1l+kPD;$@1l&T}CWKOu2?!b3K7x#RSRkLW*a0*Xu0;N56)AH>l;of}k%gxx_)|!ri;R#PB6IWjxCytKK zmLn7Sl1(dyw2>0xo0pNZ0o3XdJ?mNG8;MJ|ZRTSOqN8qvEodL1%Yy^ zHs_2}4=_)wfKL64XUweFQf>9KKU@G)hiG7C5ww%Yb)fe#LF`!W<|LQSwvb8nfemH$ z_0Lu5R9P(jBl(mzp+%thX*Oa7?OYcd6BAjM5aqBJ`p$P52D|GncIbEb_SYtr6?jIL zr>&veI--tn1p$uUCZ#t@B=7hppjV6PjoAE8n8dR`gAg`}Q_X#Er@LQLjaSp!SIo_S z3(Vzo!pjebhvD@-|37I!aAD@$(k>)}Ap>T~^p!P|FmZATZ7%>rZ zQt*XV18{psjW@D(c{0?Dx)|EO`W>3!yYFyNOOm`UqnIM5U9hSFRn@)^Slf#iWNU%V zCXZ4XDCjz(7TL~Fu{iFQw3SU)r?1Z_Y10?OnL|Hi!_6*hvKIqo8jek5A4eGB88yd+-FIP%Hf5@WVAF>!(|5Hq zC|8h{yaa((zf}eYk?mxoTW0cwKj#dAJ6!vU|EMSr`2Y!*a0MJI;gME+lTJoL?R(`2 z&FTWf;UKzDQwCUa+WUK|@&DqYQZgt+xnONq zw*qM5yVvG^I#~y9MRtM~|66tGKaVQ;Ra*bMYh(XT2mXH_bNyf8Q0t^1);;uXuF37E zt1ld$Gk>JWk$xJgJzdF85*S%M!U~3Guiit0Xy5O}$xHY&jMc%v21qd`yRU`_E%NVe z3>_1Q2>xvLbH43yI~nM195rqT(yillzTkG=pZrP8njyB_=WLmqpQKt(Gk5rBqsug{epcz}P~1=`;Y<=ouW_-d180W>?#{KrlFIeF#Dq`wEf;Hf@GEwXa8#yh z6{umG*{3`|P2ldE)2vbB?V_pHX6>G3Ud!$20hK12Qj@Vt*vyzFmKHJ)-O_U|4(a!+ zxJLiM=fnpbM)|WOD!+pno5w5y31oV}=LC02q$dn%sozj*695W{;jBsABgV&sGrXr5 za8bi&f_ro(w2>@pXK=Z>dCL#lVMXZp?jFj>pHZ9U z451=iC^YY^xGDGL1^$m`ce_&>kop{m9N z(4V2bRQR9C9*`(QmUe@KWeTU&6*!&+D}MAMj317Q4b}YDHj`^DQ(%%?;$iQfZKkF_ z+e~?sPuf%bh0(D>s4p`G#W!4Js_zQ4c<(f$6f5C07PiA*ipH1?pS2-C)}x0t(w(c6 zOo9u|pG8W-5H=dA(~QU=MPG@J+og6!@P+SkuhG&?I0zBJ4^z<+9y#SsehJ9PMNBmp z)eM300oYaKU|2d?`GMM@K4}~rTCL>Ffc?GJSNCCaQ!*^{;lu>=MR}@V%}V@GE;>=n z{qtPH3vVwN*VuROi?C8O@$h@wex3b-%JpxC(a-^|?8~%tyXCka0pvN+Zfmp|rUbtw z&7C5o%olfGVuql%`MJB4RHI%3ou(~Tx#I_}%;xLtrw)FqaLml@*eJBy#Dyig+=)g= z(-gaa-kqx$?^`^l(zhM7al#w&UJ5@P&uBv$3vEf0>G@+bR5nmRVTuzR-M6FOYHVWM zx7@GQ{kElt2(o13<1n#VK#eI3Z3ZebW&qT@$m;9+emG}BB^;SCZ*r8yYKoOi8#QH4Zqf8{T$XK` zIC&PdjpT_hQ4a=p9Nbu2UKu`?&NI-veP1cdYUo@EsGm^W1MLiy9NLi;?F#}v9O%v} z-^89qn>dyZ9InjCdtpng8f*BonsKMVSM5qgE`O-izXrRWicXB8pZ+d7z349K7Z#&y zY_~@dvOb{mY+am_!t|7F4{bk*c}Rfj*s;E|nS|ePNz2S78XVddl$|TW{RR73IMrZn z?{bND&2zZevp)I250`ry_eMCJoQg$1TGCo1BW7z$3-hHojrcJEJ{+)75I{5-lXmdo zoQp~!Zdx5^W^iEAd(p(H<0QLQs;mF?p0pdh;AfHhJIr^;w>5uF0vMmX(OcYq3)Q+@ z;g-p-QM2JZ!&E7E@;BzgoaAKlgVu@Chqc?_9>#5;jG-Y!h`iJEfZsyUYzBW_^5K?`z*bkokqL2X@Y3 z<*z0DIDTXc8k>S=$CzQfRD!}g(pmu}koFlvF?D@Wnrms&J zUN1XV+IGMHNhCKhNq)lclv+KRq9%q{C)aL%(S<;3D!%|7KWGu2DA(qZhz0a+>at(Y zcDxzvoy5E~k4)|r(ttlMFoFxUSQ2XoRS#8qJ72 zJUT##tF3xtNG4OR|5XD-GT+V~l8JU}$GX2|xomZ$ZZ%})so8bpIL`KhiS#T&P=p#Qs(8VU+ArcC{eBB8F zt`x58h~FQ1hqfn0c$)96HIDr8j0kc0v^@b(oMM95xzI2 zdWIZ-8=F4!r$N;>-1RDCwK$|=JZyUs&ZlUxl${r`Ib#_oIJQ83^(nv~p?F?RcVyK; z*$2r(f3ikp#k2gI;9tlfT?m7IPg)3gjvY%(|4dHXB&d$c#k%vZ^d%AMic#>B8Bhrc z+4#!KNF><`%~CZHUE?VUM=_XU0RDLXkX9pSA*OsK>_BQ3$O^0r^ggG01(T+zpi|Z#1C1< zUb37u(|&QO>}&0wVDFP5o=w%Fh^1G`&o3vUPLq7XUhMwr#Oc>v9>hRS;m_ghr=%D2 zpWylG*TppwyxIf3gp+ey=ar_GphetRnU3AQ7+m}G3(|gdO=ZZ`Dm#I3{aQv z^}Ke~D4y2vC&T_a^pyvUn_iy)?5Z(v80zqA6)+Ln;{yQ$cR_)PQ2*PjgES^}N(s>Y zkGYa4EUN5cEK1Qf;I=_GfW88!@%E?l0CaYv!}ADmVFjJm+S=N<_MxgI4_g1_zyW@U zHVyRyT!wi2M4kF7$K2iIqSF;x*rP!;|hEwqVnpI7=AIDO& z(5#1L5;4p+x!E;q=E(;!IZNCRP0;0%%Ih)w@+A()MVGei3URetw!)Q0)UVn;LOE-V z+5>Jb06_Niz1X$VZ||Qke$i50dQHdZu)nFfyg2vv6Ze`lLRjRuNmUe`E9eg^5Ydl$ zVAODG_B=6^euiI!IHkyK^p1A4p-{=afU@Ohj^ToWi)ey^bP-~JVo#_FON2Lns=$All&*S+L6)pV`wmkgre6y!}f2mFLKEal1Uz9x($ zR8TD(wBJcD4K=|LxfAWVnvjPHf2r%29Ph@QqwlDb;0PKyvMF$Oq3j8fj$#T&!iwe$ zaP#^GAKFDahVzzMQ1KOf^Fu?;IB3Wmzgs6u9l8hX7TX(QlrZ`A&boG$Y<+#xvKEp= z>&MCa;i%E39(3`nxNgalaP%Dar<^GNGXcFzv~b2o*;wPqKmy;&i_JS0qS=+b_Uu=8 z^o+SYMX37@FQrlPaKHaVVr<%KMz*(Sf`f~*t6wozWC4X(WFntc%res!rlqCwxFx=Q zb$^W+EOD}}11AX?q^P`{=a9I*PGp@8>eDCu!D`?^T>6{q!^-_P+7%Z?bs=R}VKiP@ z-{u=~#7O%nfp~?_=jKzA3$)CRbcFjj{0G?6n{X{$JSft+bkQkHrijnxJkAPX^?Kh@ z593RqqJKwa+!^vr;d3!b+Zi=MrRS=cVrvsChI*6gR(~gIGutGzg4>HyH7f9>a-}(J zZ-9-9bYBWV=mn2ySwOLGGRyCu!PP;V^g_1{663-vva|bdR2`$M28BNjh2s{ckEf3Z z4j;tVzfdV66QS12$o#grl1f^A`vYiZ?NBlf_zUkO1zEiOj61yX@?nho(c2gj1($vy z{*HWS2$l8o(O4*u;P=k^4nBKd#J!J`5iah{pfjvmNWB1V$Blxry+OJi%j{UF}jF%*2|#*o_$1U?UBI$3#B` zakZgnl`Uw!L+TIFi&TqHD`E=75!GJrwjP>ew@jKE&H;S9 z#bbQUvV7rV^`R-#5#o{^0oy#Mf{;FMlJ)F8ejE#k`$7tdytVCt+RGY2U$+=KI(7eu zW4uQVU@ze}wwUluuJ{fv7~9A4P2c!a*GaL%jM9i_0HSrybip%X)NSk*mpFe~a*WGi zcL~!D0sULu{Rf0T0!)_F_9+VcWYJkcX-e^&)KjC>X=x7Y7CToe8-95i4lPL9a2^rd zB;WQ`Uo_`b7J~>77OrNZ`rw5!#Yx!Z)GFicBv+m_*>Qp0uS zZ|jLssCkz6wCF_knUPjcR(E{?nDEc&9$Zcq=(uIJFKy9ti=m}>k6PDIG5P6R$>ENj zKB5pZ55ZQ%@0?gJ*0kAHZmS>*!u-BUv#Xy=w)8FBR{qiqHBGcJ(~z}?CDb`SH{Y4B zR>)GR<)VMRIT}2Jku!d3!d5l|nP0|7UCq+NpYZztJFe@nwz`FS^?b@>CdP~ruiUx^ zoWVSKjpDCXJAaw8f~^~6Z^ey#q{@tQh~D^9j^|6(g64~Zg}mDpeG)ij+j^cV9_4a; ziHty>kxXq!PQrt$fkgM{Mp~!Dv(fzReKqr6=2IH?TUxMY_CshebUJ4#yFv9vmP9Yj zOZadGWq|CQlRVChdbmYSl?HFX!6)hCpdFU*mo#>w#;Q-=62)d~L?# z&B>53sMe*9ZF;@J=N*-ida96DYhJFi3|by<`1gJn6_hW@Kl?uhM#BwanQbZzp_&+FerymwJXM%q$(4qeQqyYcQy3w%1i5`FEpgd+s+`LikPC;jeb8i|J6oFyeFrg zIfT&8FZPAeW;rqC#`hh0!B-o71HmN3((#Q5{$uY;RWtg}UCvFyN>xExIa_wQND6#< zf^s~hQ3htx4H|JqDr(kO!P-t=Dnx#M`ox2fK4)SnV40DA)_UhY5HfjAiuIqL8vsIkRG zS)WBs18z17k{g)ndE=Yf`(Mnh>Dk_S;w&BIR>q~awBRBrRk&%BRxdz1G69%iq-o7` z;Nb?G->NN@Hs*)$JO4V#n@o3isPVb9hP`fFpVR(=9gglGrc_}rt1j@s6C@B=^>qn? zxU>Hy4~NG7ojm-%h@tkCg5mF^5rNYYKD$-xY;bUJj=u;Np8vZ!fO5aseYJmV?f-wk z0Sr-FROp;7W=atY1{uIr9DbhR`p_4bzb#z1VF^t%HJSlgD^~U-YY9w*b?4p$@+Hl^ z$)7sVN2jss(LLfDtsk8DaV$hEGq=s+5SG8MPKAV}L#9Zl5yQI5^^#$Gp1FZR_OH`7 z6B%`eoz$Fr?VzuQR<${1zb4TyqY)fsoL=K%9^hhxxI4l)xyS zGttL4>bpYp%CtJoHa~UOm|~^sZ63=b`9S!p{>wT6tMv<$T74OUAZ?-6jipA?Y93>@ z*Y{oqy{o&UK-t~xr=L$?8wLze2y^DWVf~3W8a*>|Fl8LRDX4UqH5WOFB;vJQn@3Rw z2-?JEI3iw3tDs;wZ{={O@e&M~#u(;odVU_`@<)Q_9;fCi?_nVkz|kK+$yxFt*Wh%7 zdkh-#TqA=D*p&&38>|l9O!HcEz4HCSAdi~o$l`uk zJ;AhvSSD{u#85%wf$Cl^m?FOS!N}2{5`plm$)~8~SNCxI+?Symf$1OObGOSse?JU# zV!&nwRcmw`;s>8^?N z-?N3hbTsZi!5QwIZ!`NTj*4{Z(kPd`cY z;TKBS=pK9V$1V70DZ2M>F7wRQoIVVEP@WpTFB2H**gDoTv%{aaV&&hVXT+8ncc~PK z`SghaTkjQc;Zz-{X2lOrqDlXJlgeWNN%E60fXuryr^LVHE7}M`Si!VF>J81bta`q? zMa^9@#Z!1O(u}4sUA_WBVj++3GjQpRNtt&#s-cShVEMq@?K>p*y^LInoZekv9BEUj zyK`Euq%BY3V=6PB#_pF*6)X+A1aeC+D_(n{AY0H+MOS$scW{*xm1`h`LoNbrzP`a*UhOeC9V6H8efT? z9mS%JE-|0S3y-`d+BdLAa9xC%q@~T{bOkAyW)4{C6%NhsRJES3Q-9T?k#fIX+oDn)93%&xaoz#Noxt23P(h8@%p2#(}PhL}ytO$zy3 zUr9*l#S=wpaNpNTei-ZRT-MK>Q)GhtPvAXbuDtiAK3lVP-W!;eLM!o*cswn% zMw2)5<+jZxLc*|T=QP+ONt>4uHP|p@u`yAwV5A_0H#mt#r+KR#@RHQXAW0`gi+a4-aFL3tDSk{dQZZvV?>OpYsqw5ngF=iDnnW+ghIff<(Re z6rubq7Ga0Tfe79^6>O133;EBTfm>$As2~*gK4KpZJl=K%e}wz?_iBs!;jC#Kgl!&r znojRLvoeyObMZrkSx(PX)PYAje#LECX7_&NNrmo_SYDC$VvpQ+eJq0>B!ZwL`R8H} zKTdMRwnNQDoWk((E~R`D^^qyQp>PhQKQt8jiqbXflRJ7LO+2`^{up$>?{|u6cq(B? z?r!-VY`COAW=~uMh6zsJkF<-pM(ng6y~G_oJt0)%3nzRbE3CE4kc<`fSSEE041%WC z9ndAA0hb}1_4oAS$n&?QUxbcVOC5)Ljk-px&m#$DA`IS_wDQU?bc2b=r`+_l3s8QU zO134@g|k6S3>OAzpKHjc(vnNczA?&gO1=!$(g|~h*+c*I<~Pb^p4ivH#DxS@&M!;t z$Lm=e3s!nW< z7*BHA1;zI!1u;U?opsu7I7p>+92NE1ogY%g+z#Ew9~YN&=~R@$yb@-MeuBw!juVi& zGm{cCQb_%MQclUZCuz;+)$l_wLta%H5^TRGeY?1u;(p&Qz42aac;@yQZ@tH5L#=`C zTy|OhEJh8N?F5hKU;{y<0f5vE0TL5KnFZF^#uw zADnf}?gHR(otnDPvizUOk6_i5pAqxly`d9=wV3B~t~_60gB{B%8&9qN2Xk*36lbGn z`{M3y!QI{6-QC?GNRSYMyITnE!5u1L?%vVMpdtfFOO zv?~O91|UiE_)33H!P| zKRp8O>N;StPUZ@|%v*Z>{06wIuh&}L-Q6uzuQE9AUgjf#qJV<`_);agr3nsxUl@|N zD^_aLlHjF;(7djF`w}DePnBy(ZZKV7V$M$pn z1Kz(72A2LDiCyeZCS9c6^>l|OBA_`VBK%uappl>f!w2(eDUMA5v$mGVjo=Z|-aMf^=%mw1q#uO?_P5zkAHp=pqT$hO+kQ}d zJ!Y$&o>IK(tFJNZ2o-JIK$H^X38$0R{v6%G1?u5D$iX1usVd*8i#|yH^0^+aDwdZc zAjVXnms}U9=UQ=i{3*MG4VwDyU-y8o{SXxYDApDmVK>ygi5!A-W-Dtn#*i7^qSabf zM#qD4I`7e(%bZ#w+sIfZt`NvZA5eCpX*K?%5NYLm9v<^QHE7h;d~MW@4+U!U&$BbM z>MCA$pVC_jb%v!TcIWXNt${{O5uJw*LG!kaJ|I}HjL|{Lpv3V$Bk!()ancG%bUE5d5_9b}7PNMn z({y2)Ev5Ll#hG6}wtW2$!+*7;Uv5a*JgZJljw2l%i{PLx)!8dRpjy3#MRoZEXMb;H zl=>JoOz&5d!BMJR5VKj-+|x=_PodTIx3B6-D*x9c6k>gSaTJCI%oh%XzBky;*oVwh z7m!>d*pd0`0e*XYpK%XLq=lkCmWKIkqc9fUO!thhuSg?hNJbmlbs#WT_Bjru0iv^p z!?TjktT-w{iyY8m9trsx54)M9^%xM5?|MmlKD&;@K1NEs4*vc=0Lf?5V!6T6S!?ic zjiO_hT1_+Hw0T}urPS_4k;;%aU`VGpyf~@tCic&$v2;XlFmFQ^OW?rn=KQ2XxH7eo zSN9*Zl)+L}$ratW%$_xK!0K%w%F zK4UHI*0!BeU?q=8+1B=eH9DDp_UUewYo^o;jtR2ZMt`EX1m2^rby0! z@ko*autV_27E$!=IG9<)!Q3MnqnIuPM;&Z~L5s6a7Ga@dIi%sim_R0hf%j6u7X{+` za#&Nnr0PCsI>B&9 z5@H|uOKa2Nv`CcP-n`Nq)Z%{1iG-q?=odZ^F~jk=1N{XLG+KHiiq?TSAE>#y+-HU>(05?t=$?y+~JT?A$t3 z#)Y8Jn6UPSh$k^UQkZN2{>uZ>)0x9wg;z+wKVIgoG8)QPU$1gn9}+!XQHrhavtNVB zG}^6k6Z2rQPJ_+CqbBTm4iC|mp>Tm4ofnk`)U2r~CkZM>&Q-5f#Mhw(gSeMSsMHt&MDN`OTQhd(4&M*0KR1d5 zWOcN<*gEXO#lI=Ry0$!Ri1FBL)+KQn(K{k}|K=l8*4%LcMeMmR(xHw=ub6$A<64^~ z$VK&Z^2(xAKKBPC{NN6{;{}$C?t`;aW(eXj4==_8w>_-F*S9WG?x3T`v;9;_a|Q5_ zx+t!}=f)7A>Gq~KvO1AoBLQBvJ)M#%A9QmYZcdy|JUzQdJ@(pB3zKwn5t=WSYn=%0 zHO2weFG&Un?5D2T*=c6VVN*Yt|@Xr{@PZ3ZK^` zEC^E#T0wk2LIGI?>OhzvR+sk3*|UeY)pS<9+_wgqP38|t^2HFKjqgfcUZVLM!iv;_ zL^5g2k9b5wAZF*@yiB5?mPLmXSU)HE`f#No#iSJz^WwpKhr5!iB5-LnIMBoUmnx`O2&0xx zfuw^^oIfxOubq(k5-kE6|J_E-qYE%zk(guBh+D`tL}5!Gfa6L2%UUdAhJWK!lpij( zVg&XiVRvlzeGU?^=Jo{-jFfzo|Mm!xACRpi`?D z3g}7vg^|<_14c4D%@;k{ETI zAbc(}p;A}1;8tvp%RWtA{B4VWp@y`Y&?bXcP`w~1xs2Fk2zxY(3 z9`)ta4lFs{UH#Mpw$6boF(?)vXsHUkK3)b|s`7>3p5KCZftD%}|J!Mt_h(yuKugsr zFa!mNNg-hC9LP$2t1)`fqVBsxAW*o#g4Nhj{=bUIKa#Qve;tmrMG|-+vlR!| zY*IAA&+g*a^QR5<%m2}aS_inA#b5p|_X_HUxBZ_5Pyt&xVf{r+YM45M(vT`{gzYlZ z9Iaz4V7(5WM|XyxnR|;0>^~Lkzf-1Y32$ZW^{6dsy0d33zL|E_^*=z-&OtzN()rui zBoX@$V>1LE-FpL$QNt!%K!5Z$8X@+jq3OfRsA`)xq}jL?+jZbxcCGedt#(iRCjWLd(ebj9Ez{{~;=4otay2op8nTjv zgyJH47}OQ$KjrvtnWtBgHjY2-AU%#KlA)~LeHQvb#h+I}HJbSWH&htJ?%#VzAaJN% z@$GpM=Xt|pIEN!`riJkdCFwkdxw`KhxRLy^Ha$8l#~S`5sAkOg&PjjVss7>;+^j<= z?asxnNiNEAtO<2{#bmO_<@hBM8FOLQ|_n;~R0^Z42Hda@4|FJuesDI{YQ2w5zMzIEI} zqjT2}gE)99DMW;I#p^m&b@qkXLNr}BU!;yVfwfU|gsf10oaqz@qM;2Dq7_Bd>FePZ z+I!4Ij(7zdONj{$P|sp35uD0W;a_^P!swRijKD>Fl=U4?r0_@} z1IGL2aDB=Te_Wl}wf2&VtrZH2ogi|wbl)JpDd5U!PeA(OAq?1! zNVl-QW*+d&+$J4@r~nhJ1Uy4HbEGxm4L_6cAKW^x1s_3?2^k^c&I@ro<6!m(kg7RRTAPh3c{q+B6W*$v#}av#B`g2CRXD*(<=}L8LS+KjjXQ zQ>~9&#xMuDT@oe93)4zAOb&}QTw!>p*G+=Ja1h~emBxOT8K@PD)J8d;=DZn04YQB>I(v(RwyzsGxigMg_8g_0?>fvj zg+#Jo@GX*$!^mY~Tsw-3dj!`d>l-AgG5kJMx}1I)n@)yqEq5|&*fwa0_Ffn^nn2R2 zxJ#n12IM#fpVdFceOq7gAaaHyv3&QSa7Q%A<}<9CLg|Yj6SEoZ|0E9_^@9VvN?>6O z?f6J{&!W4Im`}5^_PhPk-+eu$ZccJ?nKxTq?-nz4I;loWBXy*%>cgQ&LLXzF2v$D3@v&I`os?l2z3d}M= z3IZztUFy`phg8IkRxn23;Hq^?uW$HIOzx=fVj*gZS9|I=t-Eo|`dVc2@V7 z(a2VS#$5u|Ij*4G6sPD)T>r*!S-$EN1v>_ZTP~e(w_EuwAq5Ku|A*^quC5hy)-s=u z^D@QiQ24GzL-{8M*v-|q*!M{T(?eXkca+vhIA%Lycj)>x)yIs zorj{#lamBfsBqUr%-d$xB(4H{iJA6Gsa?0DEgBS)vAJfefIB}T>4*q%Rd*U(Hf(y@ z&S6G+{?{hPfLn4l;ce)I3pF8N^ohKx&&|=kwd3`p8K7zdUat8D^^M>0(d_%&JfJqV zp*uGpxw?m^@LQBpC7!&g2dq(W5nL?ASzp)LOBpZ*GdVK zW6B4FJ$VjWpFj|G+8~{h6~CW(9K=1P}YtYJQKc@e)yJna?rcCWLxEXvwj%2D6=iaHeF&1(l6UA zMR>R#2Xtm*Mjv+cvk$5<)?s1o9Z3&mWqK9G)>t_**|jY*FY0$UQM8Q-27*ijka=ZC z+na6yWS#N(@7(YK196^rG*WB$RSwvO`IiW@1oD8VrQ7Pegu!eNPeA&j^mQdsHV%{h zGEL6T#3S{Xd;>GnDN40lxVl{u%e3}0#?yz9)ZoxBWw7Dhb8u>EVK|y0y!`V9w=6Ai9M8|IHxvvmiutKlsA%(K|T1t-t zSBp^MGjPctK_I&z_y1m#>F$?FX^oY@JU-D8rzMh6+!vkW0h*c+m3*Z&N32vb+VW*L#ftRz?|b6lvJts{2LR>cbV-DH(LvUgrMCl%XVxZ}0I-?~ z^o1FuWTdW#g*2~oAt+c?;cd;^ETHi&>HjfX7e;vd-CUKu_lw*5X0LDJdsPo_yK`HX zj{bR_FPEJJ+g14M%j1guBpx>zHHz_iTy5l`Il*|;&1kmCY}Z&q8fY&@Fj`GbJQz}3 z-Qu7+z0D-Ll0t*-=;3KtUWy6i{-bE>KWhJhLE8TdIsjhI z{70;Ro%s{~dFJ2ZJ3)5*fa4Q8USB#@e@xsmB2eDBQ9l;Ih{ zdKX^U>X>^#;O(2KJ!12poz|(0ueZBcSnqbVl%A&+EN8{6dLz9ithAOErONa z;rRhQRV?9i7w$`IGPlLZ-Fwmn{ybakchK!;o_t(tz{$Vk>)+yd6@5ZJStiY9=x>%) z03(p%l{W!upw|D6vfd?s5GAm@cr9@3`L6O8)oKH`1=j@SMf*xz??#N;N_Z;`av{3nX!f)@DJAp57(RMhc0+={nKjo`SCRN`KRo~J5VC%yrZnaY!IGWjQbyRc`xr5+_hKA?V(7)PId1IJYGlE@*@G)E^k^ zw6nifBuw7`hkxC5pN@yl9sF*InEdtVFZVI#e0UNGrLF9nGy4s^?=VA*c^-krHgj%k z;uWG(49pg)nqE+HbRneGE_<1fe}a8xf02SF)vi~wncXpP^jBs0{`lVPcJH_v9%h34 z*rb^{D58NcETZK#MUEfplWDK=UR!Gv9eqL%gVHFWR1z^wy-e`S%!DKXm= ztsykTLTxUu{_diAU9L%hPM-6}=h z?xkwVd$XHf7uMtRbNdVK#0V!>;kX|<`GsBG!)hvnZ=?u#P!qB74F#b0jEK)AT0M?p?o-vR2?t>vTr88XaU&3CR$H3> z<^v#QI&dIJ$%B&_{o?5Z0)FlKrba*yGo!FXsY&6YIG`gfqNXY<^DTM=y&{TkaIZim z51Fe7b283YAWs>wZN}kkV82i~ixG?6X|>j1FS-@)AR+V%yojcy4x?Er;MZcmsO85G zB~Xmel9_d!n}0!I5dZ`hfmA_b;N2%n*-)vBf?JwCwwxdC(kNP`bfSFX7hYdtA^9{V ztD`j=C+hJf*2m6c_)*qBL-@_VOCaOd#URJ7|4FQn1-62AQPPCFvDCuC&&mpme>=Cvg_?+c33jP~TT7fZ zxv>zPu09;!iqYTFHQgws$<5rh_^U&`a;Rbq2&acYSbECv84C-)9C1~*`gXrbR31!) z%Uj=8PJSOGEXGsJzR-@&e+JL8h>8Wx2^3ouJ!tampc|N_ylUi+E_UCRNH~G1z#z^1 zL6OwVd`QmGRtD={Z%nXSgyZy>@@pLyIogp{2h#osD~x$~%QU7dR5S-&5go$_1x*`a zE>5Y!U0G4SxRqHu6_C$k7!*u?uF>*e*6JC&rJ5!45=#^e`QLuJMM0phT6b5y2}@TL z-nI;#wp@dArxi39EW--+&S?|Y5~vJW>pSCpmFA=(J+L@Z%+G7!CjEn->xjzc~ztTYQWtgK19{CfK~YM6sxA(xx?^@O9K9kAznzP(~=q0M+T zMpPC)8?kVhd0UFm&C$60f*RCgsknBRb`7+*gnT{sr&gKmA0_^OMo-FVaNHCGF|u#3J} zrnH9l`OB9kH5RrGPmNM1BQm9;kIlff#U$(?zLoM#MgE$cHH2zaka69X$6L`9T8Hm@s5`0o zA8^prD{=J244s%f;n=^zInb_vlV1wJ3hn@` zu%#s`U&#<~_v;ZHFLGUzyGwirz83_ic06hmCx~X)MZ-6G;Lk)OG0jfzrjP1^okjX9VW|%@)SBP0(3j1C1J~jZr)he#xk!>YNkqb5f?e6Dx)cvxS?sjK zw2U%4X)oZch26jhTsMKPTn@`4+~*{ah|dzE@e&R{d2u;adU+!AYksE@k%2S6$qv8X zn$Do+(FQzcBZhkGHWiKtb}1~Y#Bb~$v?{_T6el;wtF3cLw_Wg1`AaumTfsE$E$>SN z@93v65Ms(j^iup*i5`9L5)FrlR?S`>64lDX7hMu!bR06k#e|Q{fM{wvzz`bu&BU4z zl@}ZCyvsPSgMpfl75E80iGMvbiRwwrXEs%j$VFUvn?+MMFYn!$9ZV;ui0n4@sWmxQ z$39HKVY-&+3b3N%fkMcB5gDhCZ{E9B`OE-zv?G4^C_z{|8+3^X?r5>1*v?;wtsv|9 zo@?GX{HL85vZ=AXiN^MU>Se=<0dKA|O^gqbQiFyTnncZ?x=kDXnNSW%t@0AJ!HYKo zrc{-pnpMsk>t7VVjN5CI>i9wHe(F<zC*f-2g&8I{LeB$$Nxt~*JYDC(y$_? zx}`yd)jTuM7ORi_&p|D;y?;Owe-CQ?_XNz$LU%To>ST01^g}huOpSB?;16x)Pn(Xe z!o6?Ls&JY+-5Ca3F!U~ZP3M19i!wG+*SVz|U1Y7OR5)ftdB#xmI_9$O!hMPjb;lFQ z>*qas`{eI;282Ui%<5<@z^rbK`4_W#+icwPv9itbdHSkhaNg(2dKWD~=EbXK2fXSv zz^lfjG(_c(d-7m^Sexits&m;-X#c}I6bIRs9FdPv?dqFT)q~f-BuB6m+g|@xkGEVo z`Go0<%P*2dv8!2W(}p?t4XqM{n#wO4xT=oNXnh91ptvlk6PQAzZv{!it#HDs&0iG0{2(Eihl<;^5GAwtvd$IJa91uh!g$M=C5S=ZYT>l zOEP1`0MB}D`Y+EqG~<8M=IfoRfHr@+KPB6cY)=F!!Ndffy361m??ip{X;r4x*9Yh4 zki(fq(Tw%gX&0~DdY9gMaw5#TjpnTLqv{}ite7a@nbLe<@U5EorY(1LZ{yo74_;My zSN*d?O|K4p^|$zAsLzEJXzI9W*WiaoBWV2>{3x+!`S}s!H6_VUlxQ(6FxA}J)-i^V z=vUIum`(hgummotZ;G>BM}-(|kYZ^S{(upc4SLPfZKWkX?6s*)Tuo4d@6f}!t~m^` z*d^>ML#m|yW^=8@b~s8o7>eTi`?bMFY1jCA(9G5=_<{*O+}OO~s`5CAc2EKV=W=p~ z4D3-@G`A$uqwEf|DBt9k1=hSIT)@=qXT$PowHWIo$fw_7FIJ3$YJ&0jEB9_w=`a$8 zoOdymDP4(Hq1SM&TQ~%eU`FVs%`?I^q@iGFK2DTT1n}-nmC9>IdCC9?ZyP6o@cvxo z8#$P3ujW;9dJ*qbFUd5|4ECen{TTfP40B=55kLW(8KA51PGmKiVpp$ z@)c5Xm-Tm1?&vuj6+@FNLSAP8p}-c9_cbUcb=oOZ*y|@)lPfd&anN993F$$o^_f zQV{5^y!#XMu*1w-7EGd5FLn#}Yx7d(0cuTkP{_0#F2G$NBq+lcF$T z?!yho=FMgD~}48_g(pvN6(2k)y=!t547qoj#PAJCL7ma}K4 z29^S;EzfXdCSJ$Ig`82Ttl%xjCIkPE6R8zt5?1?-aD2=GIe#g};{CIi(&SHz^vViku*V7Is^$#2sdngxqubAX^%Vwkxm*D zzo(mzH@hjfXaYZiDDvvnM+9+4_Q<5>*Al6>oeQa;b(gc-MUw+(V-RyoqzY`AO zYdt7TN^pgbm!Oc+FYlhtcZ+7;@TyB#1)~{YLV0 zQR>^rUKxj*0`OL!J9|+R!syA{W6Eep_ytqd&#bAMSf9D**M*wokDZVZ6WgSy;R}D} z{q1$1QGE#`jy=y9?`7#$D8B`GjLTZs*-_EUJGaCrF3R^aS`rr`7&+~$KdrIrJGq&Q zWAgK|CCYq;^i!^xTURU0vEDSrM; z#l%0G@a1gyBiHy`UWj493@3Z##l0QIE=dp3DXerJE=9_~f1vt)lAkCF?uNRt4o}1U zI}7V}oaO@oT)vJCXbKSTDyfl1M@d`xp_&=IyV;b!4Dl>$>c$*?V%n{$9Qz>RP#ORg z4p3%%vPgzP#n04DOOcHKb>0_~zfmgv15oqTWr3U% zJgDCn&$6*`;`EG+*)J#S+lFzZ;>x%Wk<#HiJ8Hd=(5sl=<#Lnx9+sO^@hM?;TRV8{ zz}1ogmG>(@fPPT%iJ}0n`!P5VJh~o`x+yaBGjAQpcR4&kj9i&z;`86SM^{IJ;RdWe z5gBYmL|QYp)d6U+1%MV8|B475ny!PG4uR~G--;&dJ49aPfm>Qu5=J2>Nu%o^++Nz> zXrsY^fFg{-%9BHil;B3{ME3oVh<2_>!SWmPZt7Kk6Lk60UHKd9`xjLTW|h2v!$~H= z-)4>V48~Fxf=iQU3bbfM{JUeF#p~kkH`?~o(GSi&^P*>w{1$P^#1xp4WdQ^c*>$ipTHw!adQ3&f^f z7N3%w7V=>{JPpk!0CO9}5$rE|9g-XMYL?&X#EaD#DU(V=*n^B#uuQ*HWr|if-ZF*^ zr8|%&F3~@a6$T`x36t{%1$Ns6Rn86NH_a<+3Aa3{O8yeXOV#>qFLO&g;@`f_7WP6Z zZTQ^*B5nx6(SiMcIUtjjVb508{qJqUta*jE96YqdvV-qv!AY%jw1dt&bR;7IA^STuk-Mh|D=81^tAnTjLi==nO9l)c$v8nHNeq%k_nEw*5PA7R-QCQO*(%c<8fHPJsaGQ7`d7 zMV9y_1cp}G@cgF7uzi|5N8-JCXK{m|SPbmt{Rbsr$&3nX3FP)qCHFd7^OtpQy5z zE*Y*tqw{w4*$9V1oKysNgCCE?PTEtbIk|S@8J+mY5AC)``8aP5f>YvIT<#5R&b}1eViI zfMB01chAS`>#T+6^`1IY^o<#Tx*hx`K62S-wO>V<=T=njE3**1Y@0vjq+}inhQIUk z<6O|U1_6B#M*o_bn@J|Js(ie;`EAtqrwd{RLQX}x{Q{D|+XHhVYW?p4V}Cz`IFQ{?=d-0(8LdbXKA zw8<0UF+iK#g}JC)Q;`-QeIE1g=aa+jk#Pug=ZVWxK9w8>Cq(GkwY?>02>R`5}LA?e~|~?n;9a ztro$$X?lo@jX2i$kfxc)O?Z$Xwu*CwF@M9gy~50>sAwiFe4+5uLy z0sq?tQ+d5;5gewdX`n7LCZ?9xFHH5{J zm+fVXQvrTi-isAw3|0r2vEa9%WQa!^vvs~yV|R*gWNQ%)2Bjn!e%HB6z(|#_? z5rjzXx_F#>wKZtL7iI_)4s2$533gt`CwOu4#(YR@> zh}h%!%3BFZ7^3H0rw$4R&753>=kWX17=t?&p!4tNx|C)!4TvD^3kqA&OZ4P14j2O( zTX$Qy3YYt^Tn8n;Nu@$@mSO#6k;79k-PyQE+czd$N8;NG%!O_i2r@e3wDeKYnj=Wa zD?LD!%9r1U2N!?l@tngfDVisJu5Wdd#U0bW5GEvO-#8aA^p!((nh3Z7>R%Yqv$Hv4 zM@_7ac#^e0g88(t#+92WqEQY(qpiX0+R|)SyzR9ieFEv7-+uQOEz;6INfj-G)KOamg`MXGdne5)injLrZ_nuzW&vl>wpS5}}7rXzcLQ^QG)Ga*tx zmcZP#)MrcoBs^<=q-U;Sdq8XF9-u{B^lN^IQhqzr71d4+!!XjVt}Z)y9e}5|wPT@y z7|2T!&tNDGPmv)YA?$68zC3u~%XLA`GeAwd-WQV*s&DbKa71#3O968+=1bLK0}5wn zeAl>g*&IRcMHydu>oFWfg#QLiq|6DFXn~D0bNVp2?R9{JkT+{Jqn4D7<*yJ86rKa~S&ap(MmzB~=UoZkC%}0h`_VV&}k;*=dO9gUF1;%pq$p{E#f}{62EN569=ApD20Vut~xF zvGqRAdSiguvtv(nItoF^1rR3AZ&0eJ&NJYaVTjRJv+&)dZ*GGX9%pQ-!heIVZuP3|?b|wHQZ8Qmku0CsphX7rL;#5&Mi>(~KlH0sX*SVV zUzVK#seiVg)gJg5y0o0+8}#-22}(T@$n1BA)3xANlTWTu>A)3Y361_Jw=IlZRwrR3 zQB0FC_N?Kz8+yoE=(QRQc-Vx1fCj{d(lUTyjmKcq+f11Rr_@8j(-zKg0Rt6}y|wdD zx|G79Nu>IYMRlD&G68~>y@#I`KVS6*tdX^T6kM=IqWuA(xd8_XkjrVf*gYSCA_=Nw ziYhl84HYml{Jqx4eLA0}!f2-ZV_RDmdDC|($FbEbE$oAP8RtlDKRD|S0+e`OswDJI zf?n+IQGZdxgpj+==G#}*Z6cojC#Qh)$)~yz*a6~v2@qUotURK{qal>ZbUs>8V%NyV z0zO^>Okc<&m0m@Ra=o=J=zYt?R%t->MxE&;BREmGfHcs?kvSzNrzGLo;aqoe>J@Xc zU^dKK`w|a?Ob6nDZ9sJ}nAwM*ol0OXRx~D(;~ne-(ND~WGxv+-S7M&w5HpG*N9>^*efxe-7j?M7{Ae{lc`%f|vh%d@{ z{G*CO=fNlnk|zDXrxEyz-os8u;A5S;%A-i#+y2Is0U{K}tE_$IRL%Ht=yoTsr%ijO ze`j~7Y%DI!HG=eSE7*~>k^kse{iw& zq1mOoOVInXU_ts^Yf5Ie0$qC-yA>~baFT;vJ+igwJRFp7aMp7ewso=Z>qnz*bVAY@(WeIsPAI6BN?sxDm25iB7@NXFA2VPd^Du&#v9A{N+>gi8TGCF+b#fTFQZ4>#xSGM_%|}@ zdEcE+RP30MUhI&5TrdY6fS9TQixPKJ;mP#ShjH`d*hg=`C7}GauLWyNZ=e)+dM(P# z|1)4%7kf=@{OoN?oj&nHTxPT{$38!nIZE=dNg{M9x2w0}`?X?&K8AwdgkfvV4gt|_ zG&s%j_6Al*p%!>=Bu-AW#t=0T$iOjdVGd0KeY}kcE@6CW(1U;t5#How zg@QT(awiJxc+Pl0nhP6{6Qt^N1&JZOFMC-3_UudCTZi7cGpyKOLF3Clc)|(k9h!;w zOM3Y&;-1)-#NfFV0MCsQ*6%JhB6Sg6g)pBkpk^A%@+IN6FtUCyB?q~W&lZpx=?@CX zVNm5?;0bQcb=gV7R^8rHubkBJuOYxVXL9kj!FCj)40S(a|DV|eq{u)RtB|)pC83LcRq5W z8Tf?VL#?rgQ_#qconIdiipF))lv;4e3>ZDCo4Hv^jbZ+Y3uba8w6xLi=zAqCR^N6_ z0u3aqU^RxPvs;FxV9Vbh>xHXQAD^hbU=*TS02paLr>HYz@EC3F`Wa!WW58V@6e`H~ z0){pCo%jQUcp{eCwrRDKH2fZdWhIvEcCNbO)6W7(-2yIggYUc#+mG*CHIQ^zO*l*8 z<%7Mw5<8L7?++pTM-k5aabnC0kbH!M4K<=wyo1GY899GEyVb3T_|cucp}kbelpL z*g=Y%j&5>r4weu~BP*Jf}s|Oy~y#t2PLj7Q%GD-+^!Lc^|3QG*U0XbAAsJ;7ZrW`v;xK`J<@~f#oNFCUC$*Z2SO3qG3Y9c%cQ3glUgS~+RL`yn@bk1eJX?Cba z>x;d7J2Yau8>0i7cmER_3{n6ov5+BU_M!%T=VM#jhH+n8^h^{dLTJia{^9*C91iTr<%rLHdL;XeQM-=zRwg9=z1%kTorflx-GUbVy?ZoQVIa7T4$js#_F{l zU?L+=0vsJs0DM&Oiu`x--61ex_FD)zEMZ4*$y z-#@-BQI$7GYd4pfl?uT_p`+V+C2DH#{ISAj>-p! zWMCRH92WK3V}7)IxOwOBi+-euuIoPBez+$I z&~L707^m!6dXfCScbEk=5M##thwKK`wF8 zQ-@FLAbJ$OMMv0a8{KeYYd9hn?j`dxV0-qUAhi9LIJJQY@Q%0kfGZ3{gMI~SMM3DBlLqIfAva)XkQjFL z1&`5>di5mLt`7Fij01;Et&bO%&AGb7FUD{cTr*~brxoXn+?wg#TB+lGOmC|x^D>Lp zJWenXLYn4&CAWSBmfZEya{yUI zIA9G=ll&!Jue8maS{1bmhhg~|rcJb1E`akuOVmb&5P9>i&rO#XO< zNxV)mM5f6H@6OWX3In*op9u}abZK^D?=HjN{#Bm_28Mp0g*vY9fO6ntwbRl7;KOG3 z7JT&sn;#ec47!O)*2!>B@Ly)|E0y5@7bg68pgC<$4theKMskL_0@GK-M;II0t9r;y z^;+ z-=$gxNo&TCHfcmO1@5il%z)z;ZR;IRc(fjjr58vR_RNZ{sni`<-px99ZS@mXvly6NFfyi&r#3Eb&#YlC z;pceW^ab+|90vJ2k;J#Tdyz=_0TDJcq&@ZU!1MLa7m&+AM4iaCSg>`Q#m83C2Ut^W zSp05^$Tyo3HF&?)q29yudda|Z(DsqG1s0olTgBNIO0|^SkTTx; z{OJl7hQTNxPMT)Fewv=vbqs0|YvEZ=^o{$gUJZ>YfObh1S8g8I%n|4cSuKsp&%P(` z{R)Lt^+xYYkMluV&*p#{p#>V;V3-~*6~0CQg7n+%aU!=^hVA4_X=L{e3uc^ycTr2` zDoG=hC}f1E1lYY6675P!g?56|2p{cW&EB<8N%s|MhmU5;LgnllK+px77gja5kR-B9 zYnATQ756zN(tWy3ML~gmWqr!5kSRC}sQfuk5yE#50jEg_vk7rqL&^p82%Wx^==n>r z83lBwq$TTsuFdo5bE`2U1R=5zIKdTt6Ic%%uuN#_=!L;}wWs{!1R9yUhXv(J)MHOF z-L|$>EC0HXJ;b+DJt__mdX22rO^qAH$Ku;?4uya)jg3UUUq6NTXrt)Daw>flF;Q@{TS1AQmr2siyQ8FVJ@&rjAF0vk9K6!$ZxsK@5kRw+kq z^RzOU*=Pup1_t5|C-U-MF9jL1KtYB>WTyQ%iGt!->khilWz)?qVZ@OF7$p{IFaV`R znsp_o-!$xlRt@Riptx|`T+;w=^Lp@gUg&5L?z8zWnt`nRJd??qcWXPOh@#;)pFQMA zhB|X(VfiQW*K!G!3SB}4(#7gw0%X4I@@cJe!^UnUxIObkZB2XQ5iD>5 zq7S!+ac@RG-JkRVAqTEfdGFF7;!E{mcIryfQiSc4xk;@_*6#=yWf!Y(EJ zD7lkCLXyz{w{!!2X>~q=>f}I;!p$@J18<)HQp5k3XWqGr#1~1>Xbb(8zH%QE;)F{}JRWC?tdi4r3NQIv7& z;iGM`?33S_dxv{u%3qVY8q?RX$T@jlHfO*Y_4+m!(oT(Ck|?+)58{f^-Aoid3WLxO z%kqr}dek{DqL7qhSQA|u@C!Y|L@RFr3L&X3Cs6BmoV|y48Ei|R73VuUR&1~L7H&*PnFINB@qjP8t;3XSE>m;!>? z)N+E!2RZsza$PleyTp2Kk=@Ter;qTY)~x6ywuX zRNsC#lr@J#+%vej9S}Ael7vz@+{46wTO#L^`(S#w8Y-tHwkz)Fw+y zm-(f=Eovl_3ZDMy30365;n5PbnW;GCQ{Sd&NzSgTTNsc3Xd(VkBA&^|sfaI^-H_yN zsR;JjmE@CRckH?peCcuH2fg&Toy=Mv;W%~}gUD9CzIq#;nT4FV0q@%e=^T^4TUJ(4 zH04<^*eklJTmrqAwI_EZhM$eM4Q<+zg_-Y+iQhZ~)j1G*eJgkLes8;6X)L$m+Ck)& z$zi4qeLjJWu5DpE>(ecm5sDpx9U&P;pd$(`Il+P#M$CXICG&qV_m)9%z5ACh?m>dP zJHg%E-2w!60>Og^clV&dH9&yi?(PnO;6Vd4L7LFO-6ZFC&dk*O>sHOJnLDq#D7ts| z)4gG@UeEe|)}Zjm)TADtH!}jF_dy5D4&LSHbzyUByaMnfl=2gSr8sI|UyfW5^852A z%NbHo{Gz&IkiqGbnWA2YMV7lQaHyT>T`)%hk zpSGVLAj=}{N?*d6VZb$?j7L{}OMj?fdi<6O8A3iCld+s(NJ(5mW$P;~lsp#~!;>TS z(c;xyICV2v5@xN-x5tBON!?<{SGz_+uUB(g6SFi@#Joo=BfTjIQxA~thwc9L?f&-_ ziAKAHZwqfQ!(GF*ORvEuutY zMq4d$wsMl-hs4zlzCmq}n(uHDonoR=zu)(5ebkong5k1=SJs7`PTN}u-+VK8o%nI>5SYGI*KTsvEI=wB9wI7!f@d()q^^Q5!-f0Lkq z25XlW*6G5f>Mb1;HYx#_H{HjHJdasW5{@E12sZP49UC7L{;a#!=n-ir5?ie?zcg&)mH`!AcLSU8O*N1Hk#yY&a2 zbY!$yrr3*W!r>oHI)!6Zj9MRR(_-^%@xS~j)DQ;fZlWEz5uHez>nT33r?vfh6X<~g zx!pb1J`?>5T6!vKU0C!Yv3yi9Pnwtajn#(SZ62&fS;8c?veC`k}F$0KTPSlcbx11Px2?5VqLJ>UA* z_D(j@0Zym6eHP0;7jv zos6q$J41|GRnB-Hi!%Tk9OA*^{8jO+br1TFPV;{B9zNHUvwmxZzn-(kI1h=plTe&FLiyGH=gh8;Yaoa!=YQs1w31Kt3Obl*M+_E(^| zz=sePXk>@4#N=}hcdkClnV|>Ahdo|+3p3)=7YJ{TOOdszQvJP!xn3GhGZPLC9V5iM}^NicO5f~=0J)zOU>ck z3PM6_B3B_gk;+Bk``{h&n}Z96h;%m}&)gI|V4|r8a}|IKU;WDy)?JrG^nHgeyS*0c z6z%2Ra`Zegcqi(ju(EJ%T$(UMIP)u{>JG$L2xeUk>1hK1HGxr8U`8xyT@z;#7)B^n zOyw(2@JPI(c?|R#u42SbxSAo zeo1ZO__FC&FUk_Ifa<|rLDtUs=8Tl*Ezpo?CE}kzt~dAKHlk#EM83ylSDDll{Ecwn zGuO{<=w*Cne+$apfz%z0C073a+n^5seWTyvlP+XH;*jJC$=U#u%iP|uF4RITbhyPG z_WovFK>6ThN9X_u^9AC0a4a+ms*dX=)R$yoe_NZpQy=&e3#-$w#X>rBIkk&og_k>J& zdDGODlKFsl*LfF;ve3{KQP91$XB1Z7F|{-EBM^=lL^*n`%25GF{~nGGS4@a*!mFAS z>zO$xee!a#@BkckvAf%vzH{Dm3(E5rqxaSD7qXd7yJ@KK)|c{9_stQjA-RoQ4qRs- z)j&Z3M6JW7Tk$@!FNC*xF69UE;Ix&J`y`CGM-R-z;C)|ki0V|E4Gg0nVerbPtW5JG zJbiH=?D?p-$u03-4kq++eqC3o*xf{!;S$uSK21Iyc)rJQJ={1uLdW)Vyp_kF5Crsd z2qM{kDGu}YRR)-)9Qjl9~NHLgNEh;LC8(OtxH3AH&Dv8ju7Xh>42olCe6fZ?o%-($GjW*qy%0|ryvmeTz2=x~A zFgRtkWRXms6A(8P!_$VXxZzwFO1Mw!Q1?Eg3T!2LtbrkbHWB4roifSNHP162P_lP#g>3{MbL6QNdox)=`BlG(Q+6W$T>g)JFSqEiA z-p@ZEV*2Y|f%*(M_?0*fgAZ@R{NS>{wxqM4289LWqx;RgiVVsG!vE-a;M)&vr9(dy zAY>zH|2rXzXz0E?6XUCSew-Q21_p%!FpZpbnWimi)4hMnGgxnPaysdoQetG6r%IIk zx59RVtc&a0f!r!m2S6&8yti|qbo2Z(x;I9+6%^iF9j`YOuKmOpjQjopdVASp;oGsu z1l;Mm99JNMqdR(!NdaL6;~=tdXd}IEZk@K zKN_n;9N(q=_nz-5G2r>SL#bXGPhPxnZpl7nQilkG{Sm}pUpmsJY@Ul}?A-J>+-d$1 z+!)a&HivX&Ux+$MV8-9pj_n!%4zFNW3Z!U`{EORi+c{7Wck>Op8g>vJP_jFbeZzo~ z?O}62@2ni>EPAnubmK6Tt0RPW8|Ez3Q|g(6>7om3v47^$(+BNvD$8c^C;sb5=J70M zv>Of$? z7J>Ni+RDfYC*6ToL+rgcD)jBbJU&X3p)VXY5pu{W`$vk2i{O>LgLb69ZxQmF(i7T$ zGV>$B$I2}u%q@^pffoex|EYqTdLFpbB%q^&%cf=2Isj)rK+awo!Cl2b=Yu!Cn|(jMx;0S0x)oQJeDTVL)Ot=z$*0F(6Aexh|9W2`VMCWmCFos3~dNz6tsRL z9ib^@=d5^QZ=nMM3Tk~QD>we`=3dT$>gq+&jhdI-fZ?k=*I=XT%aGWa7y9)Gu%Ekd*7MBqqgT1#l}NX9p`wC!bKu6bOO%?Qos zb4Sg%Bap*s&Ay6Z`M!k|VNo`MwB5_kv=9>+?+}sLpe~Aty~Wm~kff!x^W8OV65#qG>W#Xo-v@&+6{} zOtK}ZUKOfmf#R##%a<@Cay#qiHr$uFPHK?os38i5(KA#OXF(J7mPjqB!?bP4#Gvfk zIf*vx(b!RCSdAgxCD2|Y?O>1_S-NeymA?>XZh<0RG*m>-GaC+K5iJ&>DV%~CjC(7w zMPVLovNnMjaX(1!nS=tnmN2Sgp)p-3-emDdnetaI2<}kurEhBVDJ7e!G5jRsRyMvw z%v@I59JuM&qo}1m*kybb8F8J1eX1&YvBSI3PLm5iIL)HsOwMrOJR-9s!#}-SvO)0? zUd%PhkEN|+DB~KVm`zPA;_a2uce#NESmA_0Y!rfanfYAWgi=^NNGt%N07|q3 zte+VakCVWs`kBcebUJk*%;r`zoOc|Q?iEh*7}oeXL|Y!?@NxyuH%ala^@avuR@8N+ z=qL=5PtrH>I)n@xuIwfVB8`NO`a*V50&$xKI#IuQuTNqUsBdlB85z|VW4#&2VHPPb zSpH#}I#7y~OaDPSynj5AONlUm=3#)Zh9ozRtH={GfQs{~l%cB^UqQC-#3X1m@T=vZ z7>+A^Xw4Bn-s&!XWNK!ILO3Ippy*hb%+v^7hai74a#6pq?w=Bjt!zyQN!c%DO%>2> zHduNt+kV&V(-oOmZSg(5$Y$>krYKQ`H+bl0g?3P66*)gYFb~dJ$6DoNjD6X_oGpYe zT2lIkeSry=;0+Lq5nELY;=_|@AB6Nq;*vQ?e5r(g+xm{duuyo>swwl#Wi(338a69zAEA{WV@U_+zC1sUDocJgv{%|($GB-sf{ zB_=s3fHstM|D+E5WeVUPUhMwq!f=rSTv7@yFS?2n@P_w?D^-7Ry#GzFk$Zgia?*`# z?O!oDVOL|gchR}|4(@kcE0QPe!P*udhdRl-M|C=3gXHHbwjz`p!i9LeF*|fO5WuUT zxL3}9kXps;m`O@O>gRQFJ~3GdQw~G)uk96T>aLdfphSl(jUGP0fC_wrR)?5|9)?z* z%CS01Jx2-}bj+Rza(5B$!!`^!G3^cISuGx1u6F!@vsD~~@g>34(@qxS@-%`DmO3dS zH`VNn8eW(;_U3xq@_;Vu>W7*&jO++zs%*dO^8ND9+Wyh47qTGOHiXe}r^;PYG>?KEN85UiMM0+K_;7f}IIk_EM(o(>eBnke<3JpL~ zH`@TW`&=&iUx+@oS=UrNYsENvQgr=!B<9sQ`E}iT|4-I`01>}pjo-vk=8MjO)t?J_ zDWd*4NS<`j^Uf(%OIzPQx`k62{%w0BO7+0cESI<83f?m{ghL2#wG0df zjJ9`VT9NN4Yfx%)J|9+zW{n>r{Im|!iZ?P}=cLj4SFy0x<$sEWZAgVz;&`4b*p!cf zVqtf}|H#Mq?^A9MZ*K>I5W%*PueBdQV|#cOkmUf4P3oD(<_!KvL&oEquW|FO{k1~z z04ubzkc98wR%jPyvde#Mh5l$Xv%1wF?*%E7Doj;Xk?kRd>!K{>aFnbac5gnH3oqK- zId@3{1#JB{GwJwCrV&{&C)j@{%64$NXYY#&&r8H+AhPEstv=UdebMCVnNQD*;yDhU zn9+W8D)=`SY<_6* zAvFpHLIMFT(9>u0-FbD436VBA5kSl}YgN+=6qCmceT0@Mk^Fov0FEnD=+CB3)*7o<1?${mlAwrNu|b|N*jnX+}}_+UyL z4SW{kz9B{2WBk^#=hOPnF_Pr%Bf)%5@6xhlBY&+o1wO0@F;VVgeiM0gscXaDNog;_ z>UzckK4Uq;6EsB5I*u-S_rgowi z?-^)%5M5v{<9C38vFVP4fv)J`;5Ukz(v)-$9hA+QcQZuN-`Epq+48sVba+=|(_>?U zcC(>`U`lBEXL%4S*ypw25;wKy5)xy`c;Rn=A^1uOZ^=Ku4PrXn3({TvE^Jsx2myusC&762>Yd ztKlfxk^0#JHFfkwGhMP8>`u$so`FXRf=MH%2j;};y)Y)}q^Y<&R0M)E-o|Jk(WE9n z3EK9+P!WZthfbmPu3@rpu?)mkji^J+t5B@Dv;q?%qmu~jP#7OS4C8`u;>7S5le|K- zu$WeuY)+)vyvY?H`hw}EH_l(l5CSOawiUcM8j9_5vc69^Oj4t$RY-a(7MU9L*4C{c zAu@hC`ZV0qV&30-JH!M3*&*Z@I=_U21&d8?_cWTEW|@lK+Oq$-Vb~!2+Vk7^v(QAc zh&D+j>_Lww`Td2fuU$;bozV|r+4$(@HUeA3`22wBQM~(x3ux+yVry;h+;Zi6$Bxub ze?x`i`K!9}8LMVQ>v4e0sEhPwrOskgr`E%E-`2m_334iWs`+Mq}6FHz;YDMRrQuf~$?p@Q+Oi34p6k+0uCEkaC zJ)O=8AF(>V4Cn1)aN%`O?HOVjF0AQgcCPTve&9M#Y1pxq>Ij^gmBS6-meC`~kI@gM z9h}BT7pU3F5ULQSG4xsVM#@W%tx}gMT1msgtaVss7y-vle-5Z?RDOWQ+qUaJ{-x}v z1-D7C>hO$SSl^#o-A!N{rV;TkU!DV?2YbaslCpWFk&!z#9A=v6d0z(Jygxb;={x>C z#?AR6zFG90%n)~kjHJKnluxe^T2ko)(o*ZY3QOT33qWso6we#1rA>Q9bvkHFJI#2j zG7Lyn7ONbF+Jt^ey_PErDb(;3>n=@~anY!5~+v3o9o!k$ojxF=T{i z3URH9=F`q67ovorU{8r5s1m(2K&KwW&;2VO>t_yEP@nhANgG0pmXB9T)-(uFcB2Ez zgkd(^2|vk?6QFiUWR*s0Dm#xwY2anG4Y;Pe!Jy^7iJ`*ngviS$y(#s{Nut~khVk$Tt2(hbfMMZmVa*4R@ofRfS*%+=}t?RCR@H4=8@}KypkQhfjHX?J~=|GYo z8nDlvf&Dj*`hT$FO&kJWQ?CWEla2%)1wJ;eHBqsXKJWj}{}e6FIxe!fY>L+bd{H{8 zKW6Qpp10P2UjF~@J#YWc(t2*A$X%uMp;G&*dlEGu_BPA!7lAYyH&<~tVW(@=8&%yb zBr@6YE5gR3lGGtzVUBmgjQBkGP<2@MC-q~eJ>dSMW*s+IVZRu42^)u<|5_aaK!9o- zz`j0z9sXv(rm?7iFUAfs39t6ApDV7UZg7kBMstf~?K$bCrl_kMx=bzFHSYw56xCm? zDibsqMcxFk`@AVo+!0-pZgf(O^wA z7-#%QvagF1;3!OwXsBm;Mq%=l!-OKH0|^5yR)JZF(Bwbr)3b1Np7}q_kCP+c6HB zjILYQh1bSn92s--Ev0za8IAdME|4(i=9La%zNWX2ge?ZUOmPgA4e^~I30{O|1es8N z96?Y4@qSy=#d)ggisCkYVOx-JJo zULMS&lQnoF@I8dQo9z0^)xNp92nYMpm(WpK-nTgT{&$c@NKi>9p>}qy*<^b{Gm680 zo5~T2MNV3Z?ovxRI4&1z%#;aE=rERTk3fopMa7$LYTjD!)7D2OGutHAHmi>Nud{*z zIpXsXZ(X;FMjS2Z&KIp_q3oR~en(#`Kxx?Zn8HxLabd0Uo?osF960RfK(5-S zYd4h={Itwft-j+z+4)I8CtaBmHZCc7qGKsH_zSjK6X*BM3V}3k0(VZTI118OexJ)x z8a-pTgx5;NSk$AD7cm!=eAFdC-4vnox?X)XkXGy4~WU`u}UUH zK`-IIe1uO)oq^I10Ut^hg#CWMTRJbtPeFgH_fnO`LM@I3=;v7VlRv3v_%Y%bh;9wm z(PQbm*bYszh3@ZV_FrinkrY-6bzvPy#4#|e7w2`bGjOSE{v<%FM>V~wO%=j{Ki)^d zCX=$UF#6?M5fcexNScRWeI`gqhRSY_@v&HWKLST=k=hj_FMbqp&GaaaVDor_@oZHe^m*?=zg1eSBT0J+DENAt)qB5dWfU1Qo5B z)w)#J2wZfBBXTy6%3M2EoRwl!80(81C`lKN&6hrkwetbz(#8T$g~2~yn5N?-+1#t! z#1%;=y&hA^rwi*GO3YQD0U=?W_u|p;paJEf5m>tcl{#ilv?dko{rjBdci0v!#r+PK zr_0gsNU8Sh42pB#mCHgRxl>krNlcVM^a?}VeY4t|9z!gL3FU@;Lf4O4&E3=IKkjn_ z9!ED4)fiEFT0WV}&&f8w-9TKH_prD4)~|@PE8u6&=K77dpJ&RfNWyXJf?U47miwG> zupYII@FjYlbq3HAFh?@$B^r1ChUHLH(wPj#m71Pl*uL=IZ#@tYT^9rhw(I9);rEg7 z4fSP(*f?StLXOly*J0w^t0~-DOn&o}yS2(rm75Y!Fbls>6qd}+$mnwivi(TfvY!a8 z>0p?u!Kf56MdIXR`l;W*&ef*BPgxP=r3t@lCGiV#q}&k|vr+>ji57lOtdTuN7jNos z;*7bPCu60!AbbR*AIBvJskRD9Zi5vGxMJ2=#EVDl-?ZlR&h^H)GjMqIv2{_Z(SmU= zloP-I@PQc1xTBo`fSb2bjY>ztNlh6U5b-}#fjP3|v)ZxG z)r8ZVb;wOA=pFiQ=~TKn80!l$+yW&jkLaeRVj6oSbc;2G&^}y$e*D@%z#uftcH9?h zh%1VlsGZrL4=sC&fYm&mS_B5nTj%Mn*C-dz;TOeilW=A}`M&m=z)Ck4B0Gq}0^vSY znBIWzX%L3Ek4U=PjW7*pGqOIVfH=}!Cx*sjqPr6q@waz!YB_qOn6Tn-YS0x;>hAH` zh$Y-J&m;)71_LfjzCR2=Y|iy<4F9g~41!F%Dyv0uguTbM0L z9#Pt2R0+THihnTg*}4u(crB?7&$0(jKWH=iTwmOb<@4pcJ0*DuA4WVq5^;+bcf4cpC zF-V?ffir(7;7@tsl~q{+7LrMLnke|-l_Ylj3%xVfO`KeGWK~Vj?-Z{y+iGhm$B^eV z-Le5Ml-+{B-_qCXPP7(}R1jrz(V|`a2*t_P751Z3F9l!xlB8x~5&8}O{97-`WMDpH zEjy9yJL4{Fkwq@_PBwp^_)r4zq0LIyV^Ji8UZy?dpxo9HB$S#Pr+f#+(on176lC@r zlGBGbXb6DvI!X#r#vNjF>F656VL(y(e45oM{7OgWK#D5g*M@f><`C1{17t zX*38HYzuhX-0|=R6AN3VyHGq{5=5fFBEY}&d69)lPlYQ!-PwuOO^r|7eG5*j0 zR?zcxXV4(|)7PHX57C$BC+3@85(jx0-kV~?*7{sVsHBmB6j{_nELc5B$f(-LGcS{)Sw`1yL7m{63`!%@QyssC-}2h9j4-I7OM((&Ye8S; zNr~&<&TFqOc>&Zdm+wnvgFh~DXL5`BO}IGAbW&7u4eRD?mll*u@M#DzCMwHbp>k>4u*WR=J z*RI`Ue0;7y_Wt$b`eYesyz{xc+>gO$`y>|dc)I!(XuK2kzT5-t4g-yMKH!T2#@}1L zAwc8ZX1nM48Blw7w%+>T{-*j(v*Y&;@75*I_|4Q=F+jWjvBUp;qlXK#!{(fp zyAu$xx#$KKbgX^_l-^b2;x^hZK9}CDiFCUBztrAUj{YyE1v;1>cUIp0lU;}o`2Sj- zw;sk^wRh`_Pj-FWKA&&Y8Pf#bWBpxmCxuDZjenkP4lXt*Yeir#+ox)D7T|a};GB%6 zjn8)4*58&*oBIkE_A5bN9Q}U;_8B0sYXIdcvAJ*YeTwjiuXlc{nO}8!*m@=AyxWPb z`S%9HR&B4SQ!P)&ZuZg5`^Moq6(z^ouv7VRZRZNK4cFMTe1rN-cH4H$pHHS&O_FH? z-Zg|C!0wi55Ur8&sf%h}fpX7_cSOV!cCI`zO@&}L>aV5y+pjMs zaVH-jqW-l)PMR*q!wp!Q2@JLP5tQy$nvDK6;xE!|`8BKV4v9#*-psZ>OJ@7p8XQ!j zZ`zxynH%S)Z(;TZjKhhs=ExY`JY^im@Xsdmdu!#JjJDeN6y zYeV{9RZy~o2;iUQb9E)c!Va?gp2X8fG!PCsB*FI0P-#_sV{;DRHK32crfUl^1TNK? zGV`lYg1z^F*;*NH8J%UOwIwyf+Ty&6m^|@S43|;FP*6;!uR0O~v8XRF3_>`0i>cj2 zu;E(g3mQJHYNX?}va9}3KT+*nj%s*6W4JW-l}cS9q<2Pqj#eIV*H+jn^N_=BY|=x@ znDx`kG496UJha!f(_x_|p>dsjn$eqwe(l4XN>9vZwHU%DTxXZ}BL1qob#2qr@(FW2 zGwL z799XaCrlw?puFak2^Bj!~tcA;5C#u zh>H=A7k-(UGQF!9^LK%};u%N@)~x!aGVROld=6Gpi5I__UqgE24`9CIdk4+dIg%5~ zSD<%B{>~2P4V!W$^S^)}%4<1}VSZe9-WlRtZQqMx737W~X_Y$^a-&Y8jJZ$3toilv zB$&{l;KU6lfHAN3YM&Tn3kF%m;p{O5<%ryz&A1}+?MV>|>OaV#qpKF1&tmbvNz>bU}$(QZy_X#I$3i>`~)Hm#F@te>D-ZZPu=LrmRVrC?S*xR>UA zHenSTTp?Ef;-->}+zaPN(o%6h0hA*s7J9!u7z9D>d3h~5indXIo6YSRzUlQ{Np1;x zthINe*VL?X@kGICghr+%0tJy*ig$3*NPLza~KtB9D)7X7Mx|jvyiv55U%APah^(M0)T*caw{&hI39-`2j&NEpOD7`!zA%!c3U$d9kJRP5S zjG+aQKhsLeD*z*w?N-o>q{p%o{j_Eao=D+;#q(yNPKn!|yNI`9{k@Fz?en=%X$UG3 z!1M}h7H8+T+z7b=hajTDX$8e$^%oE0Sfj=_@*4WZ=fy!kp95HDgdtVgNo~6$@h@fs zcI-(j?RK>2Ft@N_f^;CxHYTc^roWQ)lF#J`+e}$%W;K^>5{()2df=wdQdbbnzK$l1 z@p|$93Q9Y3i1a(V!23$?6i(n~0lJ<*pM6Ow>8hFhT)z?To}rS_YhXnQhdyuWT*Tnb ztX9a98H=FJX6srkHI#q|l>*nJM!nv7HDT$rg{J9{^A$>ALJxTjArM9vOb#|u7| zPEa7pI(h%x4B#+E@UWo~`|V!1pxwH|$5XtB-!(qN^J1#Z#m2dvYF#7XAUts+ET~o& zxwI5Fz%P~yada&UA2;9}g^z7;)W#w(f_o^+aE~iU;s;j&Y`{7mW@N>V*c|~*W{@A{ zErWghNGfHeWAc4<EwK*K2PF@qFbw4kzM43GGv#Dzj~BqIeqAyHG7iU}G%3&+F`F zZ?9Y0`3T$oa5|(&i9NQZ!I)|EQFtJ)uK7|?G-_CKt3f$@wOF7k4Nt-}7-6?h)FP|D zT|#%)h0jo9fvgZ*a;s6xt=gPP$NcE%Oa8Qa;NJTU+=}SAd8Z}vP)zbT)2rn^g*Z5r zpRp}K0ik(+D;p;f*Y}Ogo3YZG0A;vV9cG9)3ku!IF@48#I-tlV_F{JbB30SoMast= zI10g-d-|24GN16&x9^T`=UPPkw>SgV>{^h<`g>diwL1s`bSZg;vN-B~HwBkT=1L_t zQ|P;J1a4joxR?lDa*aT{(2b*!Zatzx{58Bt{kV)}KhJ~iqqM?F#}`a7{X$<095%dA zV&UGlyiXD*>S#zKbf6ioZIXbIW8=maX1$C8gUjiaKF+#xEUSl_C|$=*WbE>3yps%JGZo8S9SeEF5fVOw(pZX{zQQzmBg52kWb_e zMlqHXliY6TF+H>Ykzv5G599;nS46L*2om2WKA?B`EJFt2rY=#-oGGgQN>7wNd;8-5 z=wMeEr=YsMjJj@4In>ixbiV+Ojwq~GvT1JlvrAwgJfTTM9`GePARe4iq|(7l<1Qi> zq5rO4SB3Yz>mbXUX5Dg>agu*YYF8My|MF1JQdsnlc)LS6oSG%#1^aUSOPJHfF8{Y$ zct3RG)MAvwJi#g+Zv~L$WJO-r76yq-Cl<4R1U1<|5-`x8S9M#D5k`@P`F(6w3BVh( zmo$Br-C$KvH=t)Hv-!T50GO!b#pmYX4lfYo?w8Hg|Mg>%n9tGB&@e+wq%`&sXwwV)JNWjmo1RKc z%z&WJr#MeS7d4-FULk(|Hs=wg`+>)CO3BM zSmNLyJHtnJdXb&{kF{FWo0%}^#J_(b=t;Fn7`JgZcGukKc^a69zOs~)ED2V!x=pvJ zf?G_U43+p6X8VL#)Qnq6Gvzi}i~WiK9+lUb^54RG znjRVa^)+P_B}bLwikS@w8v_3j{kp6QWpb#}j)oOfl=$Dz2VK6~jAQiKS*$;!s$; zRq62QQm{~K;K)iWu1xr5G@Lp`))1#T_G%)>M?UMTog31Uljq6Nhu<+GKVQ0>rRcgZq8r)D@-YEZ_4#)lvSMLQU z=%!_=Ti*Uvw)G5>DAw+zAGYoWcUz4IHx=&Ju|+nb$m@smCbmSflVC}NyYelUnM$(& zkg^Nq;5kkAxDep{zHEVxZjk3wC5h)wHbe#NPL`;rr=OidU&s_(>DB}G?};0tuCLh2 zVMiWoSHhqaFe&Bw0BI68-y_|@RP2K18w%}FY{ZpN9bHVs3;xt`0hJB&fJ6*atc zl#(rgid!Q{Y2#y3OXLY5eRPO(xtjpORWfNO6Z3dt{kACu{UZ_Lb|H<-)C;@UwW}ZL z6#J{*6D@*SDqYtRiXkf(e|IFY@wT2$$ks5 zM3m>x@p4sQ7(*zEORA0kSO_K&i-9KN_3jbBH8QGpRDmWO_g}a62sHIPYI|n|0VNg? zZxUgQAAlch-(4PLMaF`FW%YuU++eatwO+5Tw4mYr6a3|W{h#r4yJE84I9ET!? zl;reZqy9->Us}~dacWh9IG(D2il$G;jud#DUB`b!vf8 zSng(6TUa8Z+=a%4d`krBBa$&<-WH?0rA|271Z&-ccsltwff&1^ZsKoqC=UOE`_qRY zp1Kg}h&gpxRnuR>F>Ps=^Kt}maz&&iMuFnSg4iS8fpHJuzr3c~8e6>oW7!@Yr z-l=6NtMec+>ok@xl76-%@6^JcF|Jp@nCXm4kVV|NxFp>=MM zWb^U(pTQYt>J_P=^B`y|$e?Z~E-F{sXyZe%Rvp$IdX@HKp{}Px@uYk|H_T7kY&B=#f;u<2{??F9KzH8!{I>kX#L3aI%kS>;?$-fe*M5M2L4Y(p2Y!8f zwgITo%?@uao(~VduRp*3Vhr4GXE5eqI#0~^=JaU3Ozo4fH}HV#>udK)o3}+`Z=o9l z4a8sN15Z^BzW@XL*rSfOvP<&xbHk|c7PM*2qtpD&GN|i)*f-132X_tfpEhqu^@01h z@cdoDAC(&1h0cukTBsb}J=+HX&DLBXL2sA?)>#N$kcy$4Ld{LBnS0O>NPpX#h`l=Y$H3_vG9*`BA@bhAw6rJ!NkT}<{VwS6r)@7{)BZ$`W(oa*M@NLt?{oi;KCusogV%LPrFoO$hL(fi`9kk=S*2ds+r||0>4a^RQ03 zUvv9a$cQKEyH4q#%jzt_MJ}z!u=VNg7FRLTr~HKk0x>P0JXV$QdS>ocT7$OuMsfXh zgXqqdo9u7Jw&Gm{e&ePw;YsGwQp;P-AKOWD?QPzsW`s;uubd0pL>SQdnjYu6shQ%s zq+iR1YE$oT*x(ewz$)N#l+WtxeT`oEMlH>xp6$*vjUc$h|5WusdPs~BPML5(OI^ao zkJ19oETi)kpIKP1w?S+p)x?k|(ksqELsVxm>&UnKK?Tip#A9F;^nzKfY?(p#3{^(0 zJD5{Jh&Ur3GKZrD1G^DpVy&Hg6gsUzcKyI9j+*`F8!u|;7g;mfV~LEg^>?)kitI>y zp~MD>TWc!G@4x6Vzp*UPV9`uwu7|GKvJc2Kf}3u0j%DcC?`TtX8aO%xUm z8lj}9!z_LaQ3wR~<*EFWCIS!buZUZIqzE(5nxJ1RcekrRi7dksbhEVvb=Y$2Pu4-q z%vIXrd&2RnuGwy=4Aq-h`$)b^MM(H`*GM7}`gf)_gjbM1#xHZv_%>#7nL zVETb_)#QS6sKG=A8flblpa@k0b@BvFt{;d=$Q75P5vqQCi~n-S>0Mk(PR6Sg{e}@k z{t_hKxON3sIEInL4lN`aSCmK_$c&G<#}1cesgsvL1tUIA(${g0JximMvqb^fQ056UES4eiaNNaz=I_HY{Q9Q1zS0U z*kv$dtBKm>*D`RxW2V^RE|*Np2oY`Z3D%HUyL0txs9nh)@4`jGK^9{T6GzeoQJUaD zlbTw)fdUHoQ*=`!WfyV}R|g&<(8xMKzNmo3G%A0mKFD_wVLnJBo*&nagsFYEHeq=j)jLhLU*--MKC4k}y9SgcG z2NU9XG?J+fqV=|C)!c~Aca)5rZx*vDCv7K@1PGcLMn)+oS*aFS8m$Kj0yr}%x|FTQ z#P93JB6%J&H+@~tKR=9bKMn{y(u%R)-nU4Sb%TSkP6_eP8yDHP4UkBb;+9cDa{8-| zHrfyZBvJ!a4oo8g(FsiTG-z52#t6GNlk$Il;nG<9kQ^)!D^@ykEIu=kfOKy)K(kqg zi#Elh`bX1dY0GmLethWE#;^86VQ>Dz>G3nB++PTq6QipFsXl?fXoMP`U!TkB{~_)z zUAwKC^R)VX%Q*df2i98Qt5G%iM2K5b!9XJBCJ zA8ZMfsZMktD|IgrOKx|hYFu}xI7X7!P)+U=HvrM{S|8}*_^q2+EW}hBIKcByvnr+F zLp0Wqc*Ly~LbNdX6k0(8Ns=TXmRS$$IK|o)JV8H3{Xq`aNB5C0f*`S13UFQEPd+NA z>p1CCN^E+;VFjO~s!BG9$x$Z|rq1%Ap3PjNoG=4I;8BEU`nF~{eaFHVehI}93g!aw z)DhYo70vmvOk3HUE+0y_vDs`+$+TG?eW3SSRu(cTOLe8_E|lJu(P5HgXpSgqAW>x~ zO-4+X^vzmv8VpvDWD?F1WO^^?bApa53PAD0uT00}9BH3yH~-|jQnMb-vMnjL2gGJ6 zK|GS3G(y+;r-~c9D*xU2n^HqG7Qw`8f{HjtxCJ3C)q(ZF`*6@Q^}aL3Jy-T~E85~n z#r@mhS9qW81(}6^Hs0HUHf1^yQwtPNwnpT)B--X9I}m6f zU-=--Hi_$0JVx8*4p$~L=8*pkTq=%&Z0mrNi{0Q&K#V*Ulq_18?UGwgu`4)8*P@Pa zpiu!MeRV%&uL}yfh?J_FCSssB81`m92&Rop7f6X@$HhfK%*#`%^ayWX!`=gUn1UL9P{)Q zhtl2?sqUqo{0u|6S49q5O`bXd2fT^zd-H~76hB^oY(rN&?%F5PSaz5cmsx#Q$;b~U$?4#Fqk3)^&3L?ATnZMN{C8y0l^jZRt6!ZdS^~N zghLav*woUk|9dFOyr(XDdaI*AvO=3&ZQGvR6TTCh0PP+%@0b>R`*#X-<$9lv=1_({ z95d~0Bi!?>@iXJ(L52+>+>s7q2Hj9yTGk~~wfPp%^+XB!owOAO$*!eQn7H2nsf9%m zPP_Fq(l;tDL_-Bj?;GULpBG*^fn>{+`(9HqyN|*~&Xy*7Gec-)@np|*zu%OVvA+FG zZaDecw2BF7^@WPRX5tVcd8hvSQ*{g6ts*^z$kG%7p)eo1)!&yA)F&BG+M<`#=a6;! z=EbWtfXZpOMl_oxcjM-=F?<2sKTIFmoz%#Q#`GNh&I!*DzLTfcdo}%ch5~v5310~$vhlZ;rLBKL+ z3(X@MQM4%4Lak+Uc76Ral>s)o#|!%!Wt#4(?EwpM`Y0)@t(JRDT$({6tg}F}|rS zgdZg4cfL3)an(t*>=+`Zz*y;w@Auhv(O}HO9<;!Ra)LAW4ZWf(&j@qIZ3@`?u61gVl%i#Jw4jw9XOlyB53PQki0`%?=KF6kHcwqQZCnPC_wUzY3SPYL|8 zO$!i|7|EH|5#DALux%6ZP6+++b7sOY`w-p~;Z&$j=%)Tn>mc+KtQA-s1JL+msh z1A;#AFNVMQgW*F=A0IH>tcaVpy8E?PI0@t}>A8SpCKrU!jy}f|xoUV=Sj?KOQY#Pt zt-izuG8k4tQ6MJyJ(Y%uNuY@S-OdiJaU)n*ZGr3c$a9{~^AiR|eboW^zc2Nqy~kWo z5jzu9N{|iJpsScNBgR`Q2fr5uQ`}Z-|C-JYXSun&kNc{VUCRE05j;z}A+I$<@-`jJ zhKhAfVA`#E7jyTAe@esbx>;C95lJc34~=Sy8P4(*D)!1yk0f$ z|C;iKTEh7kZpuxzd(ge|xlI(?JM@Wk#C>JD-st@HY!p}ANwdF1&%H@r-tuV_$n0$oqES?_3K6wn1M73r$K3r(@ zNf9G&Cr;|M5pT|gLNzWKi`%>?7y@pI!y9dsy& zw}9QlEW~vL&9U>|%c(G(_Bf*BAGYQ|Ub(8yBl7abrKlX?PtCSqvZL%N;)~ZQ9uv zCia}J%*+j?6mk7QOpvi`*SSGvmv=Mo(d_ zw&KVLs4eM!ns01Up_pzS$Y<^g)3V64v3a! zV+{Y0#T^b)oqWe4AVj+b-OlOZ7wAPprYhzdL^Hd$ED}(-TN_`+0MJVPOMTo*7P7MR{Dg@1dQ#*62d(IYIAOc8mLqXn?mT(IYlwl zRM({?b}@dA8H3Lf$t>TqF*6EQ1MeWK7BKI^%O51qeok1jB@!dAQol8u8Zzv)D>A4b zjSn+bD2Mbf zg&@7^DTXrxhCu$PV+xX>81DXY0Sc$F(zhfpSvHm&ElM5S30MOU&7-NcC67asn?R+N15)IL{CD4g_Ta~26Xq{;zi*Zv zB4$p%FKlF*hlxjOy{+1rx5cqM zz^@4uxmsU_kms3BlLQ|7O5JYh<>F*y(^#s<4E;_i=ej4Yq&DD(872wJ+hX}AD;ZW^ zGy7Uq?nosJpG+SOX$cWL0HU`d?k91fu{&Z6A1c=MESPkfM!>@ zfovTQf%R0qUCV4l2mZUNPSzwHOz~YG3B)!L ze1DS%C%29?5~MOwZb`H^G+W8DFO-(pDAKK{hpcR;ZTX*LVlu;>9mnNdz%V;fo8q)? z<`GN5r2yhACDy)IKi<4r6!n-7K(7v6uP9o#C2{7K+Jze>;B|WwP#kPG9aqPge8L+O zO9u+HzBtb!*NU}jTh(V}DD+gKhPgQKSTWM`jLS@Wv!sJ(vx#93|BTI!bX4 zj`m_CP_)^%Jv?$md{gQMj6xjyHkde1_;^3?UVI@1)9Nx>8S(^n*ukgs+#M|eeZJJg zO78G6q5`VGr@Vtl }w=MUIa*OPX<`_1%QfOX{U3 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 diff --git a/docs/static/img/services.png b/docs/static/img/services.png deleted file mode 100644 index f0bd89399571a1bfa0ee590ec5d10a4d020fb4cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 80153 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 %mshP;Q1| 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⪙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

9|m9x?Tv^iCCvK6}>LGQzp4Yc0FkU@E)qZ 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 AcmMzZ diff --git a/internal/graph/graph.go b/internal/graph/graph.go index 1197bbbf..73081fc9 100644 --- a/internal/graph/graph.go +++ b/internal/graph/graph.go @@ -6,7 +6,7 @@ package graph import ( "context" - "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/azcore" arg "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph" "github.com/rs/zerolog/log" @@ -41,7 +41,7 @@ func (q *GraphQuery) Query(ctx context.Context, query string, subscriptionIDs [] Query: &query, Options: &arg.QueryRequestOptions{ ResultFormat: &format, - Top: ref.Of(int32(1000)), + Top: to.Ptr(int32(1000)), }, } diff --git a/internal/renderers/csv/csv.go b/internal/renderers/csv/csv.go new file mode 100644 index 00000000..6cb5f1c6 --- /dev/null +++ b/internal/renderers/csv/csv.go @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package csv + +import ( + "encoding/csv" + "fmt" + "os" + + "github.com/rs/zerolog/log" + + "github.com/Azure/azqr/internal/renderers" +) + +func CreateCsvReport(data *renderers.ReportData) { + records := data.ServicesTable() + writeData(records, data.OutputFileName, "services") + + records = data.DefenderTable() + writeData(records, data.OutputFileName, "defender") + + records = data.AdvisorTable() + writeData(records, data.OutputFileName, "advisor") + + records = data.CostTable() + writeData(records, data.OutputFileName, "costs") +} + +func writeData(data [][]string, fileName, extension string) { + filename := fmt.Sprintf("%s.%s.csv", fileName, extension) + log.Info().Msgf("Generating Report: %s", filename) + + f, err := os.Create(filename) + if err != nil { + log.Fatal().Err(err).Msg("error creating csv:") + } + + w := csv.NewWriter(f) + err = w.WriteAll(data) // calls Flush internally + + if err != nil { + log.Fatal().Err(w.Error()).Msg("error writing csv:") + } +} diff --git a/internal/renderers/advisor.go b/internal/renderers/excel/advisor.go similarity index 73% rename from internal/renderers/advisor.go rename to internal/renderers/excel/advisor.go index 6b5d83e3..57783a05 100644 --- a/internal/renderers/advisor.go +++ b/internal/renderers/excel/advisor.go @@ -1,33 +1,31 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package renderers +package excel import ( _ "image/png" + "github.com/Azure/azqr/internal/renderers" "github.com/rs/zerolog/log" "github.com/xuri/excelize/v2" ) -func renderAdvisor(f *excelize.File, data ReportData) { +func renderAdvisor(f *excelize.File, data *renderers.ReportData) { if len(data.AdvisorData) > 0 { _, err := f.NewSheet("Advisor") if err != nil { log.Fatal().Err(err).Msg("Failed to create Advisor sheet") } - headers := data.AdvisorData[0].GetProperties() - - rows := [][]string{} - for _, r := range data.AdvisorData { - rows = append(mapToRow(headers, r.ToMap(data.Mask)), rows...) - } + records := data.AdvisorTable() + headers := records[0] + records = records[1:] createFirstRow(f, "Advisor", headers) currentRow := 4 - for _, row := range rows { + for _, row := range records { currentRow += 1 cell, err := excelize.CoordinatesToCellName(1, currentRow) if err != nil { diff --git a/internal/renderers/cost.go b/internal/renderers/excel/cost.go similarity index 55% rename from internal/renderers/cost.go rename to internal/renderers/excel/cost.go index f1a3fa3f..29301519 100644 --- a/internal/renderers/cost.go +++ b/internal/renderers/excel/cost.go @@ -1,47 +1,31 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package renderers +package excel import ( - "fmt" _ "image/png" + "github.com/Azure/azqr/internal/renderers" "github.com/rs/zerolog/log" "github.com/xuri/excelize/v2" ) -func renderCosts(f *excelize.File, data ReportData) { +func renderCosts(f *excelize.File, data *renderers.ReportData) { if data.CostData != nil && len(data.CostData.Items) > 0 { _, err := f.NewSheet("Costs") if err != nil { log.Fatal().Err(err).Msg("Failed to create Costs sheet") } - headers := data.CostData.GetProperties() - - rows := [][]string{} - for _, r := range data.CostData.Items { - rows = append(mapToRow(headers, r.ToMap(data.Mask)), rows...) - } + records := data.CostTable() + headers := records[0] + records = records[1:] createFirstRow(f, "Costs", headers) - cell, err := excelize.CoordinatesToCellName(2, 1) - if err != nil { - log.Fatal().Err(err).Msg("Failed to get cell") - } - - err = f.SetCellDefault( - "Costs", - cell, - fmt.Sprintf("Costs from %s to %s", data.CostData.From.Format("2006-01-02"), data.CostData.To.Format("2006-01-02"))) - if err != nil { - log.Fatal().Err(err).Msg("Failed to set cell") - } - currentRow := 4 - for _, row := range rows { + for _, row := range records { currentRow += 1 cell, err := excelize.CoordinatesToCellName(1, currentRow) if err != nil { diff --git a/internal/renderers/defender.go b/internal/renderers/excel/defender.go similarity index 73% rename from internal/renderers/defender.go rename to internal/renderers/excel/defender.go index fc473fd2..931b22cc 100644 --- a/internal/renderers/defender.go +++ b/internal/renderers/excel/defender.go @@ -1,33 +1,31 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package renderers +package excel import ( _ "image/png" + "github.com/Azure/azqr/internal/renderers" "github.com/rs/zerolog/log" "github.com/xuri/excelize/v2" ) -func renderDefender(f *excelize.File, data ReportData) { +func renderDefender(f *excelize.File, data *renderers.ReportData) { if len(data.DefenderData) > 0 { _, err := f.NewSheet("Defender") if err != nil { log.Fatal().Err(err).Msg("Failed to create Defender sheet") } - headers := data.DefenderData[0].GetProperties() - - rows := [][]string{} - for _, r := range data.DefenderData { - rows = append(mapToRow(headers, r.ToMap(data.Mask)), rows...) - } + records := data.DefenderTable() + headers := records[0] + records = records[1:] createFirstRow(f, "Defender", headers) currentRow := 4 - for _, row := range rows { + for _, row := range records { currentRow += 1 cell, err := excelize.CoordinatesToCellName(1, currentRow) if err != nil { diff --git a/internal/renderers/excel.go b/internal/renderers/excel/excel.go similarity index 96% rename from internal/renderers/excel.go rename to internal/renderers/excel/excel.go index ed7e5be6..2414e181 100644 --- a/internal/renderers/excel.go +++ b/internal/renderers/excel/excel.go @@ -1,19 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package renderers +package excel import ( "fmt" _ "image/png" "unicode/utf8" + "github.com/Azure/azqr/internal/renderers" "github.com/Azure/azqr/internal/embeded" "github.com/rs/zerolog/log" "github.com/xuri/excelize/v2" ) -func CreateExcelReport(data ReportData) { +func CreateExcelReport(data *renderers.ReportData) { filename := fmt.Sprintf("%s.xlsx", data.OutputFileName) log.Info().Msgf("Generating Report: %s", filename) f := excelize.NewFile() @@ -23,7 +24,6 @@ func CreateExcelReport(data ReportData) { } }() - renderOverview(f, data) renderRecommendations(f, data) renderServices(f, data) renderDefender(f, data) diff --git a/internal/renderers/recommendations.go b/internal/renderers/excel/recommendations.go similarity index 71% rename from internal/renderers/recommendations.go rename to internal/renderers/excel/recommendations.go index 513e95ac..c1cd9855 100644 --- a/internal/renderers/recommendations.go +++ b/internal/renderers/excel/recommendations.go @@ -1,37 +1,36 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package renderers +package excel import ( _ "image/png" + "github.com/Azure/azqr/internal/renderers" "github.com/rs/zerolog/log" "github.com/xuri/excelize/v2" ) -func renderRecommendations(f *excelize.File, data ReportData) { +func renderRecommendations(f *excelize.File, data *renderers.ReportData) { if len(data.MainData) > 0 { - _, err := f.NewSheet("Recommendations") + err := f.SetSheetName("Sheet1", "Recommendations") if err != nil { log.Fatal().Err(err).Msg("Failed to create Recommendations sheet") } renderedRules := map[string]bool{} - headers := []string{"Id", "Category", "Subcategory", "Description", "Severity", "Learn"} + headers := []string{"Category", "Impact", "Recommendation", "Learn"} rows := [][]string{} for _, result := range data.MainData { for _, rr := range result.Rules { _, exists := renderedRules[rr.Id] - if !exists && rr.IsBroken { + if !exists && rr.NotCompliant { rulesToRender := map[string]string{ - "Id": rr.Id, - "Category": string(rr.Category), - "Subcategory": string(rr.Subcategory), - "Description": rr.Description, - "Severity": string(rr.Severity), - "Learn": rr.Learn, + "Category": string(rr.Category), + "Impact": string(rr.Impact), + "Recommendation": rr.Recommendation, + "Learn": rr.Learn, } renderedRules[rr.Id] = true rows = append(rows, mapToRow(headers, rulesToRender)...) diff --git a/internal/renderers/excel/services.go b/internal/renderers/excel/services.go new file mode 100644 index 00000000..08ab225d --- /dev/null +++ b/internal/renderers/excel/services.go @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package excel + +import ( + _ "image/png" + + "github.com/Azure/azqr/internal/renderers" + "github.com/rs/zerolog/log" + "github.com/xuri/excelize/v2" +) + +func renderServices(f *excelize.File, data *renderers.ReportData) { + if len(data.MainData) > 0 { + _, err := f.NewSheet("Services") + if err != nil { + log.Fatal().Err(err).Msg("Failed to create Services sheet") + } + + records := data.ServicesTable() + headers := records[0] + records = records[1:] + + createFirstRow(f, "Services", headers) + + currentRow := 4 + for _, row := range records { + currentRow += 1 + cell, err := excelize.CoordinatesToCellName(1, currentRow) + if err != nil { + log.Fatal().Err(err).Msg("Failed to get cell") + } + err = f.SetSheetRow("Services", cell, &row) + if err != nil { + log.Fatal().Err(err).Msg("Failed to set row") + } + setHyperLink(f, "Services", 12, currentRow) + } + + configureSheet(f, "Services", headers, currentRow) + } else { + log.Info().Msg("Skipping Services. No data to render") + } +} diff --git a/internal/renderers/overview.go b/internal/renderers/overview.go deleted file mode 100644 index afc51b91..00000000 --- a/internal/renderers/overview.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package renderers - -import ( - _ "image/png" - - "github.com/rs/zerolog/log" - "github.com/xuri/excelize/v2" -) - -func renderOverview(f *excelize.File, data ReportData) { - if len(data.MainData) > 0 { - err := f.SetSheetName("Sheet1", "Overview") - if err != nil { - log.Fatal().Err(err).Msg("Failed to rename sheet") - } - - headers := data.MainData[0].GetHeaders() - - rows := [][]string{} - for _, r := range data.MainData { - rows = append(mapToRow(headers, r.ToMap(data.Mask)), rows...) - } - - createFirstRow(f, "Overview", headers) - - currentRow := 4 - for _, row := range rows { - currentRow += 1 - cell, err := excelize.CoordinatesToCellName(1, currentRow) - if err != nil { - log.Fatal().Err(err).Msg("Failed to get cell") - } - err = f.SetSheetRow("Overview", cell, &row) - if err != nil { - log.Fatal().Err(err).Msg("Failed to set row") - } - } - - configureSheet(f, "Overview", headers, currentRow) - } else { - log.Info().Msg("Skipping Overview. No data to render") - } -} diff --git a/internal/renderers/pbi.go b/internal/renderers/pbi.go deleted file mode 100644 index 54b95c3a..00000000 --- a/internal/renderers/pbi.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package renderers - -import ( - "archive/zip" - "bytes" - "io" - "os" - "path/filepath" - "runtime" - "strings" - - "github.com/Azure/azqr/internal/embeded" - "github.com/rs/zerolog/log" - "golang.org/x/text/encoding/unicode" - "golang.org/x/text/transform" -) - -func CreatePBIReport(source string) { - if runtime.GOOS != "windows" { - log.Info().Msg("Skipping PowerBI report generation. Since it's only supported on Windows") - return - } - - if source == "" { - log.Fatal().Msg("Please specify the path to the Excel report file") - } - - fileName := strings.Replace(source, ".xlsx", "", -1) - log.Info().Msgf("Generating Power BI dashboard template: %s.pbit", fileName) - - xlsx, err := filepath.Abs(source) - if err != nil { - panic(err) - } - xlsx = strings.Replace(xlsx, "\\", "\\\\", -1) - - azqrPath := ".azqr" - if _, err := os.Stat(azqrPath); err == nil { - err := os.RemoveAll(azqrPath) - if err != nil { - panic(err) - } - } - err = os.Mkdir(azqrPath, 0755) - if err != nil { - panic(err) - } - pbitPath := ".azqr/azqr.pbit" - if _, err := os.Stat(pbitPath); err == nil { - err := os.Remove(pbitPath) - if err != nil { - panic(err) - } - } - - pbit := embeded.GetTemplates("azqr.pbit") - err = os.WriteFile(pbitPath, []byte(pbit), 0644) - if err != nil { - panic(err) - } - - unzip(pbitPath, ".azqr/output") - - replacePath(".azqr/output/DataModelSchema", xlsx) - - replacePath(".azqr/output/UnappliedChanges", xlsx) - - err = zipFolder(".azqr/output", strings.Replace(source, ".xlsx", ".pbit", -1)) - if err != nil { - panic(err) - } - - err = os.RemoveAll(azqrPath) - if err != nil { - panic(err) - } -} - -func unzip(source, destination string) { - archive, err := zip.OpenReader(source) - if err != nil { - panic(err) - } - defer archive.Close() - - for _, f := range archive.File { - filePath := filepath.Join(destination, f.Name) - - if !strings.HasPrefix(filePath, filepath.Clean(destination)+string(os.PathSeparator)) { - return - } - if f.FileInfo().IsDir() { - err := os.MkdirAll(filePath, os.ModePerm) - if err != nil { - panic(err) - } - continue - } - - if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { - panic(err) - } - - dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - panic(err) - } - - fileInArchive, err := f.Open() - if err != nil { - panic(err) - } - - if _, err := io.Copy(dstFile, fileInArchive); err != nil { - panic(err) - } - - dstFile.Close() - fileInArchive.Close() - } -} - -func zipFolder(source, destination string) error { - zipfile, err := os.Create(destination) - if err != nil { - return err - } - defer zipfile.Close() - - archive := zip.NewWriter(zipfile) - defer archive.Close() - - err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() { - if source == path { - return nil - } - path += "/" - } - - header, err := zip.FileInfoHeader(info) - if err != nil { - return err - } - header.Name = strings.Replace(path[len(source)+1:], "\\", "/", -1) - header.Method = zip.Deflate - - writer, err := archive.CreateHeader(header) - if err != nil { - return err - } - - if info.IsDir() { - return nil - } - - file, err := os.Open(path) - if err != nil { - return err - } - defer file.Close() - _, err = io.Copy(writer, file) - return err - }) - if err != nil { - return err - } - if err = archive.Flush(); err != nil { - return err - } - return nil -} - -func readFileUTF16(filename string) ([]byte, error) { - // Read the file into a []byte: - raw, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - - // Make an tranformer that converts MS-Win default to UTF8: - win16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) - - // Make a transformer that is like win16le, but abides by BOM: - utf16bom := unicode.BOMOverride(win16le.NewDecoder()) - - // Make a Reader that uses utf16bom: - unicodeReader := transform.NewReader(bytes.NewReader(raw), utf16bom) - - // decode: - decoded, err := io.ReadAll(unicodeReader) - return decoded, err -} - -func writeFileUTF16(content string) ([]byte, error) { - e := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder() - - var b bytes.Buffer - - unicodeWriter := transform.NewWriter(&b, e) - - _, err := unicodeWriter.Write([]byte(content)) - if err != nil { - return nil, err - } - - return b.Bytes(), err -} - -func replacePath(source, xlsx string) { - contentBytes, err := readFileUTF16(source) - if err != nil { - panic(err) - } - content := string(contentBytes) - content = strings.Replace(content, "AZQR_REPORT_PATH", xlsx, -1) - contentBytes, err = writeFileUTF16(content) - if err != nil { - panic(err) - } - - err = os.WriteFile(source, contentBytes, 0644) - if err != nil { - panic(err) - } -} diff --git a/internal/renderers/pbi/pbi.go b/internal/renderers/pbi/pbi.go new file mode 100644 index 00000000..1eb26738 --- /dev/null +++ b/internal/renderers/pbi/pbi.go @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package pbi + +import ( + "os" + "path/filepath" + "runtime" + + "github.com/Azure/azqr/internal/embeded" + "github.com/rs/zerolog/log" +) + +func CreatePBIReport(path string) { + if runtime.GOOS != "windows" { + log.Info().Msg("Skipping PowerBI report generation. Since it's only supported on Windows") + return + } + + if path == "" { + log.Fatal().Msg("Please specify the path were the PowerBI template will be created using --template-path option") + } + + log.Info().Msgf("Generating Power BI dashboard template: %s.pbit", path) + + pbit := embeded.GetTemplates("azqr.pbit") + err := os.WriteFile(filepath.Join(path, "azqr.pbit"), []byte(pbit), 0644) + if err != nil { + panic(err) + } +} diff --git a/internal/renderers/report_data.go b/internal/renderers/report_data.go index 416e8e2d..d26cad60 100644 --- a/internal/renderers/report_data.go +++ b/internal/renderers/report_data.go @@ -4,15 +4,107 @@ package renderers import ( + "fmt" + "github.com/Azure/azqr/internal/scanners" ) type ReportData struct { - OutputFileName string - EnableDetailedScan bool - Mask bool - MainData []scanners.AzureServiceResult - DefenderData []scanners.DefenderResult - AdvisorData []scanners.AdvisorResult - CostData *scanners.CostResult + OutputFileName string + Mask bool + MainData []scanners.AzureServiceResult + DefenderData []scanners.DefenderResult + AdvisorData []scanners.AdvisorResult + CostData *scanners.CostResult +} + +func (rd *ReportData) ServicesTable() [][]string { + headers := []string{"Subscription", "Resource Group", "Location", "Type", "Service Name", "Compliant", "Impact", "Category", "Recommendation", "Result", "Learn"} + + rbroken := [][]string{} + rok := [][]string{} + for _, d := range rd.MainData { + for _, r := range d.Rules { + row := []string{ + scanners.MaskSubscriptionID(d.SubscriptionID, rd.Mask), + d.ResourceGroup, + scanners.ParseLocation(d.Location), + d.Type, + d.ServiceName, + fmt.Sprintf("%t", !r.NotCompliant), + string(r.Impact), + string(r.Category), + r.Recommendation, + r.Result, + r.Learn, + } + if r.NotCompliant { + rbroken = append([][]string{row}, rbroken...) + } else { + rok = append([][]string{row}, rok...) + } + } + } + + rows := append(rbroken, rok...) + rows = append([][]string{headers}, rows...) + return rows +} + +func (rd *ReportData) CostTable() [][]string { + headers := []string{"From", "To", "Subscription", "ServiceName", "Value", "Currency"} + + rows := [][]string{} + for _, r := range rd.CostData.Items { + row := []string{ + rd.CostData.From.Format("2006-01-02"), + rd.CostData.To.Format("2006-01-02"), + scanners.MaskSubscriptionID(r.SubscriptionID, rd.Mask), + r.ServiceName, + r.Value, + r.Currency, + } + rows = append(rows, row) + } + + rows = append([][]string{headers}, rows...) + return rows +} + +func (rd *ReportData) DefenderTable() [][]string { + headers := []string{"Subscription", "Name", "Tier", "Deprecated"} + rows := [][]string{} + for _, d := range rd.DefenderData { + row := []string{ + scanners.MaskSubscriptionID(d.SubscriptionID, rd.Mask), + d.Name, + d.Tier, + fmt.Sprintf("%t", d.Deprecated), + } + rows = append(rows, row) + } + + rows = append([][]string{headers}, rows...) + return rows +} + +func (rd *ReportData) AdvisorTable() [][]string { + headers := []string{"Subscription", "Name", "Type", "Category", "Description", "PotentialBenefits", "Risk", "LearnMoreLink"} + rows := [][]string{} + for _, d := range rd.AdvisorData { + row := []string{ + scanners.MaskSubscriptionID(d.SubscriptionID, rd.Mask), + d.Name, + d.Type, + d.Category, + d.Description, + d.PotentialBenefits, + d.Risk, + d.LearnMoreLink, + } + rows = append(rows, row) + } + + rows = append([][]string{headers}, rows...) + return rows } diff --git a/internal/renderers/services.go b/internal/renderers/services.go deleted file mode 100644 index baa1308d..00000000 --- a/internal/renderers/services.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -package renderers - -import ( - "fmt" - _ "image/png" - - "github.com/Azure/azqr/internal/scanners" - "github.com/rs/zerolog/log" - "github.com/xuri/excelize/v2" -) - -func renderServices(f *excelize.File, data ReportData) { - if len(data.MainData) > 0 { - _, err := f.NewSheet("Services") - if err != nil { - log.Fatal().Err(err).Msg("Failed to create Services sheet") - } - - headers := []string{"Subscription", "Resource Group", "Location", "Type", "Service Name", "Broken", "Category", "Subcategory", "Severity", "Description", "Result", "Learn"} - - rbroken := [][]string{} - rok := [][]string{} - for _, d := range data.MainData { - for _, r := range d.Rules { - row := []string{ - scanners.MaskSubscriptionID(d.SubscriptionID, data.Mask), - d.ResourceGroup, - scanners.ParseLocation(d.Location), - d.Type, - d.ServiceName, - fmt.Sprintf("%t", r.IsBroken), - string(r.Category), - string(r.Subcategory), - string(r.Severity), - r.Description, - r.Result, - r.Learn, - } - if r.IsBroken { - rbroken = append([][]string{row}, rbroken...) - } else { - rok = append([][]string{row}, rok...) - } - } - } - - createFirstRow(f, "Services", headers) - - rows := append(rbroken, rok...) - - currentRow := 4 - for _, row := range rows { - currentRow += 1 - cell, err := excelize.CoordinatesToCellName(1, currentRow) - if err != nil { - log.Fatal().Err(err).Msg("Failed to get cell") - } - err = f.SetSheetRow("Services", cell, &row) - if err != nil { - log.Fatal().Err(err).Msg("Failed to set row") - } - setHyperLink(f, "Services", 12, currentRow) - } - - configureSheet(f, "Services", headers, currentRow) - } else { - log.Info().Msg("Skipping Services. No data to render") - } -} diff --git a/internal/scan.go b/internal/scan.go index 9da7f532..941beefb 100644 --- a/internal/scan.go +++ b/internal/scan.go @@ -10,12 +10,14 @@ import ( "sync" "time" - "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/renderers" + "github.com/Azure/azqr/internal/renderers/csv" + "github.com/Azure/azqr/internal/renderers/excel" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/Azure/azqr/internal/renderers" "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" @@ -68,6 +70,7 @@ type ScanParams struct { Advisor bool Cost bool Mask bool + Xlsx bool Debug bool ServiceScanners []scanners.IAzureScanner ForceAzureCliCredential bool @@ -80,6 +83,7 @@ func Scan(params *ScanParams) { defender := params.Defender advisor := params.Advisor cost := params.Cost + createXlsx := params.Xlsx mask := params.Mask debug := params.Debug forceAzureCliCredential := params.ForceAzureCliCredential @@ -329,10 +333,11 @@ func Scan(params *ScanParams) { CostData: costResult, } - renderers.CreateExcelReport(reportData) + if createXlsx { + excel.CreateExcelReport(&reportData) + } - xslx := fmt.Sprintf("%s.xlsx", reportData.OutputFileName) - renderers.CreatePBIReport(xslx) + csv.CreateCsvReport(&reportData) log.Info().Msg("Scan completed.") } @@ -412,8 +417,8 @@ func listSubscriptions(ctx context.Context, cred azcore.TokenCredential, options } for _, s := range pageResp.Value { - if s.State != ref.Of(armsubscription.SubscriptionStateDisabled) && - s.State != ref.Of(armsubscription.SubscriptionStateDeleted) { + if s.State != to.Ptr(armsubscription.SubscriptionStateDisabled) && + s.State != to.Ptr(armsubscription.SubscriptionStateDeleted) { subscriptions = append(subscriptions, s) } } diff --git a/internal/scanners/adf/rules.go b/internal/scanners/adf/rules.go index 9bf1011f..4a6445df 100644 --- a/internal/scanners/adf/rules.go +++ b/internal/scanners/adf/rules.go @@ -14,64 +14,55 @@ import ( func (a *DataFactoryScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "adf-001": { - Id: "adf-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Data Factory should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "adf-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Data Factory should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armdatafactory.Factory) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/data-factory/monitor-configure-diagnostics", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/data-factory/monitor-configure-diagnostics", }, "adf-002": { - Id: "adf-002", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Azure Data Factory should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "adf-002", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Data Factory should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armdatafactory.Factory) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, - Field: scanners.OverviewFieldPrivate, }, "adf-003": { - Id: "adf-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Data Factory SLA", - Severity: scanners.SeverityHigh, + Id: "adf-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Data Factory SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", }, "adf-004": { - Id: "adf-004", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Data Factory Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "adf-004", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Data Factory Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armdatafactory.Factory) caf := strings.HasPrefix(*c.Name, "adf") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "adf-005": { - Id: "adf-005", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Data Factory should have tags", - Severity: scanners.SeverityLow, + Id: "adf-005", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Data Factory should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armdatafactory.Factory) return len(c.Tags) == 0, "" diff --git a/internal/scanners/adf/rules_test.go b/internal/scanners/adf/rules_test.go index 8329cdef..b9d5c559 100644 --- a/internal/scanners/adf/rules_test.go +++ b/internal/scanners/adf/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/datafactory/armdatafactory" ) @@ -32,7 +32,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { fields: fields{ rule: "adf-001", target: &armdatafactory.Factory{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -48,9 +48,9 @@ func TestDataExplorerScanner_Rules(t *testing.T) { { name: "DataFactoryScanner Private Endpoint", fields: fields{ - rule: "adf-002", + rule: "adf-002", target: &armdatafactory.Factory{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ PrivateEndpoints: map[string]bool{ @@ -80,7 +80,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { fields: fields{ rule: "adf-004", target: &armdatafactory.Factory{ - Name: ref.Of("adf-test"), + Name: to.Ptr("adf-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/advisor.go b/internal/scanners/advisor.go index 70c1a162..c42da1df 100644 --- a/internal/scanners/advisor.go +++ b/internal/scanners/advisor.go @@ -18,34 +18,6 @@ type AdvisorScanner struct { client *armadvisor.RecommendationsClient } -// GetProperties - Returns the properties of the AdvisorResult -func (a AdvisorResult) GetProperties() []string { - return []string{ - "SubscriptionID", - "Name", - "Type", - "Category", - "Description", - "PotentialBenefits", - "Risk", - "LearnMoreLink", - } -} - -// ToMap - Returns the properties of the AdvisorResult as a map -func (a AdvisorResult) ToMap(mask bool) map[string]string { - return map[string]string{ - "SubscriptionID": MaskSubscriptionID(a.SubscriptionID, mask), - "Name": a.Name, - "Type": a.Type, - "Category": a.Category, - "Description": a.Description, - "Potential Benefits": a.PotentialBenefits, - "Risk": a.Risk, - "Learn": a.LearnMoreLink, - } -} - // Init - Initializes the Advisor Scanner func (s *AdvisorScanner) Init(config *ScannerConfig) error { s.config = config diff --git a/internal/scanners/afd/rules.go b/internal/scanners/afd/rules.go index 42d04e30..9854c4f9 100644 --- a/internal/scanners/afd/rules.go +++ b/internal/scanners/afd/rules.go @@ -14,64 +14,55 @@ import ( func (a *FrontDoorScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "afd-001": { - Id: "afd-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure FrontDoor should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "afd-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure FrontDoor should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcdn.Profile) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-logs", }, "afd-003": { - Id: "afd-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure FrontDoor SLA", - Severity: scanners.SeverityHigh, + Id: "afd-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure FrontDoor SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.azure.cn/en-us/support/sla/cdn/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/cdn/", }, "afd-005": { - Id: "afd-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure FrontDoor SKU", - Severity: scanners.SeverityHigh, + Id: "afd-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure FrontDoor SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcdn.Profile) return false, string(*c.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/tier-comparison", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/tier-comparison", }, "afd-006": { - Id: "afd-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure FrontDoor Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "afd-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure FrontDoor Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcdn.Profile) caf := strings.HasPrefix(*c.Name, "afd") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "afd-007": { - Id: "afd-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure FrontDoor should have tags", - Severity: scanners.SeverityLow, + Id: "afd-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure FrontDoor should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcdn.Profile) return len(c.Tags) == 0, "" diff --git a/internal/scanners/afd/rules_test.go b/internal/scanners/afd/rules_test.go index 597bca65..7e4ec533 100644 --- a/internal/scanners/afd/rules_test.go +++ b/internal/scanners/afd/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cdn/armcdn" ) @@ -32,7 +32,7 @@ func TestFrontDoorScanner_Rules(t *testing.T) { fields: fields{ rule: "afd-001", target: &armcdn.Profile{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -63,7 +63,7 @@ func TestFrontDoorScanner_Rules(t *testing.T) { rule: "afd-005", target: &armcdn.Profile{ SKU: &armcdn.SKU{ - Name: ref.Of(armcdn.SKUNameStandardMicrosoft), + Name: to.Ptr(armcdn.SKUNameStandardMicrosoft), }, }, scanContext: &scanners.ScanContext{}, @@ -78,7 +78,7 @@ func TestFrontDoorScanner_Rules(t *testing.T) { fields: fields{ rule: "afd-006", target: &armcdn.Profile{ - Name: ref.Of("afd-test"), + Name: to.Ptr("afd-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/afw/rules.go b/internal/scanners/afw/rules.go index a3fc4523..5e9ecb58 100644 --- a/internal/scanners/afw/rules.go +++ b/internal/scanners/afw/rules.go @@ -13,39 +13,34 @@ import ( func (a *FirewallScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "afw-001": { - Id: "afw-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Firewall should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "afw-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Firewall should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armnetwork.AzureFirewall) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://docs.microsoft.com/en-us/azure/firewall/logs-and-metrics", }, "afw-002": { - Id: "afw-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Azure Firewall should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "afw-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Firewall should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.AzureFirewall) zones := len(g.Zones) > 1 return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/firewall/features#availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/firewall/features#availability-zones", }, "afw-003": { - Id: "afw-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Firewall SLA", - Severity: scanners.SeverityHigh, + Id: "afw-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Firewall SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.AzureFirewall) sla := "99.95%" @@ -55,42 +50,36 @@ func (a *FirewallScanner) GetRules() map[string]scanners.AzureRule { return false, sla }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", }, "afw-005": { - Id: "afw-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Firewall SKU", - Severity: scanners.SeverityHigh, + Id: "afw-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Firewall SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.AzureFirewall) return false, string(*c.Properties.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/firewall/choose-firewall-sku", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/firewall/choose-firewall-sku", }, "afw-006": { - Id: "afw-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Firewall Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "afw-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Firewall Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.AzureFirewall) caf := strings.HasPrefix(*c.Name, "afw") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "afw-007": { - Id: "afw-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Firewall should have tags", - Severity: scanners.SeverityLow, + Id: "afw-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Firewall should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.AzureFirewall) return len(c.Tags) == 0, "" diff --git a/internal/scanners/afw/rules_test.go b/internal/scanners/afw/rules_test.go index 1d779ff5..6d7bb2d9 100644 --- a/internal/scanners/afw/rules_test.go +++ b/internal/scanners/afw/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ) @@ -32,7 +32,7 @@ func TestFirewallScanner_Rules(t *testing.T) { fields: fields{ rule: "afw-001", target: &armnetwork.AzureFirewall{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -50,8 +50,8 @@ func TestFirewallScanner_Rules(t *testing.T) { fields: fields{ rule: "afw-002", target: &armnetwork.AzureFirewall{ - ID: ref.Of("test"), - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + ID: to.Ptr("test"), + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -77,7 +77,7 @@ func TestFirewallScanner_Rules(t *testing.T) { fields: fields{ rule: "afw-003", target: &armnetwork.AzureFirewall{ - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -93,7 +93,7 @@ func TestFirewallScanner_Rules(t *testing.T) { target: &armnetwork.AzureFirewall{ Properties: &armnetwork.AzureFirewallPropertiesFormat{ SKU: &armnetwork.AzureFirewallSKU{ - Name: ref.Of(armnetwork.AzureFirewallSKUNameAZFWVnet), + Name: to.Ptr(armnetwork.AzureFirewallSKUNameAZFWVnet), }, }, }, @@ -109,7 +109,7 @@ func TestFirewallScanner_Rules(t *testing.T) { fields: fields{ rule: "afw-006", target: &armnetwork.AzureFirewall{ - Name: ref.Of("afw-test"), + Name: to.Ptr("afw-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/agw/rules.go b/internal/scanners/agw/rules.go index d83656f1..f9971d04 100644 --- a/internal/scanners/agw/rules.go +++ b/internal/scanners/agw/rules.go @@ -14,11 +14,10 @@ import ( func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "agw-001": { - Id: "agw-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityScaling, - Description: "Application Gateway: Ensure autoscaling is used with a minimum of 2 instances", - Severity: scanners.SeverityHigh, + Id: "agw-001", + Category: scanners.RulesCategoryScalability, + Recommendation: "Application Gateway: Ensure autoscaling is used with a minimum of 2 instances", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) autoscale := g.Properties.AutoscaleConfiguration != nil && g.Properties.AutoscaleConfiguration.MinCapacity != nil && *g.Properties.AutoscaleConfiguration.MinCapacity >= 2 @@ -27,11 +26,10 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant", }, "agw-002": { - Id: "agw-002", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecuritySSL, - Description: "Application Gateway: Secure all incoming connections with SSL", - Severity: scanners.SeverityHigh, + Id: "agw-002", + Category: scanners.RulesCategorySecurity, + Recommendation: "Application Gateway: Secure all incoming connections with SSL", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) sslPort := false @@ -49,11 +47,10 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/well-architected/services/networking/azure-application-gateway#security", }, "agw-003": { - Id: "agw-003", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityFirewall, - Description: "Application Gateway: Enable WAF policies", - Severity: scanners.SeverityHigh, + Id: "agw-003", + Category: scanners.RulesCategorySecurity, + Recommendation: "Application Gateway: Enable WAF policies", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) waf := g.Properties.WebApplicationFirewallConfiguration != nil && g.Properties.WebApplicationFirewallConfiguration.Enabled != nil && *g.Properties.WebApplicationFirewallConfiguration.Enabled @@ -62,11 +59,10 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/application-gateway/features#web-application-firewall", }, "agw-004": { - Id: "agw-004", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Application Gateway: Use Application GW V2 instead of V1", - Severity: scanners.SeverityHigh, + Id: "agw-004", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Application Gateway: Use Application GW V2 instead of V1", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) v2 := g.Properties.SKU != nil && g.Properties.SKU.Name != nil && strings.Contains(string(*g.Properties.SKU.Name), "_v2") @@ -75,39 +71,34 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { Url: "https://azure.microsoft.com/en-us/updates/application-gateway-v1-will-be-retired-on-28-april-2026-transition-to-application-gateway-v2/", }, "agw-005": { - Id: "agw-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Application Gateway: Monitor and Log the configurations and traffic", - Severity: scanners.SeverityMedium, + Id: "agw-005", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Application Gateway: Monitor and Log the configurations and traffic", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armnetwork.ApplicationGateway) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-diagnostics#diagnostic-logging", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-diagnostics#diagnostic-logging", }, "agw-007": { - Id: "agw-007", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Application Gateway should have availability zones enabled", - Severity: scanners.SeverityMedium, + Id: "agw-007", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Application Gateway should have availability zones enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) zones := g.Zones != nil && len(g.Zones) > 1 return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/application-gateway/application-gateway-autoscaling-zone-redundant", }, "agw-008": { - Id: "agw-008", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityMaintenance, - Description: "Application Gateway: Plan for backend maintenance by using connection draining", - Severity: scanners.SeverityMedium, + Id: "agw-008", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Application Gateway: Plan for backend maintenance by using connection draining", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) @@ -128,50 +119,43 @@ func (a *ApplicationGatewayScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/application-gateway/features#connection-draining", }, "agw-103": { - Id: "agw-103", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Application Gateway SLA", - Severity: scanners.SeverityHigh, + Id: "agw-103", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Application Gateway SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, - Url: "https://www.azure.cn/en-us/support/sla/application-gateway/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/application-gateway/", }, "agw-104": { - Id: "agw-104", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Application Gateway SKU", - Severity: scanners.SeverityHigh, + Id: "agw-104", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Application Gateway SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) return false, string(*g.Properties.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/application-gateway/understanding-pricing", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/application-gateway/understanding-pricing", }, "agw-105": { - Id: "agw-105", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Application Gateway Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "agw-105", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Application Gateway Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { g := target.(*armnetwork.ApplicationGateway) caf := strings.HasPrefix(*g.Name, "agw") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "agw-106": { - Id: "agw-106", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Application Gateway should have tags", - Severity: scanners.SeverityLow, + Id: "agw-106", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Application Gateway should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.ApplicationGateway) return len(c.Tags) == 0, "" diff --git a/internal/scanners/agw/rules_test.go b/internal/scanners/agw/rules_test.go index 5af92f4c..ac288b23 100644 --- a/internal/scanners/agw/rules_test.go +++ b/internal/scanners/agw/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ) @@ -32,7 +32,7 @@ func TestApplicationGatewayScanner_Rules(t *testing.T) { fields: fields{ rule: "agw-005", target: &armnetwork.ApplicationGateway{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -50,8 +50,8 @@ func TestApplicationGatewayScanner_Rules(t *testing.T) { fields: fields{ rule: "agw-007", target: &armnetwork.ApplicationGateway{ - ID: ref.Of("test"), - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + ID: to.Ptr("test"), + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -79,7 +79,7 @@ func TestApplicationGatewayScanner_Rules(t *testing.T) { target: &armnetwork.ApplicationGateway{ Properties: &armnetwork.ApplicationGatewayPropertiesFormat{ SKU: &armnetwork.ApplicationGatewaySKU{ - Name: ref.Of(armnetwork.ApplicationGatewaySKUNameStandardV2), + Name: to.Ptr(armnetwork.ApplicationGatewaySKUNameStandardV2), }, }, }, @@ -95,7 +95,7 @@ func TestApplicationGatewayScanner_Rules(t *testing.T) { fields: fields{ rule: "agw-105", target: &armnetwork.ApplicationGateway{ - Name: ref.Of("agw-test"), + Name: to.Ptr("agw-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/aks/rules.go b/internal/scanners/aks/rules.go index 19450e0d..2601bd2a 100644 --- a/internal/scanners/aks/rules.go +++ b/internal/scanners/aks/rules.go @@ -6,8 +6,8 @@ package aks import ( "strings" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4" ) @@ -15,25 +15,22 @@ import ( func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "aks-001": { - Id: "aks-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "AKS Cluster should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "aks-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "AKS Cluster should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcontainerservice.ManagedCluster) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/aks/monitor-aks#collect-resource-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/aks/monitor-aks#collect-resource-logs", }, "aks-002": { - Id: "aks-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "AKS Cluster should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "aks-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "AKS Cluster should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { cluster := target.(*armcontainerservice.ManagedCluster) zones := true @@ -45,15 +42,13 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { } return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/aks/availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/aks/availability-zones", }, "aks-003": { - Id: "aks-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "AKS Cluster should have an SLA", - Severity: scanners.SeverityHigh, + Id: "aks-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "AKS Cluster should have an SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) @@ -78,29 +73,25 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { } return sla == "None", sla }, - Url: "https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers#uptime-sla-terms-and-conditions", - Field: scanners.OverviewFieldSLA, + Url: "https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers#uptime-sla-terms-and-conditions", }, "aks-004": { - Id: "aks-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "AKS Cluster should be private", - Severity: scanners.SeverityHigh, + Id: "aks-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS Cluster should be private", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) pe := c.Properties.APIServerAccessProfile != nil && c.Properties.APIServerAccessProfile.EnablePrivateCluster != nil && *c.Properties.APIServerAccessProfile.EnablePrivateCluster return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/aks/private-clusters", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/aks/private-clusters", }, "aks-005": { - Id: "aks-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "AKS Production Cluster should use Standard SKU", - Severity: scanners.SeverityHigh, + Id: "aks-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "AKS Production Cluster should use Standard SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) sku := "Free" @@ -109,29 +100,25 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { } return sku == "Free", sku }, - Url: "https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers", }, "aks-006": { - Id: "aks-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "AKS Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "aks-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "AKS Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) caf := strings.HasPrefix(*c.Name, "aks") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "aks-007": { - Id: "aks-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "AKS should integrate authentication with AAD (Managed)", - Severity: scanners.SeverityMedium, + Id: "aks-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS should integrate authentication with AAD (Managed)", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) aad := c.Properties.AADProfile != nil && c.Properties.AADProfile.Managed != nil && *c.Properties.AADProfile.Managed @@ -140,11 +127,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/aks/managed-azure-ad", }, "aks-008": { - Id: "aks-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "AKS should be RBAC enabled.", - Severity: scanners.SeverityMedium, + Id: "aks-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS should be RBAC enabled.", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) rbac := *c.Properties.EnableRBAC @@ -153,11 +139,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/manage-azure-rbac", }, "aks-009": { - Id: "aks-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "AKS should have local accounts disabled", - Severity: scanners.SeverityMedium, + Id: "aks-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS should have local accounts disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) @@ -169,11 +154,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/managed-aad#disable-local-accounts", }, "aks-010": { - Id: "aks-010", - Category: scanners.RulesCategorySecurity, - Subcategory: "Best Practices", - Description: "AKS should have httpApplicationRouting disabled", - Severity: scanners.SeverityMedium, + Id: "aks-010", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS should have httpApplicationRouting disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) p, exists := c.Properties.AddonProfiles["httpApplicationRouting"] @@ -183,11 +167,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/http-application-routing", }, "aks-011": { - Id: "aks-011", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityMonitoring, - Description: "AKS should have Container Insights enabled", - Severity: scanners.SeverityMedium, + Id: "aks-011", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "AKS should have Container Insights enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) p, exists := c.Properties.AddonProfiles["omsagent"] @@ -197,11 +180,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/azure-monitor/insights/container-insights-overview", }, "aks-012": { - Id: "aks-012", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "AKS should have outbound type set to user defined routing", - Severity: scanners.SeverityHigh, + Id: "aks-012", + Category: scanners.RulesCategorySecurity, + Recommendation: "AKS should have outbound type set to user defined routing", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) broken := c.Properties.NetworkProfile.OutboundType == nil || *c.Properties.NetworkProfile.OutboundType != armcontainerservice.OutboundTypeUserDefinedRouting @@ -210,11 +192,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/limit-egress-traffic", }, "aks-013": { - Id: "aks-013", - Category: scanners.RulesCategoryPerformanceEfficienccy, - Subcategory: scanners.RulesSubcategoryPerformanceEfficienccyNetworking, - Description: "AKS should avoid using kubenet network plugin", - Severity: scanners.SeverityMedium, + Id: "aks-013", + Category: scanners.RulesCategoryScalability, + Recommendation: "AKS should avoid using kubenet network plugin", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) out := *c.Properties.NetworkProfile.NetworkPlugin == armcontainerservice.NetworkPluginKubenet @@ -223,11 +204,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/operator-best-practices-network", }, "aks-014": { - Id: "aks-014", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryReliabilityScaling, - Description: "AKS should have autoscaler enabled", - Severity: scanners.SeverityMedium, + Id: "aks-014", + Category: scanners.RulesCategoryScalability, + Recommendation: "AKS should have autoscaler enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) if c.Properties.AgentPoolProfiles != nil { @@ -244,11 +224,10 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/aks/concepts-scale", }, "aks-015": { - Id: "aks-015", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "AKS should have tags", - Severity: scanners.SeverityLow, + Id: "aks-015", + Category: scanners.RulesCategoryGovernance, + Recommendation: "AKS should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) return len(c.Tags) == 0, "" @@ -256,16 +235,15 @@ func (a *AKSScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "aks-016": { - Id: "aks-016", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "AKS Node Pools should have MaxSurge set", - Severity: scanners.SeverityLow, + Id: "aks-016", + Category: scanners.RulesCategoryScalability, + Recommendation: "AKS Node Pools should have MaxSurge set", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerservice.ManagedCluster) defaultMaxSurge := false for _, profile := range c.Properties.AgentPoolProfiles { - if profile.UpgradeSettings == nil || profile.UpgradeSettings.MaxSurge == nil || (profile.UpgradeSettings.MaxSurge == ref.Of("1")) { + if profile.UpgradeSettings == nil || profile.UpgradeSettings.MaxSurge == nil || (profile.UpgradeSettings.MaxSurge == to.Ptr("1")) { defaultMaxSurge = true break } diff --git a/internal/scanners/aks/rules_test.go b/internal/scanners/aks/rules_test.go index 2e9c6eb4..a9989ca0 100644 --- a/internal/scanners/aks/rules_test.go +++ b/internal/scanners/aks/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v4" ) @@ -32,7 +32,7 @@ func TestAKSScanner_Rules(t *testing.T) { fields: fields{ rule: "aks-001", target: &armcontainerservice.ManagedCluster{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,12 +51,12 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-002", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ { - AvailabilityZones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + AvailabilityZones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, }, }, @@ -74,11 +74,11 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-004", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ APIServerAccessProfile: &armcontainerservice.ManagedClusterAPIServerAccessProfile{ - EnablePrivateCluster: ref.Of(true), + EnablePrivateCluster: to.Ptr(true), }, }, }, @@ -95,7 +95,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-003", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierFree), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierFree), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ @@ -118,7 +118,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-003", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ @@ -141,12 +141,12 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-003", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ { - AvailabilityZones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + AvailabilityZones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, }, }, @@ -164,7 +164,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-005", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierFree), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierFree), }, }, scanContext: &scanners.ScanContext{}, @@ -179,7 +179,7 @@ func TestAKSScanner_Rules(t *testing.T) { fields: fields{ rule: "aks-006", target: &armcontainerservice.ManagedCluster{ - Name: ref.Of("aks-test"), + Name: to.Ptr("aks-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -195,7 +195,7 @@ func TestAKSScanner_Rules(t *testing.T) { target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ AADProfile: &armcontainerservice.ManagedClusterAADProfile{ - Managed: ref.Of(true), + Managed: to.Ptr(true), }, }, }, @@ -228,7 +228,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-008", target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ - EnableRBAC: ref.Of(true), + EnableRBAC: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -244,7 +244,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-008", target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ - EnableRBAC: ref.Of(false), + EnableRBAC: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -260,7 +260,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-009", target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ - DisableLocalAccounts: ref.Of(true), + DisableLocalAccounts: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -294,7 +294,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AddonProfiles: map[string]*armcontainerservice.ManagedClusterAddonProfile{ "httpApplicationRouting": { - Enabled: ref.Of(true), + Enabled: to.Ptr(true), }, }, }, @@ -314,7 +314,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AddonProfiles: map[string]*armcontainerservice.ManagedClusterAddonProfile{ "httpApplicationRouting": { - Enabled: ref.Of(false), + Enabled: to.Ptr(false), }, }, }, @@ -334,7 +334,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AddonProfiles: map[string]*armcontainerservice.ManagedClusterAddonProfile{ "omsagent": { - Enabled: ref.Of(true), + Enabled: to.Ptr(true), }, }, }, @@ -354,7 +354,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AddonProfiles: map[string]*armcontainerservice.ManagedClusterAddonProfile{ "omsagent": { - Enabled: ref.Of(false), + Enabled: to.Ptr(false), }, }, }, @@ -405,7 +405,7 @@ func TestAKSScanner_Rules(t *testing.T) { target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ NetworkProfile: &armcontainerservice.NetworkProfile{ - OutboundType: ref.Of(armcontainerservice.OutboundTypeUserDefinedRouting), + OutboundType: to.Ptr(armcontainerservice.OutboundTypeUserDefinedRouting), }, }, }, @@ -423,7 +423,7 @@ func TestAKSScanner_Rules(t *testing.T) { target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ NetworkProfile: &armcontainerservice.NetworkProfile{ - OutboundType: ref.Of(armcontainerservice.OutboundTypeLoadBalancer), + OutboundType: to.Ptr(armcontainerservice.OutboundTypeLoadBalancer), }, }, }, @@ -457,7 +457,7 @@ func TestAKSScanner_Rules(t *testing.T) { target: &armcontainerservice.ManagedCluster{ Properties: &armcontainerservice.ManagedClusterProperties{ NetworkProfile: &armcontainerservice.NetworkProfile{ - NetworkPlugin: ref.Of(armcontainerservice.NetworkPluginKubenet), + NetworkPlugin: to.Ptr(armcontainerservice.NetworkPluginKubenet), }, }, }, @@ -508,7 +508,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ { - EnableAutoScaling: ref.Of(false), + EnableAutoScaling: to.Ptr(false), }, }, }, @@ -528,7 +528,7 @@ func TestAKSScanner_Rules(t *testing.T) { Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ { - EnableAutoScaling: ref.Of(true), + EnableAutoScaling: to.Ptr(true), }, }, }, @@ -546,7 +546,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-016", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ @@ -569,7 +569,7 @@ func TestAKSScanner_Rules(t *testing.T) { rule: "aks-016", target: &armcontainerservice.ManagedCluster{ SKU: &armcontainerservice.ManagedClusterSKU{ - Tier: ref.Of(armcontainerservice.ManagedClusterSKUTierStandard), + Tier: to.Ptr(armcontainerservice.ManagedClusterSKUTierStandard), }, Properties: &armcontainerservice.ManagedClusterProperties{ AgentPoolProfiles: []*armcontainerservice.ManagedClusterAgentPoolProfile{ diff --git a/internal/scanners/apim/rules.go b/internal/scanners/apim/rules.go index aa6db6da..b28c3c99 100644 --- a/internal/scanners/apim/rules.go +++ b/internal/scanners/apim/rules.go @@ -15,39 +15,34 @@ import ( func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "apim-001": { - Id: "apim-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "APIM should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "apim-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "APIM should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armapimanagement.ServiceResource) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-azure-monitor#resource-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-azure-monitor#resource-logs", }, "apim-002": { - Id: "apim-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "APIM should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "apim-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "APIM should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armapimanagement.ServiceResource) zones := len(a.Zones) > 0 return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/reliability/migrate-api-mgt", }, "apim-003": { - Id: "apim-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "APIM should have a SLA", - Severity: scanners.SeverityHigh, + Id: "apim-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "APIM should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armapimanagement.ServiceResource) sku := string(*a.SKU.Name) @@ -60,57 +55,49 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { return sla == "None", sla }, - Url: "https://www.azure.cn/en-us/support/sla/api-management/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/api-management/", }, "apim-004": { - Id: "apim-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "APIM should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "apim-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "APIM should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armapimanagement.ServiceResource) pe := len(a.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/api-management/private-endpoint", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/api-management/private-endpoint", }, "apim-005": { - Id: "apim-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure APIM SKU", - Severity: scanners.SeverityHigh, + Id: "apim-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure APIM SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armapimanagement.ServiceResource) sku := string(*a.SKU.Name) return strings.Contains(sku, "Developer"), sku }, - Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-features", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-features", }, "apim-006": { - Id: "apim-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "APIM should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "apim-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "APIM should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapimanagement.ServiceResource) caf := strings.HasPrefix(*c.Name, "apim") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "apim-007": { - Id: "apim-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "APIM should have tags", - Severity: scanners.SeverityLow, + Id: "apim-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "APIM should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapimanagement.ServiceResource) return len(c.Tags) == 0, "" @@ -118,11 +105,10 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "apim-008": { - Id: "apim-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "APIM should use Managed Identities", - Severity: scanners.SeverityMedium, + Id: "apim-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "APIM should use Managed Identities", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapimanagement.ServiceResource) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armapimanagement.ApimIdentityTypeNone, "" @@ -130,11 +116,10 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-use-managed-service-identity", }, "apim-009": { - Id: "apim-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "APIM should only accept a minimum of TLS 1.2", - Severity: scanners.SeverityHigh, + Id: "apim-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "APIM should only accept a minimum of TLS 1.2", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { notAllowed := []string{ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10", @@ -162,11 +147,10 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers", }, "apim-010": { - Id: "apim-010", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityCyphers, - Description: "APIM should should not accept weak or deprecated ciphers.", - Severity: scanners.SeverityHigh, + Id: "apim-010", + Category: scanners.RulesCategorySecurity, + Recommendation: "APIM should should not accept weak or deprecated ciphers.", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { notAllowed := []string{ "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168", @@ -196,11 +180,10 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-manage-protocols-ciphers", }, "apim-011": { - Id: "apim-011", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityCertificates, - Description: "APIM: Renew expiring certificates", - Severity: scanners.SeverityHigh, + Id: "apim-011", + Category: scanners.RulesCategorySecurity, + Recommendation: "APIM: Renew expiring certificates", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapimanagement.ServiceResource) if c.Properties.HostnameConfigurations != nil { @@ -218,11 +201,10 @@ func (a *APIManagementScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/api-management/configure-custom-domain?tabs=custom", }, "apim-012": { - Id: "apim-012", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "APIM: Migrate instance hosted on the stv1 platform to stv2", - Severity: scanners.SeverityHigh, + Id: "apim-012", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "APIM: Migrate instance hosted on the stv1 platform to stv2", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapimanagement.ServiceResource) return *c.Properties.PlatformVersion == armapimanagement.PlatformVersionStv1, "" diff --git a/internal/scanners/apim/rules_test.go b/internal/scanners/apim/rules_test.go index 12ed0019..20ff553c 100644 --- a/internal/scanners/apim/rules_test.go +++ b/internal/scanners/apim/rules_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement" ) @@ -33,7 +33,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { fields: fields{ rule: "apim-001", target: &armapimanagement.ServiceResource{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { fields: fields{ rule: "apim-002", target: &armapimanagement.ServiceResource{ - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -80,7 +80,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-003", target: &armapimanagement.ServiceResource{ SKU: &armapimanagement.ServiceSKUProperties{ - Name: ref.Of(armapimanagement.SKUTypeDeveloper), + Name: to.Ptr(armapimanagement.SKUTypeDeveloper), }, Zones: []*string{}, Properties: &armapimanagement.ServiceProperties{ @@ -100,9 +100,9 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-003", target: &armapimanagement.ServiceResource{ SKU: &armapimanagement.ServiceSKUProperties{ - Name: ref.Of(armapimanagement.SKUTypePremium), + Name: to.Ptr(armapimanagement.SKUTypePremium), }, - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, Properties: &armapimanagement.ServiceProperties{ AdditionalLocations: []*armapimanagement.AdditionalLocation{}, }, @@ -120,7 +120,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-003", target: &armapimanagement.ServiceResource{ SKU: &armapimanagement.ServiceSKUProperties{ - Name: ref.Of(armapimanagement.SKUTypeConsumption), + Name: to.Ptr(armapimanagement.SKUTypeConsumption), }, Zones: []*string{}, Properties: &armapimanagement.ServiceProperties{ @@ -142,7 +142,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { Properties: &armapimanagement.ServiceProperties{ PrivateEndpointConnections: []*armapimanagement.RemotePrivateEndpointConnectionWrapper{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -160,7 +160,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-005", target: &armapimanagement.ServiceResource{ SKU: &armapimanagement.ServiceSKUProperties{ - Name: ref.Of(armapimanagement.SKUTypeDeveloper), + Name: to.Ptr(armapimanagement.SKUTypeDeveloper), }, }, scanContext: &scanners.ScanContext{}, @@ -175,7 +175,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { fields: fields{ rule: "apim-006", target: &armapimanagement.ServiceResource{ - Name: ref.Of("apim-test"), + Name: to.Ptr("apim-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -190,7 +190,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-008", target: &armapimanagement.ServiceResource{ Identity: &armapimanagement.ServiceIdentity{ - Type: ref.Of(armapimanagement.ApimIdentityTypeNone), + Type: to.Ptr(armapimanagement.ApimIdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, @@ -207,12 +207,12 @@ func TestAPIManagementScanner_Rules(t *testing.T) { target: &armapimanagement.ServiceResource{ Properties: &armapimanagement.ServiceProperties{ CustomProperties: map[string]*string{ - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": ref.Of("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": to.Ptr("false"), }, }, }, @@ -246,14 +246,14 @@ func TestAPIManagementScanner_Rules(t *testing.T) { target: &armapimanagement.ServiceResource{ Properties: &armapimanagement.ServiceProperties{ CustomProperties: map[string]*string{ - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": ref.Of("false"), - "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": ref.Of("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": to.Ptr("false"), + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256": to.Ptr("false"), }, }, }, @@ -289,7 +289,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { HostnameConfigurations: []*armapimanagement.HostnameConfiguration{ { Certificate: &armapimanagement.CertificateInformation{ - Expiry: ref.Of(time.Now()), + Expiry: to.Ptr(time.Now()), }, }, }, @@ -311,7 +311,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { HostnameConfigurations: []*armapimanagement.HostnameConfiguration{ { Certificate: &armapimanagement.CertificateInformation{ - Expiry: ref.Of(time.Now().Add(time.Hour * 24 * 45)), + Expiry: to.Ptr(time.Now().Add(time.Hour * 24 * 45)), }, }, }, @@ -330,7 +330,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-012", target: &armapimanagement.ServiceResource{ Properties: &armapimanagement.ServiceProperties{ - PlatformVersion: ref.Of(armapimanagement.PlatformVersionStv1), + PlatformVersion: to.Ptr(armapimanagement.PlatformVersionStv1), }, }, scanContext: &scanners.ScanContext{}, @@ -346,7 +346,7 @@ func TestAPIManagementScanner_Rules(t *testing.T) { rule: "apim-012", target: &armapimanagement.ServiceResource{ Properties: &armapimanagement.ServiceProperties{ - PlatformVersion: ref.Of(armapimanagement.PlatformVersionStv2), + PlatformVersion: to.Ptr(armapimanagement.PlatformVersionStv2), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/appcs/rules.go b/internal/scanners/appcs/rules.go index 13550e09..d327548d 100644 --- a/internal/scanners/appcs/rules.go +++ b/internal/scanners/appcs/rules.go @@ -14,25 +14,22 @@ import ( func (a *AppConfigurationScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "appcs-001": { - Id: "appcs-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "AppConfiguration should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "appcs-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "AppConfiguration should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappconfiguration.ConfigurationStore) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/azure-app-configuration/monitor-app-configuration?tabs=portal", }, "appcs-003": { - Id: "appcs-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "AppConfiguration should have a SLA", - Severity: scanners.SeverityHigh, + Id: "appcs-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "AppConfiguration should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armappconfiguration.ConfigurationStore) sku := *a.SKU.Name @@ -43,57 +40,49 @@ func (a *AppConfigurationScanner) GetRules() map[string]scanners.AzureRule { return sla == "None", sla }, - Url: "https://www.azure.cn/en-us/support/sla/app-configuration/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/app-configuration/", }, "appcs-004": { - Id: "appcs-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "AppConfiguration should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "appcs-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "AppConfiguration should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armappconfiguration.ConfigurationStore) pe := len(a.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-private-endpoint", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/azure-app-configuration/concept-private-endpoint", }, "appcs-005": { - Id: "appcs-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "AppConfiguration SKU", - Severity: scanners.SeverityHigh, + Id: "appcs-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "AppConfiguration SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { a := target.(*armappconfiguration.ConfigurationStore) sku := string(*a.SKU.Name) return false, sku }, - Url: "https://azure.microsoft.com/en-us/pricing/details/app-configuration/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/app-configuration/", }, "appcs-006": { - Id: "appcs-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "AppConfiguration Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "appcs-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "AppConfiguration Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappconfiguration.ConfigurationStore) caf := strings.HasPrefix(*c.Name, "appcs") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "appcs-007": { - Id: "appcs-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "AppConfiguration should have tags", - Severity: scanners.SeverityLow, + Id: "appcs-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "AppConfiguration should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappconfiguration.ConfigurationStore) return len(c.Tags) == 0, "" @@ -101,11 +90,10 @@ func (a *AppConfigurationScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "appcs-008": { - Id: "appcs-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "AppConfiguration should have local authentication disabled", - Severity: scanners.SeverityMedium, + Id: "appcs-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "AppConfiguration should have local authentication disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappconfiguration.ConfigurationStore) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth @@ -114,11 +102,10 @@ func (a *AppConfigurationScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-app-configuration/howto-disable-access-key-authentication?tabs=portal#disable-access-key-authentication", }, "appcs-009": { - Id: "appcs-009", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "AppConfiguration should have purge protection enabled", - Severity: scanners.SeverityMedium, + Id: "appcs-009", + Category: scanners.RulesCategoryDisasterRecovery, + Recommendation: "AppConfiguration should have purge protection enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappconfiguration.ConfigurationStore) purgeProtection := c.Properties.EnablePurgeProtection != nil && *c.Properties.EnablePurgeProtection diff --git a/internal/scanners/appcs/rules_test.go b/internal/scanners/appcs/rules_test.go index e3717b1b..35f1147e 100644 --- a/internal/scanners/appcs/rules_test.go +++ b/internal/scanners/appcs/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appconfiguration/armappconfiguration" ) @@ -32,7 +32,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { fields: fields{ rule: "appcs-001", target: &armappconfiguration.ConfigurationStore{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { rule: "appcs-003", target: &armappconfiguration.ConfigurationStore{ SKU: &armappconfiguration.SKU{ - Name: ref.Of("Free"), + Name: to.Ptr("Free"), }, }, scanContext: &scanners.ScanContext{}, @@ -67,7 +67,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { rule: "appcs-003", target: &armappconfiguration.ConfigurationStore{ SKU: &armappconfiguration.SKU{ - Name: ref.Of("Standard"), + Name: to.Ptr("Standard"), }, }, scanContext: &scanners.ScanContext{}, @@ -85,7 +85,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { Properties: &armappconfiguration.ConfigurationStoreProperties{ PrivateEndpointConnections: []*armappconfiguration.PrivateEndpointConnectionReference{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -103,7 +103,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { rule: "appcs-005", target: &armappconfiguration.ConfigurationStore{ SKU: &armappconfiguration.SKU{ - Name: ref.Of("Standard"), + Name: to.Ptr("Standard"), }, }, scanContext: &scanners.ScanContext{}, @@ -118,7 +118,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { fields: fields{ rule: "appcs-006", target: &armappconfiguration.ConfigurationStore{ - Name: ref.Of("appcs-test"), + Name: to.Ptr("appcs-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -133,7 +133,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { rule: "appcs-008", target: &armappconfiguration.ConfigurationStore{ Properties: &armappconfiguration.ConfigurationStoreProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -149,7 +149,7 @@ func TestAppConfigurationScanner_Rules(t *testing.T) { rule: "appcs-009", target: &armappconfiguration.ConfigurationStore{ Properties: &armappconfiguration.ConfigurationStoreProperties{ - EnablePurgeProtection: ref.Of(true), + EnablePurgeProtection: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/appi/rules.go b/internal/scanners/appi/rules.go index dcb2c5e1..4552f75f 100644 --- a/internal/scanners/appi/rules.go +++ b/internal/scanners/appi/rules.go @@ -14,37 +14,32 @@ import ( func (a *AppInsightsScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "appi-001": { - Id: "appi-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Application Insights SLA", - Severity: scanners.SeverityHigh, + Id: "appi-001", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Application Insights SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, - Url: "https://www.azure.cn/en-us/support/sla/application-insights/index.html", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/application-insights/index.html", }, "appi-002": { - Id: "appi-002", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Application Insights Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "appi-002", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Application Insights Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapplicationinsights.Component) caf := strings.HasPrefix(*c.Name, "appi") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "appi-003": { - Id: "appi-003", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Application Insights should have tags", - Severity: scanners.SeverityLow, + Id: "appi-003", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Application Insights should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapplicationinsights.Component) return len(c.Tags) == 0, "" @@ -52,11 +47,10 @@ func (a *AppInsightsScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "appi-004": { - Id: "appi-004", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Azure Application Insights should store data in a Log Analytics Workspace", - Severity: scanners.SeverityLow, + Id: "appi-004", + Category: scanners.RulesCategoryScalability, + Recommendation: "Azure Application Insights should store data in a Log Analytics Workspace", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armapplicationinsights.Component) diff --git a/internal/scanners/appi/rules_test.go b/internal/scanners/appi/rules_test.go index b955087b..b68f04fb 100644 --- a/internal/scanners/appi/rules_test.go +++ b/internal/scanners/appi/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights" ) @@ -44,7 +44,7 @@ func TestAppInsightsScanner_Rules(t *testing.T) { fields: fields{ rule: "appi-002", target: &armapplicationinsights.Component{ - Name: ref.Of("appi-test"), + Name: to.Ptr("appi-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/asp/rules.go b/internal/scanners/asp/rules.go index 421972ff..086893da 100644 --- a/internal/scanners/asp/rules.go +++ b/internal/scanners/asp/rules.go @@ -28,38 +28,33 @@ func (a *AppServiceScanner) GetRules() map[string]scanners.AzureRule { func (a *AppServiceScanner) getPlanRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "asp-001": { - Id: "asp-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Plan should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "asp-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Plan should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappservice.Plan) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Field: scanners.OverviewFieldDiagnostics, }, "asp-002": { - Id: "asp-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Plan should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "asp-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Plan should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Plan) zones := *i.Properties.ZoneRedundant return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/reliability/migrate-app-service", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/reliability/migrate-app-service", }, "asp-003": { - Id: "asp-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Plan should have a SLA", - Severity: scanners.SeverityHigh, + Id: "asp-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Plan should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Plan) sku := string(*i.SKU.Tier) @@ -69,42 +64,36 @@ func (a *AppServiceScanner) getPlanRules() map[string]scanners.AzureRule { } return sla == "None", sla }, - Url: "https://www.azure.cn/en-us/support/sla/app-service/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/app-service/", }, "asp-005": { - Id: "asp-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Plan SKU", - Severity: scanners.SeverityHigh, + Id: "asp-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Plan SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Plan) return false, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-hosting-plans", }, "asp-006": { - Id: "asp-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Plan Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "asp-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Plan Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Plan) caf := strings.HasPrefix(*c.Name, "asp") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "asp-007": { - Id: "asp-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Plan should have tags", - Severity: scanners.SeverityLow, + Id: "asp-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Plan should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Plan) return len(c.Tags) == 0, "" @@ -117,53 +106,46 @@ func (a *AppServiceScanner) getPlanRules() map[string]scanners.AzureRule { func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "app-001": { - Id: "app-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "App Service should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "app-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "App Service should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#send-logs-to-azure-monitor", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs#send-logs-to-azure-monitor", }, "app-004": { - Id: "app-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "App Service should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "app-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/app-service/networking/private-endpoint", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/app-service/networking/private-endpoint", }, "app-006": { - Id: "app-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "App Service Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "app-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "App Service Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "app") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "app-007": { - Id: "app-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "App Service should use HTTPS only", - Severity: scanners.SeverityHigh, + Id: "app-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should use HTTPS only", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := *c.Properties.HTTPSOnly @@ -172,11 +154,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "app-008": { - Id: "app-008", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "App Service should have tags", - Severity: scanners.SeverityLow, + Id: "app-008", + Category: scanners.RulesCategoryGovernance, + Recommendation: "App Service should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" @@ -184,11 +165,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "app-009": { - Id: "app-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "App Service should use VNET integration", - Severity: scanners.SeverityMedium, + Id: "app-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should use VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" @@ -196,11 +176,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "app-010": { - Id: "app-010", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "App Service should have VNET Route all enabled for VNET integration", - Severity: scanners.SeverityMedium, + Id: "app-010", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should have VNET Route all enabled for VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" @@ -208,11 +187,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "app-011": { - Id: "app-011", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "App Service should use TLS 1.2", - Severity: scanners.SeverityHigh, + Id: "app-011", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should use TLS 1.2", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" @@ -220,11 +198,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "app-012": { - Id: "app-012", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "App Service remote debugging should be disabled", - Severity: scanners.SeverityHigh, + Id: "app-012", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service remote debugging should be disabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" @@ -232,11 +209,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "app-013": { - Id: "app-013", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "App Service should not allow insecure FTP", - Severity: scanners.SeverityHigh, + Id: "app-013", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should not allow insecure FTP", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.FtpsState == nil || *scanContext.SiteConfig.Properties.FtpsState == armappservice.FtpsStateAllAllowed return broken, "" @@ -244,11 +220,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/deploy-ftp?tabs=portal", }, "app-014": { - Id: "app-014", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "App Service should have Always On enabled", - Severity: scanners.SeverityHigh, + Id: "app-014", + Category: scanners.RulesCategoryScalability, + Recommendation: "App Service should have Always On enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.AlwaysOn == nil || !*scanContext.SiteConfig.Properties.AlwaysOn return broken, "" @@ -256,11 +231,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/configure-common?tabs=portal", }, "app-015": { - Id: "app-015", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "App Service should avoid using Client Affinity", - Severity: scanners.SeverityMedium, + Id: "app-015", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "App Service should avoid using Client Affinity", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" @@ -268,11 +242,10 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "app-016": { - Id: "app-016", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "App Service should use Managed Identities", - Severity: scanners.SeverityMedium, + Id: "app-016", + Category: scanners.RulesCategorySecurity, + Recommendation: "App Service should use Managed Identities", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone, "" @@ -285,53 +258,46 @@ func (a *AppServiceScanner) getAppRules() map[string]scanners.AzureRule { func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "func-001": { - Id: "func-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Function should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "func-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Function should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitor-log-analytics?tabs=csharp", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-monitor-log-analytics?tabs=csharp", }, "func-004": { - Id: "func-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Function should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "func-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-vnet", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/azure-functions/functions-create-vnet", }, "func-006": { - Id: "func-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Function Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "func-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Function Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "func") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "func-007": { - Id: "func-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "Function should use HTTPS only", - Severity: scanners.SeverityHigh, + Id: "func-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should use HTTPS only", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := c.Properties.HTTPSOnly != nil && *c.Properties.HTTPSOnly @@ -340,11 +306,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "func-008": { - Id: "func-008", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Function should have tags", - Severity: scanners.SeverityLow, + Id: "func-008", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Function should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" @@ -352,11 +317,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "func-009": { - Id: "func-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "Function should use VNET integration", - Severity: scanners.SeverityMedium, + Id: "func-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should use VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" @@ -364,11 +328,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "func-010": { - Id: "func-010", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "Function should have VNET Route all enabled for VNET integration", - Severity: scanners.SeverityMedium, + Id: "func-010", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should have VNET Route all enabled for VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" @@ -376,11 +339,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "func-011": { - Id: "func-011", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "Function should use TLS 1.2", - Severity: scanners.SeverityMedium, + Id: "func-011", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should use TLS 1.2", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" @@ -388,11 +350,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "func-012": { - Id: "func-012", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "Function remote debugging should be disabled", - Severity: scanners.SeverityMedium, + Id: "func-012", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function remote debugging should be disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" @@ -400,11 +361,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "func-013": { - Id: "func-013", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Function should avoid using Client Affinity", - Severity: scanners.SeverityMedium, + Id: "func-013", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Function should avoid using Client Affinity", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" @@ -412,11 +372,10 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "func-014": { - Id: "func-014", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Function should use Managed Identities", - Severity: scanners.SeverityMedium, + Id: "func-014", + Category: scanners.RulesCategorySecurity, + Recommendation: "Function should use Managed Identities", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone, "" @@ -429,53 +388,46 @@ func (a *AppServiceScanner) getFunctionRules() map[string]scanners.AzureRule { func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "logics-001": { - Id: "logics-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Logic App should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "logics-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Logic App should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappservice.Site) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data", }, "logics-004": { - Id: "logics-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Logic App should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "logics-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armappservice.Site) _, pe := scanContext.PrivateEndpoints[*i.ID] return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/logic-apps/secure-single-tenant-workflow-virtual-network-private-endpoint", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/logic-apps/secure-single-tenant-workflow-virtual-network-private-endpoint", }, "logics-006": { - Id: "logics-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Logic App Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "logics-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Logic App Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) caf := strings.HasPrefix(*c.Name, "logic") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "logics-007": { - Id: "logics-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "Logic App should use HTTPS only", - Severity: scanners.SeverityHigh, + Id: "logics-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should use HTTPS only", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) h := c.Properties.HTTPSOnly != nil && *c.Properties.HTTPSOnly @@ -484,11 +436,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/app-service/configure-ssl-bindings#enforce-https", }, "logics-008": { - Id: "logics-008", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Logic App should have tags", - Severity: scanners.SeverityLow, + Id: "logics-008", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Logic App should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return len(c.Tags) == 0, "" @@ -496,11 +447,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "logics-009": { - Id: "logics-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "Logic App should use VNET integration", - Severity: scanners.SeverityMedium, + Id: "logics-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should use VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VirtualNetworkSubnetID == nil || len(*c.Properties.VirtualNetworkSubnetID) == 0, "" @@ -508,11 +458,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "logics-010": { - Id: "logics-010", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "Logic App should have VNET Route all enabled for VNET integration", - Severity: scanners.SeverityMedium, + Id: "logics-010", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should have VNET Route all enabled for VNET integration", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.VnetRouteAllEnabled == nil || !*c.Properties.VnetRouteAllEnabled, "" @@ -520,11 +469,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-vnet-integration", }, "logics-011": { - Id: "logics-011", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "Logic App should use TLS 1.2", - Severity: scanners.SeverityMedium, + Id: "logics-011", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should use TLS 1.2", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.MinTLSVersion == nil || *scanContext.SiteConfig.Properties.MinTLSVersion != armappservice.SupportedTLSVersionsOne2 return broken, "" @@ -532,11 +480,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/app-service/overview-tls", }, "logics-012": { - Id: "logics-012", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "Logic App remote debugging should be disabled", - Severity: scanners.SeverityMedium, + Id: "logics-012", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App remote debugging should be disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { broken := scanContext.SiteConfig.Properties.RemoteDebuggingEnabled == nil || *scanContext.SiteConfig.Properties.RemoteDebuggingEnabled return broken, "" @@ -544,11 +491,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-azure-app-service?view=vs-2022#enable-remote-debugging", }, "logics-013": { - Id: "logics-013", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Logic App should avoid using Client Affinity", - Severity: scanners.SeverityMedium, + Id: "logics-013", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Logic App should avoid using Client Affinity", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Properties.ClientAffinityEnabled != nil && *c.Properties.ClientAffinityEnabled, "" @@ -556,11 +502,10 @@ func (a *AppServiceScanner) getLogicRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-app-service/reliability#checklist", }, "logics-014": { - Id: "logics-014", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Logic App should use Managed Identities", - Severity: scanners.SeverityMedium, + Id: "logics-014", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should use Managed Identities", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappservice.Site) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappservice.ManagedServiceIdentityTypeNone, "" diff --git a/internal/scanners/asp/rules_test.go b/internal/scanners/asp/rules_test.go index 6168bee5..f366e4b0 100644 --- a/internal/scanners/asp/rules_test.go +++ b/internal/scanners/asp/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2" ) @@ -32,7 +32,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { fields: fields{ rule: "asp-001", target: &armappservice.Plan{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { rule: "asp-002", target: &armappservice.Plan{ Properties: &armappservice.PlanProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -67,7 +67,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { rule: "asp-003", target: &armappservice.Plan{ SKU: &armappservice.SKUDescription{ - Tier: ref.Of("Free"), + Tier: to.Ptr("Free"), }, }, scanContext: &scanners.ScanContext{}, @@ -83,7 +83,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { rule: "asp-003", target: &armappservice.Plan{ SKU: &armappservice.SKUDescription{ - Tier: ref.Of("ElasticPremium"), + Tier: to.Ptr("ElasticPremium"), }, }, scanContext: &scanners.ScanContext{}, @@ -99,7 +99,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { rule: "asp-005", target: &armappservice.Plan{ SKU: &armappservice.SKUDescription{ - Name: ref.Of("EP1"), + Name: to.Ptr("EP1"), }, }, scanContext: &scanners.ScanContext{}, @@ -114,7 +114,7 @@ func TestAppServiceScanner_Rules(t *testing.T) { fields: fields{ rule: "asp-006", target: &armappservice.Plan{ - Name: ref.Of("asp-test"), + Name: to.Ptr("asp-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -160,7 +160,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { fields: fields{ rule: "app-001", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -178,7 +178,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { fields: fields{ rule: "app-004", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ PrivateEndpoints: map[string]bool{ @@ -196,7 +196,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { fields: fields{ rule: "app-006", target: &armappservice.Site{ - Name: ref.Of("app-test"), + Name: to.Ptr("app-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -211,7 +211,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-007", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - HTTPSOnly: ref.Of(true), + HTTPSOnly: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -227,7 +227,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-009", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VirtualNetworkSubnetID: ref.Of("test"), + VirtualNetworkSubnetID: to.Ptr("test"), }, }, scanContext: &scanners.ScanContext{}, @@ -259,7 +259,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(true), + VnetRouteAllEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -275,7 +275,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(false), + VnetRouteAllEnabled: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -310,7 +310,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - MinTLSVersion: ref.Of(armappservice.SupportedTLSVersionsOne2), + MinTLSVersion: to.Ptr(armappservice.SupportedTLSVersionsOne2), }, }, }, @@ -330,7 +330,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - RemoteDebuggingEnabled: ref.Of(true), + RemoteDebuggingEnabled: to.Ptr(true), }, }, }, @@ -350,7 +350,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - FtpsState: ref.Of(armappservice.FtpsStateAllAllowed), + FtpsState: to.Ptr(armappservice.FtpsStateAllAllowed), }, }, }, @@ -370,7 +370,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - AlwaysOn: ref.Of(false), + AlwaysOn: to.Ptr(false), }, }, }, @@ -387,7 +387,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-015", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - ClientAffinityEnabled: ref.Of(true), + ClientAffinityEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -403,7 +403,7 @@ func TestAppServiceScanner_AppRules(t *testing.T) { rule: "app-016", target: &armappservice.Site{ Identity: &armappservice.ManagedServiceIdentity{ - Type: ref.Of(armappservice.ManagedServiceIdentityTypeNone), + Type: to.Ptr(armappservice.ManagedServiceIdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, @@ -450,7 +450,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { fields: fields{ rule: "func-001", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -468,7 +468,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { fields: fields{ rule: "func-004", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ PrivateEndpoints: map[string]bool{ @@ -486,7 +486,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { fields: fields{ rule: "func-006", target: &armappservice.Site{ - Name: ref.Of("func-test"), + Name: to.Ptr("func-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -501,7 +501,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-007", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - HTTPSOnly: ref.Of(true), + HTTPSOnly: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -517,7 +517,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-009", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VirtualNetworkSubnetID: ref.Of("test"), + VirtualNetworkSubnetID: to.Ptr("test"), }, }, scanContext: &scanners.ScanContext{}, @@ -549,7 +549,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(true), + VnetRouteAllEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -565,7 +565,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(false), + VnetRouteAllEnabled: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -600,7 +600,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - MinTLSVersion: ref.Of(armappservice.SupportedTLSVersionsOne2), + MinTLSVersion: to.Ptr(armappservice.SupportedTLSVersionsOne2), }, }, }, @@ -620,7 +620,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - RemoteDebuggingEnabled: ref.Of(true), + RemoteDebuggingEnabled: to.Ptr(true), }, }, }, @@ -637,7 +637,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-013", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - ClientAffinityEnabled: ref.Of(true), + ClientAffinityEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -653,7 +653,7 @@ func TestAppServiceScanner_FunctionRules(t *testing.T) { rule: "func-014", target: &armappservice.Site{ Identity: &armappservice.ManagedServiceIdentity{ - Type: ref.Of(armappservice.ManagedServiceIdentityTypeNone), + Type: to.Ptr(armappservice.ManagedServiceIdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, @@ -700,7 +700,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { fields: fields{ rule: "logics-001", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -718,7 +718,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { fields: fields{ rule: "logics-004", target: &armappservice.Site{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ PrivateEndpoints: map[string]bool{ @@ -736,7 +736,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { fields: fields{ rule: "logics-006", target: &armappservice.Site{ - Name: ref.Of("logics-test"), + Name: to.Ptr("logics-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -751,7 +751,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-007", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - HTTPSOnly: ref.Of(true), + HTTPSOnly: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -767,7 +767,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-009", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VirtualNetworkSubnetID: ref.Of("test"), + VirtualNetworkSubnetID: to.Ptr("test"), }, }, scanContext: &scanners.ScanContext{}, @@ -799,7 +799,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(true), + VnetRouteAllEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -815,7 +815,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-010", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - VnetRouteAllEnabled: ref.Of(false), + VnetRouteAllEnabled: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -850,7 +850,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - MinTLSVersion: ref.Of(armappservice.SupportedTLSVersionsOne2), + MinTLSVersion: to.Ptr(armappservice.SupportedTLSVersionsOne2), }, }, }, @@ -870,7 +870,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { SiteConfig: &armappservice.WebAppsClientGetConfigurationResponse{ SiteConfigResource: armappservice.SiteConfigResource{ Properties: &armappservice.SiteConfig{ - RemoteDebuggingEnabled: ref.Of(true), + RemoteDebuggingEnabled: to.Ptr(true), }, }, }, @@ -887,7 +887,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-013", target: &armappservice.Site{ Properties: &armappservice.SiteProperties{ - ClientAffinityEnabled: ref.Of(true), + ClientAffinityEnabled: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -903,7 +903,7 @@ func TestAppServiceScanner_LogicRules(t *testing.T) { rule: "logics-014", target: &armappservice.Site{ Identity: &armappservice.ManagedServiceIdentity{ - Type: ref.Of(armappservice.ManagedServiceIdentityTypeNone), + Type: to.Ptr(armappservice.ManagedServiceIdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/ca/rules.go b/internal/scanners/ca/rules.go index 6cf9f697..bf1a4d78 100644 --- a/internal/scanners/ca/rules.go +++ b/internal/scanners/ca/rules.go @@ -14,37 +14,32 @@ import ( func (a *ContainerAppsScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "ca-003": { - Id: "ca-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "ContainerApp should have a SLA", - Severity: scanners.SeverityHigh, + Id: "ca-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerApp should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, - Url: "https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/", - Field: scanners.OverviewFieldSLA, + Url: "https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/", }, "ca-006": { - Id: "ca-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "ContainerApp Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "ca-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerApp Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) caf := strings.HasPrefix(*c.Name, "ca") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "ca-007": { - Id: "ca-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "ContainerApp should have tags", - Severity: scanners.SeverityLow, + Id: "ca-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerApp should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) return len(c.Tags) == 0, "" @@ -52,11 +47,10 @@ func (a *ContainerAppsScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "ca-008": { - Id: "ca-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "ContainerApp should not allow insecure ingress traffic", - Severity: scanners.SeverityLow, + Id: "ca-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerApp should not allow insecure ingress traffic", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) if c.Properties.Configuration != nil && c.Properties.Configuration.Ingress != nil && c.Properties.Configuration.Ingress.AllowInsecure != nil { @@ -67,11 +61,10 @@ func (a *ContainerAppsScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/container-apps/ingress-how-to?pivots=azure-cli", }, "ca-009": { - Id: "ca-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "ContainerApp should use Managed Identities", - Severity: scanners.SeverityLow, + Id: "ca-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerApp should use Managed Identities", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armappcontainers.ManagedServiceIdentityTypeNone, "" @@ -79,11 +72,10 @@ func (a *ContainerAppsScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=portal%2Cdotnet", }, "ca-010": { - Id: "ca-010", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "ContainerApp should use Azure Files to persist container data", - Severity: scanners.SeverityLow, + Id: "ca-010", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerApp should use Azure Files to persist container data", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) ok := true @@ -100,11 +92,10 @@ func (a *ContainerAppsScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/container-apps/storage-mounts?pivots=azure-cli", }, "ca-011": { - Id: "ca-011", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "ContainerApp should avoid using session affinity", - Severity: scanners.SeverityLow, + Id: "ca-011", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerApp should avoid using session affinity", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ContainerApp) return c.Properties.Configuration != nil && diff --git a/internal/scanners/ca/rules_test.go b/internal/scanners/ca/rules_test.go index 1d85dde4..89a47c32 100644 --- a/internal/scanners/ca/rules_test.go +++ b/internal/scanners/ca/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers/v2" ) @@ -44,7 +44,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { fields: fields{ rule: "ca-006", target: &armappcontainers.ContainerApp{ - Name: ref.Of("ca-test"), + Name: to.Ptr("ca-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -61,7 +61,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { Properties: &armappcontainers.ContainerAppProperties{ Configuration: &armappcontainers.Configuration{ Ingress: &armappcontainers.Ingress{ - AllowInsecure: ref.Of(true), + AllowInsecure: to.Ptr(true), }, }, }, @@ -79,7 +79,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { rule: "ca-009", target: &armappcontainers.ContainerApp{ Identity: &armappcontainers.ManagedServiceIdentity{ - Type: ref.Of(armappcontainers.ManagedServiceIdentityTypeNone), + Type: to.Ptr(armappcontainers.ManagedServiceIdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, @@ -98,7 +98,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { Template: &armappcontainers.Template{ Volumes: []*armappcontainers.Volume{ { - StorageType: ref.Of(armappcontainers.StorageTypeAzureFile), + StorageType: to.Ptr(armappcontainers.StorageTypeAzureFile), }, }, }, @@ -120,7 +120,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { Template: &armappcontainers.Template{ Volumes: []*armappcontainers.Volume{ { - StorageType: ref.Of(armappcontainers.StorageTypeEmptyDir), + StorageType: to.Ptr(armappcontainers.StorageTypeEmptyDir), }, }, }, @@ -142,7 +142,7 @@ func TestContainerAppsScanner_Rules(t *testing.T) { Configuration: &armappcontainers.Configuration{ Ingress: &armappcontainers.Ingress{ StickySessions: &armappcontainers.IngressStickySessions{ - Affinity: ref.Of(armappcontainers.AffinitySticky), + Affinity: to.Ptr(armappcontainers.AffinitySticky), }, }, }, diff --git a/internal/scanners/cae/rules.go b/internal/scanners/cae/rules.go index ac7f03a1..7981ba5f 100644 --- a/internal/scanners/cae/rules.go +++ b/internal/scanners/cae/rules.go @@ -14,79 +14,68 @@ import ( func (a *ContainerAppsEnvironmentScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "cae-001": { - Id: "cae-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Container Apps Environment should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "cae-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Container Apps Environment should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armappcontainers.ManagedEnvironment) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-apps/log-options#diagnostic-settings", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/container-apps/log-options#diagnostic-settings", }, "cae-002": { - Id: "cae-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Container Apps Environment should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "cae-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Container Apps Environment should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { app := target.(*armappcontainers.ManagedEnvironment) zones := *app.Properties.ZoneRedundant return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-apps/disaster-recovery?tabs=bash#set-up-zone-redundancy-in-your-container-apps-environment", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/container-apps/disaster-recovery?tabs=bash#set-up-zone-redundancy-in-your-container-apps-environment", }, "cae-003": { - Id: "cae-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Container Apps Environment should have a SLA", - Severity: scanners.SeverityHigh, + Id: "cae-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Container Apps Environment should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, - Url: "https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/", - Field: scanners.OverviewFieldSLA, + Url: "https://azure.microsoft.com/en-us/support/legal/sla/container-apps/v1_0/", }, "cae-004": { - Id: "cae-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Container Apps Environment should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "cae-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Container Apps Environment should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { app := target.(*armappcontainers.ManagedEnvironment) pe := app.Properties.VnetConfiguration != nil && *app.Properties.VnetConfiguration.Internal return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-apps/vnet-custom-internal?tabs=bash&pivots=azure-portal", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/container-apps/vnet-custom-internal?tabs=bash&pivots=azure-portal", }, "cae-006": { - Id: "cae-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Container Apps Environment Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "cae-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Container Apps Environment Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ManagedEnvironment) caf := strings.HasPrefix(*c.Name, "cae") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "cae-007": { - Id: "cae-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Container Apps Environment should have tags", - Severity: scanners.SeverityLow, + Id: "cae-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Container Apps Environment should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armappcontainers.ManagedEnvironment) return len(c.Tags) == 0, "" diff --git a/internal/scanners/cae/rules_test.go b/internal/scanners/cae/rules_test.go index f6956fdb..bb270ee3 100644 --- a/internal/scanners/cae/rules_test.go +++ b/internal/scanners/cae/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers/v2" ) @@ -32,7 +32,7 @@ func TestContainerAppsEnvironmentScanner_Rules(t *testing.T) { fields: fields{ rule: "cae-001", target: &armappcontainers.ManagedEnvironment{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestContainerAppsEnvironmentScanner_Rules(t *testing.T) { rule: "cae-002", target: &armappcontainers.ManagedEnvironment{ Properties: &armappcontainers.ManagedEnvironmentProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -96,7 +96,7 @@ func TestContainerAppsEnvironmentScanner_Rules(t *testing.T) { target: &armappcontainers.ManagedEnvironment{ Properties: &armappcontainers.ManagedEnvironmentProperties{ VnetConfiguration: &armappcontainers.VnetConfiguration{ - Internal: ref.Of(true), + Internal: to.Ptr(true), }, }, }, @@ -112,7 +112,7 @@ func TestContainerAppsEnvironmentScanner_Rules(t *testing.T) { fields: fields{ rule: "cae-006", target: &armappcontainers.ManagedEnvironment{ - Name: ref.Of("cae-test"), + Name: to.Ptr("cae-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/ci/rules.go b/internal/scanners/ci/rules.go index 2e934b0c..aa0dd38a 100644 --- a/internal/scanners/ci/rules.go +++ b/internal/scanners/ci/rules.go @@ -14,37 +14,32 @@ import ( func (a *ContainerInstanceScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "ci-002": { - Id: "ci-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "ContainerInstance should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "ci-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerInstance should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerinstance.ContainerGroup) zones := len(i.Zones) > 0 return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-instances/availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/container-instances/availability-zones", }, "ci-003": { - Id: "ci-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "ContainerInstance should have a SLA", - Severity: scanners.SeverityHigh, + Id: "ci-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerInstance should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, - Url: "https://www.azure.cn/en-us/support/sla/container-instances/v1_0/index.html", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/container-instances/v1_0/index.html", }, "ci-004": { - Id: "ci-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateIP, - Description: "ContainerInstance should use private IP addresses", - Severity: scanners.SeverityHigh, + Id: "ci-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerInstance should use private IP addresses", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerinstance.ContainerGroup) pe := false @@ -53,41 +48,35 @@ func (a *ContainerInstanceScanner) GetRules() map[string]scanners.AzureRule { } return !pe, "" }, - Field: scanners.OverviewFieldPrivate, }, "ci-005": { - Id: "ci-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "ContainerInstance SKU", - Severity: scanners.SeverityHigh, + Id: "ci-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerInstance SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerinstance.ContainerGroup) return false, string(*i.Properties.SKU) }, - Url: "https://azure.microsoft.com/en-us/pricing/details/container-instances/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/container-instances/", }, "ci-006": { - Id: "ci-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "ContainerInstance Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "ci-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerInstance Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerinstance.ContainerGroup) caf := strings.HasPrefix(*c.Name, "ci") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "ci-007": { - Id: "ci-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "ContainerInstance should have tags", - Severity: scanners.SeverityLow, + Id: "ci-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerInstance should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerinstance.ContainerGroup) return len(c.Tags) == 0, "" diff --git a/internal/scanners/ci/rules_test.go b/internal/scanners/ci/rules_test.go index ca9f3181..bd14ca18 100644 --- a/internal/scanners/ci/rules_test.go +++ b/internal/scanners/ci/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance" ) @@ -32,7 +32,7 @@ func TestContainerInstanceScanner_Rules(t *testing.T) { fields: fields{ rule: "ci-002", target: &armcontainerinstance.ContainerGroup{ - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -94,7 +94,7 @@ func TestContainerInstanceScanner_Rules(t *testing.T) { target: &armcontainerinstance.ContainerGroup{ Properties: &armcontainerinstance.ContainerGroupProperties{ IPAddress: &armcontainerinstance.IPAddress{ - Type: ref.Of(armcontainerinstance.ContainerGroupIPAddressTypePrivate), + Type: to.Ptr(armcontainerinstance.ContainerGroupIPAddressTypePrivate), }, }, }, @@ -111,7 +111,7 @@ func TestContainerInstanceScanner_Rules(t *testing.T) { rule: "ci-005", target: &armcontainerinstance.ContainerGroup{ Properties: &armcontainerinstance.ContainerGroupProperties{ - SKU: ref.Of(armcontainerinstance.ContainerGroupSKUStandard), + SKU: to.Ptr(armcontainerinstance.ContainerGroupSKUStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -126,7 +126,7 @@ func TestContainerInstanceScanner_Rules(t *testing.T) { fields: fields{ rule: "ci-006", target: &armcontainerinstance.ContainerGroup{ - Name: ref.Of("ci-test"), + Name: to.Ptr("ci-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/cog/rules.go b/internal/scanners/cog/rules.go index d44e38f3..79c02bb0 100644 --- a/internal/scanners/cog/rules.go +++ b/internal/scanners/cog/rules.go @@ -14,78 +14,67 @@ import ( func (a *CognitiveScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "cog-001": { - Id: "cog-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Cognitive Service Account should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "cog-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Cognitive Service Account should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcognitiveservices.Account) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing", }, "cog-003": { - Id: "cog-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Cognitive Service Account should have a SLA", - Severity: scanners.SeverityHigh, + Id: "cog-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Cognitive Service Account should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", }, "cog-004": { - Id: "cog-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Cognitive Service Account should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "cog-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Cognitive Service Account should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcognitiveservices.Account) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-virtual-networks", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/cognitive-services/cognitive-services-virtual-networks", }, "cog-005": { - Id: "cog-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Cognitive Service Account SKU", - Severity: scanners.SeverityHigh, + Id: "cog-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Cognitive Service Account SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcognitiveservices.Account) return false, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts?pivots=deployment-language-bicep#sku", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/templates/microsoft.cognitiveservices/accounts?pivots=deployment-language-bicep#sku", }, "cog-006": { - Id: "cog-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Cognitive Service Account Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "cog-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Cognitive Service Account Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcognitiveservices.Account) caf := strings.HasPrefix(*c.Name, "cog") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "cog-007": { - Id: "cog-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Cognitive Service Account should have tags", - Severity: scanners.SeverityLow, + Id: "cog-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Cognitive Service Account should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcognitiveservices.Account) return len(c.Tags) == 0, "" @@ -93,11 +82,10 @@ func (a *CognitiveScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "cog-008": { - Id: "cog-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Cognitive Service Account should have local authentication disabled", - Severity: scanners.SeverityMedium, + Id: "cog-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Cognitive Service Account should have local authentication disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcognitiveservices.Account) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth diff --git a/internal/scanners/cog/rules_test.go b/internal/scanners/cog/rules_test.go index a358031d..3606afc8 100644 --- a/internal/scanners/cog/rules_test.go +++ b/internal/scanners/cog/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cognitiveservices/armcognitiveservices" ) @@ -32,7 +32,7 @@ func TestCognitiveScanner_Rules(t *testing.T) { fields: fields{ rule: "cog-001", target: &armcognitiveservices.Account{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -65,7 +65,7 @@ func TestCognitiveScanner_Rules(t *testing.T) { Properties: &armcognitiveservices.AccountProperties{ PrivateEndpointConnections: []*armcognitiveservices.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -83,7 +83,7 @@ func TestCognitiveScanner_Rules(t *testing.T) { rule: "cog-005", target: &armcognitiveservices.Account{ SKU: &armcognitiveservices.SKU{ - Name: ref.Of("test"), + Name: to.Ptr("test"), }, }, scanContext: &scanners.ScanContext{}, @@ -98,7 +98,7 @@ func TestCognitiveScanner_Rules(t *testing.T) { fields: fields{ rule: "cog-006", target: &armcognitiveservices.Account{ - Name: ref.Of("cog-test"), + Name: to.Ptr("cog-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -127,7 +127,7 @@ func TestCognitiveScanner_Rules(t *testing.T) { rule: "cog-008", target: &armcognitiveservices.Account{ Properties: &armcognitiveservices.AccountProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/cosmos/rules.go b/internal/scanners/cosmos/rules.go index 0990e3d0..1015c5e3 100644 --- a/internal/scanners/cosmos/rules.go +++ b/internal/scanners/cosmos/rules.go @@ -14,25 +14,22 @@ import ( func (a *CosmosDBScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "cosmos-001": { - Id: "cosmos-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "CosmosDB should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "cosmos-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "CosmosDB should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcosmos.DatabaseAccountGetResults) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/monitor-resource-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/monitor-resource-logs", }, "cosmos-002": { - Id: "cosmos-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "CosmosDB should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "cosmos-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "CosmosDB should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcosmos.DatabaseAccountGetResults) availabilityZones := false @@ -51,15 +48,13 @@ func (a *CosmosDBScanner) GetRules() map[string]scanners.AzureRule { return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability", }, "cosmos-003": { - Id: "cosmos-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "CosmosDB should have a SLA", - Severity: scanners.SeverityHigh, + Id: "cosmos-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "CosmosDB should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcosmos.DatabaseAccountGetResults) sla := "99.99%" @@ -81,56 +76,48 @@ func (a *CosmosDBScanner) GetRules() map[string]scanners.AzureRule { } return false, sla }, - Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability#slas", - Field: scanners.OverviewFieldSLA, + Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/high-availability#slas", }, "cosmos-004": { - Id: "cosmos-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "CosmosDB should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "cosmos-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "CosmosDB should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcosmos.DatabaseAccountGetResults) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-configure-private-endpoints", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-configure-private-endpoints", }, "cosmos-005": { - Id: "cosmos-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "CosmosDB SKU", - Severity: scanners.SeverityHigh, + Id: "cosmos-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "CosmosDB SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcosmos.DatabaseAccountGetResults) return false, string(*i.Properties.DatabaseAccountOfferType) }, - Url: "https://azure.microsoft.com/en-us/pricing/details/cosmos-db/autoscale-provisioned/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/cosmos-db/autoscale-provisioned/", }, "cosmos-006": { - Id: "cosmos-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "CosmosDB Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "cosmos-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "CosmosDB Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcosmos.DatabaseAccountGetResults) caf := strings.HasPrefix(*c.Name, "cosmos") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "cosmos-007": { - Id: "cosmos-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "CosmosDB should have tags", - Severity: scanners.SeverityLow, + Id: "cosmos-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "CosmosDB should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcosmos.DatabaseAccountGetResults) return len(c.Tags) == 0, "" @@ -138,11 +125,10 @@ func (a *CosmosDBScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "cosmos-008": { - Id: "cosmos-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "CosmosDB should have local authentication disabled", - Severity: scanners.SeverityHigh, + Id: "cosmos-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "CosmosDB should have local authentication disabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcosmos.DatabaseAccountGetResults) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth @@ -151,11 +137,10 @@ func (a *CosmosDBScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/cosmos-db/how-to-setup-rbac#disable-local-auth", }, "cosmos-009": { - Id: "cosmos-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurity, - Description: "CosmosDB: disable write operations on metadata resources (databases, containers, throughput) via account keys", - Severity: scanners.SeverityHigh, + Id: "cosmos-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "CosmosDB: disable write operations on metadata resources (databases, containers, throughput) via account keys", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcosmos.DatabaseAccountGetResults) disabled := c.Properties.DisableKeyBasedMetadataWriteAccess != nil && *c.Properties.DisableKeyBasedMetadataWriteAccess diff --git a/internal/scanners/cosmos/rules_test.go b/internal/scanners/cosmos/rules_test.go index 423e7747..c5a051ff 100644 --- a/internal/scanners/cosmos/rules_test.go +++ b/internal/scanners/cosmos/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cosmos/armcosmos" ) @@ -32,7 +32,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { fields: fields{ rule: "cosmos-001", target: &armcosmos.DatabaseAccountGetResults{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -53,10 +53,10 @@ func TestCosmosDBScanner_Rules(t *testing.T) { Properties: &armcosmos.DatabaseAccountGetProperties{ Locations: []*armcosmos.Location{ { - IsZoneRedundant: ref.Of(true), + IsZoneRedundant: to.Ptr(true), }, { - IsZoneRedundant: ref.Of(true), + IsZoneRedundant: to.Ptr(true), }, }, }, @@ -90,7 +90,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { Properties: &armcosmos.DatabaseAccountGetProperties{ Locations: []*armcosmos.Location{ { - IsZoneRedundant: ref.Of(true), + IsZoneRedundant: to.Ptr(true), }, }, }, @@ -110,10 +110,10 @@ func TestCosmosDBScanner_Rules(t *testing.T) { Properties: &armcosmos.DatabaseAccountGetProperties{ Locations: []*armcosmos.Location{ { - IsZoneRedundant: ref.Of(true), + IsZoneRedundant: to.Ptr(true), }, { - IsZoneRedundant: ref.Of(true), + IsZoneRedundant: to.Ptr(true), }, }, }, @@ -133,7 +133,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { Properties: &armcosmos.DatabaseAccountGetProperties{ PrivateEndpointConnections: []*armcosmos.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -151,7 +151,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { rule: "cosmos-005", target: &armcosmos.DatabaseAccountGetResults{ Properties: &armcosmos.DatabaseAccountGetProperties{ - DatabaseAccountOfferType: ref.Of("Standard"), + DatabaseAccountOfferType: to.Ptr("Standard"), }, }, scanContext: &scanners.ScanContext{}, @@ -166,7 +166,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { fields: fields{ rule: "cosmos-006", target: &armcosmos.DatabaseAccountGetResults{ - Name: ref.Of("cosmos-test"), + Name: to.Ptr("cosmos-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -181,7 +181,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { rule: "cosmos-008", target: &armcosmos.DatabaseAccountGetResults{ Properties: &armcosmos.DatabaseAccountGetProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -197,7 +197,7 @@ func TestCosmosDBScanner_Rules(t *testing.T) { rule: "cosmos-009", target: &armcosmos.DatabaseAccountGetResults{ Properties: &armcosmos.DatabaseAccountGetProperties{ - DisableKeyBasedMetadataWriteAccess: ref.Of(true), + DisableKeyBasedMetadataWriteAccess: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/cost.go b/internal/scanners/cost.go index e282ecc8..d36797c2 100644 --- a/internal/scanners/cost.go +++ b/internal/scanners/cost.go @@ -6,7 +6,7 @@ import ( "fmt" "time" - "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/costmanagement/armcostmanagement" ) @@ -27,26 +27,6 @@ type CostScanner struct { client *armcostmanagement.QueryClient } -// GetProperties - Returns the properties of the CostResult -func (d CostResult) GetProperties() []string { - return []string{ - "SubscriptionID", - "ServiceName", - "Value", - "Currency", - } -} - -// ToMap - Returns the properties of the CostResult as a map -func (r CostResultItem) ToMap(mask bool) map[string]string { - return map[string]string{ - "SubscriptionID": MaskSubscriptionID(r.SubscriptionID, mask), - "ServiceName": r.ServiceName, - "Value": r.Value, - "Currency": r.Currency, - } -} - // Init - Initializes the Cost Scanner func (s *CostScanner) Init(config *ScannerConfig) error { s.config = config @@ -78,13 +58,13 @@ func (s *CostScanner) QueryCosts() (*CostResult, error) { // Granularity: &daily, Aggregation: map[string]*armcostmanagement.QueryAggregation{ "TotalCost": { - Name: ref.Of("Cost"), + Name: to.Ptr("Cost"), Function: &sum, }, }, Grouping: []*armcostmanagement.QueryGrouping{ { - Name: ref.Of("ServiceName"), + Name: to.Ptr("ServiceName"), Type: &dimension, }, }, diff --git a/internal/scanners/cr/rules.go b/internal/scanners/cr/rules.go index 5311b716..8c0ace38 100644 --- a/internal/scanners/cr/rules.go +++ b/internal/scanners/cr/rules.go @@ -14,92 +14,79 @@ import ( func (a *ContainerRegistryScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "cr-001": { - Id: "cr-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "ContainerRegistry should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "cr-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "ContainerRegistry should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcontainerregistry.Registry) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/container-registry/monitor-service", }, "cr-002": { - Id: "cr-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "ContainerRegistry should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "cr-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerRegistry should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerregistry.Registry) zones := *i.Properties.ZoneRedundancy == armcontainerregistry.ZoneRedundancyEnabled return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-registry/zone-redundancy", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/container-registry/zone-redundancy", }, "cr-003": { - Id: "cr-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "ContainerRegistry should have a SLA", - Severity: scanners.SeverityHigh, + Id: "cr-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerRegistry should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, - Url: "https://www.azure.cn/en-us/support/sla/container-registry/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/container-registry/", }, "cr-004": { - Id: "cr-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "ContainerRegistry should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "cr-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerRegistry should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerregistry.Registry) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-private-link", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-private-link", }, "cr-005": { - Id: "cr-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "ContainerRegistry SKU", - Severity: scanners.SeverityHigh, + Id: "cr-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "ContainerRegistry SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armcontainerregistry.Registry) return false, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-skus", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/container-registry/container-registry-skus", }, "cr-006": { - Id: "cr-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "ContainerRegistry Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "cr-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerRegistry Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerregistry.Registry) caf := strings.HasPrefix(*c.Name, "cr") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "cr-007": { - Id: "cr-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "ContainerRegistry should have anonymous pull access disabled", - Severity: scanners.SeverityMedium, + Id: "cr-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerRegistry should have anonymous pull access disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerregistry.Registry) apull := c.Properties.AnonymousPullEnabled != nil && *c.Properties.AnonymousPullEnabled @@ -108,11 +95,10 @@ func (a *ContainerRegistryScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/container-registry/anonymous-pull-access#configure-anonymous-pull-access", }, "cr-008": { - Id: "cr-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "ContainerRegistry should have the Administrator account disabled", - Severity: scanners.SeverityMedium, + Id: "cr-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "ContainerRegistry should have the Administrator account disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerregistry.Registry) admin := c.Properties.AdminUserEnabled != nil && *c.Properties.AdminUserEnabled @@ -121,11 +107,10 @@ func (a *ContainerRegistryScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/container-registry/container-registry-authentication-managed-identity", }, "cr-009": { - Id: "cr-009", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "ContainerRegistry should have tags", - Severity: scanners.SeverityLow, + Id: "cr-009", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerRegistry should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerregistry.Registry) return len(c.Tags) == 0, "" @@ -133,11 +118,10 @@ func (a *ContainerRegistryScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "cr-010": { - Id: "cr-010", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceRetentionPolicies, - Description: "ContainerRegistry should use retention policies", - Severity: scanners.SeverityMedium, + Id: "cr-010", + Category: scanners.RulesCategoryGovernance, + Recommendation: "ContainerRegistry should use retention policies", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcontainerregistry.Registry) return c.Properties.Policies == nil || diff --git a/internal/scanners/cr/rules_test.go b/internal/scanners/cr/rules_test.go index c0ab1eef..38e4d982 100644 --- a/internal/scanners/cr/rules_test.go +++ b/internal/scanners/cr/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerregistry/armcontainerregistry" ) @@ -32,7 +32,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { fields: fields{ rule: "cr-001", target: &armcontainerregistry.Registry{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { rule: "cr-002", target: &armcontainerregistry.Registry{ Properties: &armcontainerregistry.RegistryProperties{ - ZoneRedundancy: ref.Of(armcontainerregistry.ZoneRedundancyEnabled), + ZoneRedundancy: to.Ptr(armcontainerregistry.ZoneRedundancyEnabled), }, }, scanContext: &scanners.ScanContext{}, @@ -81,7 +81,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { Properties: &armcontainerregistry.RegistryProperties{ PrivateEndpointConnections: []*armcontainerregistry.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -99,7 +99,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { rule: "cr-005", target: &armcontainerregistry.Registry{ SKU: &armcontainerregistry.SKU{ - Name: ref.Of(armcontainerregistry.SKUNameStandard), + Name: to.Ptr(armcontainerregistry.SKUNameStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -114,7 +114,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { fields: fields{ rule: "cr-006", target: &armcontainerregistry.Registry{ - Name: ref.Of("cr-test"), + Name: to.Ptr("cr-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -143,7 +143,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { rule: "cr-007", target: &armcontainerregistry.Registry{ Properties: &armcontainerregistry.RegistryProperties{ - AnonymousPullEnabled: ref.Of(false), + AnonymousPullEnabled: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -173,7 +173,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { rule: "cr-008", target: &armcontainerregistry.Registry{ Properties: &armcontainerregistry.RegistryProperties{ - AdminUserEnabled: ref.Of(false), + AdminUserEnabled: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -205,7 +205,7 @@ func TestContainerRegistryScanner_Rules(t *testing.T) { Properties: &armcontainerregistry.RegistryProperties{ Policies: &armcontainerregistry.Policies{ RetentionPolicy: &armcontainerregistry.RetentionPolicy{ - Status: ref.Of(armcontainerregistry.PolicyStatusDisabled), + Status: to.Ptr(armcontainerregistry.PolicyStatusDisabled), }, }, }, diff --git a/internal/scanners/dbw/rules.go b/internal/scanners/dbw/rules.go index 34b08a2c..8f675583 100644 --- a/internal/scanners/dbw/rules.go +++ b/internal/scanners/dbw/rules.go @@ -6,8 +6,8 @@ package dbw import ( "strings" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/databricks/armdatabricks" ) @@ -15,81 +15,70 @@ import ( func (a *DatabricksScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "dbw-001": { - Id: "dbw-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Databricks should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "dbw-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Databricks should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armdatabricks.Workspace) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/account-settings/audit-log-delivery", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/account-settings/audit-log-delivery", }, "dbw-003": { - Id: "dbw-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Databricks should have a SLA", - Severity: scanners.SeverityHigh, + Id: "dbw-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Databricks should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", }, "dbw-004": { - Id: "dbw-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Azure Databricks should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "dbw-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Databricks should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armdatabricks.Workspace) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/cloud-configurations/azure/private-link", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/cloud-configurations/azure/private-link", }, "dbw-005": { - Id: "dbw-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Databricks SKU", - Severity: scanners.SeverityHigh, + Id: "dbw-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Databricks SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armdatabricks.Workspace) return false, string(*i.SKU.Name) }, - Url: "https://azure.microsoft.com/en-us/pricing/details/databricks/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/databricks/", }, "dbw-006": { - Id: "dbw-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Databricks Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "dbw-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Databricks Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armdatabricks.Workspace) caf := strings.HasPrefix(*c.Name, "dbw") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "dbw-007": { - Id: "dbw-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Azure Databricks should have the Public IP disabled", - Severity: scanners.SeverityMedium, + Id: "dbw-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Databricks should have the Public IP disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armdatabricks.Workspace) - broken := c.Properties.Parameters.EnableNoPublicIP != nil && c.Properties.Parameters.EnableNoPublicIP.Value == ref.Of(true) + broken := c.Properties.Parameters.EnableNoPublicIP != nil && c.Properties.Parameters.EnableNoPublicIP.Value == to.Ptr(true) return broken, "" }, Url: "https://learn.microsoft.com/en-us/azure/databricks/security/network/secure-cluster-connectivity", diff --git a/internal/scanners/dbw/rules_test.go b/internal/scanners/dbw/rules_test.go index fbae0b9b..3adfe9a8 100644 --- a/internal/scanners/dbw/rules_test.go +++ b/internal/scanners/dbw/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/databricks/armdatabricks" ) @@ -32,7 +32,7 @@ func TestDatabricksScanner_Rules(t *testing.T) { fields: fields{ rule: "dbw-001", target: &armdatabricks.Workspace{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -65,7 +65,7 @@ func TestDatabricksScanner_Rules(t *testing.T) { Properties: &armdatabricks.WorkspaceProperties{ PrivateEndpointConnections: []*armdatabricks.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -83,7 +83,7 @@ func TestDatabricksScanner_Rules(t *testing.T) { rule: "dbw-005", target: &armdatabricks.Workspace{ SKU: &armdatabricks.SKU{ - Name: ref.Of("Standard"), + Name: to.Ptr("Standard"), }, }, scanContext: &scanners.ScanContext{}, @@ -98,7 +98,7 @@ func TestDatabricksScanner_Rules(t *testing.T) { fields: fields{ rule: "dbw-006", target: &armdatabricks.Workspace{ - Name: ref.Of("dbw-test"), + Name: to.Ptr("dbw-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -115,7 +115,7 @@ func TestDatabricksScanner_Rules(t *testing.T) { Properties: &armdatabricks.WorkspaceProperties{ Parameters: &armdatabricks.WorkspaceCustomParameters{ EnableNoPublicIP: &armdatabricks.WorkspaceCustomBooleanParameter{ - Value: ref.Of(true), + Value: to.Ptr(true), }, }, }, diff --git a/internal/scanners/dec/rules.go b/internal/scanners/dec/rules.go index 4ab031c1..dd398f4a 100644 --- a/internal/scanners/dec/rules.go +++ b/internal/scanners/dec/rules.go @@ -14,25 +14,22 @@ import ( func (a *DataExplorerScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "dec-001": { - Id: "dec-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Data Explorer should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "dec-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Data Explorer should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armkusto.Cluster) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/data-explorer/using-diagnostic-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/data-explorer/using-diagnostic-logs", }, "dec-002": { - Id: "dec-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Data Explorer SLA", - Severity: scanners.SeverityHigh, + Id: "dec-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Data Explorer SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) sla := "99.9%" @@ -42,15 +39,13 @@ func (a *DataExplorerScanner) GetRules() map[string]scanners.AzureRule { return sla == "None", sla }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", }, "dec-003": { - Id: "dec-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Data Explorer Production Cluster should not use Dev SKU", - Severity: scanners.SeverityHigh, + Id: "dec-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Data Explorer Production Cluster should not use Dev SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) broken := false @@ -60,43 +55,37 @@ func (a *DataExplorerScanner) GetRules() map[string]scanners.AzureRule { } return broken, string(*c.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/data-explorer/manage-cluster-choose-sku", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/data-explorer/manage-cluster-choose-sku", }, "dec-004": { - Id: "dec-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Azure Data Explorer should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "dec-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Data Explorer should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armkusto.Cluster) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/data-explorer/security-network-private-endpoint", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/data-explorer/security-network-private-endpoint", }, "dec-006": { - Id: "dec-004", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Data Explorer Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "dec-004", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Data Explorer Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) caf := strings.HasPrefix(*c.Name, "dec") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "dec-007": { - Id: "dec-005", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Data Explorer should have tags", - Severity: scanners.SeverityLow, + Id: "dec-005", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Data Explorer should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) return c.Tags == nil || len(c.Tags) == 0, "" @@ -104,11 +93,10 @@ func (a *DataExplorerScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "dec-008": { - Id: "dec-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityDiskEncryption, - Description: "Azure Data Explorer should use Disk Encryption", - Severity: scanners.SeverityHigh, + Id: "dec-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Data Explorer should use Disk Encryption", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) return c.Properties.EnableDiskEncryption == nil || !*c.Properties.EnableDiskEncryption, "" @@ -116,11 +104,10 @@ func (a *DataExplorerScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/data-explorer/cluster-encryption-overview", }, "dec-009": { - Id: "dec-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Azure Data Explorer should use Managed Identities", - Severity: scanners.SeverityLow, + Id: "dec-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Data Explorer should use Managed Identities", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkusto.Cluster) return c.Identity == nil || c.Identity.Type == nil || *c.Identity.Type == armkusto.IdentityTypeNone, "" diff --git a/internal/scanners/dec/rules_test.go b/internal/scanners/dec/rules_test.go index c7b04dd3..b527d52c 100644 --- a/internal/scanners/dec/rules_test.go +++ b/internal/scanners/dec/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/kusto/armkusto" ) @@ -32,7 +32,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { fields: fields{ rule: "dec-001", target: &armkusto.Cluster{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { rule: "dec-002", target: &armkusto.Cluster{ SKU: &armkusto.AzureSKU{ - Name: ref.Of(armkusto.AzureSKUNameDevNoSLAStandardD11V2), + Name: to.Ptr(armkusto.AzureSKUNameDevNoSLAStandardD11V2), }, }, scanContext: &scanners.ScanContext{}, @@ -67,7 +67,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { rule: "dec-002", target: &armkusto.Cluster{ SKU: &armkusto.AzureSKU{ - Name: ref.Of(armkusto.AzureSKUNameStandardD11V2), + Name: to.Ptr(armkusto.AzureSKUNameStandardD11V2), }, }, scanContext: &scanners.ScanContext{}, @@ -83,7 +83,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { rule: "dec-003", target: &armkusto.Cluster{ SKU: &armkusto.AzureSKU{ - Name: ref.Of(armkusto.AzureSKUNameDevNoSLAStandardD11V2), + Name: to.Ptr(armkusto.AzureSKUNameDevNoSLAStandardD11V2), }, }, scanContext: &scanners.ScanContext{}, @@ -101,7 +101,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { Properties: &armkusto.ClusterProperties{ PrivateEndpointConnections: []*armkusto.PrivateEndpointConnection{ { - Name: ref.Of("test"), + Name: to.Ptr("test"), }, }, }, @@ -118,7 +118,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { fields: fields{ rule: "dec-006", target: &armkusto.Cluster{ - Name: ref.Of("dec-test"), + Name: to.Ptr("dec-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -133,7 +133,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { rule: "dec-008", target: &armkusto.Cluster{ Properties: &armkusto.ClusterProperties{ - EnableDiskEncryption: ref.Of(false), + EnableDiskEncryption: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -165,7 +165,7 @@ func TestDataExplorerScanner_Rules(t *testing.T) { rule: "dec-009", target: &armkusto.Cluster{ Identity: &armkusto.Identity{ - Type: ref.Of(armkusto.IdentityTypeNone), + Type: to.Ptr(armkusto.IdentityTypeNone), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/defender.go b/internal/scanners/defender.go index 69bf2b17..97f611e2 100644 --- a/internal/scanners/defender.go +++ b/internal/scanners/defender.go @@ -5,7 +5,6 @@ package scanners import ( "github.com/rs/zerolog/log" - "strconv" "strings" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity" @@ -24,26 +23,6 @@ type DefenderScanner struct { defenderFunc func() ([]DefenderResult, error) } -// GetProperties - Returns the properties of the DefenderResult -func (d DefenderResult) GetProperties() []string { - return []string{ - "SubscriptionID", - "Name", - "Tier", - "Deprecated", - } -} - -// ToMap - Returns the properties of the DefenderResult as a map -func (r DefenderResult) ToMap(mask bool) map[string]string { - return map[string]string{ - "SubscriptionID": MaskSubscriptionID(r.SubscriptionID, mask), - "Name": r.Name, - "Tier": r.Tier, - "Deprecated": strconv.FormatBool(r.Deprecated), - } -} - // Init - Initializes the Defender Scanner func (s *DefenderScanner) Init(config *ScannerConfig) error { s.config = config diff --git a/internal/scanners/evgd/rules.go b/internal/scanners/evgd/rules.go index 33e88b6d..4e8ec0cc 100644 --- a/internal/scanners/evgd/rules.go +++ b/internal/scanners/evgd/rules.go @@ -14,77 +14,66 @@ import ( func (a *EventGridScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "evgd-001": { - Id: "evgd-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Event Grid Domain should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "evgd-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Event Grid Domain should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armeventgrid.Domain) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-grid/diagnostic-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/event-grid/diagnostic-logs", }, "evgd-003": { - Id: "evgd-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Event Grid Domain should have a SLA", - Severity: scanners.SeverityHigh, + Id: "evgd-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Event Grid Domain should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.azure.cn/en-us/support/sla/event-grid/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/event-grid/", }, "evgd-004": { - Id: "evgd-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Event Grid Domain should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "evgd-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Event Grid Domain should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armeventgrid.Domain) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-grid/configure-private-endpoints", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/event-grid/configure-private-endpoints", }, "evgd-005": { - Id: "evgd-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Event Grid Domain SKU", - Severity: scanners.SeverityHigh, + Id: "evgd-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Event Grid Domain SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "None" }, - Url: "https://azure.microsoft.com/en-gb/pricing/details/event-grid/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-gb/pricing/details/event-grid/", }, "evgd-006": { - Id: "evgd-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Event Grid Domain Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "evgd-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Event Grid Domain Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventgrid.Domain) caf := strings.HasPrefix(*c.Name, "evgd") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "evgd-007": { - Id: "evgd-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Event Grid Domain should have tags", - Severity: scanners.SeverityLow, + Id: "evgd-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Event Grid Domain should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventgrid.Domain) return len(c.Tags) == 0, "" @@ -92,11 +81,10 @@ func (a *EventGridScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "evgd-008": { - Id: "evgd-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Event Grid Domain should have local authentication disabled", - Severity: scanners.SeverityMedium, + Id: "evgd-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Event Grid Domain should have local authentication disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventgrid.Domain) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth diff --git a/internal/scanners/evgd/rules_test.go b/internal/scanners/evgd/rules_test.go index 2991b542..2bfe84d0 100644 --- a/internal/scanners/evgd/rules_test.go +++ b/internal/scanners/evgd/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventgrid/armeventgrid" ) @@ -32,7 +32,7 @@ func TestEventGridScanner_Rules(t *testing.T) { fields: fields{ rule: "evgd-001", target: &armeventgrid.Domain{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -65,7 +65,7 @@ func TestEventGridScanner_Rules(t *testing.T) { Properties: &armeventgrid.DomainProperties{ PrivateEndpointConnections: []*armeventgrid.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -94,7 +94,7 @@ func TestEventGridScanner_Rules(t *testing.T) { fields: fields{ rule: "evgd-006", target: &armeventgrid.Domain{ - Name: ref.Of("evgd-test"), + Name: to.Ptr("evgd-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -109,7 +109,7 @@ func TestEventGridScanner_Rules(t *testing.T) { rule: "evgd-008", target: &armeventgrid.Domain{ Properties: &armeventgrid.DomainProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/evh/rules.go b/internal/scanners/evh/rules.go index 328521be..84974f84 100644 --- a/internal/scanners/evh/rules.go +++ b/internal/scanners/evh/rules.go @@ -14,39 +14,34 @@ import ( func (a *EventHubScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "evh-001": { - Id: "evh-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Event Hub Namespace should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "evh-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Event Hub Namespace should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armeventhub.EHNamespace) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/event-hubs/monitor-event-hubs#collection-and-routing", }, "evh-002": { - Id: "evh-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Event Hub Namespace should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "evh-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Event Hub Namespace should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armeventhub.EHNamespace) zones := *i.Properties.ZoneRedundant return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-premium-overview#high-availability-with-availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/event-hubs/event-hubs-premium-overview#high-availability-with-availability-zones", }, "evh-003": { - Id: "evh-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Event Hub Namespace should have a SLA", - Severity: scanners.SeverityHigh, + Id: "evh-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Event Hub Namespace should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armeventhub.EHNamespace) sku := string(*i.SKU.Name) @@ -56,56 +51,48 @@ func (a *EventHubScanner) GetRules() map[string]scanners.AzureRule { } return false, sla }, - Url: "https://www.azure.cn/en-us/support/sla/event-hubs/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/event-hubs/", }, "evh-004": { - Id: "evh-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Event Hub Namespace should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "evh-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Event Hub Namespace should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armeventhub.EHNamespace) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/event-hubs/network-security", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/event-hubs/network-security", }, "evh-005": { - Id: "evh-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Event Hub Namespace SKU", - Severity: scanners.SeverityHigh, + Id: "evh-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Event Hub Namespace SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armeventhub.EHNamespace) return false, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/event-hubs/compare-tiers", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/event-hubs/compare-tiers", }, "evh-006": { - Id: "evh-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Event Hub Namespace Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "evh-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Event Hub Namespace Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventhub.EHNamespace) caf := strings.HasPrefix(*c.Name, "evh") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "evh-007": { - Id: "evh-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Event Hub should have tags", - Severity: scanners.SeverityLow, + Id: "evh-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Event Hub should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventhub.EHNamespace) return len(c.Tags) == 0, "" @@ -113,11 +100,10 @@ func (a *EventHubScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "evh-008": { - Id: "evh-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Event Hub should have local authentication disabled", - Severity: scanners.SeverityMedium, + Id: "evh-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Event Hub should have local authentication disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armeventhub.EHNamespace) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth diff --git a/internal/scanners/evh/rules_test.go b/internal/scanners/evh/rules_test.go index 491b6d99..acad704e 100644 --- a/internal/scanners/evh/rules_test.go +++ b/internal/scanners/evh/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/eventhub/armeventhub" ) @@ -32,7 +32,7 @@ func TestEventHubScanner_Rules(t *testing.T) { fields: fields{ rule: "evh-001", target: &armeventhub.EHNamespace{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestEventHubScanner_Rules(t *testing.T) { rule: "evh-002", target: &armeventhub.EHNamespace{ Properties: &armeventhub.EHNamespaceProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -67,7 +67,7 @@ func TestEventHubScanner_Rules(t *testing.T) { rule: "evh-003", target: &armeventhub.EHNamespace{ SKU: &armeventhub.SKU{ - Name: ref.Of(armeventhub.SKUNameStandard), + Name: to.Ptr(armeventhub.SKUNameStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -83,7 +83,7 @@ func TestEventHubScanner_Rules(t *testing.T) { rule: "evh-003", target: &armeventhub.EHNamespace{ SKU: &armeventhub.SKU{ - Name: ref.Of(armeventhub.SKUNamePremium), + Name: to.Ptr(armeventhub.SKUNamePremium), }, }, scanContext: &scanners.ScanContext{}, @@ -101,7 +101,7 @@ func TestEventHubScanner_Rules(t *testing.T) { Properties: &armeventhub.EHNamespaceProperties{ PrivateEndpointConnections: []*armeventhub.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -119,7 +119,7 @@ func TestEventHubScanner_Rules(t *testing.T) { rule: "evh-005", target: &armeventhub.EHNamespace{ SKU: &armeventhub.SKU{ - Name: ref.Of(armeventhub.SKUNamePremium), + Name: to.Ptr(armeventhub.SKUNamePremium), }, }, scanContext: &scanners.ScanContext{}, @@ -134,7 +134,7 @@ func TestEventHubScanner_Rules(t *testing.T) { fields: fields{ rule: "evh-006", target: &armeventhub.EHNamespace{ - Name: ref.Of("evh-test"), + Name: to.Ptr("evh-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -149,7 +149,7 @@ func TestEventHubScanner_Rules(t *testing.T) { rule: "evh-008", target: &armeventhub.EHNamespace{ Properties: &armeventhub.EHNamespaceProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/kv/rules.go b/internal/scanners/kv/rules.go index 8f7ec2d0..e18761b3 100644 --- a/internal/scanners/kv/rules.go +++ b/internal/scanners/kv/rules.go @@ -6,8 +6,8 @@ package kv import ( "strings" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault" ) @@ -15,78 +15,67 @@ import ( func (a *KeyVaultScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "kv-001": { - Id: "kv-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Key Vault should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "kv-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Key Vault should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armkeyvault.Vault) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/monitor-key-vault", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/monitor-key-vault", }, "kv-003": { - Id: "kv-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Key Vault should have a SLA", - Severity: scanners.SeverityHigh, + Id: "kv-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Key Vault should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.azure.cn/en-us/support/sla/key-vault/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/key-vault/", }, "kv-004": { - Id: "kv-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Key Vault should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "kv-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Key Vault should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armkeyvault.Vault) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service", }, "kv-005": { - Id: "kv-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Key Vault SKU", - Severity: scanners.SeverityHigh, + Id: "kv-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Key Vault SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armkeyvault.Vault) return false, string(*i.Properties.SKU.Name) }, - Url: "https://azure.microsoft.com/en-us/pricing/details/key-vault/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/key-vault/", }, "kv-006": { - Id: "kv-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Key Vault Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "kv-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Key Vault Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkeyvault.Vault) caf := strings.HasPrefix(*c.Name, "kv") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "kv-007": { - Id: "kv-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Key Vault should have tags", - Severity: scanners.SeverityLow, + Id: "kv-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Key Vault should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkeyvault.Vault) return len(c.Tags) == 0, "" @@ -94,26 +83,24 @@ func (a *KeyVaultScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "kv-008": { - Id: "kv-008", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Key Vault should have soft delete enabled", - Severity: scanners.SeverityMedium, + Id: "kv-008", + Category: scanners.RulesCategoryDisasterRecovery, + Recommendation: "Key Vault should have soft delete enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkeyvault.Vault) - return c.Properties.EnableSoftDelete == nil || c.Properties.EnableSoftDelete == ref.Of(false), "" + return c.Properties.EnableSoftDelete == nil || c.Properties.EnableSoftDelete == to.Ptr(false), "" }, Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview", }, "kv-009": { - Id: "kv-009", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Key Vault should have purge protection enabled", - Severity: scanners.SeverityMedium, + Id: "kv-009", + Category: scanners.RulesCategoryDisasterRecovery, + Recommendation: "Key Vault should have purge protection enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armkeyvault.Vault) - return c.Properties.EnablePurgeProtection == nil || c.Properties.EnablePurgeProtection == ref.Of(false), "" + return c.Properties.EnablePurgeProtection == nil || c.Properties.EnablePurgeProtection == to.Ptr(false), "" }, Url: "https://learn.microsoft.com/en-us/azure/key-vault/general/soft-delete-overview#purge-protection", }, diff --git a/internal/scanners/kv/rules_test.go b/internal/scanners/kv/rules_test.go index 63fc51be..317577d6 100644 --- a/internal/scanners/kv/rules_test.go +++ b/internal/scanners/kv/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/keyvault/armkeyvault" ) @@ -32,7 +32,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { fields: fields{ rule: "kv-001", target: &armkeyvault.Vault{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -65,7 +65,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { Properties: &armkeyvault.VaultProperties{ PrivateEndpointConnections: []*armkeyvault.PrivateEndpointConnectionItem{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -84,7 +84,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { target: &armkeyvault.Vault{ Properties: &armkeyvault.VaultProperties{ SKU: &armkeyvault.SKU{ - Name: ref.Of(armkeyvault.SKUNameStandard), + Name: to.Ptr(armkeyvault.SKUNameStandard), }, }, }, @@ -100,7 +100,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { fields: fields{ rule: "kv-006", target: &armkeyvault.Vault{ - Name: ref.Of("kv-test"), + Name: to.Ptr("kv-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -115,7 +115,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { rule: "kv-008", target: &armkeyvault.Vault{ Properties: &armkeyvault.VaultProperties{ - EnableSoftDelete: ref.Of(true), + EnableSoftDelete: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -131,7 +131,7 @@ func TestKeyVaultScanner_Rules(t *testing.T) { rule: "kv-009", target: &armkeyvault.Vault{ Properties: &armkeyvault.VaultProperties{ - EnablePurgeProtection: ref.Of(true), + EnablePurgeProtection: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/lb/rules.go b/internal/scanners/lb/rules.go index ff8fb5fa..c81667cd 100644 --- a/internal/scanners/lb/rules.go +++ b/internal/scanners/lb/rules.go @@ -14,25 +14,22 @@ import ( func (a *LoadBalancerScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "lb-001": { - Id: "lb-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Load Balancer should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "lb-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Load Balancer should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armnetwork.LoadBalancer) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/load-balancer/monitor-load-balancer#creating-a-diagnostic-setting", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/load-balancer/monitor-load-balancer#creating-a-diagnostic-setting", }, "lb-002": { - Id: "lb-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Load Balancer should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "lb-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Load Balancer should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armnetwork.LoadBalancer) broken := false @@ -51,15 +48,13 @@ func (a *LoadBalancerScanner) GetRules() map[string]scanners.AzureRule { return broken, "" }, - Url: "https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-standard-availability-zones#zone-redundant", }, "lb-003": { - Id: "lb-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Load Balancer should have a SLA", - Severity: scanners.SeverityHigh, + Id: "lb-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Load Balancer should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armnetwork.LoadBalancer) sla := "99.99%" @@ -69,29 +64,25 @@ func (a *LoadBalancerScanner) GetRules() map[string]scanners.AzureRule { } return sla == "None", sla }, - Url: "https://learn.microsoft.com/en-us/azure/load-balancer/skus", - Field: scanners.OverviewFieldSLA, + Url: "https://learn.microsoft.com/en-us/azure/load-balancer/skus", }, "lb-005": { - Id: "lb-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Load Balancer SKU", - Severity: scanners.SeverityHigh, + Id: "lb-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Load Balancer SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armnetwork.LoadBalancer) sku := *i.SKU.Name return sku != armnetwork.LoadBalancerSKUNameStandard, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/azure/load-balancer/skus", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/load-balancer/skus", }, "lb-006": { - Id: "lb-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Load Balancer Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "lb-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Load Balancer Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.LoadBalancer) hasPrivateIP := false @@ -113,15 +104,13 @@ func (a *LoadBalancerScanner) GetRules() map[string]scanners.AzureRule { caf := (strings.HasPrefix(*c.Name, "lbi") && hasPrivateIP) || (strings.HasPrefix(*c.Name, "lbe") && hasPublicIP) return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "lb-007": { - Id: "lb-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Load Balancer should have tags", - Severity: scanners.SeverityLow, + Id: "lb-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Load Balancer should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.LoadBalancer) return len(c.Tags) == 0, "" diff --git a/internal/scanners/lb/rules_test.go b/internal/scanners/lb/rules_test.go index 91afcc27..8665b779 100644 --- a/internal/scanners/lb/rules_test.go +++ b/internal/scanners/lb/rules_test.go @@ -7,7 +7,7 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azqr/internal/scanners" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ) @@ -32,7 +32,7 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { fields: fields{ rule: "lb-001", target: &armnetwork.LoadBalancer{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,18 +51,18 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { rule: "lb-002", target: &armnetwork.LoadBalancer{ SKU: &armnetwork.LoadBalancerSKU{ - Name: ref.Of(armnetwork.LoadBalancerSKUNameStandard), + Name: to.Ptr(armnetwork.LoadBalancerSKUNameStandard), }, Properties: &armnetwork.LoadBalancerPropertiesFormat{ FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{ { Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{ - PrivateIPAddress: ref.Of("127.0.0.1"), + PrivateIPAddress: to.Ptr("127.0.0.1"), }, Zones: []*string{ - ref.Of("1"), - ref.Of("2"), - ref.Of("3"), + to.Ptr("1"), + to.Ptr("2"), + to.Ptr("3"), }, }, }, @@ -81,7 +81,7 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { rule: "lb-003", target: &armnetwork.LoadBalancer{ SKU: &armnetwork.LoadBalancerSKU{ - Name: ref.Of(armnetwork.LoadBalancerSKUNameStandard), + Name: to.Ptr(armnetwork.LoadBalancerSKUNameStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -97,7 +97,7 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { rule: "lb-005", target: &armnetwork.LoadBalancer{ SKU: &armnetwork.LoadBalancerSKU{ - Name: ref.Of(armnetwork.LoadBalancerSKUNameStandard), + Name: to.Ptr(armnetwork.LoadBalancerSKUNameStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -112,12 +112,12 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { fields: fields{ rule: "lb-006", target: &armnetwork.LoadBalancer{ - Name: ref.Of("lbi"), + Name: to.Ptr("lbi"), Properties: &armnetwork.LoadBalancerPropertiesFormat{ FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{ { Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{ - PrivateIPAddress: ref.Of("10.0.0.1"), + PrivateIPAddress: to.Ptr("10.0.0.1"), }, }, }, @@ -135,7 +135,7 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { fields: fields{ rule: "lb-006", target: &armnetwork.LoadBalancer{ - Name: ref.Of("lbe"), + Name: to.Ptr("lbe"), Properties: &armnetwork.LoadBalancerPropertiesFormat{ FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{ { @@ -169,4 +169,3 @@ func TestLoadBalancerScanner_Rules(t *testing.T) { }) } } - diff --git a/internal/scanners/logic/rules.go b/internal/scanners/logic/rules.go index 1da912c6..75da148c 100644 --- a/internal/scanners/logic/rules.go +++ b/internal/scanners/logic/rules.go @@ -14,37 +14,32 @@ import ( func (a *LogicAppScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "logic-001": { - Id: "logic-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Logic App should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "logic-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Logic App should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armlogic.Workflow) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/logic-apps/monitor-workflows-collect-diagnostic-data", }, "logic-003": { - Id: "logic-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Logic App should have a SLA", - Severity: scanners.SeverityHigh, + Id: "logic-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Logic App should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", }, "logic-004": { - Id: "logic-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityFirewall, - Description: "Logic App should limit access to Http Triggers", - Severity: scanners.SeverityHigh, + Id: "logic-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Logic App should limit access to Http Triggers", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armlogic.Workflow) http := false @@ -71,26 +66,23 @@ func (a *LogicAppScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/logic-apps/logic-apps-securing-a-logic-app?tabs=azure-portal#restrict-access-by-ip-address-range", }, "logic-006": { - Id: "logic-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Logic App Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "logic-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Logic App Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armlogic.Workflow) caf := strings.HasPrefix(*c.Name, "logic") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "logic-007": { - Id: "logic-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Logic App should have tags", - Severity: scanners.SeverityLow, + Id: "logic-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Logic App should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armlogic.Workflow) return len(c.Tags) == 0, "" diff --git a/internal/scanners/logic/rules_test.go b/internal/scanners/logic/rules_test.go index b9d05f3d..ee708447 100644 --- a/internal/scanners/logic/rules_test.go +++ b/internal/scanners/logic/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/logic/armlogic" ) @@ -32,7 +32,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { fields: fields{ rule: "logic-001", target: &armlogic.Workflow{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -50,7 +50,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { fields: fields{ rule: "logic-004", target: &armlogic.Workflow{ - ID: ref.Of("test"), + ID: to.Ptr("test"), Properties: &armlogic.WorkflowProperties{ Definition: map[string]interface{}{}, }, @@ -67,7 +67,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { fields: fields{ rule: "logic-004", target: &armlogic.Workflow{ - ID: ref.Of("test"), + ID: to.Ptr("test"), Properties: &armlogic.WorkflowProperties{ Definition: map[string]interface{}{ "triggers": map[string]interface{}{ @@ -96,7 +96,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { fields: fields{ rule: "logic-004", target: &armlogic.Workflow{ - ID: ref.Of("test"), + ID: to.Ptr("test"), Properties: &armlogic.WorkflowProperties{ Definition: map[string]interface{}{ "triggers": map[string]interface{}{ @@ -110,7 +110,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { Triggers: &armlogic.FlowAccessControlConfigurationPolicy{ AllowedCallerIPAddresses: []*armlogic.IPAddressRange{ { - AddressRange : ref.Of("127.0.0.1/32"), + AddressRange: to.Ptr("127.0.0.1/32"), }, }, }, @@ -129,7 +129,7 @@ func TestLogicAppScanner_Rules(t *testing.T) { fields: fields{ rule: "logic-006", target: &armlogic.Workflow{ - Name: ref.Of("logic-test"), + Name: to.Ptr("logic-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/maria/rules.go b/internal/scanners/maria/rules.go index c492eaeb..67b637b3 100644 --- a/internal/scanners/maria/rules.go +++ b/internal/scanners/maria/rules.go @@ -14,62 +14,53 @@ import ( func (a *MariaScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "maria-001": { - Id: "maria-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "MariaDB should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "maria-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "MariaDB should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armmariadb.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Field: scanners.OverviewFieldDiagnostics, }, "maria-002": { - Id: "maria-002", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "MariaDB should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "maria-002", + Category: scanners.RulesCategorySecurity, + Recommendation: "MariaDB should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmariadb.Server) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Field: scanners.OverviewFieldPrivate, }, "maria-003": { - Id: "maria-003", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "MariaDB server Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "maria-003", + Category: scanners.RulesCategoryGovernance, + Recommendation: "MariaDB server Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmariadb.Server) caf := strings.HasPrefix(*c.Name, "maria") return !caf, "" }, Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, }, "maria-004": { - Id: "maria-004", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "MariaDB server should have a SLA", - Severity: scanners.SeverityHigh, + Id: "maria-004", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "MariaDB server should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Field: scanners.OverviewFieldSLA, }, "maria-005": { - Id: "maria-005", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "MariaDB should have tags", - Severity: scanners.SeverityLow, + Id: "maria-005", + Category: scanners.RulesCategoryGovernance, + Recommendation: "MariaDB should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmariadb.Server) return len(c.Tags) == 0, "" @@ -77,11 +68,10 @@ func (a *MariaScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "maria-006": { - Id: "maria-006", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "MariaDB should enforce TLS >= 1.2", - Severity: scanners.SeverityLow, + Id: "maria-006", + Category: scanners.RulesCategorySecurity, + Recommendation: "MariaDB should enforce TLS >= 1.2", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmariadb.Server) return c.Properties.MinimalTLSVersion == nil || *c.Properties.MinimalTLSVersion != armmariadb.MinimalTLSVersionEnumTLS12, "" @@ -95,11 +85,10 @@ func (a *MariaScanner) GetRules() map[string]scanners.AzureRule { func (a *MariaScanner) GetDatabaseRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "CAF": { - Id: "mariadb-001", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "MariaDB Database Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "mariadb-001", + Category: scanners.RulesCategoryGovernance, + Recommendation: "MariaDB Database Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmariadb.Database) caf := strings.HasPrefix(*c.Name, "mariadb") diff --git a/internal/scanners/maria/rules_test.go b/internal/scanners/maria/rules_test.go index b91ba6c9..26c533e0 100644 --- a/internal/scanners/maria/rules_test.go +++ b/internal/scanners/maria/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mariadb/armmariadb" ) @@ -32,7 +32,7 @@ func TestMariaScanner_Rules(t *testing.T) { fields: fields{ rule: "maria-001", target: &armmariadb.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -53,7 +53,7 @@ func TestMariaScanner_Rules(t *testing.T) { Properties: &armmariadb.ServerProperties{ PrivateEndpointConnections: []*armmariadb.ServerPrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -70,7 +70,7 @@ func TestMariaScanner_Rules(t *testing.T) { fields: fields{ rule: "maria-003", target: &armmariadb.Server{ - Name: ref.Of("maria-test"), + Name: to.Ptr("maria-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -96,7 +96,7 @@ func TestMariaScanner_Rules(t *testing.T) { rule: "maria-006", target: &armmariadb.Server{ Properties: &armmariadb.ServerProperties{ - MinimalTLSVersion: ref.Of(armmariadb.MinimalTLSVersionEnumTLS12), + MinimalTLSVersion: to.Ptr(armmariadb.MinimalTLSVersionEnumTLS12), }, }, scanContext: &scanners.ScanContext{}, @@ -143,7 +143,7 @@ func TestMariaScanner_DatabaseRules(t *testing.T) { fields: fields{ rule: "CAF", target: &armmariadb.Database{ - Name: ref.Of("mariadb-test"), + Name: to.Ptr("mariadb-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/mysql/rules.go b/internal/scanners/mysql/rules.go index a5709060..d2d1705e 100644 --- a/internal/scanners/mysql/rules.go +++ b/internal/scanners/mysql/rules.go @@ -15,89 +15,77 @@ import ( func (a *MySQLScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "mysql-001": { - Id: "mysql-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Database for MySQL - Flexible Server should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "mysql-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Database for MySQL - Flexible Server should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armmysql.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-monitoring#server-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-monitoring#server-logs", }, "mysql-003": { - Id: "mysql-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Database for MySQL - Flexible Server should have a SLA", - Severity: scanners.SeverityHigh, + Id: "mysql-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Flexible Server should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.azure.cn/en-us/support/sla/mysql/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/mysql/", }, "mysql-004": { - Id: "mysql-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Azure Database for MySQL - Flexible Server should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "mysql-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Database for MySQL - Flexible Server should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysql.Server) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-data-access-security-private-link", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-data-access-security-private-link", }, "mysql-005": { - Id: "mysql-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Database for MySQL - Flexible Server SKU", - Severity: scanners.SeverityHigh, + Id: "mysql-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Flexible Server SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysql.Server) return false, *i.SKU.Name }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-pricing-tiers", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/concepts-pricing-tiers", }, "mysql-006": { - Id: "mysql-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Database for MySQL - Flexible Server Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "mysql-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Database for MySQL - Flexible Server Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmysql.Server) caf := strings.HasPrefix(*c.Name, "mysql") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "mysql-007": { - Id: "mysql-007", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Database for MySQL - Single Server is on the retirement path", - Severity: scanners.SeverityHigh, + Id: "mysql-007", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Single Server is on the retirement path", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return true, "" }, Url: "https://learn.microsoft.com/en-us/azure/mysql/single-server/whats-happening-to-mysql-single-server", }, "mysql-008": { - Id: "mysql-008", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Database for MySQL - Single Server should have tags", - Severity: scanners.SeverityLow, + Id: "mysql-008", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Database for MySQL - Single Server should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmysql.Server) return len(c.Tags) == 0, "" @@ -111,39 +99,34 @@ func (a *MySQLScanner) GetRules() map[string]scanners.AzureRule { func (a *MySQLFlexibleScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "mysqlf-001": { - Id: "mysqlf-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Azure Database for MySQL - Flexible Server should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "mysqlf-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Azure Database for MySQL - Flexible Server should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armmysqlflexibleservers.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/tutorial-query-performance-insights#set-up-diagnostics", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/tutorial-query-performance-insights#set-up-diagnostics", }, "mysqlf-002": { - Id: "mysqlf-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Azure Database for MySQL - Flexible Server should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "mysqlf-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Flexible Server should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysqlflexibleservers.Server) zones := *i.Properties.HighAvailability.Mode == armmysqlflexibleservers.HighAvailabilityModeZoneRedundant return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-configure-high-availability-cli", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-configure-high-availability-cli", }, "mysqlf-003": { - Id: "mysqlf-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Azure Database for MySQL - Flexible Server should have a SLA", - Severity: scanners.SeverityHigh, + Id: "mysqlf-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Flexible Server should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysqlflexibleservers.Server) sla := "99.9%" @@ -156,56 +139,48 @@ func (a *MySQLFlexibleScanner) GetRules() map[string]scanners.AzureRule { } return false, sla }, - Url: "hhttps://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", - Field: scanners.OverviewFieldSLA, + Url: "hhttps://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", }, "mysqlf-004": { - Id: "mysqlf-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateIP, - Description: "Azure Database for MySQL - Flexible Server should have private access enabled", - Severity: scanners.SeverityHigh, + Id: "mysqlf-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Azure Database for MySQL - Flexible Server should have private access enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysqlflexibleservers.Server) pe := *i.Properties.Network.PublicNetworkAccess == armmysqlflexibleservers.EnableStatusEnumDisabled return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-virtual-network-cli", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-virtual-network-cli", }, "mysqlf-005": { - Id: "mysqlf-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Azure Database for MySQL - Flexible Server SKU", - Severity: scanners.SeverityHigh, + Id: "mysqlf-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Azure Database for MySQL - Flexible Server SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armmysqlflexibleservers.Server) return false, *i.SKU.Name }, - Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage", }, "mysqlf-006": { - Id: "mysqlf-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Azure Database for MySQL - Flexible Server Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "mysqlf-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Database for MySQL - Flexible Server Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmysqlflexibleservers.Server) caf := strings.HasPrefix(*c.Name, "mysql") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "mysqlf-007": { - Id: "mysqlf-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Azure Database for MySQL - Flexible Server should have tags", - Severity: scanners.SeverityLow, + Id: "mysqlf-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Azure Database for MySQL - Flexible Server should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armmysqlflexibleservers.Server) return len(c.Tags) == 0, "" diff --git a/internal/scanners/mysql/rules_test.go b/internal/scanners/mysql/rules_test.go index ee70d51c..1d22a1bb 100644 --- a/internal/scanners/mysql/rules_test.go +++ b/internal/scanners/mysql/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysql" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/mysql/armmysqlflexibleservers" ) @@ -33,7 +33,7 @@ func TestMySQLScanner_Rules(t *testing.T) { fields: fields{ rule: "mysql-001", target: &armmysql.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -66,7 +66,7 @@ func TestMySQLScanner_Rules(t *testing.T) { Properties: &armmysql.ServerProperties{ PrivateEndpointConnections: []*armmysql.ServerPrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -84,7 +84,7 @@ func TestMySQLScanner_Rules(t *testing.T) { rule: "mysql-005", target: &armmysql.Server{ SKU: &armmysql.SKU{ - Name: ref.Of("GPGen58"), + Name: to.Ptr("GPGen58"), }, }, scanContext: &scanners.ScanContext{}, @@ -99,7 +99,7 @@ func TestMySQLScanner_Rules(t *testing.T) { fields: fields{ rule: "mysql-006", target: &armmysql.Server{ - Name: ref.Of("mysql-test"), + Name: to.Ptr("mysql-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -157,7 +157,7 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { fields: fields{ rule: "mysqlf-001", target: &armmysqlflexibleservers.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -177,7 +177,7 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { target: &armmysqlflexibleservers.Server{ Properties: &armmysqlflexibleservers.ServerProperties{ HighAvailability: &armmysqlflexibleservers.HighAvailability{ - Mode: ref.Of(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), + Mode: to.Ptr(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), }, }, }, @@ -209,10 +209,10 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { target: &armmysqlflexibleservers.Server{ Properties: &armmysqlflexibleservers.ServerProperties{ HighAvailability: &armmysqlflexibleservers.HighAvailability{ - Mode: ref.Of(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), - StandbyAvailabilityZone: ref.Of("2"), + Mode: to.Ptr(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), + StandbyAvailabilityZone: to.Ptr("2"), }, - AvailabilityZone: ref.Of("1"), + AvailabilityZone: to.Ptr("1"), }, }, scanContext: &scanners.ScanContext{}, @@ -229,10 +229,10 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { target: &armmysqlflexibleservers.Server{ Properties: &armmysqlflexibleservers.ServerProperties{ HighAvailability: &armmysqlflexibleservers.HighAvailability{ - Mode: ref.Of(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), - StandbyAvailabilityZone: ref.Of("1"), + Mode: to.Ptr(armmysqlflexibleservers.HighAvailabilityModeZoneRedundant), + StandbyAvailabilityZone: to.Ptr("1"), }, - AvailabilityZone: ref.Of("1"), + AvailabilityZone: to.Ptr("1"), }, }, scanContext: &scanners.ScanContext{}, @@ -249,7 +249,7 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { target: &armmysqlflexibleservers.Server{ Properties: &armmysqlflexibleservers.ServerProperties{ Network: &armmysqlflexibleservers.Network{ - PublicNetworkAccess: ref.Of(armmysqlflexibleservers.EnableStatusEnumDisabled), + PublicNetworkAccess: to.Ptr(armmysqlflexibleservers.EnableStatusEnumDisabled), }, }, }, @@ -266,7 +266,7 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { rule: "mysqlf-005", target: &armmysqlflexibleservers.Server{ SKU: &armmysqlflexibleservers.SKU{ - Name: ref.Of("StandardD4sv3"), + Name: to.Ptr("StandardD4sv3"), }, }, scanContext: &scanners.ScanContext{}, @@ -281,7 +281,7 @@ func TestMySQLFlexibleScanner_Rules(t *testing.T) { fields: fields{ rule: "mysqlf-006", target: &armmysqlflexibleservers.Server{ - Name: ref.Of("mysql-test"), + Name: to.Ptr("mysql-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/psql/rules.go b/internal/scanners/psql/rules.go index c05881e8..bef39ec1 100644 --- a/internal/scanners/psql/rules.go +++ b/internal/scanners/psql/rules.go @@ -15,78 +15,67 @@ import ( func (a *PostgreScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "psql-001": { - Id: "psql-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "PostgreSQL should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "psql-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "PostgreSQL should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armpostgresql.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-server-logs#resource-logs", - Field: scanners.OverviewFieldDiagnostics, }, "psql-003": { - Id: "psql-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "PostgreSQL should have a SLA", - Severity: scanners.SeverityHigh, + Id: "psql-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "PostgreSQL should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, Url: "https://www.azure.cn/en-us/support/sla/postgresql/", - Field: scanners.OverviewFieldSLA, }, "psql-004": { - Id: "psql-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "PostgreSQL should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "psql-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "PostgreSQL should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresql.Server) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-data-access-and-security-private-link", - Field: scanners.OverviewFieldPrivate, }, "psql-005": { - Id: "psql-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "PostgreSQL SKU", - Severity: scanners.SeverityHigh, + Id: "psql-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "PostgreSQL SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresql.Server) return false, *i.SKU.Name }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-pricing-tiers", - Field: scanners.OverviewFieldSKU, }, "psql-006": { - Id: "psql-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "PostgreSQL Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "psql-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "PostgreSQL Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresql.Server) caf := strings.HasPrefix(*c.Name, "psql") return !caf, "" }, Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, }, "psql-007": { - Id: "psql-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "PostgreSQL should have tags", - Severity: scanners.SeverityLow, + Id: "psql-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "PostgreSQL should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresql.Server) return len(c.Tags) == 0, "" @@ -94,11 +83,10 @@ func (a *PostgreScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "psql-008": { - Id: "psql-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecuritySSL, - Description: "PostgreSQL should enforce SSL", - Severity: scanners.SeverityHigh, + Id: "psql-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "PostgreSQL should enforce SSL", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresql.Server) return c.Properties.SSLEnforcement == nil || *c.Properties.SSLEnforcement == armpostgresql.SSLEnforcementEnumDisabled, "" @@ -106,11 +94,10 @@ func (a *PostgreScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/postgresql/single-server/concepts-ssl-connection-security#enforcing-tls-connections", }, "psql-009": { - Id: "psql-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "PostgreSQL should enforce TLS >= 1.2", - Severity: scanners.SeverityLow, + Id: "psql-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "PostgreSQL should enforce TLS >= 1.2", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresql.Server) return c.Properties.MinimalTLSVersion == nil || *c.Properties.MinimalTLSVersion != armpostgresql.MinimalTLSVersionEnumTLS12, "" @@ -124,39 +111,34 @@ func (a *PostgreScanner) GetRules() map[string]scanners.AzureRule { func (a *PostgreFlexibleScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "psqlf-001": { - Id: "psqlf-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "PostgreSQL should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "psqlf-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "PostgreSQL should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armpostgresqlflexibleservers.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/howto-configure-and-access-logs", - Field: scanners.OverviewFieldDiagnostics, }, "psqlf-002": { - Id: "psqlf-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "PostgreSQL should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "psqlf-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "PostgreSQL should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresqlflexibleservers.Server) zones := *i.Properties.HighAvailability.Mode == armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant return !zones, "" }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/overview#architecture-and-high-availability", - Field: scanners.OverviewFieldAZ, }, "psqlf-003": { - Id: "psqlf-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "PostgreSQL should have a SLA", - Severity: scanners.SeverityHigh, + Id: "psqlf-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "PostgreSQL should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresqlflexibleservers.Server) sla := "99.9%" @@ -170,55 +152,47 @@ func (a *PostgreFlexibleScanner) GetRules() map[string]scanners.AzureRule { return false, sla }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-compare-single-server-flexible-server", - Field: scanners.OverviewFieldSLA, }, "psqlf-004": { - Id: "psqlf-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateIP, - Description: "PostgreSQL should have private access enabled", - Severity: scanners.SeverityHigh, + Id: "psqlf-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "PostgreSQL should have private access enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresqlflexibleservers.Server) pe := *i.Properties.Network.PublicNetworkAccess == armpostgresqlflexibleservers.ServerPublicNetworkAccessStateDisabled return !pe, "" }, Url: "https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-networking#private-access-vnet-integration", - Field: scanners.OverviewFieldPrivate, }, "psqlf-005": { - Id: "psqlf-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "PostgreSQL SKU", - Severity: scanners.SeverityHigh, + Id: "psqlf-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "PostgreSQL SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armpostgresqlflexibleservers.Server) return false, *i.SKU.Name }, Url: "https://azure.microsoft.com/en-gb/pricing/details/postgresql/flexible-server/", - Field: scanners.OverviewFieldSKU, }, "psqlf-006": { - Id: "psqlf-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "PostgreSQL Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "psqlf-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "PostgreSQL Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresqlflexibleservers.Server) caf := strings.HasPrefix(*c.Name, "psql") return !caf, "" }, Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, }, "psqlf-007": { - Id: "psqlf-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "PostgreSQL should have tags", - Severity: scanners.SeverityLow, + Id: "psqlf-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "PostgreSQL should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armpostgresqlflexibleservers.Server) return len(c.Tags) == 0, "" diff --git a/internal/scanners/psql/rules_test.go b/internal/scanners/psql/rules_test.go index e9b6678d..13b84a4b 100644 --- a/internal/scanners/psql/rules_test.go +++ b/internal/scanners/psql/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresql" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/postgresql/armpostgresqlflexibleservers" ) @@ -33,7 +33,7 @@ func TestPostgreScanner_Rules(t *testing.T) { fields: fields{ rule: "psql-001", target: &armpostgresql.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -66,7 +66,7 @@ func TestPostgreScanner_Rules(t *testing.T) { Properties: &armpostgresql.ServerProperties{ PrivateEndpointConnections: []*armpostgresql.ServerPrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -84,7 +84,7 @@ func TestPostgreScanner_Rules(t *testing.T) { rule: "psql-005", target: &armpostgresql.Server{ SKU: &armpostgresql.SKU{ - Name: ref.Of("GPGen58"), + Name: to.Ptr("GPGen58"), }, }, scanContext: &scanners.ScanContext{}, @@ -99,7 +99,7 @@ func TestPostgreScanner_Rules(t *testing.T) { fields: fields{ rule: "psql-006", target: &armpostgresql.Server{ - Name: ref.Of("psql-test"), + Name: to.Ptr("psql-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -114,7 +114,7 @@ func TestPostgreScanner_Rules(t *testing.T) { rule: "psql-008", target: &armpostgresql.Server{ Properties: &armpostgresql.ServerProperties{ - SSLEnforcement: ref.Of(armpostgresql.SSLEnforcementEnumEnabled), + SSLEnforcement: to.Ptr(armpostgresql.SSLEnforcementEnumEnabled), }, }, scanContext: &scanners.ScanContext{}, @@ -130,7 +130,7 @@ func TestPostgreScanner_Rules(t *testing.T) { rule: "psql-009", target: &armpostgresql.Server{ Properties: &armpostgresql.ServerProperties{ - MinimalTLSVersion: ref.Of(armpostgresql.MinimalTLSVersionEnumTLS12), + MinimalTLSVersion: to.Ptr(armpostgresql.MinimalTLSVersionEnumTLS12), }, }, scanContext: &scanners.ScanContext{}, @@ -177,7 +177,7 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { fields: fields{ rule: "psqlf-001", target: &armpostgresqlflexibleservers.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -197,7 +197,7 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { target: &armpostgresqlflexibleservers.Server{ Properties: &armpostgresqlflexibleservers.ServerProperties{ HighAvailability: &armpostgresqlflexibleservers.HighAvailability{ - Mode: ref.Of(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), + Mode: to.Ptr(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), }, }, }, @@ -229,10 +229,10 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { target: &armpostgresqlflexibleservers.Server{ Properties: &armpostgresqlflexibleservers.ServerProperties{ HighAvailability: &armpostgresqlflexibleservers.HighAvailability{ - Mode: ref.Of(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), - StandbyAvailabilityZone: ref.Of("2"), + Mode: to.Ptr(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), + StandbyAvailabilityZone: to.Ptr("2"), }, - AvailabilityZone: ref.Of("1"), + AvailabilityZone: to.Ptr("1"), }, }, scanContext: &scanners.ScanContext{}, @@ -249,10 +249,10 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { target: &armpostgresqlflexibleservers.Server{ Properties: &armpostgresqlflexibleservers.ServerProperties{ HighAvailability: &armpostgresqlflexibleservers.HighAvailability{ - Mode: ref.Of(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), - StandbyAvailabilityZone: ref.Of("1"), + Mode: to.Ptr(armpostgresqlflexibleservers.HighAvailabilityModeZoneRedundant), + StandbyAvailabilityZone: to.Ptr("1"), }, - AvailabilityZone: ref.Of("1"), + AvailabilityZone: to.Ptr("1"), }, }, scanContext: &scanners.ScanContext{}, @@ -269,7 +269,7 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { target: &armpostgresqlflexibleservers.Server{ Properties: &armpostgresqlflexibleservers.ServerProperties{ Network: &armpostgresqlflexibleservers.Network{ - PublicNetworkAccess: ref.Of(armpostgresqlflexibleservers.ServerPublicNetworkAccessStateDisabled), + PublicNetworkAccess: to.Ptr(armpostgresqlflexibleservers.ServerPublicNetworkAccessStateDisabled), }, }, }, @@ -286,7 +286,7 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { rule: "psqlf-005", target: &armpostgresqlflexibleservers.Server{ SKU: &armpostgresqlflexibleservers.SKU{ - Name: ref.Of("StandardD4sv3"), + Name: to.Ptr("StandardD4sv3"), }, }, scanContext: &scanners.ScanContext{}, @@ -301,7 +301,7 @@ func TestPostgreFlexibleScanner_Rules(t *testing.T) { fields: fields{ rule: "psqlf-006", target: &armpostgresqlflexibleservers.Server{ - Name: ref.Of("psql-test"), + Name: to.Ptr("psql-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/redis/rules.go b/internal/scanners/redis/rules.go index dc2c54a1..b702f6e2 100644 --- a/internal/scanners/redis/rules.go +++ b/internal/scanners/redis/rules.go @@ -14,92 +14,79 @@ import ( func (a *RedisScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "redis-001": { - Id: "redis-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Redis should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "redis-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Redis should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armredis.ResourceInfo) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-monitor-diagnostic-settings", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-monitor-diagnostic-settings", }, "redis-002": { - Id: "redis-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Redis should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "redis-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Redis should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armredis.ResourceInfo) zones := len(i.Zones) > 0 return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-high-availability", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-high-availability", }, "redis-003": { - Id: "redis-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Redis should have a SLA", - Severity: scanners.SeverityHigh, + Id: "redis-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Redis should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", }, "redis-004": { - Id: "redis-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Redis should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "redis-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Redis should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armredis.ResourceInfo) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-private-link", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-private-link", }, "redis-005": { - Id: "redis-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Redis SKU", - Severity: scanners.SeverityHigh, + Id: "redis-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Redis SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armredis.ResourceInfo) return false, string(*i.Properties.SKU.Name) }, - Url: "https://azure.microsoft.com/en-gb/pricing/details/cache/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-gb/pricing/details/cache/", }, "redis-006": { - Id: "redis-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Redis Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "redis-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Redis Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armredis.ResourceInfo) caf := strings.HasPrefix(*c.Name, "redis") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "redis-007": { - Id: "redis-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Redis should have tags", - Severity: scanners.SeverityLow, + Id: "redis-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Redis should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armredis.ResourceInfo) return len(c.Tags) == 0, "" @@ -107,11 +94,10 @@ func (a *RedisScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "redis-008": { - Id: "redis-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecuritySSL, - Description: "Redis should not enable non SSL ports", - Severity: scanners.SeverityHigh, + Id: "redis-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Redis should not enable non SSL ports", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armredis.ResourceInfo) return c.Properties.EnableNonSSLPort != nil && *c.Properties.EnableNonSSLPort, "" @@ -119,11 +105,10 @@ func (a *RedisScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-configure#access-ports", }, "redis-009": { - Id: "redis-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "Redis should enforce TLS >= 1.2", - Severity: scanners.SeverityLow, + Id: "redis-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Redis should enforce TLS >= 1.2", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armredis.ResourceInfo) return c.Properties.MinimumTLSVersion == nil || *c.Properties.MinimumTLSVersion != armredis.TLSVersionOne2, "" diff --git a/internal/scanners/redis/rules_test.go b/internal/scanners/redis/rules_test.go index bbff4cf6..1a6aea99 100644 --- a/internal/scanners/redis/rules_test.go +++ b/internal/scanners/redis/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/redis/armredis" ) @@ -32,7 +32,7 @@ func TestRedisScanner_Rules(t *testing.T) { fields: fields{ rule: "redis-001", target: &armredis.ResourceInfo{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -50,7 +50,7 @@ func TestRedisScanner_Rules(t *testing.T) { fields: fields{ rule: "redis-002", target: &armredis.ResourceInfo{ - Zones: []*string{ref.Of("1"), ref.Of("2"), ref.Of("3")}, + Zones: []*string{to.Ptr("1"), to.Ptr("2"), to.Ptr("3")}, }, scanContext: &scanners.ScanContext{}, }, @@ -79,7 +79,7 @@ func TestRedisScanner_Rules(t *testing.T) { Properties: &armredis.Properties{ PrivateEndpointConnections: []*armredis.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -98,7 +98,7 @@ func TestRedisScanner_Rules(t *testing.T) { target: &armredis.ResourceInfo{ Properties: &armredis.Properties{ SKU: &armredis.SKU{ - Name: ref.Of(armredis.SKUNamePremium), + Name: to.Ptr(armredis.SKUNamePremium), }, }, }, @@ -114,7 +114,7 @@ func TestRedisScanner_Rules(t *testing.T) { fields: fields{ rule: "redis-006", target: &armredis.ResourceInfo{ - Name: ref.Of("redis-test"), + Name: to.Ptr("redis-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -129,7 +129,7 @@ func TestRedisScanner_Rules(t *testing.T) { rule: "redis-008", target: &armredis.ResourceInfo{ Properties: &armredis.Properties{ - EnableNonSSLPort: ref.Of(false), + EnableNonSSLPort: to.Ptr(false), }, }, scanContext: &scanners.ScanContext{}, @@ -145,7 +145,7 @@ func TestRedisScanner_Rules(t *testing.T) { rule: "redis-009", target: &armredis.ResourceInfo{ Properties: &armredis.Properties{ - MinimumTLSVersion: ref.Of(armredis.TLSVersionOne2), + MinimumTLSVersion: to.Ptr(armredis.TLSVersionOne2), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/sb/rules.go b/internal/scanners/sb/rules.go index fc1cdda6..4af0474d 100644 --- a/internal/scanners/sb/rules.go +++ b/internal/scanners/sb/rules.go @@ -14,25 +14,22 @@ import ( func (a *ServiceBusScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "sb-001": { - Id: "sb-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Service Bus should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "sb-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Service Bus should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armservicebus.SBNamespace) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, Url: "https://learn.microsoft.com/en-us/azure/service-bus-messaging/monitor-service-bus#collection-and-routing", - Field: scanners.OverviewFieldDiagnostics, }, "sb-002": { - Id: "sb-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Service Bus should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "sb-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Service Bus should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armservicebus.SBNamespace) sku := string(*i.SKU.Name) @@ -40,14 +37,12 @@ func (a *ServiceBusScanner) GetRules() map[string]scanners.AzureRule { return !zones, "" }, Url: "https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-outages-disasters#availability-zones", - Field: scanners.OverviewFieldAZ, }, "sb-003": { - Id: "sb-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Service Bus should have a SLA", - Severity: scanners.SeverityHigh, + Id: "sb-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Service Bus should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armservicebus.SBNamespace) sku := string(*i.SKU.Name) @@ -58,55 +53,47 @@ func (a *ServiceBusScanner) GetRules() map[string]scanners.AzureRule { return false, sla }, Url: "https://www.azure.cn/en-us/support/sla/service-bus/", - Field: scanners.OverviewFieldSLA, }, "sb-004": { - Id: "sb-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Service Bus should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "sb-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Service Bus should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armservicebus.SBNamespace) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, Url: "https://learn.microsoft.com/en-us/azure/service-bus-messaging/network-security", - Field: scanners.OverviewFieldPrivate, }, "sb-005": { - Id: "sb-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Service Bus SKU", - Severity: scanners.SeverityHigh, + Id: "sb-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Service Bus SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armservicebus.SBNamespace) return false, string(*i.SKU.Name) }, Url: "https://azure.microsoft.com/en-us/pricing/details/service-bus/", - Field: scanners.OverviewFieldSKU, }, "sb-006": { - Id: "sb-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Service Bus Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "sb-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Service Bus Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armservicebus.SBNamespace) caf := strings.HasPrefix(*c.Name, "sb") return !caf, "" }, Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, }, "sb-007": { - Id: "sb-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Service Bus should have tags", - Severity: scanners.SeverityLow, + Id: "sb-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Service Bus should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armservicebus.SBNamespace) return len(c.Tags) == 0, "" @@ -114,11 +101,10 @@ func (a *ServiceBusScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "sb-008": { - Id: "sb-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityIdentity, - Description: "Service Bus should have local authentication disabled", - Severity: scanners.SeverityMedium, + Id: "sb-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Service Bus should have local authentication disabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armservicebus.SBNamespace) localAuth := c.Properties.DisableLocalAuth != nil && *c.Properties.DisableLocalAuth diff --git a/internal/scanners/sb/rules_test.go b/internal/scanners/sb/rules_test.go index 91e60ccb..f70d6c64 100644 --- a/internal/scanners/sb/rules_test.go +++ b/internal/scanners/sb/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/servicebus/armservicebus" ) @@ -32,7 +32,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { fields: fields{ rule: "sb-001", target: &armservicebus.SBNamespace{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,10 +51,10 @@ func TestServiceBusScanner_Rules(t *testing.T) { rule: "sb-002", target: &armservicebus.SBNamespace{ SKU: &armservicebus.SBSKU{ - Name: ref.Of(armservicebus.SKUNamePremium), + Name: to.Ptr(armservicebus.SKUNamePremium), }, Properties: &armservicebus.SBNamespaceProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -70,7 +70,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { rule: "sb-003", target: &armservicebus.SBNamespace{ SKU: &armservicebus.SBSKU{ - Name: ref.Of(armservicebus.SKUNameStandard), + Name: to.Ptr(armservicebus.SKUNameStandard), }, }, scanContext: &scanners.ScanContext{}, @@ -86,7 +86,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { rule: "sb-003", target: &armservicebus.SBNamespace{ SKU: &armservicebus.SBSKU{ - Name: ref.Of(armservicebus.SKUNamePremium), + Name: to.Ptr(armservicebus.SKUNamePremium), }, }, scanContext: &scanners.ScanContext{}, @@ -104,7 +104,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { Properties: &armservicebus.SBNamespaceProperties{ PrivateEndpointConnections: []*armservicebus.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -122,7 +122,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { rule: "sb-005", target: &armservicebus.SBNamespace{ SKU: &armservicebus.SBSKU{ - Name: ref.Of(armservicebus.SKUNamePremium), + Name: to.Ptr(armservicebus.SKUNamePremium), }, }, scanContext: &scanners.ScanContext{}, @@ -137,7 +137,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { fields: fields{ rule: "sb-006", target: &armservicebus.SBNamespace{ - Name: ref.Of("sb-test"), + Name: to.Ptr("sb-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -152,7 +152,7 @@ func TestServiceBusScanner_Rules(t *testing.T) { rule: "sb-008", target: &armservicebus.SBNamespace{ Properties: &armservicebus.SBNamespaceProperties{ - DisableLocalAuth: ref.Of(true), + DisableLocalAuth: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, diff --git a/internal/scanners/scanner.go b/internal/scanners/scanner.go index 4c174af3..7ef117f9 100644 --- a/internal/scanners/scanner.go +++ b/internal/scanners/scanner.go @@ -6,13 +6,13 @@ package scanners import ( "context" "fmt" - "strconv" "strings" "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/resourcemanager/appservice/armappservice/v2" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" "github.com/rs/zerolog/log" ) @@ -27,10 +27,11 @@ type ( // ScanContext - Struct for Scanner Context ScanContext struct { - PrivateEndpoints map[string]bool - DiagnosticsSettings map[string]bool - PublicIPs map[string]*armnetwork.PublicIPAddress - SiteConfig *armappservice.WebAppsClientGetConfigurationResponse + PrivateEndpoints map[string]bool + DiagnosticsSettings map[string]bool + PublicIPs map[string]*armnetwork.PublicIPAddress + SiteConfig *armappservice.WebAppsClientGetConfigurationResponse + BlobServiceProperties *armstorage.BlobServicesClientGetServicePropertiesResponse } // IAzureScanner - Interface for all Azure Scanners @@ -51,56 +52,38 @@ type ( } AzureRule struct { - Id string - Category RulesCategory - Subcategory RulesSubCategory - Description string - Severity SeverityType - Url string - Field OverviewField - Eval func(target interface{}, scanContext *ScanContext) (bool, string) + Id string + Category RulesCategory + Recommendation string + Impact ImpactType + Url string + Eval func(target interface{}, scanContext *ScanContext) (bool, string) } AzureRuleResult struct { - Id string - Category RulesCategory - Subcategory RulesSubCategory - Description string - Severity SeverityType - Learn string - Result string - Field OverviewField - IsBroken bool + Id string + Category RulesCategory + Recommendation string + Impact ImpactType + Learn string + Result string + NotCompliant bool } RuleEngine struct{} - - OverviewField int -) - -const ( - OverviewFieldNone OverviewField = iota - OverviewFieldSKU - OverviewFieldSLA - OverviewFieldAZ - OverviewFieldPrivate - OverviewFieldDiagnostics - OverviewFieldCAF ) func (e *RuleEngine) EvaluateRule(rule AzureRule, target interface{}, scanContext *ScanContext) AzureRuleResult { broken, result := rule.Eval(target, scanContext) return AzureRuleResult{ - Id: rule.Id, - Category: rule.Category, - Subcategory: rule.Subcategory, - Description: rule.Description, - Severity: rule.Severity, - Learn: rule.Url, - Result: result, - IsBroken: broken, - Field: rule.Field, + Id: rule.Id, + Category: rule.Category, + Recommendation: rule.Recommendation, + Impact: rule.Impact, + Learn: rule.Url, + Result: result, + NotCompliant: broken, } } @@ -114,69 +97,6 @@ func (e *RuleEngine) EvaluateRules(rules map[string]AzureRule, target interface{ return results } -// ToMap - Returns a map representation of the Azure Service Result -func (r AzureServiceResult) ToMap(mask bool) map[string]string { - sku := "" - sla := "" - az := "" - pvt := "" - ds := "" - caf := "" - - for _, v := range r.Rules { - switch v.Field { - case OverviewFieldSKU: - sku = v.Result - case OverviewFieldSLA: - sla = v.Result - case OverviewFieldAZ: - az = strconv.FormatBool(!v.IsBroken) - case OverviewFieldPrivate: - pvt = strconv.FormatBool(!v.IsBroken) - case OverviewFieldDiagnostics: - ds = strconv.FormatBool(!v.IsBroken) - case OverviewFieldCAF: - caf = strconv.FormatBool(!v.IsBroken) - } - } - - return map[string]string{ - "SubscriptionID": MaskSubscriptionID(r.SubscriptionID, mask), - "ResourceGroup": r.ResourceGroup, - "Location": ParseLocation(r.Location), - "Type": r.Type, - "Name": r.ServiceName, - "SKU": sku, - "SLA": sla, - "AZ": az, - "PVT": pvt, - "DS": ds, - "CAF": caf, - } -} - -// GetResourceType - Returns the resource type of the Azure Service Result -func (r AzureServiceResult) GetResourceType() string { - return r.Type -} - -// GetHeaders - Returns the headers of the Azure Service Result -func (r AzureServiceResult) GetHeaders() []string { - return []string{ - "SubscriptionID", - "ResourceGroup", - "Location", - "Type", - "Name", - "SKU", - "SLA", - "AZ", - "PVT", - "DS", - "CAF", - } -} - func ParseLocation(location string) string { return strings.ToLower(strings.ReplaceAll(location, " ", "")) } @@ -198,47 +118,18 @@ func LogSubscriptionScan(subscriptionID string, serviceName string) { log.Info().Msgf("Scanning subscriptions/...%s for %s", subscriptionID[29:], serviceName) } -type SeverityType string +type ImpactType string type RulesCategory string -type RulesSubCategory string const ( - SeverityHigh SeverityType = "High" - SeverityMedium SeverityType = "Medium" - SeverityLow SeverityType = "Low" - - RulesCategoryReliability RulesCategory = "Reliability" - RulesCategorySecurity RulesCategory = "Security" - RulesCategoryCostOptimization RulesCategory = "Cost Optimization" - RulesCategoryOperationalExcellence RulesCategory = "Operational Excellence" - RulesCategoryPerformanceEfficienccy RulesCategory = "Performance Efficiency" - - RulesSubcategoryReliabilityAvailabilityZones RulesSubCategory = "Availability Zones" - RulesSubcategoryReliabilitySLA RulesSubCategory = "SLA" - RulesSubcategoryReliabilitySKU RulesSubCategory = "SKU" - RulesSubcategoryReliabilityScaling RulesSubCategory = "Scaling" - RulesSubcategoryReliabilityDiagnosticLogs RulesSubCategory = "Diagnostic Logs" - RulesSubcategoryReliabilityMonitoring RulesSubCategory = "Monitoring" - RulesSubcategoryReliabilityReliability RulesSubCategory = "Reliability" - RulesSubcategoryReliabilityMaintenance RulesSubCategory = "Maintenance" - - RulesSubcategoryOperationalExcellenceCAF RulesSubCategory = "Naming Convention (CAF)" - RulesSubcategoryOperationalExcellenceTags RulesSubCategory = "Tags" - RulesSubcategoryOperationalExcellenceRetentionPolicies RulesSubCategory = "Retention Policies" - - RulesSubcategorySecurityNetworkSecurityGroups RulesSubCategory = "Network Security Groups" - RulesSubcategorySecuritySSL RulesSubCategory = "SSL" - RulesSubcategorySecurityHTTPS RulesSubCategory = "HTTPS Only" - RulesSubcategorySecurityCyphers RulesSubCategory = "Cyphers" - RulesSubcategorySecurityCertificates RulesSubCategory = "Certificates" - RulesSubcategorySecurityTLS RulesSubCategory = "TLS" - RulesSubcategorySecurityPrivateEndpoint RulesSubCategory = "Private Endpoint" - RulesSubcategorySecurityPrivateIP RulesSubCategory = "Private IP Address" - RulesSubcategorySecurityFirewall RulesSubCategory = "Firewall" - RulesSubcategorySecurityIdentity RulesSubCategory = "Identity and Access Control" - RulesSubcategorySecurityNetworking RulesSubCategory = "Networking" - RulesSubcategorySecurityDiskEncryption RulesSubCategory = "Disk Encryption" - RulesSubcategorySecurity RulesSubCategory = "Security" - - RulesSubcategoryPerformanceEfficienccyNetworking RulesSubCategory = "Networking" + ImpactHigh ImpactType = "High" + ImpactMedium ImpactType = "Medium" + ImpactLow ImpactType = "Low" + + RulesCategoryHighAvailability RulesCategory = "High Availability" + RulesCategoryMonitoringAndAlerting RulesCategory = "Monitoring and Alerting" + RulesCategoryScalability RulesCategory = "Scalability" + RulesCategoryDisasterRecovery RulesCategory = "Disaster Recovery" + RulesCategorySecurity RulesCategory = "Security" + RulesCategoryGovernance RulesCategory = "Governance" ) diff --git a/internal/scanners/sigr/rules.go b/internal/scanners/sigr/rules.go index 95592cad..6e0467f9 100644 --- a/internal/scanners/sigr/rules.go +++ b/internal/scanners/sigr/rules.go @@ -14,25 +14,22 @@ import ( func (a *SignalRScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "sigr-001": { - Id: "sigr-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "SignalR should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "sigr-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "SignalR should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armsignalr.ResourceInfo) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, Url: "https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-howto-diagnostic-logs", - Field: scanners.OverviewFieldDiagnostics, }, "sigr-002": { - Id: "sigr-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "SignalR should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "sigr-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SignalR should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsignalr.ResourceInfo) sku := string(*i.SKU.Name) @@ -43,67 +40,57 @@ func (a *SignalRScanner) GetRules() map[string]scanners.AzureRule { return !zones, "" }, Url: "https://learn.microsoft.com/en-us/azure/azure-signalr/availability-zones", - Field: scanners.OverviewFieldAZ, }, "sigr-003": { - Id: "sigr-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "SignalR should have a SLA", - Severity: scanners.SeverityHigh, + Id: "sigr-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SignalR should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.9%" }, Url: "https://www.azure.cn/en-us/support/sla/signalr-service/", - Field: scanners.OverviewFieldSLA, }, "sigr-004": { - Id: "sigr-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "SignalR should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "sigr-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "SignalR should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsignalr.ResourceInfo) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, Url: "https://learn.microsoft.com/en-us/azure/azure-signalr/howto-private-endpoints", - Field: scanners.OverviewFieldPrivate, }, "sigr-005": { - Id: "sigr-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "SignalR SKU", - Severity: scanners.SeverityHigh, + Id: "sigr-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SignalR SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsignalr.ResourceInfo) return false, string(*i.SKU.Name) }, Url: "https://azure.microsoft.com/en-us/pricing/details/signalr-service/", - Field: scanners.OverviewFieldSKU, }, "sigr-006": { - Id: "sigr-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "SignalR Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "sigr-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SignalR Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsignalr.ResourceInfo) caf := strings.HasPrefix(*c.Name, "sigr") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "sigr-007": { - Id: "sigr-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "SignalR should have tags", - Severity: scanners.SeverityLow, + Id: "sigr-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SignalR should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsignalr.ResourceInfo) return len(c.Tags) == 0, "" diff --git a/internal/scanners/sigr/rules_test.go b/internal/scanners/sigr/rules_test.go index a723b455..69614196 100644 --- a/internal/scanners/sigr/rules_test.go +++ b/internal/scanners/sigr/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/signalr/armsignalr" ) @@ -32,7 +32,7 @@ func TestSignalRScanner_Rules(t *testing.T) { fields: fields{ rule: "sigr-001", target: &armsignalr.ResourceInfo{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestSignalRScanner_Rules(t *testing.T) { rule: "sigr-002", target: &armsignalr.ResourceInfo{ SKU: &armsignalr.ResourceSKU{ - Name: ref.Of("Premium"), + Name: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -81,7 +81,7 @@ func TestSignalRScanner_Rules(t *testing.T) { Properties: &armsignalr.Properties{ PrivateEndpointConnections: []*armsignalr.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -99,7 +99,7 @@ func TestSignalRScanner_Rules(t *testing.T) { rule: "sigr-005", target: &armsignalr.ResourceInfo{ SKU: &armsignalr.ResourceSKU{ - Name: ref.Of("Premium"), + Name: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -114,7 +114,7 @@ func TestSignalRScanner_Rules(t *testing.T) { fields: fields{ rule: "sigr-006", target: &armsignalr.ResourceInfo{ - Name: ref.Of("sigr-test"), + Name: to.Ptr("sigr-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/sql/rules.go b/internal/scanners/sql/rules.go index 8c72bcac..ad51c612 100644 --- a/internal/scanners/sql/rules.go +++ b/internal/scanners/sql/rules.go @@ -22,51 +22,44 @@ func (a *SQLScanner) GetRules() map[string]scanners.AzureRule { func (a *SQLScanner) getServerRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "sql-001": { - Id: "sql-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "SQL should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "sql-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "SQL should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armsql.Server) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Field: scanners.OverviewFieldDiagnostics, }, "sql-004": { - Id: "sql-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "SQL should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "sql-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "SQL should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsql.Server) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Field: scanners.OverviewFieldPrivate, }, "sql-006": { - Id: "sql-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "SQL Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "sql-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SQL Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsql.Server) caf := strings.HasPrefix(*c.Name, "sql") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "sql-007": { - Id: "sql-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "SQL should have tags", - Severity: scanners.SeverityLow, + Id: "sql-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SQL should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsql.Server) return len(c.Tags) == 0, "" @@ -74,11 +67,10 @@ func (a *SQLScanner) getServerRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "sql-008": { - Id: "sql-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "SQL should enforce TLS >= 1.2", - Severity: scanners.SeverityLow, + Id: "sql-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "SQL should enforce TLS >= 1.2", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsql.Server) return c.Properties.MinimalTLSVersion == nil || *c.Properties.MinimalTLSVersion != "1.2", "" @@ -91,24 +83,21 @@ func (a *SQLScanner) getServerRules() map[string]scanners.AzureRule { func (a *SQLScanner) getDatabaseRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "sqldb-001": { - Id: "sqldb-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "SQL Database should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "sqldb-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "SQL Database should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armsql.Database) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Field: scanners.OverviewFieldDiagnostics, }, "sqldb-002": { - Id: "sqldb-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "SQL Database should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "sqldb-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SQL Database should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsql.Database) zones := false @@ -117,14 +106,12 @@ func (a *SQLScanner) getDatabaseRules() map[string]scanners.AzureRule { } return !zones, "" }, - Field: scanners.OverviewFieldAZ, }, "sqldb-003": { - Id: "sqldb-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "SQL Database should have a SLA", - Severity: scanners.SeverityHigh, + Id: "sqldb-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SQL Database should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsql.Database) sla := "99.99%" @@ -133,41 +120,35 @@ func (a *SQLScanner) getDatabaseRules() map[string]scanners.AzureRule { } return false, sla }, - Field: scanners.OverviewFieldSLA, }, "sqldb-005": { - Id: "sqldb-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "SQL Database SKU", - Severity: scanners.SeverityHigh, + Id: "sqldb-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "SQL Database SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armsql.Database) return false, string(*i.SKU.Name) }, - Url: "https://docs.microsoft.com/en-us/azure/azure-sql/database/service-tiers-vcore?tabs=azure-portal", - Field: scanners.OverviewFieldSKU, + Url: "https://docs.microsoft.com/en-us/azure/azure-sql/database/service-tiers-vcore?tabs=azure-portal", }, "sqldb-006": { - Id: "sqldb-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "SQL Database Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "sqldb-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SQL Database Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsql.Database) caf := *c.Name == "master" || strings.HasPrefix(*c.Name, "sqldb") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "sqldb-007": { - Id: "sqldb-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "SQL Database should have tags", - Severity: scanners.SeverityLow, + Id: "sqldb-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "SQL Database should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armsql.Database) return len(c.Tags) == 0, "" diff --git a/internal/scanners/sql/rules_test.go b/internal/scanners/sql/rules_test.go index 002a408d..30f7d75e 100644 --- a/internal/scanners/sql/rules_test.go +++ b/internal/scanners/sql/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/sql/armsql" ) @@ -32,7 +32,7 @@ func TestSQLScanner_Rules(t *testing.T) { fields: fields{ rule: "sql-001", target: &armsql.Server{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -53,7 +53,7 @@ func TestSQLScanner_Rules(t *testing.T) { Properties: &armsql.ServerProperties{ PrivateEndpointConnections: []*armsql.ServerPrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -70,7 +70,7 @@ func TestSQLScanner_Rules(t *testing.T) { fields: fields{ rule: "sql-006", target: &armsql.Server{ - Name: ref.Of("sql-test"), + Name: to.Ptr("sql-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -85,7 +85,7 @@ func TestSQLScanner_Rules(t *testing.T) { rule: "sql-008", target: &armsql.Server{ Properties: &armsql.ServerProperties{ - MinimalTLSVersion: ref.Of("1.2"), + MinimalTLSVersion: to.Ptr("1.2"), }, }, scanContext: &scanners.ScanContext{}, @@ -132,7 +132,7 @@ func TestSQLScanner_DatabaseRules(t *testing.T) { fields: fields{ rule: "sqldb-001", target: &armsql.Database{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -151,7 +151,7 @@ func TestSQLScanner_DatabaseRules(t *testing.T) { rule: "sqldb-002", target: &armsql.Database{ Properties: &armsql.DatabaseProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -167,10 +167,10 @@ func TestSQLScanner_DatabaseRules(t *testing.T) { rule: "sqldb-003", target: &armsql.Database{ Properties: &armsql.DatabaseProperties{ - ZoneRedundant: ref.Of(true), + ZoneRedundant: to.Ptr(true), }, SKU: &armsql.SKU{ - Tier: ref.Of("Premium"), + Tier: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -200,7 +200,7 @@ func TestSQLScanner_DatabaseRules(t *testing.T) { rule: "sqldb-005", target: &armsql.Database{ SKU: &armsql.SKU{ - Name: ref.Of("P3"), + Name: to.Ptr("P3"), }, }, scanContext: &scanners.ScanContext{}, @@ -215,7 +215,7 @@ func TestSQLScanner_DatabaseRules(t *testing.T) { fields: fields{ rule: "sqldb-006", target: &armsql.Database{ - Name: ref.Of("sqldb-test"), + Name: to.Ptr("sqldb-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/st/rules.go b/internal/scanners/st/rules.go index b533a6e2..f2cb5550 100644 --- a/internal/scanners/st/rules.go +++ b/internal/scanners/st/rules.go @@ -14,25 +14,22 @@ import ( func (a *StorageScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "st-001": { - Id: "st-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Storage should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "st-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Storage should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armstorage.Account) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/storage/blobs/monitor-blob-storage", }, "st-002": { - Id: "st-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Storage should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "st-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Storage should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armstorage.Account) sku := string(*i.SKU.Name) @@ -42,15 +39,13 @@ func (a *StorageScanner) GetRules() map[string]scanners.AzureRule { } return !zones, "" }, - Url: "https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/EN-US/azure/reliability/migrate-storage", }, "st-003": { - Id: "st-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Storage should have a SLA", - Severity: scanners.SeverityHigh, + Id: "st-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Storage should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armstorage.Account) tier := "" @@ -70,56 +65,48 @@ func (a *StorageScanner) GetRules() map[string]scanners.AzureRule { } return false, sla }, - Url: "https://www.azure.cn/en-us/support/sla/storage/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/storage/", }, "st-004": { - Id: "st-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Storage should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "st-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Storage should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armstorage.Account) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/storage/common/storage-private-endpoints", }, "st-005": { - Id: "st-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Storage SKU", - Severity: scanners.SeverityHigh, + Id: "st-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Storage SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armstorage.Account) return false, string(*i.SKU.Name) }, - Url: "https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types", - Field: scanners.OverviewFieldSKU, + Url: "https://learn.microsoft.com/en-us/rest/api/storagerp/srp_sku_types", }, "st-006": { - Id: "st-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Storage Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "st-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Storage Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armstorage.Account) caf := strings.HasPrefix(*c.Name, "st") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "st-007": { - Id: "st-007", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "Storage Account should use HTTPS only", - Severity: scanners.SeverityHigh, + Id: "st-007", + Category: scanners.RulesCategorySecurity, + Recommendation: "Storage Account should use HTTPS only", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armstorage.Account) h := *c.Properties.EnableHTTPSTrafficOnly @@ -128,11 +115,10 @@ func (a *StorageScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/storage/common/storage-require-secure-transfer", }, "st-008": { - Id: "st-008", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Storage Account should have tags", - Severity: scanners.SeverityLow, + Id: "st-008", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Storage Account should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armstorage.Account) return len(c.Tags) == 0, "" @@ -140,16 +126,41 @@ func (a *StorageScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "st-009": { - Id: "st-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityTLS, - Description: "Storage Account should enforce TLS >= 1.2", - Severity: scanners.SeverityLow, + Id: "st-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Storage Account should enforce TLS >= 1.2", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armstorage.Account) return c.Properties.MinimumTLSVersion == nil || *c.Properties.MinimumTLSVersion != armstorage.MinimumTLSVersionTLS12, "" }, Url: "https://learn.microsoft.com/en-us/azure/storage/common/transport-layer-security-configure-minimum-version?tabs=portal", }, + "st-010": { + Id: "st-010", + Category: scanners.RulesCategoryDisasterRecovery, + Recommendation: "Storage Account should have inmutable storage versioning enabled", + Impact: scanners.ImpactLow, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + c := target.(*armstorage.Account) + return c.Properties.ImmutableStorageWithVersioning == nil || c.Properties.ImmutableStorageWithVersioning.Enabled == nil || !*c.Properties.ImmutableStorageWithVersioning.Enabled, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/storage-accounts/reliability", + }, + "st-011": { + Id: "st-011", + Category: scanners.RulesCategoryDisasterRecovery, + Recommendation: "Storage Account should have soft delete enabled", + Impact: scanners.ImpactMedium, + Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { + broken := false + broken = scanContext.BlobServiceProperties != nil && (scanContext.BlobServiceProperties.BlobServiceProperties.BlobServiceProperties.ContainerDeleteRetentionPolicy == nil || + scanContext.BlobServiceProperties.BlobServiceProperties.BlobServiceProperties.ContainerDeleteRetentionPolicy.Enabled == nil || + !*scanContext.BlobServiceProperties.BlobServiceProperties.BlobServiceProperties.ContainerDeleteRetentionPolicy.Enabled) + + return broken, "" + }, + Url: "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/storage-accounts/reliability", + }, } } diff --git a/internal/scanners/st/rules_test.go b/internal/scanners/st/rules_test.go index be4821f0..4f129f16 100644 --- a/internal/scanners/st/rules_test.go +++ b/internal/scanners/st/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage" ) @@ -32,7 +32,7 @@ func TestStorageScanner_Rules(t *testing.T) { fields: fields{ rule: "st-001", target: &armstorage.Account{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestStorageScanner_Rules(t *testing.T) { rule: "st-002", target: &armstorage.Account{ SKU: &armstorage.SKU{ - Name: ref.Of(armstorage.SKUNamePremiumZRS), + Name: to.Ptr(armstorage.SKUNamePremiumZRS), }, }, scanContext: &scanners.ScanContext{}, @@ -67,10 +67,10 @@ func TestStorageScanner_Rules(t *testing.T) { rule: "st-003", target: &armstorage.Account{ SKU: &armstorage.SKU{ - Name: ref.Of(armstorage.SKUNamePremiumZRS), + Name: to.Ptr(armstorage.SKUNamePremiumZRS), }, Properties: &armstorage.AccountProperties{ - AccessTier: ref.Of(armstorage.AccessTierHot), + AccessTier: to.Ptr(armstorage.AccessTierHot), }, }, scanContext: &scanners.ScanContext{}, @@ -88,7 +88,7 @@ func TestStorageScanner_Rules(t *testing.T) { Properties: &armstorage.AccountProperties{ PrivateEndpointConnections: []*armstorage.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -106,7 +106,7 @@ func TestStorageScanner_Rules(t *testing.T) { rule: "st-005", target: &armstorage.Account{ SKU: &armstorage.SKU{ - Name: ref.Of(armstorage.SKUNamePremiumZRS), + Name: to.Ptr(armstorage.SKUNamePremiumZRS), }, }, scanContext: &scanners.ScanContext{}, @@ -121,7 +121,7 @@ func TestStorageScanner_Rules(t *testing.T) { fields: fields{ rule: "st-006", target: &armstorage.Account{ - Name: ref.Of("sttest"), + Name: to.Ptr("sttest"), }, scanContext: &scanners.ScanContext{}, }, @@ -136,7 +136,7 @@ func TestStorageScanner_Rules(t *testing.T) { rule: "st-007", target: &armstorage.Account{ Properties: &armstorage.AccountProperties{ - EnableHTTPSTrafficOnly: ref.Of(true), + EnableHTTPSTrafficOnly: to.Ptr(true), }, }, scanContext: &scanners.ScanContext{}, @@ -152,7 +152,7 @@ func TestStorageScanner_Rules(t *testing.T) { rule: "st-009", target: &armstorage.Account{ Properties: &armstorage.AccountProperties{ - MinimumTLSVersion: ref.Of(armstorage.MinimumTLSVersionTLS12), + MinimumTLSVersion: to.Ptr(armstorage.MinimumTLSVersionTLS12), }, }, scanContext: &scanners.ScanContext{}, @@ -162,6 +162,66 @@ func TestStorageScanner_Rules(t *testing.T) { result: "", }, }, + { + name: "StorageScanner inmutable storage versioning disabled", + fields: fields{ + rule: "st-010", + target: &armstorage.Account{ + Properties: &armstorage.AccountProperties{ + ImmutableStorageWithVersioning: &armstorage.ImmutableStorageAccount{ + Enabled: to.Ptr(false), + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: true, + result: "", + }, + }, + { + name: "StorageScanner inmutable storage versioning enabled", + fields: fields{ + rule: "st-010", + target: &armstorage.Account{ + Properties: &armstorage.AccountProperties{ + ImmutableStorageWithVersioning: &armstorage.ImmutableStorageAccount{ + Enabled: to.Ptr(true), + }, + }, + }, + scanContext: &scanners.ScanContext{}, + }, + want: want{ + broken: false, + result: "", + }, + }, + { + name: "StorageScanner inmutable storage versioning enabled", + fields: fields{ + rule: "st-011", + target: &armstorage.Account{ + Properties: &armstorage.AccountProperties{}, + }, + scanContext: &scanners.ScanContext{ + BlobServiceProperties: &armstorage.BlobServicesClientGetServicePropertiesResponse{ + BlobServiceProperties: armstorage.BlobServiceProperties{ + BlobServiceProperties: &armstorage.BlobServicePropertiesProperties{ + ContainerDeleteRetentionPolicy: &armstorage.DeleteRetentionPolicy{ + Enabled: to.Ptr(true), + }, + }, + }, + }, + }, + }, + want: want{ + broken: false, + result: "", + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/scanners/st/st.go b/internal/scanners/st/st.go index eed28ee3..70d5ca51 100644 --- a/internal/scanners/st/st.go +++ b/internal/scanners/st/st.go @@ -10,8 +10,9 @@ import ( // StorageScanner - Scanner for Storage type StorageScanner struct { - config *scanners.ScannerConfig - storageClient *armstorage.AccountsClient + config *scanners.ScannerConfig + storageClient *armstorage.AccountsClient + blobServicesClient *armstorage.BlobServicesClient } // Init - Initializes the StorageScanner @@ -19,6 +20,10 @@ func (c *StorageScanner) Init(config *scanners.ScannerConfig) error { c.config = config var err error c.storageClient, err = armstorage.NewAccountsClient(config.SubscriptionID, config.Cred, config.ClientOptions) + if err != nil { + return err + } + c.blobServicesClient, err = armstorage.NewBlobServicesClient(config.SubscriptionID, config.Cred, config.ClientOptions) return err } @@ -35,6 +40,12 @@ func (c *StorageScanner) Scan(resourceGroupName string, scanContext *scanners.Sc results := []scanners.AzureServiceResult{} for _, storage := range storage { + scanContext.BlobServiceProperties = nil + blobServicesProperties, err := c.blobServicesClient.GetServiceProperties(c.config.Ctx, resourceGroupName, *storage.Name, nil) + if err == nil { + scanContext.BlobServiceProperties = &blobServicesProperties + } + rr := engine.EvaluateRules(rules, storage, scanContext) results = append(results, scanners.AzureServiceResult{ diff --git a/internal/scanners/traf/rules.go b/internal/scanners/traf/rules.go index 60d83faa..61e0fc81 100644 --- a/internal/scanners/traf/rules.go +++ b/internal/scanners/traf/rules.go @@ -6,8 +6,8 @@ package traf import ( "strings" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" ) @@ -15,63 +15,54 @@ import ( func (a *TrafficManagerScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "traf-001": { - Id: "traf-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Traffic Manager should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "traf-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Traffic Manager should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armtrafficmanager.Profile) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-diagnostic-logs", }, "traf-002": { - Id: "traf-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Traffic Manager should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "traf-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Traffic Manager should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "" }, - Url: "https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/architecture/high-availability/reference-architecture-traffic-manager-application-gateway", }, "traf-003": { - Id: "traf-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Traffic Manager should have a SLA", - Severity: scanners.SeverityHigh, + Id: "traf-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Traffic Manager should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.99%" }, - Url: "https://www.azure.cn/en-us/support/sla/traffic-manager/", - Field: scanners.OverviewFieldSLA, + Url: "https://www.azure.cn/en-us/support/sla/traffic-manager/", }, "traf-006": { - Id: "traf-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Traffic Manager Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "traf-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Traffic Manager Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armtrafficmanager.Profile) caf := strings.HasPrefix(*c.Name, "traf") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "traf-007": { - Id: "traf-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Traffic Manager should have tags", - Severity: scanners.SeverityLow, + Id: "traf-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Traffic Manager should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armtrafficmanager.Profile) return len(c.Tags) == 0, "" @@ -79,11 +70,10 @@ func (a *TrafficManagerScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "traf-008": { - Id: "traf-008", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Traffic Manager should use at least 2 endpoints", - Severity: scanners.SeverityHigh, + Id: "traf-008", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Traffic Manager should use at least 2 endpoints", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armtrafficmanager.Profile) endpoints := 0 @@ -97,15 +87,14 @@ func (a *TrafficManagerScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-endpoint-types", }, "traf-009": { - Id: "traf-009", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityHTTPS, - Description: "Traffic Manager: HTTP endpoints should be monitored using HTTPS", - Severity: scanners.SeverityHigh, + Id: "traf-009", + Category: scanners.RulesCategorySecurity, + Recommendation: "Traffic Manager: HTTP endpoints should be monitored using HTTPS", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armtrafficmanager.Profile) httpMonitor := *c.Properties.MonitorConfig.Port == int64(80) || *c.Properties.MonitorConfig.Port == int64(443) - return httpMonitor && c.Properties.MonitorConfig.Protocol != ref.Of(armtrafficmanager.MonitorProtocolHTTPS), "" + return httpMonitor && c.Properties.MonitorConfig.Protocol != to.Ptr(armtrafficmanager.MonitorProtocolHTTPS), "" }, Url: "https://learn.microsoft.com/en-us/azure/traffic-manager/traffic-manager-monitoring", }, diff --git a/internal/scanners/traf/rules_test.go b/internal/scanners/traf/rules_test.go index 074de889..d3d71066 100644 --- a/internal/scanners/traf/rules_test.go +++ b/internal/scanners/traf/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/trafficmanager/armtrafficmanager" ) @@ -32,7 +32,7 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { fields: fields{ rule: "traf-001", target: &armtrafficmanager.Profile{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -74,7 +74,7 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { fields: fields{ rule: "traf-006", target: &armtrafficmanager.Profile{ - Name: ref.Of("traf-test"), + Name: to.Ptr("traf-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -92,12 +92,12 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { Endpoints: []*armtrafficmanager.Endpoint{ { Properties: &armtrafficmanager.EndpointProperties{ - EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + EndpointStatus: to.Ptr(armtrafficmanager.EndpointStatusEnabled), }, }, { Properties: &armtrafficmanager.EndpointProperties{ - EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + EndpointStatus: to.Ptr(armtrafficmanager.EndpointStatusEnabled), }, }, }, @@ -119,12 +119,12 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { Endpoints: []*armtrafficmanager.Endpoint{ { Properties: &armtrafficmanager.EndpointProperties{ - EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusEnabled), + EndpointStatus: to.Ptr(armtrafficmanager.EndpointStatusEnabled), }, }, { Properties: &armtrafficmanager.EndpointProperties{ - EndpointStatus: ref.Of(armtrafficmanager.EndpointStatusDisabled), + EndpointStatus: to.Ptr(armtrafficmanager.EndpointStatusDisabled), }, }, }, @@ -144,8 +144,8 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { target: &armtrafficmanager.Profile{ Properties: &armtrafficmanager.ProfileProperties{ MonitorConfig: &armtrafficmanager.MonitorConfig{ - Protocol: ref.Of(armtrafficmanager.MonitorProtocolHTTP), - Port: ref.Of(int64(80)), + Protocol: to.Ptr(armtrafficmanager.MonitorProtocolHTTP), + Port: to.Ptr(int64(80)), }, }, }, @@ -163,8 +163,8 @@ func TestTrafficManagerScanner_Rules(t *testing.T) { target: &armtrafficmanager.Profile{ Properties: &armtrafficmanager.ProfileProperties{ MonitorConfig: &armtrafficmanager.MonitorConfig{ - Protocol: ref.Of(armtrafficmanager.MonitorProtocolHTTP), - Port: ref.Of(int64(443)), + Protocol: to.Ptr(armtrafficmanager.MonitorProtocolHTTP), + Port: to.Ptr(int64(443)), }, }, }, diff --git a/internal/scanners/vm/rules.go b/internal/scanners/vm/rules.go index 6bba7dd0..e1190eb9 100644 --- a/internal/scanners/vm/rules.go +++ b/internal/scanners/vm/rules.go @@ -14,39 +14,34 @@ import ( func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "vm-001": { - Id: "vm-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Virtual Machine should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "vm-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Virtual Machine should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armcompute.VirtualMachine) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/azure-monitor/agents/diagnostics-extension-windows-install", }, "vm-002": { - Id: "vm-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Virtual Machine should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "vm-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual Machine should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { v := target.(*armcompute.VirtualMachine) hasZones := v.Zones != nil && len(v.Zones) > 1 return !hasZones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/virtual-machines/availability#availability-zones", }, "vm-003": { - Id: "vm-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Virtual Machine should have a SLA", - Severity: scanners.SeverityHigh, + Id: "vm-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual Machine should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { v := target.(*armcompute.VirtualMachine) sla := "99.9%" @@ -60,29 +55,25 @@ func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { } return false, sla }, - Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", - Field: scanners.OverviewFieldSLA, + Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services?lang=1", }, "vm-006": { - Id: "vm-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Virtual Machine Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "vm-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual Machine Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcompute.VirtualMachine) caf := strings.HasPrefix(*c.Name, "vm") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "vm-007": { - Id: "vm-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Virtual Machine should have tags", - Severity: scanners.SeverityLow, + Id: "vm-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual Machine should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcompute.VirtualMachine) return len(c.Tags) == 0, "" @@ -90,11 +81,10 @@ func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "vm-008": { - Id: "vm-008", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Virtual Machine should use managed disks", - Severity: scanners.SeverityHigh, + Id: "vm-008", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual Machine should use managed disks", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcompute.VirtualMachine) hasManagedDisks := c.Properties.StorageProfile.OSDisk.ManagedDisk != nil @@ -103,11 +93,10 @@ func (a *VirtualMachineScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/architecture/checklist/resiliency-per-service#virtual-machines", }, "vm-009": { - Id: "vm-009", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Virtual Machine should host application or database data on a data disk", - Severity: scanners.SeverityLow, + Id: "vm-009", + Category: scanners.RulesCategoryScalability, + Recommendation: "Virtual Machine should host application or database data on a data disk", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armcompute.VirtualMachine) hasDataDisks := len(c.Properties.StorageProfile.DataDisks) > 0 diff --git a/internal/scanners/vm/rules_test.go b/internal/scanners/vm/rules_test.go index b0dada0d..0d8ea0cc 100644 --- a/internal/scanners/vm/rules_test.go +++ b/internal/scanners/vm/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4" ) @@ -32,7 +32,7 @@ func TestVirtualMachineScanner_Rules(t *testing.T) { fields: fields{ rule: "vm-001", target: &armcompute.VirtualMachine{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -76,7 +76,7 @@ func TestVirtualMachineScanner_Rules(t *testing.T) { fields: fields{ rule: "vm-006", target: &armcompute.VirtualMachine{ - Name: ref.Of("vm-test"), + Name: to.Ptr("vm-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/vnet/rules.go b/internal/scanners/vnet/rules.go index a9bab949..008364eb 100644 --- a/internal/scanners/vnet/rules.go +++ b/internal/scanners/vnet/rules.go @@ -14,51 +14,44 @@ import ( func (a *VirtualNetworkScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "vnet-001": { - Id: "vnet-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Virtual Network should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "vnet-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Virtual Network should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armnetwork.VirtualNetwork) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/virtual-network/monitor-virtual-network#collection-and-routing", }, "vnet-002": { - Id: "vnet-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Virtual Network should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "vnet-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual Network should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "" }, - Url: "https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview#virtual-networks-and-availability-zones", }, "vnet-006": { - Id: "vnet-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Virtual Network Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "vnet-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual Network Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualNetwork) caf := strings.HasPrefix(*c.Name, "vnet") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "vnet-007": { - Id: "vnet-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Virtual Network should have tags", - Severity: scanners.SeverityLow, + Id: "vnet-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual Network should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualNetwork) return len(c.Tags) == 0, "" @@ -66,11 +59,10 @@ func (a *VirtualNetworkScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", }, "vnet-008": { - Id: "vnet-008", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityNetworking, - Description: "Virtual Network: All Subnets should have a Network Security Group associated", - Severity: scanners.SeverityHigh, + Id: "vnet-008", + Category: scanners.RulesCategorySecurity, + Recommendation: "Virtual Network: All Subnets should have a Network Security Group associated", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualNetwork) broken := false @@ -87,11 +79,10 @@ func (a *VirtualNetworkScanner) GetRules() map[string]scanners.AzureRule { Url: "https://learn.microsoft.com/azure/virtual-network/concepts-and-best-practices", }, "vnet-009": { - Id: "vnet-009", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityReliability, - Description: "Virtual NetworK should have at least two DNS servers assigned", - Severity: scanners.SeverityHigh, + Id: "vnet-009", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual Network should have at least two DNS servers assigned", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualNetwork) if c.Properties.DhcpOptions == nil { diff --git a/internal/scanners/vnet/rules_test.go b/internal/scanners/vnet/rules_test.go index e63f1003..ba816d7c 100644 --- a/internal/scanners/vnet/rules_test.go +++ b/internal/scanners/vnet/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ) @@ -32,7 +32,7 @@ func TestVirtualNetworkScanner_Rules(t *testing.T) { fields: fields{ rule: "vnet-001", target: &armnetwork.VirtualNetwork{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -62,7 +62,7 @@ func TestVirtualNetworkScanner_Rules(t *testing.T) { fields: fields{ rule: "vnet-006", target: &armnetwork.VirtualNetwork{ - Name: ref.Of("vnet-test"), + Name: to.Ptr("vnet-test"), }, scanContext: &scanners.ScanContext{}, }, @@ -79,10 +79,10 @@ func TestVirtualNetworkScanner_Rules(t *testing.T) { Properties: &armnetwork.VirtualNetworkPropertiesFormat{ Subnets: []*armnetwork.Subnet{ { - Name: ref.Of("subnet1"), + Name: to.Ptr("subnet1"), Properties: &armnetwork.SubnetPropertiesFormat{ NetworkSecurityGroup: &armnetwork.SecurityGroup{ - ID: ref.Of("nsg"), + ID: to.Ptr("nsg"), }, }, }, @@ -104,7 +104,7 @@ func TestVirtualNetworkScanner_Rules(t *testing.T) { Properties: &armnetwork.VirtualNetworkPropertiesFormat{ Subnets: []*armnetwork.Subnet{ { - Name: ref.Of("subnet1"), + Name: to.Ptr("subnet1"), Properties: &armnetwork.SubnetPropertiesFormat{}, }, }, @@ -125,7 +125,7 @@ func TestVirtualNetworkScanner_Rules(t *testing.T) { Properties: &armnetwork.VirtualNetworkPropertiesFormat{ DhcpOptions: &armnetwork.DhcpOptions{ DNSServers: []*string{ - ref.Of("10.0.0.5"), + to.Ptr("10.0.0.5"), }, }, }, diff --git a/internal/scanners/vwan/rules.go b/internal/scanners/vwan/rules.go index fb7ccb38..d80a51e0 100644 --- a/internal/scanners/vwan/rules.go +++ b/internal/scanners/vwan/rules.go @@ -14,76 +14,65 @@ import ( func (a *VirtualWanScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "vwa-001": { - Id: "vwa-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Virtual WAN should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "vwa-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Virtual WAN should have diagnostic settings enabled", + Impact: scanners.ImpactMedium, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armnetwork.VirtualWAN) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, Url: "https://learn.microsoft.com/en-us/azure/virtual-wan/monitor-virtual-wan", - Field: scanners.OverviewFieldDiagnostics, }, "vwa-002": { - Id: "vwa-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Virtual WAN should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "vwa-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual WAN should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "" }, Url: "https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-faq#how-are-availability-zones-and-resiliency-handled-in-virtual-wan", - Field: scanners.OverviewFieldAZ, }, "vwa-003": { - Id: "vwa-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Virtual WAN should have a SLA", - Severity: scanners.SeverityHigh, + Id: "vwa-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual WAN should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { return false, "99.95%" }, Url: "https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-faq#how-is-virtual-wan-sla-calculated", - Field: scanners.OverviewFieldSLA, }, "vwa-005": { - Id: "vwa-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Virtual WAN Type", - Severity: scanners.SeverityHigh, + Id: "vwa-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Virtual WAN Type", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armnetwork.VirtualWAN) return false, string(*i.Properties.Type) }, Url: "https://learn.microsoft.com/en-us/azure/virtual-wan/virtual-wan-about#basicstandard", - Field: scanners.OverviewFieldSKU, }, "vwa-006": { - Id: "vwa-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Virtual WAN Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "vwa-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual WAN Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualWAN) caf := strings.HasPrefix(*c.Name, "vwa") return !caf, "" }, Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, }, "vwa-007": { - Id: "vwa-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Virtual WAN should have tags", - Severity: scanners.SeverityLow, + Id: "vwa-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Virtual WAN should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armnetwork.VirtualWAN) return len(c.Tags) == 0, "" diff --git a/internal/scanners/vwan/rules_test.go b/internal/scanners/vwan/rules_test.go index 27a2abd4..fa60f7f8 100644 --- a/internal/scanners/vwan/rules_test.go +++ b/internal/scanners/vwan/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork" ) @@ -32,7 +32,7 @@ func TestVirtualWanScanner_Rules(t *testing.T) { fields: fields{ rule: "vwa-001", target: &armnetwork.VirtualWAN{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -75,7 +75,7 @@ func TestVirtualWanScanner_Rules(t *testing.T) { rule: "vwa-005", target: &armnetwork.VirtualWAN{ Properties: &armnetwork.VirtualWanProperties{ - Type: ref.Of("Standard"), + Type: to.Ptr("Standard"), }, }, scanContext: &scanners.ScanContext{}, @@ -90,7 +90,7 @@ func TestVirtualWanScanner_Rules(t *testing.T) { fields: fields{ rule: "vwa-006", target: &armnetwork.VirtualWAN{ - Name: ref.Of("vwa-test"), + Name: to.Ptr("vwa-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/scanners/wps/rules.go b/internal/scanners/wps/rules.go index f7185ec5..947f09c4 100644 --- a/internal/scanners/wps/rules.go +++ b/internal/scanners/wps/rules.go @@ -14,25 +14,22 @@ import ( func (a *WebPubSubScanner) GetRules() map[string]scanners.AzureRule { return map[string]scanners.AzureRule{ "wps-001": { - Id: "wps-001", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityDiagnosticLogs, - Description: "Web Pub Sub should have diagnostic settings enabled", - Severity: scanners.SeverityMedium, + Id: "wps-001", + Category: scanners.RulesCategoryMonitoringAndAlerting, + Recommendation: "Web Pub Sub should have diagnostic settings enabled", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { service := target.(*armwebpubsub.ResourceInfo) _, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] return !ok, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs", - Field: scanners.OverviewFieldDiagnostics, + Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-troubleshoot-resource-logs", }, "wps-002": { - Id: "wps-002", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilityAvailabilityZones, - Description: "Web Pub Sub should have availability zones enabled", - Severity: scanners.SeverityHigh, + Id: "wps-002", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Web Pub Sub should have availability zones enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armwebpubsub.ResourceInfo) sku := string(*i.SKU.Name) @@ -42,15 +39,13 @@ func (a *WebPubSubScanner) GetRules() map[string]scanners.AzureRule { } return !zones, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones", - Field: scanners.OverviewFieldAZ, + Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/concept-availability-zones", }, "wps-003": { - Id: "wps-003", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySLA, - Description: "Web Pub Sub should have a SLA", - Severity: scanners.SeverityHigh, + Id: "wps-003", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Web Pub Sub should have a SLA", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armwebpubsub.ResourceInfo) sku := string(*i.SKU.Name) @@ -61,56 +56,48 @@ func (a *WebPubSubScanner) GetRules() map[string]scanners.AzureRule { return sla == "None", sla }, - Url: "https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/", - Field: scanners.OverviewFieldSLA, + Url: "https://azure.microsoft.com/en-gb/support/legal/sla/web-pubsub/", }, "wps-004": { - Id: "wps-004", - Category: scanners.RulesCategorySecurity, - Subcategory: scanners.RulesSubcategorySecurityPrivateEndpoint, - Description: "Web Pub Sub should have private endpoints enabled", - Severity: scanners.SeverityHigh, + Id: "wps-004", + Category: scanners.RulesCategorySecurity, + Recommendation: "Web Pub Sub should have private endpoints enabled", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armwebpubsub.ResourceInfo) pe := len(i.Properties.PrivateEndpointConnections) > 0 return !pe, "" }, - Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints", - Field: scanners.OverviewFieldPrivate, + Url: "https://learn.microsoft.com/en-us/azure/azure-web-pubsub/howto-secure-private-endpoints", }, "wps-005": { - Id: "wps-005", - Category: scanners.RulesCategoryReliability, - Subcategory: scanners.RulesSubcategoryReliabilitySKU, - Description: "Web Pub Sub SKU", - Severity: scanners.SeverityHigh, + Id: "wps-005", + Category: scanners.RulesCategoryHighAvailability, + Recommendation: "Web Pub Sub SKU", + Impact: scanners.ImpactHigh, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { i := target.(*armwebpubsub.ResourceInfo) return false, string(*i.SKU.Name) }, - Url: "https://azure.microsoft.com/en-us/pricing/details/web-pubsub/", - Field: scanners.OverviewFieldSKU, + Url: "https://azure.microsoft.com/en-us/pricing/details/web-pubsub/", }, "wps-006": { - Id: "wps-006", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF, - Description: "Web Pub Sub Name should comply with naming conventions", - Severity: scanners.SeverityLow, + Id: "wps-006", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Web Pub Sub Name should comply with naming conventions", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armwebpubsub.ResourceInfo) caf := strings.HasPrefix(*c.Name, "wps") return !caf, "" }, - Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", - Field: scanners.OverviewFieldCAF, + Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", }, "wps-007": { - Id: "wps-007", - Category: scanners.RulesCategoryOperationalExcellence, - Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags, - Description: "Web Pub Sub should have tags", - Severity: scanners.SeverityLow, + Id: "wps-007", + Category: scanners.RulesCategoryGovernance, + Recommendation: "Web Pub Sub should have tags", + Impact: scanners.ImpactLow, Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { c := target.(*armwebpubsub.ResourceInfo) return len(c.Tags) == 0, "" diff --git a/internal/scanners/wps/rules_test.go b/internal/scanners/wps/rules_test.go index 653100b8..1e3d8b72 100644 --- a/internal/scanners/wps/rules_test.go +++ b/internal/scanners/wps/rules_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/Azure/azqr/internal/ref" "github.com/Azure/azqr/internal/scanners" + "github.com/Azure/azqr/internal/to" "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/webpubsub/armwebpubsub" ) @@ -32,7 +32,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { fields: fields{ rule: "wps-001", target: &armwebpubsub.ResourceInfo{ - ID: ref.Of("test"), + ID: to.Ptr("test"), }, scanContext: &scanners.ScanContext{ DiagnosticsSettings: map[string]bool{ @@ -51,7 +51,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { rule: "wps-002", target: &armwebpubsub.ResourceInfo{ SKU: &armwebpubsub.ResourceSKU{ - Name: ref.Of("Premium"), + Name: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -67,7 +67,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { rule: "wps-003", target: &armwebpubsub.ResourceInfo{ SKU: &armwebpubsub.ResourceSKU{ - Name: ref.Of("Premium"), + Name: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -83,7 +83,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { rule: "wps-003", target: &armwebpubsub.ResourceInfo{ SKU: &armwebpubsub.ResourceSKU{ - Name: ref.Of("Free"), + Name: to.Ptr("Free"), }, }, scanContext: &scanners.ScanContext{}, @@ -101,7 +101,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { Properties: &armwebpubsub.Properties{ PrivateEndpointConnections: []*armwebpubsub.PrivateEndpointConnection{ { - ID: ref.Of("test"), + ID: to.Ptr("test"), }, }, }, @@ -119,7 +119,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { rule: "wps-005", target: &armwebpubsub.ResourceInfo{ SKU: &armwebpubsub.ResourceSKU{ - Name: ref.Of("Premium"), + Name: to.Ptr("Premium"), }, }, scanContext: &scanners.ScanContext{}, @@ -134,7 +134,7 @@ func TestWebPubSubScanner_Rules(t *testing.T) { fields: fields{ rule: "wps-006", target: &armwebpubsub.ResourceInfo{ - Name: ref.Of("wps-test"), + Name: to.Ptr("wps-test"), }, scanContext: &scanners.ScanContext{}, }, diff --git a/internal/ref/ref.go b/internal/to/Ptr.go similarity index 70% rename from internal/ref/ref.go rename to internal/to/Ptr.go index 42dd89dc..23bb0289 100644 --- a/internal/ref/ref.go +++ b/internal/to/Ptr.go @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -package ref +package to -func Of[E any](e E) *E { +func Ptr[E any](e E) *E { return &e }