github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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()) 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 string) (string, error) { 138 // arn:aws:sns:us-west-2:123456789012:test-new 139 re := regexp.MustCompile("^arn:aws:sns:[^:]+:([0-9]{12}):.+") 140 matches := re.FindStringSubmatch(arn) 141 if len(matches) != 2 { 142 return "", fmt.Errorf("Unable to get account ID from ARN (%q)", arn) 143 } 144 return matches[1], nil 145 } 146 147 func buildDefaultSnsTopicPolicy(topicArn, accountId string) string { 148 return fmt.Sprintf(`{ 149 "Version": "2008-10-17", 150 "Id": "__default_policy_ID", 151 "Statement": [ 152 { 153 "Sid": "__default_statement_ID", 154 "Effect": "Allow", 155 "Principal": { 156 "AWS": "*" 157 }, 158 "Action": [ 159 "SNS:GetTopicAttributes", 160 "SNS:SetTopicAttributes", 161 "SNS:AddPermission", 162 "SNS:RemovePermission", 163 "SNS:DeleteTopic", 164 "SNS:Subscribe", 165 "SNS:ListSubscriptionsByTopic", 166 "SNS:Publish", 167 "SNS:Receive" 168 ], 169 "Resource": "%s", 170 "Condition": { 171 "StringEquals": { 172 "AWS:SourceOwner": "%s" 173 } 174 } 175 } 176 ] 177 }`, topicArn, accountId) 178 }