Skip to content

Commit

Permalink
feat: add suport for aws_cloudtrail resources
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Guibert committed Oct 24, 2022
1 parent a28bac9 commit c65846a
Show file tree
Hide file tree
Showing 14 changed files with 1,866 additions and 0 deletions.
46 changes: 46 additions & 0 deletions enumeration/remote/aws/cloudtrail_enumerator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package aws

import (
"github.com/snyk/driftctl/enumeration/remote/aws/repository"
remoteerror "github.com/snyk/driftctl/enumeration/remote/error"
"github.com/snyk/driftctl/enumeration/resource"
"github.com/snyk/driftctl/enumeration/resource/aws"
)

type CloudtrailEnumerator struct {
repository repository.CloudtrailRepository
factory resource.ResourceFactory
}

func NewCloudtrailEnumerator(repo repository.CloudtrailRepository, factory resource.ResourceFactory) *CloudtrailEnumerator {
return &CloudtrailEnumerator{
repository: repo,
factory: factory,
}
}

func (e *CloudtrailEnumerator) SupportedType() resource.ResourceType {
return aws.AwsCloudtrailResourceType
}

func (e *CloudtrailEnumerator) Enumerate() ([]*resource.Resource, error) {
trails, err := e.repository.ListAllTrails()
if err != nil {
return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType()))
}

results := make([]*resource.Resource, 0, len(trails))

for _, trail := range trails {
results = append(
results,
e.factory.CreateAbstractResource(
string(e.SupportedType()),
*trail.Name,
map[string]interface{}{},
),
)
}

