Skip to content

Commit

Permalink
support for application insights
Browse files Browse the repository at this point in the history
  • Loading branch information
cmendible committed May 23, 2023
1 parent b58b067 commit 20815d1
Show file tree
Hide file tree
Showing 10 changed files with 287 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Azure Quick Review (azqr) outputs includes an overview section with the followin
* AZ: True if the service is Availability Zone aware.
* PVT: True if the service has a private IP address.
* DS: True if the service has diagnotics settings enabled.
* CAF: True if the service is compliant with the [Cloud Adoption Framework](https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/) naming covention.
* CAF: True if 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) naming covention.

Check the [Scan Results](#scan-results) documentation for more information on Azure Quick Review (azqr) outputs.

Expand Down Expand Up @@ -55,6 +55,7 @@ Azure Quick Review (azqr) recommendations are based on a set of rules. To learn
* Azure Front Door
* Azure Storage Account
* Azure Firewall
* Azure Application Insights

## Microsoft Defender Status

Expand Down
28 changes: 28 additions & 0 deletions cmd/azqr/appi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package azqr

import (
"github.com/cmendible/azqr/internal/scanners"
"github.com/cmendible/azqr/internal/scanners/appi"
"github.com/spf13/cobra"
)

func init() {
scanCmd.AddCommand(appiCmd)
}

var appiCmd = &cobra.Command{
Use: "appi",
Short: "Scan Azure Application Insights",
Long: "Scan Azure Application Insights",
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
serviceScanners := []scanners.IAzureScanner{
&appi.AppInsightsScanner{},
}

scan(cmd, serviceScanners)
},
}
2 changes: 2 additions & 0 deletions cmd/azqr/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/cmendible/azqr/internal/scanners/sql"
"github.com/cmendible/azqr/internal/scanners/st"
"github.com/cmendible/azqr/internal/scanners/wps"
"github.com/cmendible/azqr/internal/scanners/appi"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -68,6 +69,7 @@ var rulesCmd = &cobra.Command{
&afw.FirewallScanner{},
&mysql.MySQLScanner{},
&mysql.MySQLFlexibleScanner{},
&appi.AppInsightsScanner{},
}

fmt.Println("# | Id | Category | Subcategory | Name | Severity | More Info")
Expand Down
2 changes: 2 additions & 0 deletions cmd/azqr/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/cmendible/azqr/internal/scanners/aks"
"github.com/cmendible/azqr/internal/scanners/apim"
"github.com/cmendible/azqr/internal/scanners/appcs"
"github.com/cmendible/azqr/internal/scanners/appi"
"github.com/cmendible/azqr/internal/scanners/cae"
"github.com/cmendible/azqr/internal/scanners/ci"
"github.com/cmendible/azqr/internal/scanners/cosmos"
Expand Down Expand Up @@ -88,6 +89,7 @@ var scanCmd = &cobra.Command{
&afw.FirewallScanner{},
&mysql.MySQLScanner{},
&mysql.MySQLFlexibleScanner{},
&appi.AppInsightsScanner{},
}

scan(cmd, serviceScanners)
Expand Down
6 changes: 5 additions & 1 deletion docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Azure Quick Review uses the following rules to identify Azure resources that may
4 | aks-004 | Security | Private Endpoint | AKS Cluster should be private | High | https://learn.microsoft.com/en-us/azure/aks/private-clusters
5 | aks-005 | Reliability | SKU | AKS Production Cluster should use Standard SKU | High | https://learn.microsoft.com/en-us/azure/aks/free-standard-pricing-tiers
6 | aks-006 | Operational Excellence | Naming Convention (CAF) | AKS Name should comply with naming conventions | Low | https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations
7 | aks-007 | Security | Identity and Access Control | AKS should integrate authentication with AAD | Medium | https://learn.microsoft.com/azure/aks/manage-azure-rbac
7 | aks-007 | Security | Identity and Access Control | AKS should integrate authentication with AAD (Managed) | Medium | https://learn.microsoft.com/en-us/azure/aks/managed-azure-ad
8 | aks-008 | Security | Identity and Access Control | AKS should be RBAC enabled. | Medium | https://learn.microsoft.com/azure/aks/manage-azure-rbac
9 | aks-009 | Security | Identity and Access Control | AKS should have local accounts disabled | Medium | https://learn.microsoft.com/azure/aks/managed-aad#disable-local-accounts
10 | aks-010 | Security | Best Practices | AKS should have httpApplicationRouting disabled | Medium | https://learn.microsoft.com/azure/aks/http-application-routing
Expand Down Expand Up @@ -187,3 +187,7 @@ Azure Quick Review uses the following rules to identify Azure resources that may
181 | mysqlf-005 | Reliability | SKU | Azure Database for MySQL - Flexible Server SKU | High | https://learn.microsoft.com/en-us/azure/mysql/flexible-server/concepts-service-tiers-storage
182 | mysqlf-006 | Operational Excellence | Naming Convention (CAF) | Azure Database for MySQL - Flexible Server Name should comply with naming conventions | Low | https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations
183 | mysqlf-007 | Operational Excellence | Tags | Azure Database for MySQL - Flexible Server should have tags | Low | https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json
184 | appi-001 | Reliability | SLA | Azure Application Insights SLA | High | https://www.azure.cn/en-us/support/sla/application-insights/index.html
185 | appi-002 | Operational Excellence | Naming Convention (CAF) | Azure Application Insights Name should comply with naming conventions | Low | https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations
186 | appi-003 | Operational Excellence | Tags | Azure Application Insights should have tags | Low | https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json
187 | appi-004 | Operational Excellence | Tags | Azure Application Insights should store data in a Log Analytics Workspace | Low | https://learn.microsoft.com/en-us/azure/azure-monitor/app/create-workspace-resource
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/apimanagement/armapimanagement v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appconfiguration/armappconfiguration v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.1.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cdn/armcdn v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerinstance/armcontainerinstance v1.0.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appconfiguration/armappcon
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appconfiguration/armappconfiguration v1.0.0/go.mod h1:YW819qVTop21KS8LJLEf3Z47LBaRHIaz/IRLZpKqTIU=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.0.0 h1:zIQzosd251uW2j2+MIbMDeyqkISOFV88XYE7pvkWIZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/armappcontainers v1.0.0/go.mod h1:/OjYJjDeOIdCSJmuQH0BDpegn00BI747f5WJseOm26o=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.1.1 h1:hBrFatNIiVAwDb5GzMLjpkQ6l2/waFSvBWMBWZRH8WI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights v1.1.1/go.mod h1:uxknLoFj+nBXpfGngz0B4ciNur04Y0EX4AREpy2GIvk=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.0.0 h1:UmNl2Ud7IVziQHJvEvKL5b6JMvO8MhmMYwYC1nF63J0=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.0.0/go.mod h1:q3eRORy1pK+iwccG8yKZfRjhdJXTUs5LF2j9hA/MNNs=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/cdn/armcdn v1.0.0 h1:mZYozuxvzO83ZtKST8TYGqvj++a3ehiworh8zuxfxOo=
Expand Down
75 changes: 75 additions & 0 deletions internal/scanners/appi/appi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package appi

import (
"log"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/cmendible/azqr/internal/scanners"
)

// AppInsightsScanner - Scanner for Front Door
type AppInsightsScanner struct {
config *scanners.ScannerConfig
diagnosticsSettings scanners.DiagnosticsSettings
client *armapplicationinsights.ComponentsClient
}

// Init - Initializes the Application Insights Scanner
func (a *AppInsightsScanner) Init(config *scanners.ScannerConfig) error {
a.config = config
var err error
a.client, err = armapplicationinsights.NewComponentsClient(config.SubscriptionID, a.config.Cred, a.config.ClientOptions)
if err != nil {
return err
}
a.diagnosticsSettings = scanners.DiagnosticsSettings{}
err = a.diagnosticsSettings.Init(config)
if err != nil {
return err
}
return nil
}

// Scan - Scans all Application Insights in a Resource Group
func (a *AppInsightsScanner) Scan(resourceGroupName string, scanContext *scanners.ScanContext) ([]scanners.AzureServiceResult, error) {
log.Printf("Scanning Application Insights in Resource Group %s", resourceGroupName)

gateways, err := a.list(resourceGroupName)
if err != nil {
return nil, err
}
engine := scanners.RuleEngine{}
rules := a.GetRules()
results := []scanners.AzureServiceResult{}

for _, g := range gateways {
rr := engine.EvaluateRules(rules, g, scanContext)

results = append(results, scanners.AzureServiceResult{
SubscriptionID: a.config.SubscriptionID,
ResourceGroup: resourceGroupName,
Location: *g.Location,
Type: *g.Type,
ServiceName: *g.Name,
Rules: rr,
})
}
return results, nil
}

