Add aws_internet_gateway resource

main
William Beuil 2021-01-20 22:54:41 +01:00
parent a6559dc250
commit 734adf5cea
No known key found for this signature in database
GPG Key ID: BED2072C5C2BF537
24 changed files with 515973 additions and 84 deletions

View File

@ -46,6 +46,7 @@ As AWS documentation recommends, the below policy is granting only the permissio
"ec2:DescribeInstanceAttribute",
"ec2:DescribeInstances",
"ec2:DescribeInstanceCreditSpecifications",
"ec2:DescribeInternetGateways",
"ec2:DescribeKeyPairs",
"ec2:DescribeNetworkAcls",
"ec2:DescribeRouteTables",
@ -123,7 +124,6 @@ As AWS documentation recommends, the below policy is granting only the permissio
- [ ] aws_s3_bucket_object
- [ ] aws_s3_bucket_public_access_block
## EC2
- [x] aws_instance
@ -214,3 +214,4 @@ As AWS documentation recommends, the below policy is granting only the permissio
- [x] aws_route
- [x] aws_route_table_association
- [x] aws_nat_gateway
- [x] aws_internet_gateway

View File

@ -38,6 +38,7 @@ func (d DriftCTL) Run() *analyser.Analysis {
middlewares.NewVPCSecurityGroupRuleSanitizer(),
middlewares.NewIamPolicyAttachmentSanitizer(),
middlewares.AwsInstanceEIP{},
middlewares.NewAwsDefaultInternetGateway(),
middlewares.NewAwsDefaultVPC(),
middlewares.NewAwsDefaultSubnet(),
middlewares.NewAwsRouteTableExpander(),

View File

@ -46,5 +46,6 @@ func Deserializers() []deserializer.CTYDeserializer {
awsdeserializer.NewRouteDeserializer(),
awsdeserializer.NewRouteTableAssociationDeserializer(),
awsdeserializer.NewNatGatewayDeserializer(),
awsdeserializer.NewInternetGatewayDeserializer(),
}
}

View File

@ -72,6 +72,7 @@ func TestTerraformStateReader_Resources(t *testing.T) {
{name: "route", dirName: "route", wantErr: false},
{name: "route table associations", dirName: "route_assoc", wantErr: false},
{name: "NAT gateway", dirName: "aws_nat_gateway", wantErr: false},
{name: "Internet Gateway", dirName: "internet_gateway", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -0,0 +1,11 @@
[
{
"Arn": "arn:aws:ec2:eu-west-3:047081014315:internet-gateway/igw-0184eb41aadc62d1c",
"Id": "igw-0184eb41aadc62d1c",
"OwnerId": "047081014315",
"Tags": {
"Name": "main"
},
"VpcId": "vpc-0f3ac2b7909b6bedd"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,34 @@
{
"version": 4,
"terraform_version": "0.14.4",
"serial": 559,
"lineage": "cc4be827-a907-1623-961b-0fc1ce33973e",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_internet_gateway",
"name": "main",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"arn": "arn:aws:ec2:eu-west-3:047081014315:internet-gateway/igw-0184eb41aadc62d1c",
"id": "igw-0184eb41aadc62d1c",
"owner_id": "047081014315",
"tags": {
"Name": "main"
},
"vpc_id": "vpc-0f3ac2b7909b6bedd"
},
"sensitive_attributes": [],
"private": "bnVsbA==",
"dependencies": [
"aws_vpc.vpc"
]
}
]
}
]
}

View File

