-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Azure fetchers and asset provider #1310
Changes from 9 commits
bc33202
bed328c
229dc83
643aefa
c20fa48
46e80d3
b13c32f
4044768
4cdd79c
8f08bc3
53cb17c
52eeb17
5a58aa8
a9b164c
c05df25
79f6482
c3d188a
d1c4cee
3731861
5b216f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,139 @@ | ||||
// Licensed to Elasticsearch B.V. under one or more contributor | ||||
// license agreements. See the NOTICE file distributed with | ||||
// this work for additional information regarding copyright | ||||
// ownership. Elasticsearch B.V. licenses this file to you under | ||||
// the Apache License, Version 2.0 (the "License"); you may | ||||
// not use this file except in compliance with the License. | ||||
// You may obtain a copy of the License at | ||||
// | ||||
// http://www.apache.org/licenses/LICENSE-2.0 | ||||
// | ||||
// Unless required by applicable law or agreed to in writing, | ||||
// software distributed under the License is distributed on an | ||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||||
// KIND, either express or implied. See the License for the | ||||
// specific language governing permissions and limitations | ||||
// under the License. | ||||
|
||||
package fetchers | ||||
|
||||
import ( | ||||
"context" | ||||
|
||||
"github.com/elastic/elastic-agent-libs/logp" | ||||
"golang.org/x/exp/maps" | ||||
|
||||
"github.com/elastic/cloudbeat/resources/fetching" | ||||
"github.com/elastic/cloudbeat/resources/providers/azurelib/inventory" | ||||
) | ||||
|
||||
type AzureAssetsFetcher struct { | ||||
log *logp.Logger | ||||
resourceCh chan fetching.ResourceInfo | ||||
provider inventory.ServiceAPI | ||||
} | ||||
|
||||
type AzureAsset struct { | ||||
Type string | ||||
SubType string | ||||
Asset AzureAssetInfo `json:"asset,omitempty"` | ||||
} | ||||
|
||||
// TODO: Fill this struct with the required fields | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this done?
Suggested change
|
||||
type AzureAssetInfo struct { | ||||
Id string `json:"id,omitempty"` | ||||
Name string `json:"name,omitempty"` | ||||
Location string `json:"location,omitempty"` | ||||
Properties map[string]any `json:"properties,omitempty"` | ||||
ResourceGroup string `json:"resource_group,omitempty"` | ||||
SubscriptionId string `json:"subscription_id,omitempty"` | ||||
TenantId string `json:"tenant_id,omitempty"` | ||||
} | ||||
|
||||
// TODO: Implement other types | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Can we track this in a new issue? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is basically the other piece of work regarding the comment above: |
||||
var AzureAssetTypes = map[string]string{ | ||||
"microsoft.compute/virtualmachines": fetching.AzureVMType, | ||||
"microsoft.storage/storageaccounts": fetching.AzureStorageAccountType, | ||||
} | ||||
|
||||
func NewAzureAssetsFetcher(log *logp.Logger, ch chan fetching.ResourceInfo, provider inventory.ServiceAPI) *AzureAssetsFetcher { | ||||
return &AzureAssetsFetcher{ | ||||
log: log, | ||||
resourceCh: ch, | ||||
provider: provider, | ||||
} | ||||
} | ||||
|
||||
func (f *AzureAssetsFetcher) Fetch(ctx context.Context, cMetadata fetching.CycleMetadata) error { | ||||
f.log.Info("Starting AzureAssetsFetcher.Fetch") | ||||
// TODO: Maybe we should use a query per type instead of listing all assets in a single query | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Is there any benefit to that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume that it might help us If we will need to collect multiple resource types to evaluate a single rule. |
||||
assets, err := f.provider.ListAllAssetTypesByName(maps.Keys(AzureAssetTypes)) | ||||
if err != nil { | ||||
return err | ||||
} | ||||
|
||||
for _, asset := range assets { | ||||
select { | ||||
case <-ctx.Done(): | ||||
f.log.Infof("AzureAssetsFetcher.Fetch context err: %s", ctx.Err().Error()) | ||||
return nil | ||||
case f.resourceCh <- fetching.ResourceInfo{ | ||||
CycleMetadata: cMetadata, | ||||
// TODO: Safe guard this conversion | ||||
Resource: getAssetFromData(asset.(map[string]any)), | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't |
||||
}: | ||||
} | ||||
} | ||||
|
||||
return nil | ||||
} | ||||
|
||||
func getAssetFromData(data map[string]any) *AzureAsset { | ||||
assetType := getString(data, "type") | ||||
properties, _ := data["properties"].(map[string]any) | ||||
|
||||
return &AzureAsset{ | ||||
Type: AzureAssetTypes[assetType], | ||||
SubType: getAzureSubType(assetType), | ||||
Asset: AzureAssetInfo{ | ||||
Id: getString(data, "id"), | ||||
Name: getString(data, "name"), | ||||
Location: getString(data, "location"), | ||||
Properties: properties, | ||||
ResourceGroup: getString(data, "resourceGroup"), | ||||
SubscriptionId: getString(data, "subscriptionId"), | ||||
TenantId: getString(data, "tenantId"), | ||||
}, | ||||
} | ||||
} | ||||
|
||||
func getString(data map[string]any, key string) string { | ||||
value, _ := data[key].(string) | ||||
return value | ||||
} | ||||
|
||||
// TODO: Handle this function | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's resolve the TODOs, is this relevant? |
||||
func (f *AzureAssetsFetcher) Stop() { | ||||
// f.provider.Close() | ||||
} | ||||
|
||||
func (r *AzureAsset) GetData() any { | ||||
return r.Asset | ||||
} | ||||
|
||||
func (r *AzureAsset) GetMetadata() (fetching.ResourceMetadata, error) { | ||||
return fetching.ResourceMetadata{ | ||||
ID: r.Asset.Id, | ||||
Type: r.Type, | ||||
SubType: r.SubType, | ||||
Name: r.Asset.Name, | ||||
Region: r.Asset.Location, | ||||
}, nil | ||||
} | ||||
|
||||
func (r *AzureAsset) GetElasticCommonData() (map[string]any, error) { return nil, nil } | ||||
|
||||
// TODO: Implement this function | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the significance of this? Is it a blocker? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is not a blocker because we did not decide on the subTypes yet. |
||||
func getAzureSubType(assetType string) string { | ||||
return "" | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. licenses this file to you under | ||
// the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package fetchers | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_getString(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
data map[string]any | ||
key string | ||
want string | ||
}{ | ||
{ | ||
name: "nil map", | ||
data: nil, | ||
key: "key", | ||
want: "", | ||
}, | ||
{ | ||
name: "key does not exist", | ||
data: map[string]any{"key": "value"}, | ||
key: "other-key", | ||
want: "", | ||
}, | ||
{ | ||
name: "wrong type", | ||
data: map[string]any{"key": 1}, | ||
key: "key", | ||
want: "", | ||
}, | ||
{ | ||
name: "correct value", | ||
data: map[string]any{"key": "value", "other-key": 1}, | ||
key: "key", | ||
want: "value", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
assert.Equalf(t, tt.want, getString(tt.data, tt.key), "getString(%v, %s) = %s", tt.data, tt.key, tt.want) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Licensed to Elasticsearch B.V. under one or more contributor | ||
// license agreements. See the NOTICE file distributed with | ||
// this work for additional information regarding copyright | ||
// ownership. Elasticsearch B.V. licenses this file to you under | ||
// the Apache License, Version 2.0 (the "License"); you may | ||
// not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
package preset | ||
|
||
import ( | ||
"github.com/elastic/elastic-agent-libs/logp" | ||
|
||
"github.com/elastic/cloudbeat/resources/fetching" | ||
fetchers "github.com/elastic/cloudbeat/resources/fetching/fetchers/azure" | ||
"github.com/elastic/cloudbeat/resources/fetching/registry" | ||
"github.com/elastic/cloudbeat/resources/providers/azurelib/inventory" | ||
) | ||
|
||
func NewCisAzureFactory(log *logp.Logger, ch chan fetching.ResourceInfo, inventory inventory.ServiceAPI) (registry.FetchersMap, error) { | ||
log.Infof("Initializing Azure fetchers") | ||
m := make(registry.FetchersMap) | ||
|
||
assetsFetcher := fetchers.NewAzureAssetsFetcher(log, ch, inventory) | ||
m["azure_cloud_assets_fetcher"] = registry.RegisteredFetcher{Fetcher: assetsFetcher} | ||
|
||
return m, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add these one by one as we implement the rules. We can merge this PR without them.