func (a *AppInsightsScanner) list(resourceGroupName string) ([]*armapplicationinsights.Component, error) {
pager := a.client.NewListByResourceGroupPager(resourceGroupName, nil)

services := make([]*armapplicationinsights.Component, 0)
for pager.More() {
resp, err := pager.NextPage(a.config.Ctx)
if err != nil {
return nil, err
}
services = append(services, resp.Value...)
}
return services, nil
}
66 changes: 66 additions & 0 deletions internal/scanners/appi/rules.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package appi

import (
"strings"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/cmendible/azqr/internal/scanners"
)

// GetRules - Returns the rules for the FrontDoorScanner
func (a *AppInsightsScanner) GetRules() map[string]scanners.AzureRule {
return map[string]scanners.AzureRule{
"SLA": {
Id: "appi-001",
Category: scanners.RulesCategoryReliability,
Subcategory: scanners.RulesSubcategoryReliabilitySLA,
Description: "Azure Application Insights SLA",
Severity: scanners.SeverityHigh,
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",
},
"CAF": {
Id: "appi-002",
Category: scanners.RulesCategoryOperationalExcellence,
Subcategory: scanners.RulesSubcategoryOperationalExcellenceCAF,
Description: "Azure Application Insights Name should comply with naming conventions",
Severity: scanners.SeverityLow,
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",
},
"appi-003": {
Id: "appi-003",
Category: scanners.RulesCategoryOperationalExcellence,
Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags,
Description: "Azure Application Insights should have tags",
Severity: scanners.SeverityLow,
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) {
c := target.(*armapplicationinsights.Component)
return c.Tags == nil || len(c.Tags) == 0, ""
},
Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json",
},
"appi-004": {
Id: "appi-004",
Category: scanners.RulesCategoryOperationalExcellence,
Subcategory: scanners.RulesSubcategoryOperationalExcellenceTags,
Description: "Azure Application Insights should store data in a Log Analytics Workspace",
Severity: scanners.SeverityLow,
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) {
c := target.(*armapplicationinsights.Component)

return c.Properties.WorkspaceResourceID == nil, ""
},
Url: "https://learn.microsoft.com/en-us/azure/azure-monitor/app/create-workspace-resource",
},
}
}
104 changes: 104 additions & 0 deletions internal/scanners/appi/rules_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

package appi

import (
"reflect"
"testing"

"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/applicationinsights/armapplicationinsights"
"github.com/Azure/go-autorest/autorest/to"
"github.com/cmendible/azqr/internal/scanners"
)

func TestAppInsightsScanner_Rules(t *testing.T) {
type fields struct {
rule string
target interface{}
scanContext *scanners.ScanContext
diagnosticsSettings scanners.DiagnosticsSettings
}
type want struct {
broken bool
result string
}
tests := []struct {
name string
fields fields
want want
}{
{
name: "AppInsightsScanner SLA",
fields: fields{
rule: "SLA",
target: &armapplicationinsights.Component{},
scanContext: &scanners.ScanContext{},
diagnosticsSettings: scanners.DiagnosticsSettings{},
},
want: want{
broken: false,
result: "99.9%",
},
},
{
name: "AppInsightsScanner CAF",
fields: fields{
rule: "CAF",
target: &armapplicationinsights.Component{
Name: to.StringPtr("appi-test"),
},
scanContext: &scanners.ScanContext{},
diagnosticsSettings: scanners.DiagnosticsSettings{},
},
want: want{
broken: false,
result: "",
},
},
{
name: "AppInsightsScanner tags",
fields: fields{
rule: "appi-003",
target: &armapplicationinsights.Component{},
scanContext: &scanners.ScanContext{},
diagnosticsSettings: scanners.DiagnosticsSettings{},
},
want: want{
broken: true,
result: "",
},
},
{
name: "AppInsightsScanner WorkspaceId",
fields: fields{
rule: "appi-004",
target: &armapplicationinsights.Component{
Properties: &armapplicationinsights.ComponentProperties{},
},
scanContext: &scanners.ScanContext{},
diagnosticsSettings: scanners.DiagnosticsSettings{},
},
want: want{
broken: true,
result: "",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &AppInsightsScanner{
diagnosticsSettings: tt.fields.diagnosticsSettings,
}
rules := s.GetRules()
b, w := rules[tt.fields.rule].Eval(tt.fields.target, tt.fields.scanContext)
got := want{
broken: b,
result: w,
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("AppInsightsScanner Rule.Eval() = %v, want %v", got, tt.want)
}
})
}
}

0 comments on commit 20815d1

Please sign in to comment.