@ -0,0 +1,69 @@
package middlewares
import (
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/sirupsen/logrus"
)
// Each default vpc has an internet gateway attached that should not be seen as unmanaged if not managed by IaC
// This middleware ignores default internet gateway from unmanaged resources if not managed by IaC
type AwsDefaultInternetGateway struct{}
func NewAwsDefaultInternetGateway() AwsDefaultInternetGateway {
return AwsDefaultInternetGateway{}
}
func (m AwsDefaultInternetGateway) Execute(remoteResources, resourcesFromState *[]resource.Resource) error {
newRemoteResources := make([]resource.Resource, 0)
for _, remoteResource := range *remoteResources {
// Ignore all resources other than internet gateways
if remoteResource.TerraformType() != aws.AwsInternetGatewayResourceType {
newRemoteResources = append(newRemoteResources, remoteResource)
continue
}
internetGateway, _ := remoteResource.(*aws.AwsInternetGateway)
// Ignore all non-default internet gateways
if !isDefaultInternetGateway(internetGateway, remoteResources) {
newRemoteResources = append(newRemoteResources, remoteResource)
continue
}
// Check if internet gateway is managed by IaC
existInState := false
for _, stateResource := range *resourcesFromState {
if resource.IsSameResource(remoteResource, stateResource) {
existInState = true
break
}
}
// Include resource if it's managed in IaC
if existInState {
newRemoteResources = append(newRemoteResources, remoteResource)
continue
}
// Else, resource is not added to newRemoteResources slice so it will be ignored
logrus.WithFields(logrus.Fields{
"id": internetGateway.TerraformId(),
"type": internetGateway.TerraformType(),
}).Debug("Ignoring default internet gateway as it is not managed by IaC")
}
*remoteResources = newRemoteResources
return nil
}
// Return true if the internet gateway is the default one (e.g. attached to the default vpc)
func isDefaultInternetGateway(internetGateway *aws.AwsInternetGateway, remoteResources *[]resource.Resource) bool {
for _, remoteResource := range *remoteResources {
if remoteResource.TerraformType() == aws.AwsDefaultVpcResourceType {
return *internetGateway.VpcId == remoteResource.TerraformId()
}
}
return false
}

View File