return results, err
}
4 changes: 4 additions & 0 deletions enumeration/remote/aws/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func Init(version string, alerter alerter.AlerterInterface, providerLibrary *ter
kmsRepository := repository.NewKMSRepository(provider.session, repositoryCache)
iamRepository := repository.NewIAMRepository(provider.session, repositoryCache)
cloudformationRepository := repository.NewCloudformationRepository(provider.session, repositoryCache)
cloudtrailRepository := repository.NewCloudtrailRepository(provider.session, repositoryCache)
apigatewayRepository := repository.NewApiGatewayRepository(provider.session, repositoryCache)
appAutoScalingRepository := repository.NewAppAutoScalingRepository(provider.session, repositoryCache)
apigatewayv2Repository := repository.NewApiGatewayV2Repository(provider.session, repositoryCache)
Expand Down Expand Up @@ -195,6 +196,9 @@ func Init(version string, alerter alerter.AlerterInterface, providerLibrary *ter
remoteLibrary.AddEnumerator(NewCloudformationStackEnumerator(cloudformationRepository, factory))
remoteLibrary.AddDetailsFetcher(aws.AwsCloudformationStackResourceType, common.NewGenericDetailsFetcher(aws.AwsCloudformationStackResourceType, provider, deserializer))

remoteLibrary.AddEnumerator(NewCloudtrailEnumerator(cloudtrailRepository, factory))
remoteLibrary.AddDetailsFetcher(aws.AwsCloudtrailResourceType, common.NewGenericDetailsFetcher(aws.AwsCloudtrailResourceType, provider, deserializer))

remoteLibrary.AddEnumerator(NewApiGatewayRestApiEnumerator(apigatewayRepository, factory))
remoteLibrary.AddEnumerator(NewApiGatewayAccountEnumerator(apigatewayRepository, factory))
remoteLibrary.AddEnumerator(NewApiGatewayApiKeyEnumerator(apigatewayRepository, factory))
Expand Down
48 changes: 48 additions & 0 deletions enumeration/remote/aws/repository/cloudtrail_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package repository

import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudtrail"
"github.com/aws/aws-sdk-go/service/cloudtrail/cloudtrailiface"
"github.com/snyk/driftctl/enumeration/remote/cache"
)

type CloudtrailRepository interface {
ListAllTrails() ([]*cloudtrail.TrailInfo, error)
}

type cloudtrailRepository struct {
client cloudtrailiface.CloudTrailAPI
cache cache.Cache
}

func NewCloudtrailRepository(session *session.Session, c cache.Cache) *cloudtrailRepository {
return &cloudtrailRepository{
cloudtrail.New(session),
c,
}
}

func (r *cloudtrailRepository) ListAllTrails() ([]*cloudtrail.TrailInfo, error) {
cacheKey := "ListAllTrails"
if v := r.cache.Get(cacheKey); v != nil {
return v.([]*cloudtrail.TrailInfo), nil
}

var trails []*cloudtrail.TrailInfo
input := cloudtrail.ListTrailsInput{}
err := r.client.ListTrailsPages(&input,
func(resp *cloudtrail.ListTrailsOutput, lastPage bool) bool {
if resp.Trails != nil {
trails = append(trails, resp.Trails...)
}
return !lastPage
},
)
if err != nil {
return nil, err
}

r.cache.Put(cacheKey, trails)
return trails, nil
}
89 changes: 89 additions & 0 deletions enumeration/remote/aws/repository/cloudtrail_repository_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package repository

import (
"strings"
"testing"

"github.com/snyk/driftctl/enumeration/remote/cache"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudtrail"
awstest "github.com/snyk/driftctl/test/aws"

"github.com/stretchr/testify/mock"

"github.com/r3labs/diff/v2"
"github.com/stretchr/testify/assert"
)

func Test_cloudtrailRepository_ListAllTrails(t *testing.T) {
tests := []struct {
name string
mocks func(client *awstest.MockFakeCloudtrail)
want []*cloudtrail.TrailInfo
wantErr error
}{
{
name: "list multiple trail",
mocks: func(client *awstest.MockFakeCloudtrail) {
client.On("ListTrailsPages",
&cloudtrail.ListTrailsInput{},
mock.MatchedBy(func(callback func(res *cloudtrail.ListTrailsOutput, lastPage bool) bool) bool {
callback(&cloudtrail.ListTrailsOutput{
Trails: []*cloudtrail.TrailInfo{
{Name: aws.String("trail1")},
{Name: aws.String("trail2")},
{Name: aws.String("trail3")},
},
}, false)
callback(&cloudtrail.ListTrailsOutput{
Trails: []*cloudtrail.TrailInfo{
{Name: aws.String("trail4")},
{Name: aws.String("trail5")},
{Name: aws.String("trail6")},
},
}, true)
return true
})).Return(nil).Once()
},
want: []*cloudtrail.TrailInfo{
{Name: aws.String("trail1")},
{Name: aws.String("trail2")},
{Name: aws.String("trail3")},
{Name: aws.String("trail4")},
{Name: aws.String("trail5")},
{Name: aws.String("trail6")},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
store := cache.New(1)
client := awstest.MockFakeCloudtrail{}
tt.mocks(&client)
r := &cloudtrailRepository{
client: &client,
cache: store,
}
got, err := r.ListAllTrails()
assert.Equal(t, tt.wantErr, err)

if err == nil {
// Check that results were cached
cachedData, err := r.ListAllTrails()
assert.NoError(t, err)
assert.Equal(t, got, cachedData)
assert.IsType(t, []*cloudtrail.TrailInfo{}, store.Get("ListAllTrails"))
}

changelog, err := diff.Diff(got, tt.want)
assert.Nil(t, err)
if len(changelog) > 0 {
for _, change := range changelog {
t.Errorf("%s: %s -> %s", strings.Join(change.Path, "."), change.From, change.To)
}
t.Fail()
}
})
}
}
3 changes: 3 additions & 0 deletions enumeration/resource/aws/aws_cloudtrail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package aws

const AwsCloudtrailResourceType = "aws_cloudtrail"
1 change: 1 addition & 0 deletions enumeration/resource/resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ var supportedTypes = map[string]ResourceTypeMeta{
"aws_launch_configuration": {},
"aws_elb": {},
"aws_elasticache_cluster": {},
"aws_cloudtrail": {},

"github_branch_protection": {},
"github_membership": {},
Expand Down
1 change: 1 addition & 0 deletions pkg/iac/terraform/state/terraform_state_reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ func TestTerraformStateReader_AWS_Resources(t *testing.T) {
{name: "ElastiCache Cluster", dirName: "aws_elasticache_cluster", wantErr: false},
{name: "IAM Group", dirName: "aws_iam_group", wantErr: false},
{name: "ECR Repository Policy", dirName: "aws_ecr_repository_policy", wantErr: false},
{name: "cloudtrail", dirName: "aws_cloudtrail", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
23 changes: 23 additions & 0 deletions pkg/iac/terraform/state/test/aws_cloudtrail/results.golden.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"Id": "testtrailtrail",
"Type": "aws_cloudtrail",
"Attrs": {
"arn": "arn:aws:cloudtrail:eu-west-2:526954929923:trail/testtrailtrail",
"cloud_watch_logs_group_arn": "",
"cloud_watch_logs_role_arn": "",
"enable_log_file_validation": false,
"enable_logging": true,
"home_region": "eu-west-2",
"id": "testtrailtrail",
"include_global_service_events": true,
"is_multi_region_trail": false,
"is_organization_trail": false,
"kms_key_id": "",
"name": "testtrailtrail",
"s3_bucket_name": "testtrailbuckettestestest",
"s3_key_prefix": "",
"sns_topic_name": ""
}
}
]
47 changes: 47 additions & 0 deletions pkg/iac/terraform/state/test/aws_cloudtrail/terraform.tfstate
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": 4,
"terraform_version": "0.14.2",
"serial": 72,
"lineage": "0a405b90-f526-2004-0d4b-f5fd84ca6664",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_cloudtrail",
"name": "testtrailtrail",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"advanced_event_selector": [],
"arn": "arn:aws:cloudtrail:eu-west-2:526954929923:trail/testtrailtrail",
"cloud_watch_logs_group_arn": "",
"cloud_watch_logs_role_arn": "",
"enable_log_file_validation": false,
"enable_logging": true,
"event_selector": [],
"home_region": "eu-west-2",
"id": "testtrailtrail",
"include_global_service_events": true,
"insight_selector": [],
"is_multi_region_trail": false,
"is_organization_trail": false,
"kms_key_id": "",
"name": "testtrailtrail",
"s3_bucket_name": "testtrailbuckettestestest",
"s3_key_prefix": "",
"sns_topic_name": "",
"tags": null,
"tags_all": {}
},
"sensitive_attributes": [],
"private": "bnVsbA==",
"dependencies": [
"aws_s3_bucket.testtrailbuckettestestest"
]
}
]
}
]
}
16 changes: 16 additions & 0 deletions pkg/resource/aws/aws_cloudtrail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package aws

