github.com/nathanielks/terraform@v0.6.1-0.20170509030759-13e1a62319dc/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  }