add support for sns_topic_policy

modify sns_topic support so that we create sns_topic_policy for inline
policy
main
Martin Guibert 2021-02-04 16:11:51 +01:00
parent e4c03fe17c
commit 7041cf12e5
28 changed files with 687836 additions and 1 deletions

View File

@ -251,7 +251,7 @@ As AWS documentation recommends, the below policy is granting only the permissio
## SNS
- [x] aws_sns_topic
- [ ] aws_sns_topic_policy
- [x] aws_sns_topic_policy
- [ ] aws_sns_topic_subscription
- [ ] aws_sns_platform_application
- [ ] aws_sns_sms_preferences

View File

@ -50,6 +50,7 @@ func (d DriftCTL) Run() *analyser.Analysis {
middlewares.NewAwsBucketPolicyExpander(),
middlewares.NewAwsSqsQueuePolicyExpander(),
middlewares.NewAwsDefaultSqsQueuePolicy(),
middlewares.NewAwsSNSTopicPolicyExpander(),
)
logrus.Debug("Ready to run middlewares")

View File

@ -50,5 +50,6 @@ func Deserializers() []deserializer.CTYDeserializer {
awsdeserializer.NewSqsQueueDeserializer(),
awsdeserializer.NewSqsQueuePolicyDeserializer(),
awsdeserializer.NewSNSTopicDeserializer(),
awsdeserializer.NewSNSTopicPolicyDeserializer(),
}
}

View File

@ -77,6 +77,7 @@ func TestTerraformStateReader_Resources(t *testing.T) {
{name: "SQS queue", dirName: "sqs_queue", wantErr: false},
{name: "SQS queue policy", dirName: "sqs_queue_policy", wantErr: false},
{name: "SNS Topic", dirName: "sns_topic", wantErr: false},
{name: "SNS Topic Policy", dirName: "sns_topic_policy", wantErr: false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@ -0,0 +1,12 @@
[
{
"Arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"Id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"Policy": "{\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}},\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Sid\":\"__default_statement_ID\"}],\"Version\":\"2012-10-17\"}"
},
{
"Arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"Id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"Policy": "{\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}},\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Sid\":\"__default_statement_ID\"}],\"Version\":\"2012-10-17\"}"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
{
"version": 4,
"terraform_version": "0.14.5",
"serial": 20,
"lineage": "30081725-54a2-ce02-6ff5-45c4d961c652",
"outputs": {},
"resources": [
{
"mode": "data",
"type": "aws_iam_policy_document",
"name": "sns_topic_policy",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"id": "3435591602",
"json": "{\n \"Version\": \"2012-10-17\",\n \"Id\": \"__default_policy_ID\",\n \"Statement\": [\n {\n \"Sid\": \"__default_statement_ID\",\n \"Effect\": \"Allow\",\n \"Action\": [\n \"SNS:Subscribe\",\n \"SNS:SetTopicAttributes\",\n \"SNS:RemovePermission\",\n \"SNS:Receive\",\n \"SNS:Publish\",\n \"SNS:ListSubscriptionsByTopic\",\n \"SNS:GetTopicAttributes\",\n \"SNS:DeleteTopic\",\n \"SNS:AddPermission\"\n ],\n \"Resource\": \"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\n \"Principal\": {\n \"AWS\": \"*\"\n },\n \"Condition\": {\n \"StringEquals\": {\n \"AWS:SourceOwner\": []\n }\n }\n }\n ]\n}",
"override_json": null,
"policy_id": "__default_policy_ID",
"source_json": null,
"statement": [
{
"actions": [
"SNS:AddPermission",
"SNS:DeleteTopic",
"SNS:GetTopicAttributes",
"SNS:ListSubscriptionsByTopic",
"SNS:Publish",
"SNS:Receive",
"SNS:RemovePermission",
"SNS:SetTopicAttributes",
"SNS:Subscribe"
],
"condition": [
{
"test": "StringEquals",
"values": [],
"variable": "AWS:SourceOwner"
}
],
"effect": "Allow",
"not_actions": [],
"not_principals": [],
"not_resources": [],
"principals": [
{
"identifiers": [
"*"
],
"type": "AWS"
}
],
"resources": [
"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy"
],
"sid": "__default_statement_ID"
}
],
"version": "2012-10-17"
},
"sensitive_attributes": []
}
]
},
{
"mode": "managed",
"type": "aws_sns_topic_policy",
"name": "default",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"policy": "{\"Version\":\"2012-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}}}]}"
},
"sensitive_attributes": [],
"private": "bnVsbA==",
"dependencies": [
"aws_sns_topic.test",
"data.aws_iam_policy_document.sns_topic_policy"
]
}
]
},
{
"mode": "managed",
"type": "aws_sns_topic_policy",
"name": "default2",
"provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
"instances": [
{
"schema_version": 0,
"attributes": {
"arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"policy": "{\"Version\":\"2012-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}}}]}"
},
"sensitive_attributes": [],
"private": "bnVsbA==",
"dependencies": [
"aws_sns_topic.test2",
"data.aws_iam_policy_document.sns_topic_policy"
]
}
]
}
]
}

View File

@ -0,0 +1,75 @@
package middlewares
import (
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/sirupsen/logrus"
)
// Explodes policy found in aws_sns_topic from state resources to aws_sns_topic_policy resources
type AwsSNSTopicPolicyExpander struct{}
func NewAwsSNSTopicPolicyExpander() AwsSNSTopicPolicyExpander {
return AwsSNSTopicPolicyExpander{}
}
func (m AwsSNSTopicPolicyExpander) Execute(_, resourcesFromState *[]resource.Resource) error {
newList := make([]resource.Resource, 0)
for _, res := range *resourcesFromState {
// Ignore all resources other than sns_topic
if res.TerraformType() != aws.AwsSnsTopicResourceType {
newList = append(newList, res)
continue
}
topic, _ := res.(*aws.AwsSnsTopic)
newList = append(newList, res)
if m.hasPolicyAttached(topic, resourcesFromState) {
topic.Policy = nil
continue
}
err := m.splitPolicy(topic, &newList)
if err != nil {
return err
}
}
*resourcesFromState = newList
return nil
}
func (m *AwsSNSTopicPolicyExpander) splitPolicy(topic *aws.AwsSnsTopic, results *[]resource.Resource) error {
if topic.Policy == nil || *topic.Policy == "" {
return nil
}
newPolicy := &aws.AwsSnsTopicPolicy{
Id: topic.Id,
Arn: topic.Arn,
Policy: topic.Policy,
}
normalized, err := newPolicy.NormalizeForState()
if err != nil {
return err
}
*results = append(*results, normalized)
logrus.WithFields(logrus.Fields{
"id": newPolicy.TerraformId(),
}).Debug("Created new policy from sns_topic")
topic.Policy = nil
return nil
}
func (m *AwsSNSTopicPolicyExpander) hasPolicyAttached(topic *aws.AwsSnsTopic, resourcesFromState *[]resource.Resource) bool {
for _, res := range *resourcesFromState {
if res.TerraformType() == aws.AwsSnsTopicPolicyResourceType &&
res.TerraformId() == topic.Id {
return true
}
}
return false
}

View File

