github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_sns_topic_policy.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/resource" 10 "github.com/hashicorp/terraform/helper/schema" 11 12 "github.com/aws/aws-sdk-go/aws" 13 "github.com/aws/aws-sdk-go/aws/awserr" 14 "github.com/aws/aws-sdk-go/service/sns" 15 ) 16 17 func resourceAwsSnsTopicPolicy() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsSnsTopicPolicyUpsert, 20 Read: resourceAwsSnsTopicPolicyRead, 21 Update: resourceAwsSnsTopicPolicyUpsert, 22 Delete: resourceAwsSnsTopicPolicyDelete, 23 24 Schema: map[string]*schema.Schema{ 25 "arn": { 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 }, 30 "policy": { 31 Type: schema.TypeString, 32 Required: true, 33 ValidateFunc: validateJsonString, 34 DiffSuppressFunc: suppressEquivalentAwsPolicyDiffs, 35 }, 36 }, 37 } 38 } 39 40 func resourceAwsSnsTopicPolicyUpsert(d *schema.ResourceData, meta interface{}) error { 41 arn := d.Get("arn").(string) 42 req := sns.SetTopicAttributesInput{ 43 TopicArn: aws.String(arn), 44 AttributeName: aws.String("Policy"), 45 AttributeValue: aws.String(d.Get("policy").(string)), 46 } 47 48 d.SetId(arn) 49 50 // Retry the update in the event of an eventually consistent style of 51 // error, where say an IAM resource is successfully created but not 52 // actually available. See https://github.com/hashicorp/terraform/issues/3660 53 log.Printf("[DEBUG] Updating SNS Topic Policy: %s", req) 54 stateConf := &resource.StateChangeConf{ 55 Pending: []string{"retrying"}, 56 Target: []string{"success"}, 57 Refresh: resourceAwsSNSUpdateRefreshFunc(meta, req), 58 Timeout: 3 * time.Minute, 59 MinTimeout: 3 * time.Second, 60 } 61 _, err := stateConf.WaitForState() 62 if err != nil { 63 return err 64 } 65 66 return resourceAwsSnsTopicPolicyRead(d, meta) 67 } 68 69 func resourceAwsSnsTopicPolicyRead(d *schema.ResourceData, meta interface{}) error { 70 snsconn := meta.(*AWSClient).snsconn 71 72 attributeOutput, err := snsconn.GetTopicAttributes(&sns.GetTopicAttributesInput{ 73 TopicArn: aws.String(d.Id()), 74 }) 75 if err != nil { 76 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFound" { 77 log.Printf("[WARN] SNS Topic (%s) not found, error code (404)", d.Id()) 78 d.SetId("") 79 return nil 80 } 81 82 return err 83 } 84 85 if attributeOutput.Attributes == nil { 86 log.Printf("[WARN] SNS Topic (%q) attributes not found (nil)", d.Id()) 87 d.SetId("") 88 return nil 89 } 90 attrmap := attributeOutput.Attributes 91 92 policy, ok := attrmap["Policy"] 93 if !ok { 94 log.Printf("[WARN] SNS Topic (%q) policy not found in attributes", d.Id()) 95 d.SetId("") 96 return nil 97 } 98 99 d.Set("policy", policy) 100 101 return nil 102 } 103 104 func resourceAwsSnsTopicPolicyDelete(d *schema.ResourceData, meta interface{}) error { 105 accountId, err := getAccountIdFromSnsTopicArn(d.Id(), meta.(*AWSClient).partition) 106 if err != nil { 107 return err 108 } 109 110 req := sns.SetTopicAttributesInput{ 111 TopicArn: aws.String(d.Id()), 112 AttributeName: aws.String("Policy"), 113 // It is impossible to delete a policy or set to empty 114 // (confirmed by AWS Support representative) 115 // so we instead set it back to the default one 116 AttributeValue: aws.String(buildDefaultSnsTopicPolicy(d.Id(), accountId)), 117 } 118 119 // Retry the update in the event of an eventually consistent style of 120 // error, where say an IAM resource is successfully created but not 121 // actually available. See https://github.com/hashicorp/terraform/issues/3660 122 log.Printf("[DEBUG] Resetting SNS Topic Policy to default: %s", req) 123 stateConf := &resource.StateChangeConf{ 124 Pending: []string{"retrying"}, 125 Target: []string{"success"}, 126 Refresh: resourceAwsSNSUpdateRefreshFunc(meta, req), 127 Timeout: 3 * time.Minute, 128 MinTimeout: 3 * time.Second, 129 } 130 _, err = stateConf.WaitForState() 131 if err != nil { 132 return err 133 } 134 return nil 135 } 136 137 func getAccountIdFromSnsTopicArn(arn, partition string) (string, error) { 138 // arn:aws:sns:us-west-2:123456789012:test-new 139 // arn:aws-us-gov:sns:us-west-2:123456789012:test-new 140 re := regexp.MustCompile(fmt.Sprintf("^arn:%s:sns:[^:]+:([0-9]{12}):.+", partition)) 141 matches := re.FindStringSubmatch(arn) 142 if len(matches) != 2 { 143 return "", fmt.Errorf("Unable to get account ID from ARN (%q)", arn) 144 } 145 return matches[1], nil 146 } 147 148 func buildDefaultSnsTopicPolicy(topicArn, accountId string) string { 149 return fmt.Sprintf(`{ 150 "Version": "2008-10-17", 151 "Id": "__default_policy_ID", 152 "Statement": [ 153 { 154 "Sid": "__default_statement_ID", 155 "Effect": "Allow", 156 "Principal": { 157 "AWS": "*" 158 }, 159 "Action": [ 160 "SNS:GetTopicAttributes", 161 "SNS:SetTopicAttributes", 162 "SNS:AddPermission", 163 "SNS:RemovePermission", 164 "SNS:DeleteTopic", 165 "SNS:Subscribe", 166 "SNS:ListSubscriptionsByTopic", 167 "SNS:Publish", 168 "SNS:Receive" 169 ], 170 "Resource": "%s", 171 "Condition": { 172 "StringEquals": { 173 "AWS:SourceOwner": "%s" 174 } 175 } 176 } 177 ] 178 }`, topicArn, accountId) 179 }