github.com/danrjohnson/terraform@v0.7.0-rc2.0.20160627135212-d0fc1fa086ff/builtin/providers/aws/resource_aws_redshift_security_group.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "regexp" 8 "time" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/awserr" 12 "github.com/aws/aws-sdk-go/service/redshift" 13 "github.com/hashicorp/go-multierror" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceAwsRedshiftSecurityGroup() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsRedshiftSecurityGroupCreate, 22 Read: resourceAwsRedshiftSecurityGroupRead, 23 Delete: resourceAwsRedshiftSecurityGroupDelete, 24 25 Schema: map[string]*schema.Schema{ 26 "name": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 ValidateFunc: validateRedshiftSecurityGroupName, 31 }, 32 33 "description": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 ForceNew: true, 37 Default: "Managed by Terraform", 38 }, 39 40 "ingress": &schema.Schema{ 41 Type: schema.TypeSet, 42 Required: true, 43 ForceNew: true, 44 Elem: &schema.Resource{ 45 Schema: map[string]*schema.Schema{ 46 "cidr": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 }, 50 51 "security_group_name": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 Computed: true, 55 }, 56 57 "security_group_owner_id": &schema.Schema{ 58 Type: schema.TypeString, 59 Optional: true, 60 Computed: true, 61 }, 62 }, 63 }, 64 Set: resourceAwsRedshiftSecurityGroupIngressHash, 65 }, 66 }, 67 } 68 } 69 70 func resourceAwsRedshiftSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { 71 conn := meta.(*AWSClient).redshiftconn 72 73 var err error 74 var errs []error 75 76 name := d.Get("name").(string) 77 desc := d.Get("description").(string) 78 sgInput := &redshift.CreateClusterSecurityGroupInput{ 79 ClusterSecurityGroupName: aws.String(name), 80 Description: aws.String(desc), 81 } 82 log.Printf("[DEBUG] Redshift security group create: name: %s, description: %s", name, desc) 83 _, err = conn.CreateClusterSecurityGroup(sgInput) 84 if err != nil { 85 return fmt.Errorf("Error creating RedshiftSecurityGroup: %s", err) 86 } 87 88 d.SetId(d.Get("name").(string)) 89 90 log.Printf("[INFO] Redshift Security Group ID: %s", d.Id()) 91 sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta) 92 if err != nil { 93 return err 94 } 95 96 ingresses := d.Get("ingress").(*schema.Set) 97 for _, ing := range ingresses.List() { 98 err := resourceAwsRedshiftSecurityGroupAuthorizeRule(ing, *sg.ClusterSecurityGroupName, conn) 99 if err != nil { 100 errs = append(errs, err) 101 } 102 } 103 104 if len(errs) > 0 { 105 return &multierror.Error{Errors: errs} 106 } 107 108 log.Println("[INFO] Waiting for Redshift Security Group Ingress Authorizations to be authorized") 109 stateConf := &resource.StateChangeConf{ 110 Pending: []string{"authorizing"}, 111 Target: []string{"authorized"}, 112 Refresh: resourceAwsRedshiftSecurityGroupStateRefreshFunc(d, meta), 113 Timeout: 10 * time.Minute, 114 } 115 116 _, err = stateConf.WaitForState() 117 if err != nil { 118 return err 119 } 120 121 return resourceAwsRedshiftSecurityGroupRead(d, meta) 122 } 123 124 func resourceAwsRedshiftSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { 125 sg, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta) 126 if err != nil { 127 return err 128 } 129 130 rules := &schema.Set{ 131 F: resourceAwsRedshiftSecurityGroupIngressHash, 132 } 133 134 for _, v := range sg.IPRanges { 135 rule := map[string]interface{}{"cidr": *v.CIDRIP} 136 rules.Add(rule) 137 } 138 139 for _, g := range sg.EC2SecurityGroups { 140 rule := map[string]interface{}{ 141 "security_group_name": *g.EC2SecurityGroupName, 142 "security_group_owner_id": *g.EC2SecurityGroupOwnerId, 143 } 144 rules.Add(rule) 145 } 146 147 d.Set("ingress", rules) 148 d.Set("name", *sg.ClusterSecurityGroupName) 149 d.Set("description", *sg.Description) 150 151 return nil 152 } 153 154 func resourceAwsRedshiftSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { 155 conn := meta.(*AWSClient).redshiftconn 156 157 log.Printf("[DEBUG] Redshift Security Group destroy: %v", d.Id()) 158 opts := redshift.DeleteClusterSecurityGroupInput{ 159 ClusterSecurityGroupName: aws.String(d.Id()), 160 } 161 162 log.Printf("[DEBUG] Redshift Security Group destroy configuration: %v", opts) 163 _, err := conn.DeleteClusterSecurityGroup(&opts) 164 165 if err != nil { 166 newerr, ok := err.(awserr.Error) 167 if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" { 168 return nil 169 } 170 return err 171 } 172 173 return nil 174 } 175 176 func resourceAwsRedshiftSecurityGroupRetrieve(d *schema.ResourceData, meta interface{}) (*redshift.ClusterSecurityGroup, error) { 177 conn := meta.(*AWSClient).redshiftconn 178 179 opts := redshift.DescribeClusterSecurityGroupsInput{ 180 ClusterSecurityGroupName: aws.String(d.Id()), 181 } 182 183 log.Printf("[DEBUG] Redshift Security Group describe configuration: %#v", opts) 184 185 resp, err := conn.DescribeClusterSecurityGroups(&opts) 186 187 if err != nil { 188 return nil, fmt.Errorf("Error retrieving Redshift Security Groups: %s", err) 189 } 190 191 if len(resp.ClusterSecurityGroups) != 1 || 192 *resp.ClusterSecurityGroups[0].ClusterSecurityGroupName != d.Id() { 193 return nil, fmt.Errorf("Unable to find Redshift Security Group: %#v", resp.ClusterSecurityGroups) 194 } 195 196 return resp.ClusterSecurityGroups[0], nil 197 } 198 199 func validateRedshiftSecurityGroupName(v interface{}, k string) (ws []string, errors []error) { 200 value := v.(string) 201 if value == "default" { 202 errors = append(errors, fmt.Errorf("the Redshift Security Group name cannot be %q", value)) 203 } 204 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 205 errors = append(errors, fmt.Errorf( 206 "only lowercase alphanumeric characters and hyphens allowed in %q: %q", 207 k, value)) 208 } 209 if len(value) > 255 { 210 errors = append(errors, fmt.Errorf( 211 "%q cannot be longer than 32 characters: %q", k, value)) 212 } 213 return 214 215 } 216 217 func resourceAwsRedshiftSecurityGroupIngressHash(v interface{}) int { 218 var buf bytes.Buffer 219 m := v.(map[string]interface{}) 220 221 if v, ok := m["cidr"]; ok { 222 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 223 } 224 225 if v, ok := m["security_group_name"]; ok { 226 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 227 } 228 229 if v, ok := m["security_group_owner_id"]; ok { 230 buf.WriteString(fmt.Sprintf("%s-", v.(string))) 231 } 232 233 return hashcode.String(buf.String()) 234 } 235 236 func resourceAwsRedshiftSecurityGroupAuthorizeRule(ingress interface{}, redshiftSecurityGroupName string, conn *redshift.Redshift) error { 237 ing := ingress.(map[string]interface{}) 238 239 opts := redshift.AuthorizeClusterSecurityGroupIngressInput{ 240 ClusterSecurityGroupName: aws.String(redshiftSecurityGroupName), 241 } 242 243 if attr, ok := ing["cidr"]; ok && attr != "" { 244 opts.CIDRIP = aws.String(attr.(string)) 245 } 246 247 if attr, ok := ing["security_group_name"]; ok && attr != "" { 248 opts.EC2SecurityGroupName = aws.String(attr.(string)) 249 } 250 251 if attr, ok := ing["security_group_owner_id"]; ok && attr != "" { 252 opts.EC2SecurityGroupOwnerId = aws.String(attr.(string)) 253 } 254 255 log.Printf("[DEBUG] Authorize ingress rule configuration: %#v", opts) 256 _, err := conn.AuthorizeClusterSecurityGroupIngress(&opts) 257 258 if err != nil { 259 return fmt.Errorf("Error authorizing security group ingress: %s", err) 260 } 261 262 return nil 263 } 264 265 func resourceAwsRedshiftSecurityGroupStateRefreshFunc( 266 d *schema.ResourceData, meta interface{}) resource.StateRefreshFunc { 267 return func() (interface{}, string, error) { 268 v, err := resourceAwsRedshiftSecurityGroupRetrieve(d, meta) 269 270 if err != nil { 271 log.Printf("Error on retrieving Redshift Security Group when waiting: %s", err) 272 return nil, "", err 273 } 274 275 statuses := make([]string, 0, len(v.EC2SecurityGroups)+len(v.IPRanges)) 276 for _, ec2g := range v.EC2SecurityGroups { 277 statuses = append(statuses, *ec2g.Status) 278 } 279 for _, ips := range v.IPRanges { 280 statuses = append(statuses, *ips.Status) 281 } 282 283 for _, stat := range statuses { 284 // Not done 285 if stat != "authorized" { 286 return nil, "authorizing", nil 287 } 288 } 289 290 return v, "authorized", nil 291 } 292 }