@ -0,0 +1,122 @@
package middlewares
import (
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
awsresource "github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/r3labs/diff/v2"
"github.com/cloudskiff/driftctl/pkg/resource"
)
func TestAwsSNSTopicPolicyExpander_Execute(t *testing.T) {
tests := []struct {
name string
resourcesFromState *[]resource.Resource
expected *[]resource.Resource
wantErr bool
}{
{
name: "Inline policy no attached policy",
resourcesFromState: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
},
expected: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: nil,
Id: "ID",
},
&awsresource.AwsSnsTopicPolicy{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\":\"coucou\"}"),
Id: "ID",
},
},
wantErr: false,
},
{
name: "No inline policy, attached policy",
resourcesFromState: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: nil,
Id: "ID",
},
&awsresource.AwsSnsTopicPolicy{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
},
expected: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: nil,
Id: "ID",
},
&awsresource.AwsSnsTopicPolicy{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
},
wantErr: false,
},
{
name: "inline policy and attached policy",
resourcesFromState: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
&awsresource.AwsSnsTopicPolicy{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
},
expected: &[]resource.Resource{
&awsresource.AwsSnsTopic{
Arn: aws.String("arn"),
Policy: nil,
Id: "ID",
},
&awsresource.AwsSnsTopicPolicy{
Arn: aws.String("arn"),
Policy: aws.String("{\"policy\": \"coucou\"}"),
Id: "ID",
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := AwsSNSTopicPolicyExpander{}
if err := m.Execute(&[]resource.Resource{}, tt.resourcesFromState); (err != nil) != tt.wantErr {
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
}
changelog, err := diff.Diff(tt.expected, tt.resourcesFromState)
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

@ -60,6 +60,7 @@ func Init(alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary,
supplierLibrary.AddSupplier(NewSqsQueueSupplier(provider))
supplierLibrary.AddSupplier(NewSqsQueuePolicySupplier(provider))
supplierLibrary.AddSupplier(NewSNSTopicSupplier(provider))
supplierLibrary.AddSupplier(NewSNSTopicPolicySupplier(provider))
return nil
}

View File

@ -0,0 +1,67 @@
package aws
import (
"github.com/aws/aws-sdk-go/service/sns"
"github.com/cloudskiff/driftctl/pkg/remote/aws/repository"
remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error"
"github.com/sirupsen/logrus"
"github.com/zclconf/go-cty/cty"
"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"
)
type SNSTopicPolicySupplier struct {
reader terraform.ResourceReader
deserializer deserializer.CTYDeserializer
client repository.SNSRepository
runner *terraform.ParallelResourceReader
}
func NewSNSTopicPolicySupplier(provider *TerraformProvider) *SNSTopicPolicySupplier {
return &SNSTopicPolicySupplier{
provider,
awsdeserializer.NewSNSTopicPolicyDeserializer(),
repository.NewSNSClient(provider.session),
terraform.NewParallelResourceReader(provider.Runner().SubRunner()),
}
}
func (s SNSTopicPolicySupplier) Resources() ([]resource.Resource, error) {
topics, err := s.client.ListAllTopics()
if err != nil {
return nil, remoteerror.NewResourceEnumerationErrorWithType(err, aws.AwsSnsTopicPolicyResourceType, aws.AwsSnsTopicResourceType)
}
for _, topic := range topics {
topic := *topic
s.runner.Run(func() (cty.Value, error) {
return s.readTopicPolicy(topic)
})
}
retrieve, err := s.runner.Wait()
if err != nil {
return nil, err
}
return s.deserializer.Deserialize(retrieve)
}
func (s SNSTopicPolicySupplier) readTopicPolicy(topic sns.Topic) (cty.Value, error) {
val, err := s.reader.ReadResource(terraform.ReadResourceArgs{
ID: *topic.TopicArn,
Ty: aws.AwsSnsTopicPolicyResourceType,
Attributes: map[string]string{
"topic_arn": *topic.TopicArn,
},
})
if err != nil {
logrus.Error(err)
return cty.NilVal, err
}
return *val, nil
}

View File

@ -0,0 +1,102 @@
package aws
import (
"context"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/sns"
remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error"
resourceaws "github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/cloudskiff/driftctl/pkg/parallel"
awsdeserializer "github.com/cloudskiff/driftctl/pkg/resource/aws/deserializer"
"github.com/cloudskiff/driftctl/test/goldenfile"
mocks2 "github.com/cloudskiff/driftctl/test/mocks"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/cloudskiff/driftctl/mocks"
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/terraform"
"github.com/cloudskiff/driftctl/test"
)
func TestSNSTopicPolicySupplier_Resources(t *testing.T) {
cases := []struct {
test string
dirName string
mocks func(client *mocks.SNSRepository)
err error
}{
{
test: "no SNS Topic policy",
dirName: "sns_topic_policy_empty",
mocks: func(client *mocks.SNSRepository) {
client.On("ListAllTopics").Return([]*sns.Topic{}, nil)
},
err: nil,
},
{
test: "Multiple SNSTopicPolicy",
dirName: "sns_topic_policy_multiple",
mocks: func(client *mocks.SNSRepository) {
client.On("ListAllTopics").Return([]*sns.Topic{
{TopicArn: aws.String("arn:aws:sns:us-east-1:526954929923:my-topic-with-policy")},
{TopicArn: aws.String("arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2")},
}, nil)
},
err: nil,
},
{
test: "cannot list SNSTopic",
dirName: "sns_topic_policy_topic_list",
mocks: func(client *mocks.SNSRepository) {
client.On("ListAllTopics").Return(nil, awserr.NewRequestFailure(nil, 403, ""))
},
err: remoteerror.NewResourceEnumerationErrorWithType(awserr.NewRequestFailure(nil, 403, ""), resourceaws.AwsSnsTopicPolicyResourceType, resourceaws.AwsSnsTopicResourceType),
},
}
for _, c := range cases {
shouldUpdate := c.dirName == *goldenfile.Update
providerLibrary := terraform.NewProviderLibrary()
supplierLibrary := resource.NewSupplierLibrary()
if shouldUpdate {
provider, err := NewTerraFormProvider()
if err != nil {
t.Fatal(err)
}
providerLibrary.AddProvider(terraform.AWS, provider)
supplierLibrary.AddSupplier(NewSNSTopicSupplier(provider))
}
t.Run(c.test, func(tt *testing.T) {
fakeClient := mocks.SNSRepository{}
c.mocks(&fakeClient)
provider := mocks2.NewMockedGoldenTFProvider(c.dirName, providerLibrary.Provider(terraform.AWS), shouldUpdate)
SNSTopicPolicyDeserializer := awsdeserializer.NewSNSTopicPolicyDeserializer()
s := &SNSTopicPolicySupplier{
provider,
SNSTopicPolicyDeserializer,
&fakeClient,
terraform.NewParallelResourceReader(parallel.NewParallelRunner(context.TODO(), 10)),
}
got, err := s.Resources()
assert.Equal(tt, c.err, err)
mock.AssertExpectationsForObjects(tt)
test.CtyTestDiff(got, c.dirName, provider, SNSTopicPolicyDeserializer, shouldUpdate, tt)
})
}
}

View File

@ -0,0 +1,60 @@
provider "aws" {
region = "us-east-1"
}
resource "aws_sns_topic" "test" {
name = "my-topic-with-policy"
}
resource "aws_sns_topic_policy" "default" {
arn = aws_sns_topic.test.arn
policy = data.aws_iam_policy_document.sns_topic_policy.json
}
resource "aws_sns_topic" "test2" {
name = "my-topic-with-policy2"
}
resource "aws_sns_topic_policy" "default2" {
arn = aws_sns_topic.test2.arn
policy = data.aws_iam_policy_document.sns_topic_policy.json
}
data "aws_iam_policy_document" "sns_topic_policy" {
policy_id = "__default_policy_ID"
statement {
actions = [
"SNS:Subscribe",
"SNS:SetTopicAttributes",
"SNS:RemovePermission",
"SNS:Receive",
"SNS:Publish",
"SNS:ListSubscriptionsByTopic",
"SNS:GetTopicAttributes",
"SNS:DeleteTopic",
"SNS:AddPermission",
]
condition {
test = "StringEquals"
variable = "AWS:SourceOwner"
values = []
}
effect = "Allow"
principals {
type = "AWS"
identifiers = ["*"]
}
resources = [
aws_sns_topic.test.arn,
]
sid = "__default_statement_ID"
}
}

View File

@ -0,0 +1 @@
[]

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -0,0 +1,12 @@
[
{
"arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy2",
"policy": "{\"Version\":\"2012-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}}}]}"
},
{
"arn": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"id": "arn:aws:sns:us-east-1:526954929923:my-topic-with-policy",
"policy": "{\"Version\":\"2012-10-17\",\"Id\":\"__default_policy_ID\",\"Statement\":[{\"Sid\":\"__default_statement_ID\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"SNS:Subscribe\",\"SNS:SetTopicAttributes\",\"SNS:RemovePermission\",\"SNS:Receive\",\"SNS:Publish\",\"SNS:ListSubscriptionsByTopic\",\"SNS:GetTopicAttributes\",\"SNS:DeleteTopic\",\"SNS:AddPermission\"],\"Resource\":\"arn:aws:sns:us-east-1:526954929923:my-topic-with-policy\",\"Condition\":{\"StringEquals\":{\"AWS:SourceOwner\":[]}}}]}"
}
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
[]

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,22 @@ package aws
import (
"fmt"
"github.com/cloudskiff/driftctl/pkg/resource"
)
func (r *AwsSnsTopic) NormalizeForState() (resource.Resource, error) {
if r.Policy != nil && *r.Policy == "" {
r.Policy = nil
}
return r, nil
}
func (r *AwsSnsTopic) NormalizeForProvider() (resource.Resource, error) {
r.Policy = nil
return r, nil
}
func (r *AwsSnsTopic) String() string {
if r.DisplayName != nil && *r.DisplayName != "" && r.Name != nil && *r.Name != "" {
return fmt.Sprintf("%s (%s)", *r.DisplayName, *r.Name)

View File

@ -0,0 +1,18 @@
// GENERATED, DO NOT EDIT THIS FILE
package aws
const AwsSnsTopicPolicyResourceType = "aws_sns_topic_policy"
type AwsSnsTopicPolicy struct {
Arn *string `cty:"arn"`
Id string `cty:"id" computed:"true"`
Policy *string `cty:"policy"`
}
func (r *AwsSnsTopicPolicy) TerraformId() string {
return r.Id
}
func (r *AwsSnsTopicPolicy) TerraformType() string {
return AwsSnsTopicPolicyResourceType
}

View File

@ -0,0 +1,27 @@
package aws
import (
"github.com/cloudskiff/driftctl/pkg/helpers"
"github.com/cloudskiff/driftctl/pkg/resource"
)
func (r AwsSnsTopicPolicy) NormalizeForState() (resource.Resource, error) {
err := r.normalizePolicy()
return &r, err
}
func (r AwsSnsTopicPolicy) NormalizeForProvider() (resource.Resource, error) {
err := r.normalizePolicy()
return &r, err
}
func (r *AwsSnsTopicPolicy) normalizePolicy() error {
if r.Policy != nil {
jsonString, err := helpers.NormalizeJsonString(*r.Policy)
if err != nil {
return err
}
r.Policy = &jsonString
}
return nil
}

View File

@ -0,0 +1,29 @@
package aws_test
import (
"testing"
"github.com/cloudskiff/driftctl/test/acceptance"
)
func TestAcc_AwsSNSTopicPolicy(t *testing.T) {
acceptance.Run(t, acceptance.AccTestCase{
Path: "./testdata/acc/aws_sns_topic_policy",
Args: []string{"scan", "--filter", "Type=='aws_sns_topic' || Type=='aws_sns_topic_policy'"},
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.AssertDriftCountTotal(0)
result.AssertDeletedCount(0)
result.AssertManagedCount(6)
},
},
},
})
}

View File

@ -0,0 +1,40 @@
package deserializer
import (
"github.com/cloudskiff/driftctl/pkg/resource"
"github.com/cloudskiff/driftctl/pkg/resource/aws"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
)
type SNSTopicPolicyDeserializer struct {
}
func NewSNSTopicPolicyDeserializer() *SNSTopicPolicyDeserializer {
return &SNSTopicPolicyDeserializer{}
}
func (s *SNSTopicPolicyDeserializer) HandledType() resource.ResourceType {
return aws.AwsSnsTopicPolicyResourceType
}
func (s SNSTopicPolicyDeserializer) Deserialize(topicList []cty.Value) ([]resource.Resource, error) {
policies := make([]resource.Resource, 0)
for _, value := range topicList {
value := value
policy, err := decodeSNSTopicPolicy(value)
if err != nil {
return nil, err
}
policies = append(policies, policy)
}
return policies, nil
}
func decodeSNSTopicPolicy(value cty.Value) (resource.Resource, error) {
var topicPolicy aws.AwsSnsTopicPolicy
err := gocty.FromCtyValue(value, &topicPolicy)
return &topicPolicy, err
}

View File

@ -0,0 +1,19 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "3.26.0"
hashes = [
"h1:0i78FItlPeiomd+4ThZrtm56P5K33k7/6dnEe4ZePI0=",
"zh:26043eed36d070ca032cf04bc980c654a25821a8abc0c85e1e570e3935bbfcbb",
"zh:2fe68f3f78d23830a04d7fac3eda550eef1f627dfc130486f70a65dc5c254300",
"zh:3d66484c608c64678e639db25d63872783ce60363a1246e30317f21c9c23b84b",
"zh:46ffd755cfd4cf94fe66342797b5afdcef010a24e126c67fee141b357d393535",
"zh:5e96f24357e945c9067cf5e032ad1d003609629c956c2f9f642fefe714e74587",
"zh:60c27aca36bb63bf3e865c2193be80ca83b376581d00f9c220af4b013e163c4d",
"zh:896f0f22d19d41e71b22f9240b261714c3915b165ddefeb771e7734d69dc47ea",
"zh:90de9966cb2fd3e2f326df291595e55d2dd2d90e7d6dd085c2c8691dce82bdb4",
"zh:ad05a91a88ceb1d6de5a568f7cc0b0e5bc0a79f3da70bc28c1e7f3750e362d58",
"zh:e8c63f59c6465329e1f3357498face3dd7ef10a033df3c366a33aa9e94b46c01",
]
}

View File

@ -0,0 +1,83 @@
provider "aws" {
region = "us-east-1"
}
resource "aws_sns_topic" "test" {
name = "my-topic-with-policy"
}
resource "aws_sns_topic_policy" "default" {
arn = aws_sns_topic.test.arn
policy = data.aws_iam_policy_document.sns_topic_policy.json
}
resource "aws_sns_topic" "test2" {
name = "my-topic-with-policy2"
}
resource "aws_sns_topic_policy" "default2" {
arn = aws_sns_topic.test2.arn
policy = data.aws_iam_policy_document.sns_topic_policy.json
}
data "aws_iam_policy_document" "sns_topic_policy" {
policy_id = "__default_policy_ID"
statement {
actions = [
"SNS:Subscribe",
"SNS:SetTopicAttributes",
"SNS:RemovePermission",
"SNS:Receive",
"SNS:Publish",
"SNS:ListSubscriptionsByTopic",
"SNS:GetTopicAttributes",
"SNS:DeleteTopic",
"SNS:AddPermission",
]
condition {
test = "StringEquals"
variable = "AWS:SourceOwner"
values = []
}
effect = "Allow"
principals {
type = "AWS"
identifiers = ["*"]
}
resources = [
aws_sns_topic.test.arn,
]
sid = "__default_statement_ID"
}
}
resource "aws_sns_topic" "user_updates" {
name = "user-updates-topic"
delivery_policy = <<EOF
{
"http": {
"defaultHealthyRetryPolicy": {
"minDelayTarget": 20,
"maxDelayTarget": 20,
"numRetries": 3,
"numMaxDelayRetries": 0,
"numNoDelayRetries": 0,
"numMinDelayRetries": 0,
"backoffFunction": "linear"
},
"disableSubscriptionOverrides": false,
"defaultThrottlePolicy": {
"maxReceivesPerSecond": 1
}
}
}
EOF
}