-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add azure analysis service scanner (#221)
* feature: add azure analytics service scanner Co-authored-by: Carlos Mendible <266546+cmendible@users.noreply.github.com>
- Loading branch information
1 parent
e0de39d
commit 4b5718a
Showing
9 changed files
with
287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/Azure/azqr/internal/scanners" | ||
"github.com/Azure/azqr/internal/scanners/as" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func init() { | ||
scanCmd.AddCommand(asCmd) | ||
} | ||
|
||
var asCmd = &cobra.Command{ | ||
Use: "as", | ||
Short: "Scan Azure Analysis Service", | ||
Long: "Scan Azure Analysis Service", | ||
Args: cobra.NoArgs, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
serviceScanners := []scanners.IAzureScanner{ | ||
&as.AnalysisServicesScanner{}, | ||
} | ||
|
||
scan(cmd, serviceScanners) | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
package as | ||
|
||
import ( | ||
"github.com/Azure/azqr/internal/scanners" | ||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/analysisservices/armanalysisservices" | ||
) | ||
|
||
// AnalysisServicesScanner - Scanner for Analysis Services | ||
type AnalysisServicesScanner struct { | ||
config *scanners.ScannerConfig | ||
client *armanalysisservices.ServersClient | ||
} | ||
|
||
// Init - Initializes the AnalysisServicesScanner | ||
func (c *AnalysisServicesScanner) Init(config *scanners.ScannerConfig) error { | ||
c.config = config | ||
var err error | ||
c.client, err = armanalysisservices.NewServersClient(config.SubscriptionID, config.Cred, config.ClientOptions) | ||
return err | ||
} | ||
|
||
// Scan - Scans all Analysis Services in a Resource Group | ||
func (c *AnalysisServicesScanner) Scan(resourceGroupName string, scanContext *scanners.ScanContext) ([]scanners.AzureServiceResult, error) { | ||
scanners.LogResourceGroupScan(c.config.SubscriptionID, resourceGroupName, "Analysis Services") | ||
|
||
workspaces, err := c.listWorkspaces(resourceGroupName) | ||
if err != nil { | ||
return nil, err | ||
} | ||
engine := scanners.RuleEngine{} | ||
rules := c.GetRules() | ||
results := []scanners.AzureServiceResult{} | ||
|
||
for _, ws := range workspaces { | ||
rr := engine.EvaluateRules(rules, ws, scanContext) | ||
|
||
results = append(results, scanners.AzureServiceResult{ | ||
SubscriptionID: c.config.SubscriptionID, | ||
ResourceGroup: resourceGroupName, | ||
ServiceName: *ws.Name, | ||
Type: *ws.Type, | ||
Location: *ws.Location, | ||
Rules: rr, | ||
}) | ||
} | ||
return results, nil | ||
} | ||
|
||
func (c *AnalysisServicesScanner) listWorkspaces(resourceGroupName string) ([]*armanalysisservices.Server, error) { | ||
pager := c.client.NewListByResourceGroupPager(resourceGroupName, nil) | ||
|
||
registries := make([]*armanalysisservices.Server, 0) | ||
for pager.More() { | ||
resp, err := pager.NextPage(c.config.Ctx) | ||
if err != nil { | ||
return nil, err | ||
} | ||
registries = append(registries, resp.Value...) | ||
} | ||
return registries, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
package as | ||
|
||
import ( | ||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/analysisservices/armanalysisservices" | ||
"strings" | ||
|
||
"github.com/Azure/azqr/internal/scanners" | ||
) | ||
|
||
// GetRules - Returns the rules for the AnalysisServicesScanner | ||
func (a *AnalysisServicesScanner) GetRules() map[string]scanners.AzureRule { | ||
return map[string]scanners.AzureRule{ | ||
"as-001": { | ||
Id: "as-001", | ||
Category: scanners.RulesCategoryMonitoringAndAlerting, | ||
Recommendation: "Azure Analysis Service should have diagnostic settings enabled", | ||
Impact: scanners.ImpactLow, | ||
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { | ||
service := target.(*armanalysisservices.Server) | ||
_, ok := scanContext.DiagnosticsSettings[strings.ToLower(*service.ID)] | ||
return !ok, "" | ||
}, | ||
Url: "https://learn.microsoft.com/en-us/azure/analysis-services/analysis-services-logging", | ||
}, | ||
"as-002": { | ||
Id: "as-002", | ||
Category: scanners.RulesCategoryHighAvailability, | ||
Recommendation: "Azure Analysis Service should have a SLA", | ||
Impact: scanners.ImpactHigh, | ||
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { | ||
i := target.(*armanalysisservices.Server) | ||
sku := *i.SKU.Tier | ||
sla := "None" | ||
if sku != armanalysisservices.SKUTierBasic { | ||
sla = "99.9%" | ||
} | ||
return sla == "None", sla | ||
}, | ||
Url: "https://www.microsoft.com/licensing/docs/view/Service-Level-Agreements-SLA-for-Online-Services", | ||
}, | ||
"as-003": { | ||
Id: "as-003", | ||
Category: scanners.RulesCategoryHighAvailability, | ||
Recommendation: "Azure Analysis Service SKU", | ||
Impact: scanners.ImpactHigh, | ||
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { | ||
i := target.(*armanalysisservices.Server) | ||
return false, string(*i.SKU.Name) | ||
}, | ||
Url: "https://azure.microsoft.com/en-us/pricing/details/analysis-services/", | ||
}, | ||
"as-004": { | ||
Id: "as-004", | ||
Category: scanners.RulesCategoryGovernance, | ||
Recommendation: "Azure Analysis Service Name should comply with naming conventions", | ||
Impact: scanners.ImpactLow, | ||
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { | ||
c := target.(*armanalysisservices.Server) | ||
caf := strings.HasPrefix(*c.Name, "as") | ||
return !caf, "" | ||
}, | ||
Url: "https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/resource-abbreviations", | ||
}, | ||
"as-005": { | ||
Id: "as-005", | ||
Category: scanners.RulesCategoryGovernance, | ||
Recommendation: "Azure Analysis Service should have tags", | ||
Impact: scanners.ImpactLow, | ||
Eval: func(target interface{}, scanContext *scanners.ScanContext) (bool, string) { | ||
c := target.(*armanalysisservices.Server) | ||
return len(c.Tags) == 0, "" | ||
}, | ||
Url: "https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resources?tabs=json", | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
package as | ||
|
||
import ( | ||
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/analysisservices/armanalysisservices" | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/Azure/azqr/internal/scanners" | ||
"github.com/Azure/azqr/internal/to" | ||
) | ||
|
||
func TestAnalysisServicesScanner_Rules(t *testing.T) { | ||
type fields struct { | ||
rule string | ||
target interface{} | ||
scanContext *scanners.ScanContext | ||
} | ||
type want struct { | ||
broken bool | ||
result string | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
want want | ||
}{ | ||
{ | ||
name: "AnalysisServicesScanner DiagnosticSettings", | ||
fields: fields{ | ||
rule: "as-001", | ||
target: &armanalysisservices.Server{ | ||
ID: to.Ptr("test"), | ||
}, | ||
scanContext: &scanners.ScanContext{ | ||
DiagnosticsSettings: map[string]bool{ | ||
"test": true, | ||
}, | ||
}, | ||
}, | ||
want: want{ | ||
broken: false, | ||
result: "", | ||
}, | ||
}, | ||
{ | ||
name: "AnalysisServicesScanner SLA", | ||
fields: fields{ | ||
rule: "as-002", | ||
target: &armanalysisservices.Server{ | ||
SKU: &armanalysisservices.ResourceSKU{ | ||
Tier: to.Ptr(armanalysisservices.SKUTierBasic), | ||
}, | ||
}, | ||
scanContext: &scanners.ScanContext{}, | ||
}, | ||
want: want{ | ||
broken: true, | ||
result: "None", | ||
}, | ||
}, | ||
{ | ||
name: "AnalysisServicesScanner SLA", | ||
fields: fields{ | ||
rule: "as-002", | ||
target: &armanalysisservices.Server{ | ||
SKU: &armanalysisservices.ResourceSKU{ | ||
Tier: to.Ptr(armanalysisservices.SKUTierStandard), | ||
}, | ||
}, | ||
scanContext: &scanners.ScanContext{}, | ||
}, | ||
want: want{ | ||
broken: false, | ||
result: "99.9%", | ||
}, | ||
}, | ||
{ | ||
name: "AnalysisServicesScanner CAF", | ||
fields: fields{ | ||
rule: "as-004", | ||
target: &armanalysisservices.Server{ | ||
Name: to.Ptr("as-test"), | ||
}, | ||
scanContext: &scanners.ScanContext{}, | ||
}, | ||
want: want{ | ||
broken: false, | ||
result: "", | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
s := &AnalysisServicesScanner{} | ||
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("AnalysisServicesScanner Rule.Eval() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |