package pkg import ( "fmt" "time" "github.com/jmespath/go-jmespath" "github.com/sirupsen/logrus" "github.com/snyk/driftctl/pkg/memstore" globaloutput "github.com/snyk/driftctl/pkg/output" "github.com/snyk/driftctl/pkg/alerter" "github.com/snyk/driftctl/pkg/analyser" "github.com/snyk/driftctl/pkg/cmd/scan/output" "github.com/snyk/driftctl/pkg/filter" "github.com/snyk/driftctl/pkg/iac/config" "github.com/snyk/driftctl/pkg/iac/terraform/state/backend" "github.com/snyk/driftctl/pkg/middlewares" "github.com/snyk/driftctl/pkg/resource" ) type FmtOptions struct { Output output.OutputConfig } type ScanOptions struct { Coverage bool Detect bool From []config.SupplierConfig To string Output []output.OutputConfig Filter *jmespath.JMESPath Quiet bool BackendOptions *backend.Options StrictMode bool DisableTelemetry bool ProviderVersion string ConfigDir string DriftignorePath string Driftignores []string Deep bool OnlyManaged bool OnlyUnmanaged bool } type DriftCTL struct { remoteSupplier resource.Supplier iacSupplier resource.IaCSupplier alerter alerter.AlerterInterface analyzer *analyser.Analyzer resourceFactory resource.ResourceFactory scanProgress globaloutput.Progress iacProgress globaloutput.Progress resourceSchemaRepository resource.SchemaRepositoryInterface opts *ScanOptions store memstore.Store } func NewDriftCTL(remoteSupplier resource.Supplier, iacSupplier resource.IaCSupplier, alerter *alerter.Alerter, analyzer *analyser.Analyzer, resFactory resource.ResourceFactory, opts *ScanOptions, scanProgress globaloutput.Progress, iacProgress globaloutput.Progress, resourceSchemaRepository resource.SchemaRepositoryInterface, store memstore.Store) *DriftCTL { return &DriftCTL{ remoteSupplier, iacSupplier, alerter, analyzer, resFactory, scanProgress, iacProgress, resourceSchemaRepository, opts, store, } } func (d DriftCTL) Run() (*analyser.Analysis, error) { start := time.Now() remoteResources, resourcesFromState, err := d.scan() if err != nil { return nil, err } middleware := middlewares.NewChain( middlewares.NewRoute53RecordIDReconcilier(), middlewares.NewRoute53DefaultZoneRecordSanitizer(), middlewares.NewS3BucketAcl(), middlewares.NewAwsInstanceBlockDeviceResourceMapper(d.resourceFactory), middlewares.NewAwsDefaultSecurityGroupRule(), middlewares.NewVPCDefaultSecurityGroupSanitizer(), middlewares.NewVPCSecurityGroupRuleSanitizer(d.resourceFactory), middlewares.NewIamPolicyAttachmentTransformer(d.resourceFactory), middlewares.NewIamPolicyAttachmentExpander(d.resourceFactory), middlewares.AwsInstanceEIP{}, middlewares.NewAwsDefaultInternetGatewayRoute(), middlewares.NewAwsDefaultInternetGateway(), middlewares.NewAwsDefaultVPC(), middlewares.NewAwsDefaultSubnet(), middlewares.NewAwsRouteTableExpander(d.alerter, d.resourceFactory), middlewares.NewAwsDefaultRouteTable(), middlewares.NewAwsDefaultRoute(), middlewares.NewAwsDefaultNetworkACL(), middlewares.NewAwsDefaultNetworkACLRule(), middlewares.NewAwsNetworkACLExpander(d.resourceFactory), middlewares.NewAwsNatGatewayEipAssoc(), middlewares.NewAwsBucketPolicyExpander(d.resourceFactory), middlewares.NewAwsSQSQueuePolicyExpander(d.resourceFactory, d.resourceSchemaRepository), middlewares.NewAwsDefaultSQSQueuePolicy(), middlewares.NewAwsSNSTopicPolicyExpander(d.resourceFactory, d.resourceSchemaRepository), middlewares.NewAwsRoleManagedPolicyExpander(d.resourceFactory), middlewares.NewTagsAllManager(), middlewares.NewEipAssociationExpander(d.resourceFactory), middlewares.NewRDSClusterInstanceExpander(d.resourceFactory), middlewares.NewAwsApiGatewayDeploymentExpander(d.resourceFactory), middlewares.NewAwsApiGatewayResourceExpander(d.resourceFactory), middlewares.NewAwsApiGatewayApiExpander(d.resourceFactory), middlewares.NewAwsApiGatewayRestApiPolicyExpander(d.resourceFactory), middlewares.NewAwsConsoleApiGatewayGatewayResponse(), middlewares.NewAwsApiGatewayDomainNamesReconciler(), middlewares.NewGoogleIAMBindingTransformer(d.resourceFactory), middlewares.NewGoogleIAMPolicyTransformer(d.resourceFactory), middlewares.NewGoogleComputeInstanceGroupManagerReconciler(), middlewares.NewAzurermRouteExpander(d.resourceFactory), middlewares.NewAzurermSubnetExpander(d.resourceFactory), ) if !d.opts.StrictMode { middleware = append(middleware, middlewares.NewAwsDefaults(), middlewares.NewGoogleLegacyBucketIAMMember(), middlewares.NewGoogleDefaultIAMMember(), middlewares.NewAwsDefaultApiGatewayAccount(), ) } logrus.Debug("Ready to run middlewares") err = middleware.Execute(&remoteResources, &resourcesFromState) if err != nil { return nil, err } if d.opts.Filter != nil { engine := filter.NewFilterEngine(d.opts.Filter) remoteResources, err = engine.Run(remoteResources) if err != nil { return nil, err } resourcesFromState, err = engine.Run(resourcesFromState) if err != nil { return nil, err } } analysis, err := d.analyzer.Analyze(remoteResources, resourcesFromState) if err != nil { return nil, err } analysis.SetIaCSourceCount(d.iacSupplier.SourceCount()) analysis.Duration = time.Since(start) analysis.Date = time.Now() d.store.Bucket(memstore.TelemetryBucket).Set("total_resources", analysis.Summary().TotalResources) d.store.Bucket(memstore.TelemetryBucket).Set("total_managed", analysis.Summary().TotalManaged) d.store.Bucket(memstore.TelemetryBucket).Set("duration", uint(analysis.Duration.Seconds()+0.5)) d.store.Bucket(memstore.TelemetryBucket).Set("iac_source_count", d.iacSupplier.SourceCount()) return &analysis, nil } func (d DriftCTL) Stop() { stoppableSupplier, ok := d.remoteSupplier.(resource.StoppableSupplier) if ok { logrus.WithFields(logrus.Fields{ "supplier": fmt.Sprintf("%T", d.remoteSupplier), }).Debug("Stopping remote supplier") stoppableSupplier.Stop() } stoppableSupplier, ok = d.iacSupplier.(resource.StoppableSupplier) if ok { stoppableSupplier.Stop() } } func (d DriftCTL) scan() (remoteResources []*resource.Resource, resourcesFromState []*resource.Resource, err error) { logrus.Info("Start reading IaC") d.iacProgress.Start() resourcesFromState, err = d.iacSupplier.Resources() d.iacProgress.Stop() if err != nil { return nil, nil, err } logrus.Info("Start scanning cloud provider") d.scanProgress.Start() defer d.scanProgress.Stop() remoteResources, err = d.remoteSupplier.Resources() if err != nil { return nil, nil, err } return remoteResources, resourcesFromState, err }