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