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