@ -0,0 +1,126 @@
package middlewares
import (
"strings"
"testing"
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
resource2 "github.com/cloudskiff/driftctl/test/resource"
"github.com/r3labs/diff/v2"
)
func TestAwsDefaultInternetGateway_Execute(t *testing.T) {
tests := []struct {
name string
remoteResources []resource.Resource
resourcesFromState []resource.Resource
expected []resource.Resource
}{
{
"default internet gateway is not ignored when managed by IaC",
[]resource.Resource{
&resource2.FakeResource{
Id: "fake",
},
&aws.AwsDefaultVpc{
Id: "default-vpc",
},
&aws.AwsVpc{
Id: "dummy-vpc",
},
&aws.AwsInternetGateway{
Id: "default-igw",
VpcId: awssdk.String("default-vpc"),
},
&aws.AwsInternetGateway{
Id: "dummy-igw",
VpcId: awssdk.String("dummy-vpc"),
},
},
[]resource.Resource{
&aws.AwsInternetGateway{
Id: "default-igw",
VpcId: awssdk.String("default-vpc"),
},
},
[]resource.Resource{
&resource2.FakeResource{
Id: "fake",
},
&aws.AwsDefaultVpc{
Id: "default-vpc",
},
&aws.AwsVpc{
Id: "dummy-vpc",
},
&aws.AwsInternetGateway{
Id: "default-igw",
VpcId: awssdk.String("default-vpc"),
},
&aws.AwsInternetGateway{
Id: "dummy-igw",
VpcId: awssdk.String("dummy-vpc"),
},
},
},
{
"default internet gateway is ignored when not managed by IaC",
[]resource.Resource{
&resource2.FakeResource{
Id: "fake",
},
&aws.AwsDefaultVpc{
Id: "default-vpc",
},
&aws.AwsVpc{
Id: "dummy-vpc",
},
&aws.AwsInternetGateway{
Id: "default-igw",
VpcId: awssdk.String("default-vpc"),
},
&aws.AwsInternetGateway{
Id: "dummy-igw",
VpcId: awssdk.String("dummy-vpc"),
},
},
[]resource.Resource{},
[]resource.Resource{
&resource2.FakeResource{
Id: "fake",
},
&aws.AwsDefaultVpc{
Id: "default-vpc",
},
&aws.AwsVpc{
Id: "dummy-vpc",
},
&aws.AwsInternetGateway{
Id: "dummy-igw",
VpcId: awssdk.String("dummy-vpc"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := NewAwsDefaultInternetGateway()
err := m.Execute(&tt.remoteResources, &tt.resourcesFromState)
if err != nil {
t.Fatal(err)
}
changelog, err := diff.Diff(tt.expected, tt.remoteResources)
if err != nil {
t.Fatal(err)
}
if len(changelog) > 0 {
for _, change := range changelog {
t.Errorf("%s got = %v, want %v", strings.Join(change.Path, "."), awsutil.Prettify(change.From), awsutil.Prettify(change.To))
}
}
})
}
}

View File

@ -62,6 +62,7 @@ func Init(alerter *alerter.Alerter) error {
resource.AddSupplier(NewRouteSupplier(provider.Runner(), ec2.New(provider.session)))
resource.AddSupplier(NewRouteTableAssociationSupplier(provider.Runner(), ec2.New(provider.session)))
resource.AddSupplier(NewNatGatewaySupplier(provider.Runner(), ec2.New(provider.session)))
resource.AddSupplier(NewInternetGatewaySupplier(provider.Runner().SubRunner(), ec2.New(provider.session)))
return nil
}

View File

@ -0,0 +1,81 @@
package aws
import (
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/cloudskiff/driftctl/pkg/parallel"
"github.com/cloudskiff/driftctl/pkg/remote/deserializer"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
awsdeserializer "github.com/cloudskiff/driftctl/pkg/resource/aws/deserializer"
"github.com/cloudskiff/driftctl/pkg/terraform"
"github.com/sirupsen/logrus"
"github.com/zclconf/go-cty/cty"
)
type InternetGatewaySupplier struct {
reader terraform.ResourceReader
deserializer deserializer.CTYDeserializer
client ec2iface.EC2API
runner *terraform.ParallelResourceReader
}
func NewInternetGatewaySupplier(runner *parallel.ParallelRunner, client ec2iface.EC2API) *InternetGatewaySupplier {
return &InternetGatewaySupplier{
terraform.Provider(terraform.AWS),
awsdeserializer.NewInternetGatewayDeserializer(),
client,
terraform.NewParallelResourceReader(runner),
}
}
func (s InternetGatewaySupplier) Resources() ([]resource.Resource, error) {
internetGateways, err := listInternetGateways(s.client)
if err != nil {
return nil, err
}
for _, internetGateway := range internetGateways {
gtw := *internetGateway
s.runner.Run(func() (cty.Value, error) {
return s.readInternetGateway(gtw)
})
}
resources, err := s.runner.Wait()
if err != nil {
return nil, err
}
return s.deserializer.Deserialize(resources)
}
func (s InternetGatewaySupplier) readInternetGateway(internetGateway ec2.InternetGateway) (cty.Value, error) {
var Ty resource.ResourceType = aws.AwsInternetGatewayResourceType
val, err := s.reader.ReadResource(terraform.ReadResourceArgs{
Ty: Ty,
ID: *internetGateway.InternetGatewayId,
})
if err != nil {
logrus.WithFields(logrus.Fields{
"type": Ty,
}).Error(err)
return cty.NilVal, err
}
return *val, nil
}
func listInternetGateways(client ec2iface.EC2API) ([]*ec2.InternetGateway, error) {
var internetGateways []*ec2.InternetGateway
input := ec2.DescribeInternetGatewaysInput{}
err := client.DescribeInternetGatewaysPages(&input,
func(resp *ec2.DescribeInternetGatewaysOutput, lastPage bool) bool {
internetGateways = append(internetGateways, resp.InternetGateways...)
return !lastPage
},
)
if err != nil {
return nil, err
}
return internetGateways, nil
}

View File

@ -0,0 +1,98 @@
package aws
import (
"context"
"testing"
awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/cloudskiff/driftctl/mocks"
"github.com/cloudskiff/driftctl/pkg/parallel"
"github.com/cloudskiff/driftctl/pkg/remote/deserializer"
"github.com/cloudskiff/driftctl/pkg/resource"
awsdeserializer "github.com/cloudskiff/driftctl/pkg/resource/aws/deserializer"
"github.com/cloudskiff/driftctl/pkg/terraform"
"github.com/cloudskiff/driftctl/test"
"github.com/cloudskiff/driftctl/test/goldenfile"
mocks2 "github.com/cloudskiff/driftctl/test/mocks"
"github.com/stretchr/testify/mock"
)
func TestInternetGatewaySupplier_Resources(t *testing.T) {
cases := []struct {
test string
dirName string
mocks func(client *mocks.FakeEC2)
err error
}{
{
test: "no internet gateways",
dirName: "internet_gateway_empty",
mocks: func(client *mocks.FakeEC2) {
client.On("DescribeInternetGatewaysPages",
&ec2.DescribeInternetGatewaysInput{},
mock.MatchedBy(func(callback func(res *ec2.DescribeInternetGatewaysOutput, lastPage bool) bool) bool {
callback(&ec2.DescribeInternetGatewaysOutput{}, true)
return true
})).Return(nil)
},
err: nil,
},
{
test: "multiple internet gateways",
dirName: "internet_gateway_multiple",
mocks: func(client *mocks.FakeEC2) {
client.On("DescribeInternetGatewaysPages",
&ec2.DescribeInternetGatewaysInput{},
mock.MatchedBy(func(callback func(res *ec2.DescribeInternetGatewaysOutput, lastPage bool) bool) bool {
callback(&ec2.DescribeInternetGatewaysOutput{
InternetGateways: []*ec2.InternetGateway{
{
InternetGatewayId: awssdk.String("igw-0184eb41aadc62d1c"),
},
{
InternetGatewayId: awssdk.String("igw-047b487f5c60fca99"),
},
},
}, true)
return true
})).Return(nil)
},
err: nil,
},
}
for _, c := range cases {
shouldUpdate := c.dirName == *goldenfile.Update
if shouldUpdate {
provider, err := NewTerraFormProvider()
if err != nil {
t.Fatal(err)
}
terraform.AddProvider(terraform.AWS, provider)
resource.AddSupplier(NewInternetGatewaySupplier(provider.Runner(), ec2.New(provider.session)))
}
t.Run(c.test, func(tt *testing.T) {
fakeEC2 := mocks.FakeEC2{}
c.mocks(&fakeEC2)
provider := mocks2.NewMockedGoldenTFProvider(c.dirName, terraform.Provider(terraform.AWS), shouldUpdate)
internetGatewayDeserializer := awsdeserializer.NewInternetGatewayDeserializer()
s := &InternetGatewaySupplier{
provider,
internetGatewayDeserializer,
&fakeEC2,
terraform.NewParallelResourceReader(parallel.NewParallelRunner(context.TODO(), 10)),
}
got, err := s.Resources()
if c.err != err {
tt.Errorf("Expected error %+v got %+v", c.err, err)
}
mock.AssertExpectationsForObjects(tt)
deserializers := []deserializer.CTYDeserializer{internetGatewayDeserializer}
test.CtyTestDiffMixed(got, c.dirName, provider, deserializers, shouldUpdate, tt)
})
}
}

View File

@ -0,0 +1 @@
[]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
{
"Typ": "WyJvYmplY3QiLHsiYXJuIjoic3RyaW5nIiwiaWQiOiJzdHJpbmciLCJvd25lcl9pZCI6InN0cmluZyIsInRhZ3MiOlsibWFwIiwic3RyaW5nIl0sInZwY19pZCI6InN0cmluZyJ9XQ==",
"Val": "eyJhcm4iOiJhcm46YXdzOmVjMjpldS13ZXN0LTM6MDQ3MDgxMDE0MzE1OmludGVybmV0LWdhdGV3YXkvaWd3LTAxODRlYjQxYWFkYzYyZDFjIiwiaWQiOiJpZ3ctMDE4NGViNDFhYWRjNjJkMWMiLCJvd25lcl9pZCI6IjA0NzA4MTAxNDMxNSIsInRhZ3MiOnsiTmFtZSI6Im1haW4ifSwidnBjX2lkIjoidnBjLTBmM2FjMmI3OTA5YjZiZWRkIn0=",
"Err": null
}

View File

@ -0,0 +1,5 @@
{
"Typ": "WyJvYmplY3QiLHsiYXJuIjoic3RyaW5nIiwiaWQiOiJzdHJpbmciLCJvd25lcl9pZCI6InN0cmluZyIsInRhZ3MiOlsibWFwIiwic3RyaW5nIl0sInZwY19pZCI6InN0cmluZyJ9XQ==",
"Val": "eyJhcm4iOiJhcm46YXdzOmVjMjpldS13ZXN0LTM6MDQ3MDgxMDE0MzE1OmludGVybmV0LWdhdGV3YXkvaWd3LTA0N2I0ODdmNWM2MGZjYTk5IiwiaWQiOiJpZ3ctMDQ3YjQ4N2Y1YzYwZmNhOTkiLCJvd25lcl9pZCI6IjA0NzA4MTAxNDMxNSIsInRhZ3MiOnsiTmFtZSI6ImZvbyJ9LCJ2cGNfaWQiOiJ2cGMtMGE0MTY2MGRjODZjOGRlNWUifQ==",
"Err": null
}

View File

@ -0,0 +1,20 @@
[
{
"arn": "arn:aws:ec2:eu-west-3:047081014315:internet-gateway/igw-047b487f5c60fca99",
"id": "igw-047b487f5c60fca99",
"owner_id": "047081014315",
"tags": {
"Name": "foo"
},
"vpc_id": "vpc-0a41660dc86c8de5e"
},
{
"arn": "arn:aws:ec2:eu-west-3:047081014315:internet-gateway/igw-0184eb41aadc62d1c",
"id": "igw-0184eb41aadc62d1c",
"owner_id": "047081014315",
"tags": {
"Name": "main"
},
"vpc_id": "vpc-0f3ac2b7909b6bedd"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
provider "aws" {
region = "us-east-1"
}
terraform {
required_providers {
aws = "3.19.0"
}
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "main"
}
}
resource "aws_vpc" "vpc_bis" {
cidr_block = "10.1.0.0/16"
}
resource "aws_internet_gateway" "foo" {
vpc_id = aws_vpc.vpc_bis.id
tags = {
Name = "foo"
}
}

View File

@ -0,0 +1,20 @@
// GENERATED, DO NOT EDIT THIS FILE
package aws
const AwsInternetGatewayResourceType = "aws_internet_gateway"
type AwsInternetGateway struct {
Arn *string `cty:"arn" computed:"true"`
Id string `cty:"id" computed:"true"`
OwnerId *string `cty:"owner_id" computed:"true"`
Tags map[string]string `cty:"tags"`
VpcId *string `cty:"vpc_id"`
}
func (r *AwsInternetGateway) TerraformId() string {
return r.Id
}
func (r *AwsInternetGateway) TerraformType() string {
return AwsInternetGatewayResourceType
}

View File

@ -0,0 +1,28 @@
package aws_test
import (
"testing"
"github.com/cloudskiff/driftctl/test/acceptance"
)
func TestAcc_AwsInternetGateway(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_internet_gateway",
Args: []string{"scan", "--filter", "Type=='aws_internet_gateway'"},
Checks: []acceptance.AccCheck{
{
Env: map[string]string{
"AWS_REGION": "us-east-1",
},
Check: func(result *acceptance.ScanResult, stdout string, err error) {
if err != nil {
t.Fatal(err)
}
result.AssertInfrastructureIsInSync()
result.Equal(1, result.Summary().TotalManaged)
},
},
},
})
}

View File

@ -0,0 +1,43 @@
package deserializer
import (
"github.com/cloudskiff/driftctl/pkg/resource"
resourceaws "github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/sirupsen/logrus"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
)
type InternetGatewayDeserializer struct {
}
func NewInternetGatewayDeserializer() *InternetGatewayDeserializer {
return &InternetGatewayDeserializer{}
}
func (s *InternetGatewayDeserializer) HandledType() resource.ResourceType {
return resourceaws.AwsInternetGatewayResourceType
}
func (s InternetGatewayDeserializer) Deserialize(rawList []cty.Value) ([]resource.Resource, error) {
resources := make([]resource.Resource, 0)
for _, rawResource := range rawList {
resource, err := decodeInternetGateway(&rawResource)
if err != nil {
logrus.WithFields(logrus.Fields{
"type": s.HandledType(),
}).Warnf("Error when deserializing resource %+v : %+v", rawResource, err)
return nil, err
}
resources = append(resources, resource)
}
return resources, nil
}
func decodeInternetGateway(raw *cty.Value) (*resourceaws.AwsInternetGateway, error) {
var decoded resourceaws.AwsInternetGateway
if err := gocty.FromCtyValue(*raw, &decoded); err != nil {
return nil, err
}
return &decoded, nil
}

View File

@ -0,0 +1,20 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "3.19.0"
constraints = "3.19.0"
hashes = [
"h1:xur9tF49NgsovNnmwmBR8RdpN8Fcg1TD4CKQPJD6n1A=",
"zh:185a5259153eb9ee4699d4be43b3d509386b473683392034319beee97d470c3b",
"zh:2d9a0a01f93e8d16539d835c02b8b6e1927b7685f4076e96cb07f7dd6944bc6c",
"zh:703f6da36b1b5f3497baa38fccaa7765fb8a2b6440344e4c97172516b49437dd",
"zh:770855565462abadbbddd98cb357d2f1a8f30f68a358cb37cbd5c072cb15b377",
"zh:8008db43149fe4345301f81e15e6d9ddb47aa5e7a31648f9b290af96ad86e92a",
"zh:8cdd27d375da6dcb7687f1fed126b7c04efce1671066802ee876dbbc9c66ec79",
"zh:be22ae185005690d1a017c1b909e0d80ab567e239b4f06ecacdba85080667c1c",
"zh:d2d02e72dbd80f607636cd6237a6c862897caabc635c7b50c0cb243d11246723",
"zh:d8f125b66a1eda2555c0f9bbdf12036a5f8d073499a22ca9e4812b68067fea31",
"zh:f5a98024c64d5d2973ff15b093725a074c0cb4afde07ef32c542e69f17ac90bc",
]
}

View File

@ -0,0 +1,21 @@
provider "aws" {
region = "us-east-1"
}
terraform {
required_providers {
aws = "3.19.0"
}
}
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
}
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "main"
}
}