import (
"github.com/snyk/driftctl/enumeration/resource"
dctlresource "github.com/snyk/driftctl/pkg/resource"
)

const AwsCloudtrailResourceType = "aws_cloudtrail"

func initAwsCloudtrailMetaData(resourceSchemaRepository dctlresource.SchemaRepositoryInterface) {
/*resourceSchemaRepository.SetNormalizeFunc(AwsCloudfrontDistributionResourceType, func(res *resource.Resource) {
val := res.Attrs
})*/
resourceSchemaRepository.SetFlags(AwsCloudfrontDistributionResourceType, resource.FlagDeepMode)

}
1 change: 1 addition & 0 deletions pkg/resource/aws/metadatas.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInt
initAwsRDSClusterMetaData(resourceSchemaRepository)
initAwsCloudformationStackMetaData(resourceSchemaRepository)
initAwsAppAutoscalingTargetMetaData(resourceSchemaRepository)
initAwsCloudtrailMetaData(resourceSchemaRepository)
}
1 change: 1 addition & 0 deletions pkg/resource/resource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ var supportedTypes = map[string]ResourceTypeMeta{
"aws_launch_configuration": {},
"aws_elb": {},
"aws_elasticache_cluster": {},
"aws_cloudtrail": {},

"github_branch_protection": {},
"github_membership": {},
Expand Down
9 changes: 9 additions & 0 deletions test/aws/cloudtrail.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package aws

import (
"github.com/aws/aws-sdk-go/service/cloudtrail/cloudtrailiface"
)

type FakeCloudtrail interface {
cloudtrailiface.CloudTrailAPI
}
Loading

0 comments on commit c65846a

Please sign in to comment.