feat: add suport for aws_cloudtrail resources

main
Martin Guibert 2022-10-20 16:29:31 +02:00
parent a28bac977c
commit c65846a263
No known key found for this signature in database
GPG Key ID: 990E40316943BAA6
14 changed files with 1866 additions and 0 deletions

View File

@ -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
}

View File

@ -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)
@ -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))

View File

@ -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
}

View File

@ -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()
}
})
}
}

View File

@ -0,0 +1,3 @@
package aws
const AwsCloudtrailResourceType = "aws_cloudtrail"

View File

@ -175,6 +175,7 @@ var supportedTypes = map[string]ResourceTypeMeta{
"aws_launch_configuration": {},
"aws_elb": {},
"aws_elasticache_cluster": {},
"aws_cloudtrail": {},
"github_branch_protection": {},
"github_membership": {},

View File

@ -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) {

View File

@ -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": ""
}
}
]

View File

@ -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"
]
}
]
}
]
}

View File

@ -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)
}

View File

@ -69,4 +69,5 @@ func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInt
initAwsRDSClusterMetaData(resourceSchemaRepository)
initAwsCloudformationStackMetaData(resourceSchemaRepository)
initAwsAppAutoscalingTargetMetaData(resourceSchemaRepository)
initAwsCloudtrailMetaData(resourceSchemaRepository)
}

View File

@ -177,6 +177,7 @@ var supportedTypes = map[string]ResourceTypeMeta{
"aws_launch_configuration": {},
"aws_elb": {},
"aws_elasticache_cluster": {},
"aws_cloudtrail": {},
"github_branch_protection": {},
"github_membership": {},

9
test/aws/cloudtrail.go Normal file
View File

@ -0,0 +1,9 @@
package aws
import (
"github.com/aws/aws-sdk-go/service/cloudtrail/cloudtrailiface"
)
type FakeCloudtrail interface {
cloudtrailiface.CloudTrailAPI
}

File diff suppressed because it is too large Load Diff