github.com/leeprovoost/terraform@v0.6.10-0.20160119085442-96f3f76118e7/builtin/providers/aws/resource_aws_s3_bucket.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "log" 8 "time" 9 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/aws/aws-sdk-go/aws" 14 "github.com/aws/aws-sdk-go/aws/awserr" 15 "github.com/aws/aws-sdk-go/service/s3" 16 "github.com/hashicorp/terraform/helper/hashcode" 17 ) 18 19 func resourceAwsS3Bucket() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceAwsS3BucketCreate, 22 Read: resourceAwsS3BucketRead, 23 Update: resourceAwsS3BucketUpdate, 24 Delete: resourceAwsS3BucketDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "bucket": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "arn": &schema.Schema{ 34 Type: schema.TypeString, 35 Optional: true, 36 Computed: true, 37 }, 38 39 "acl": &schema.Schema{ 40 Type: schema.TypeString, 41 Default: "private", 42 Optional: true, 43 }, 44 45 "policy": &schema.Schema{ 46 Type: schema.TypeString, 47 Optional: true, 48 StateFunc: normalizeJson, 49 }, 50 51 "cors_rule": &schema.Schema{ 52 Type: schema.TypeList, 53 Optional: true, 54 Elem: &schema.Resource{ 55 Schema: map[string]*schema.Schema{ 56 "allowed_headers": &schema.Schema{ 57 Type: schema.TypeList, 58 Optional: true, 59 Elem: &schema.Schema{Type: schema.TypeString}, 60 }, 61 "allowed_methods": &schema.Schema{ 62 Type: schema.TypeList, 63 Required: true, 64 Elem: &schema.Schema{Type: schema.TypeString}, 65 }, 66 "allowed_origins": &schema.Schema{ 67 Type: schema.TypeList, 68 Required: true, 69 Elem: &schema.Schema{Type: schema.TypeString}, 70 }, 71 "expose_headers": &schema.Schema{ 72 Type: schema.TypeList, 73 Optional: true, 74 Elem: &schema.Schema{Type: schema.TypeString}, 75 }, 76 "max_age_seconds": &schema.Schema{ 77 Type: schema.TypeInt, 78 Optional: true, 79 }, 80 }, 81 }, 82 }, 83 84 "website": &schema.Schema{ 85 Type: schema.TypeList, 86 Optional: true, 87 Elem: &schema.Resource{ 88 Schema: map[string]*schema.Schema{ 89 "index_document": &schema.Schema{ 90 Type: schema.TypeString, 91 Optional: true, 92 }, 93 94 "error_document": &schema.Schema{ 95 Type: schema.TypeString, 96 Optional: true, 97 }, 98 99 "redirect_all_requests_to": &schema.Schema{ 100 Type: schema.TypeString, 101 ConflictsWith: []string{ 102 "website.0.index_document", 103 "website.0.error_document", 104 }, 105 Optional: true, 106 }, 107 }, 108 }, 109 }, 110 111 "hosted_zone_id": &schema.Schema{ 112 Type: schema.TypeString, 113 Optional: true, 114 Computed: true, 115 }, 116 117 "region": &schema.Schema{ 118 Type: schema.TypeString, 119 Optional: true, 120 Computed: true, 121 }, 122 "website_endpoint": &schema.Schema{ 123 Type: schema.TypeString, 124 Optional: true, 125 Computed: true, 126 }, 127 "website_domain": &schema.Schema{ 128 Type: schema.TypeString, 129 Optional: true, 130 Computed: true, 131 }, 132 133 "versioning": &schema.Schema{ 134 Type: schema.TypeSet, 135 Optional: true, 136 Elem: &schema.Resource{ 137 Schema: map[string]*schema.Schema{ 138 "enabled": &schema.Schema{ 139 Type: schema.TypeBool, 140 Optional: true, 141 Default: false, 142 }, 143 }, 144 }, 145 Set: func(v interface{}) int { 146 var buf bytes.Buffer 147 m := v.(map[string]interface{}) 148 buf.WriteString(fmt.Sprintf("%t-", m["enabled"].(bool))) 149 150 return hashcode.String(buf.String()) 151 }, 152 }, 153 154 "logging": &schema.Schema{ 155 Type: schema.TypeSet, 156 Optional: true, 157 Elem: &schema.Resource{ 158 Schema: map[string]*schema.Schema{ 159 "target_bucket": &schema.Schema{ 160 Type: schema.TypeString, 161 Required: true, 162 }, 163 "target_prefix": &schema.Schema{ 164 Type: schema.TypeString, 165 Optional: true, 166 }, 167 }, 168 }, 169 Set: func(v interface{}) int { 170 var buf bytes.Buffer 171 m := v.(map[string]interface{}) 172 buf.WriteString(fmt.Sprintf("%s-", m["target_bucket"])) 173 buf.WriteString(fmt.Sprintf("%s-", m["target_prefix"])) 174 return hashcode.String(buf.String()) 175 }, 176 }, 177 178 "tags": tagsSchema(), 179 180 "force_destroy": &schema.Schema{ 181 Type: schema.TypeBool, 182 Optional: true, 183 Default: false, 184 }, 185 }, 186 } 187 } 188 189 func resourceAwsS3BucketCreate(d *schema.ResourceData, meta interface{}) error { 190 s3conn := meta.(*AWSClient).s3conn 191 awsRegion := meta.(*AWSClient).region 192 193 // Get the bucket and acl 194 bucket := d.Get("bucket").(string) 195 acl := d.Get("acl").(string) 196 197 log.Printf("[DEBUG] S3 bucket create: %s, ACL: %s", bucket, acl) 198 199 req := &s3.CreateBucketInput{ 200 Bucket: aws.String(bucket), 201 ACL: aws.String(acl), 202 } 203 204 // Special case us-east-1 region and do not set the LocationConstraint. 205 // See "Request Elements: http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUT.html 206 if awsRegion != "us-east-1" { 207 req.CreateBucketConfiguration = &s3.CreateBucketConfiguration{ 208 LocationConstraint: aws.String(awsRegion), 209 } 210 } 211 212 _, err := s3conn.CreateBucket(req) 213 if err != nil { 214 return fmt.Errorf("Error creating S3 bucket: %s", err) 215 } 216 217 // Assign the bucket name as the resource ID 218 d.SetId(bucket) 219 220 return resourceAwsS3BucketUpdate(d, meta) 221 } 222 223 func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error { 224 s3conn := meta.(*AWSClient).s3conn 225 if err := setTagsS3(s3conn, d); err != nil { 226 return err 227 } 228 229 if d.HasChange("policy") { 230 if err := resourceAwsS3BucketPolicyUpdate(s3conn, d); err != nil { 231 return err 232 } 233 } 234 235 if d.HasChange("cors_rule") { 236 if err := resourceAwsS3BucketCorsUpdate(s3conn, d); err != nil { 237 return err 238 } 239 } 240 241 if d.HasChange("website") { 242 if err := resourceAwsS3BucketWebsiteUpdate(s3conn, d); err != nil { 243 return err 244 } 245 } 246 247 if d.HasChange("versioning") { 248 if err := resourceAwsS3BucketVersioningUpdate(s3conn, d); err != nil { 249 return err 250 } 251 } 252 if d.HasChange("acl") { 253 if err := resourceAwsS3BucketAclUpdate(s3conn, d); err != nil { 254 return err 255 } 256 } 257 258 if d.HasChange("logging") { 259 if err := resourceAwsS3BucketLoggingUpdate(s3conn, d); err != nil { 260 return err 261 } 262 } 263 264 return resourceAwsS3BucketRead(d, meta) 265 } 266 267 func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error { 268 s3conn := meta.(*AWSClient).s3conn 269 270 var err error 271 _, err = s3conn.HeadBucket(&s3.HeadBucketInput{ 272 Bucket: aws.String(d.Id()), 273 }) 274 if err != nil { 275 if awsError, ok := err.(awserr.RequestFailure); ok && awsError.StatusCode() == 404 { 276 log.Printf("[WARN] S3 Bucket (%s) not found, error code (404)", d.Id()) 277 d.SetId("") 278 return nil 279 } else { 280 // some of the AWS SDK's errors can be empty strings, so let's add 281 // some additional context. 282 return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err) 283 } 284 } 285 286 // Read the policy 287 pol, err := s3conn.GetBucketPolicy(&s3.GetBucketPolicyInput{ 288 Bucket: aws.String(d.Id()), 289 }) 290 log.Printf("[DEBUG] S3 bucket: %s, read policy: %v", d.Id(), pol) 291 if err != nil { 292 if err := d.Set("policy", ""); err != nil { 293 return err 294 } 295 } else { 296 if v := pol.Policy; v == nil { 297 if err := d.Set("policy", ""); err != nil { 298 return err 299 } 300 } else if err := d.Set("policy", normalizeJson(*v)); err != nil { 301 return err 302 } 303 } 304 305 // Read the CORS 306 cors, err := s3conn.GetBucketCors(&s3.GetBucketCorsInput{ 307 Bucket: aws.String(d.Id()), 308 }) 309 log.Printf("[DEBUG] S3 bucket: %s, read CORS: %v", d.Id(), cors) 310 if err != nil { 311 rules := make([]map[string]interface{}, 0, len(cors.CORSRules)) 312 for _, ruleObject := range cors.CORSRules { 313 rule := make(map[string]interface{}) 314 rule["allowed_headers"] = ruleObject.AllowedHeaders 315 rule["allowed_methods"] = ruleObject.AllowedMethods 316 rule["allowed_origins"] = ruleObject.AllowedOrigins 317 rule["expose_headers"] = ruleObject.ExposeHeaders 318 rule["max_age_seconds"] = ruleObject.MaxAgeSeconds 319 rules = append(rules, rule) 320 } 321 if err := d.Set("cors_rule", rules); err != nil { 322 return fmt.Errorf("error reading S3 bucket \"%s\" CORS rules: %s", d.Id(), err) 323 } 324 } 325 326 // Read the website configuration 327 ws, err := s3conn.GetBucketWebsite(&s3.GetBucketWebsiteInput{ 328 Bucket: aws.String(d.Id()), 329 }) 330 var websites []map[string]interface{} 331 if err == nil { 332 w := make(map[string]interface{}) 333 334 if v := ws.IndexDocument; v != nil { 335 w["index_document"] = *v.Suffix 336 } 337 338 if v := ws.ErrorDocument; v != nil { 339 w["error_document"] = *v.Key 340 } 341 342 if v := ws.RedirectAllRequestsTo; v != nil { 343 w["redirect_all_requests_to"] = *v.HostName 344 } 345 346 websites = append(websites, w) 347 } 348 if err := d.Set("website", websites); err != nil { 349 return err 350 } 351 352 // Read the versioning configuration 353 versioning, err := s3conn.GetBucketVersioning(&s3.GetBucketVersioningInput{ 354 Bucket: aws.String(d.Id()), 355 }) 356 if err != nil { 357 return err 358 } 359 log.Printf("[DEBUG] S3 Bucket: %s, versioning: %v", d.Id(), versioning) 360 if versioning.Status != nil && *versioning.Status == s3.BucketVersioningStatusEnabled { 361 vcl := make([]map[string]interface{}, 0, 1) 362 vc := make(map[string]interface{}) 363 if *versioning.Status == s3.BucketVersioningStatusEnabled { 364 vc["enabled"] = true 365 } else { 366 vc["enabled"] = false 367 } 368 vcl = append(vcl, vc) 369 if err := d.Set("versioning", vcl); err != nil { 370 return err 371 } 372 } 373 374 // Read the logging configuration 375 logging, err := s3conn.GetBucketLogging(&s3.GetBucketLoggingInput{ 376 Bucket: aws.String(d.Id()), 377 }) 378 if err != nil { 379 return err 380 } 381 log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging) 382 if v := logging.LoggingEnabled; v != nil { 383 lcl := make([]map[string]interface{}, 0, 1) 384 lc := make(map[string]interface{}) 385 if *v.TargetBucket != "" { 386 lc["target_bucket"] = *v.TargetBucket 387 } 388 if *v.TargetPrefix != "" { 389 lc["target_prefix"] = *v.TargetPrefix 390 } 391 lcl = append(lcl, lc) 392 if err := d.Set("logging", lcl); err != nil { 393 return err 394 } 395 } 396 397 // Add the region as an attribute 398 location, err := s3conn.GetBucketLocation( 399 &s3.GetBucketLocationInput{ 400 Bucket: aws.String(d.Id()), 401 }, 402 ) 403 if err != nil { 404 return err 405 } 406 var region string 407 if location.LocationConstraint != nil { 408 region = *location.LocationConstraint 409 } 410 region = normalizeRegion(region) 411 if err := d.Set("region", region); err != nil { 412 return err 413 } 414 415 // Add the hosted zone ID for this bucket's region as an attribute 416 hostedZoneID := HostedZoneIDForRegion(region) 417 if err := d.Set("hosted_zone_id", hostedZoneID); err != nil { 418 return err 419 } 420 421 // Add website_endpoint as an attribute 422 websiteEndpoint, err := websiteEndpoint(s3conn, d) 423 if err != nil { 424 return err 425 } 426 if websiteEndpoint != nil { 427 if err := d.Set("website_endpoint", websiteEndpoint.Endpoint); err != nil { 428 return err 429 } 430 if err := d.Set("website_domain", websiteEndpoint.Domain); err != nil { 431 return err 432 } 433 } 434 435 tagSet, err := getTagSetS3(s3conn, d.Id()) 436 if err != nil { 437 return err 438 } 439 440 if err := d.Set("tags", tagsToMapS3(tagSet)); err != nil { 441 return err 442 } 443 444 d.Set("arn", fmt.Sprint("arn:aws:s3:::", d.Id())) 445 446 return nil 447 } 448 449 func resourceAwsS3BucketDelete(d *schema.ResourceData, meta interface{}) error { 450 s3conn := meta.(*AWSClient).s3conn 451 452 log.Printf("[DEBUG] S3 Delete Bucket: %s", d.Id()) 453 _, err := s3conn.DeleteBucket(&s3.DeleteBucketInput{ 454 Bucket: aws.String(d.Id()), 455 }) 456 if err != nil { 457 ec2err, ok := err.(awserr.Error) 458 if ok && ec2err.Code() == "BucketNotEmpty" { 459 if d.Get("force_destroy").(bool) { 460 // bucket may have things delete them 461 log.Printf("[DEBUG] S3 Bucket attempting to forceDestroy %+v", err) 462 463 bucket := d.Get("bucket").(string) 464 resp, err := s3conn.ListObjectVersions( 465 &s3.ListObjectVersionsInput{ 466 Bucket: aws.String(bucket), 467 }, 468 ) 469 470 if err != nil { 471 return fmt.Errorf("Error S3 Bucket list Object Versions err: %s", err) 472 } 473 474 objectsToDelete := make([]*s3.ObjectIdentifier, 0) 475 476 if len(resp.DeleteMarkers) != 0 { 477 478 for _, v := range resp.DeleteMarkers { 479 objectsToDelete = append(objectsToDelete, &s3.ObjectIdentifier{ 480 Key: v.Key, 481 VersionId: v.VersionId, 482 }) 483 } 484 } 485 486 if len(resp.Versions) != 0 { 487 for _, v := range resp.Versions { 488 objectsToDelete = append(objectsToDelete, &s3.ObjectIdentifier{ 489 Key: v.Key, 490 VersionId: v.VersionId, 491 }) 492 } 493 } 494 495 params := &s3.DeleteObjectsInput{ 496 Bucket: aws.String(bucket), 497 Delete: &s3.Delete{ 498 Objects: objectsToDelete, 499 }, 500 } 501 502 _, err = s3conn.DeleteObjects(params) 503 504 if err != nil { 505 return fmt.Errorf("Error S3 Bucket force_destroy error deleting: %s", err) 506 } 507 508 // this line recurses until all objects are deleted or an error is returned 509 return resourceAwsS3BucketDelete(d, meta) 510 } 511 } 512 return fmt.Errorf("Error deleting S3 Bucket: %s", err) 513 } 514 return nil 515 } 516 517 func resourceAwsS3BucketPolicyUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 518 bucket := d.Get("bucket").(string) 519 policy := d.Get("policy").(string) 520 521 if policy != "" { 522 log.Printf("[DEBUG] S3 bucket: %s, put policy: %s", bucket, policy) 523 524 params := &s3.PutBucketPolicyInput{ 525 Bucket: aws.String(bucket), 526 Policy: aws.String(policy), 527 } 528 529 err := resource.Retry(1*time.Minute, func() error { 530 if _, err := s3conn.PutBucketPolicy(params); err != nil { 531 if awserr, ok := err.(awserr.Error); ok { 532 if awserr.Code() == "MalformedPolicy" { 533 // Retryable 534 return awserr 535 } 536 } 537 // Not retryable 538 return resource.RetryError{Err: err} 539 } 540 // No error 541 return nil 542 }) 543 544 if err != nil { 545 return fmt.Errorf("Error putting S3 policy: %s", err) 546 } 547 } else { 548 log.Printf("[DEBUG] S3 bucket: %s, delete policy: %s", bucket, policy) 549 _, err := s3conn.DeleteBucketPolicy(&s3.DeleteBucketPolicyInput{ 550 Bucket: aws.String(bucket), 551 }) 552 553 if err != nil { 554 return fmt.Errorf("Error deleting S3 policy: %s", err) 555 } 556 } 557 558 return nil 559 } 560 561 func resourceAwsS3BucketCorsUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 562 bucket := d.Get("bucket").(string) 563 rawCors := d.Get("cors_rule").([]interface{}) 564 565 if len(rawCors) == 0 { 566 // Delete CORS 567 log.Printf("[DEBUG] S3 bucket: %s, delete CORS", bucket) 568 _, err := s3conn.DeleteBucketCors(&s3.DeleteBucketCorsInput{ 569 Bucket: aws.String(bucket), 570 }) 571 if err != nil { 572 return fmt.Errorf("Error deleting S3 CORS: %s", err) 573 } 574 } else { 575 // Put CORS 576 rules := make([]*s3.CORSRule, 0, len(rawCors)) 577 for _, cors := range rawCors { 578 corsMap := cors.(map[string]interface{}) 579 r := &s3.CORSRule{} 580 for k, v := range corsMap { 581 log.Printf("[DEBUG] S3 bucket: %s, put CORS: %#v, %#v", bucket, k, v) 582 if k == "max_age_seconds" { 583 r.MaxAgeSeconds = aws.Int64(int64(v.(int))) 584 } else { 585 vMap := make([]*string, len(v.([]interface{}))) 586 for i, vv := range v.([]interface{}) { 587 str := vv.(string) 588 vMap[i] = aws.String(str) 589 } 590 switch k { 591 case "allowed_headers": 592 r.AllowedHeaders = vMap 593 case "allowed_methods": 594 r.AllowedMethods = vMap 595 case "allowed_origins": 596 r.AllowedOrigins = vMap 597 case "expose_headers": 598 r.ExposeHeaders = vMap 599 } 600 } 601 } 602 rules = append(rules, r) 603 } 604 corsInput := &s3.PutBucketCorsInput{ 605 Bucket: aws.String(bucket), 606 CORSConfiguration: &s3.CORSConfiguration{ 607 CORSRules: rules, 608 }, 609 } 610 log.Printf("[DEBUG] S3 bucket: %s, put CORS: %#v", bucket, corsInput) 611 _, err := s3conn.PutBucketCors(corsInput) 612 if err != nil { 613 return fmt.Errorf("Error putting S3 CORS: %s", err) 614 } 615 } 616 617 return nil 618 } 619 620 func resourceAwsS3BucketWebsiteUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 621 ws := d.Get("website").([]interface{}) 622 623 if len(ws) == 1 { 624 w := ws[0].(map[string]interface{}) 625 return resourceAwsS3BucketWebsitePut(s3conn, d, w) 626 } else if len(ws) == 0 { 627 return resourceAwsS3BucketWebsiteDelete(s3conn, d) 628 } else { 629 return fmt.Errorf("Cannot specify more than one website.") 630 } 631 } 632 633 func resourceAwsS3BucketWebsitePut(s3conn *s3.S3, d *schema.ResourceData, website map[string]interface{}) error { 634 bucket := d.Get("bucket").(string) 635 636 indexDocument := website["index_document"].(string) 637 errorDocument := website["error_document"].(string) 638 redirectAllRequestsTo := website["redirect_all_requests_to"].(string) 639 640 if indexDocument == "" && redirectAllRequestsTo == "" { 641 return fmt.Errorf("Must specify either index_document or redirect_all_requests_to.") 642 } 643 644 websiteConfiguration := &s3.WebsiteConfiguration{} 645 646 if indexDocument != "" { 647 websiteConfiguration.IndexDocument = &s3.IndexDocument{Suffix: aws.String(indexDocument)} 648 } 649 650 if errorDocument != "" { 651 websiteConfiguration.ErrorDocument = &s3.ErrorDocument{Key: aws.String(errorDocument)} 652 } 653 654 if redirectAllRequestsTo != "" { 655 websiteConfiguration.RedirectAllRequestsTo = &s3.RedirectAllRequestsTo{HostName: aws.String(redirectAllRequestsTo)} 656 } 657 658 putInput := &s3.PutBucketWebsiteInput{ 659 Bucket: aws.String(bucket), 660 WebsiteConfiguration: websiteConfiguration, 661 } 662 663 log.Printf("[DEBUG] S3 put bucket website: %#v", putInput) 664 665 _, err := s3conn.PutBucketWebsite(putInput) 666 if err != nil { 667 return fmt.Errorf("Error putting S3 website: %s", err) 668 } 669 670 return nil 671 } 672 673 func resourceAwsS3BucketWebsiteDelete(s3conn *s3.S3, d *schema.ResourceData) error { 674 bucket := d.Get("bucket").(string) 675 deleteInput := &s3.DeleteBucketWebsiteInput{Bucket: aws.String(bucket)} 676 677 log.Printf("[DEBUG] S3 delete bucket website: %#v", deleteInput) 678 679 _, err := s3conn.DeleteBucketWebsite(deleteInput) 680 if err != nil { 681 return fmt.Errorf("Error deleting S3 website: %s", err) 682 } 683 684 d.Set("website_endpoint", "") 685 d.Set("website_domain", "") 686 687 return nil 688 } 689 690 func websiteEndpoint(s3conn *s3.S3, d *schema.ResourceData) (*S3Website, error) { 691 // If the bucket doesn't have a website configuration, return an empty 692 // endpoint 693 if _, ok := d.GetOk("website"); !ok { 694 return nil, nil 695 } 696 697 bucket := d.Get("bucket").(string) 698 699 // Lookup the region for this bucket 700 location, err := s3conn.GetBucketLocation( 701 &s3.GetBucketLocationInput{ 702 Bucket: aws.String(bucket), 703 }, 704 ) 705 if err != nil { 706 return nil, err 707 } 708 var region string 709 if location.LocationConstraint != nil { 710 region = *location.LocationConstraint 711 } 712 713 return WebsiteEndpoint(bucket, region), nil 714 } 715 716 func WebsiteEndpoint(bucket string, region string) *S3Website { 717 domain := WebsiteDomainUrl(region) 718 return &S3Website{Endpoint: fmt.Sprintf("%s.%s", bucket, domain), Domain: domain} 719 } 720 721 func WebsiteDomainUrl(region string) string { 722 region = normalizeRegion(region) 723 724 // Frankfurt(and probably future) regions uses different syntax for website endpoints 725 // http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html 726 if region == "eu-central-1" { 727 return fmt.Sprintf("s3-website.%s.amazonaws.com", region) 728 } 729 730 return fmt.Sprintf("s3-website-%s.amazonaws.com", region) 731 } 732 733 func resourceAwsS3BucketAclUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 734 acl := d.Get("acl").(string) 735 bucket := d.Get("bucket").(string) 736 737 i := &s3.PutBucketAclInput{ 738 Bucket: aws.String(bucket), 739 ACL: aws.String(acl), 740 } 741 log.Printf("[DEBUG] S3 put bucket ACL: %#v", i) 742 743 _, err := s3conn.PutBucketAcl(i) 744 if err != nil { 745 return fmt.Errorf("Error putting S3 ACL: %s", err) 746 } 747 748 return nil 749 } 750 751 func resourceAwsS3BucketVersioningUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 752 v := d.Get("versioning").(*schema.Set).List() 753 bucket := d.Get("bucket").(string) 754 vc := &s3.VersioningConfiguration{} 755 756 if len(v) > 0 { 757 c := v[0].(map[string]interface{}) 758 759 if c["enabled"].(bool) { 760 vc.Status = aws.String(s3.BucketVersioningStatusEnabled) 761 } else { 762 vc.Status = aws.String(s3.BucketVersioningStatusSuspended) 763 } 764 } else { 765 vc.Status = aws.String(s3.BucketVersioningStatusSuspended) 766 } 767 768 i := &s3.PutBucketVersioningInput{ 769 Bucket: aws.String(bucket), 770 VersioningConfiguration: vc, 771 } 772 log.Printf("[DEBUG] S3 put bucket versioning: %#v", i) 773 774 _, err := s3conn.PutBucketVersioning(i) 775 if err != nil { 776 return fmt.Errorf("Error putting S3 versioning: %s", err) 777 } 778 779 return nil 780 } 781 782 func resourceAwsS3BucketLoggingUpdate(s3conn *s3.S3, d *schema.ResourceData) error { 783 logging := d.Get("logging").(*schema.Set).List() 784 bucket := d.Get("bucket").(string) 785 loggingStatus := &s3.BucketLoggingStatus{} 786 787 if len(logging) > 0 { 788 c := logging[0].(map[string]interface{}) 789 790 loggingEnabled := &s3.LoggingEnabled{} 791 if val, ok := c["target_bucket"]; ok { 792 loggingEnabled.TargetBucket = aws.String(val.(string)) 793 } 794 if val, ok := c["target_prefix"]; ok { 795 loggingEnabled.TargetPrefix = aws.String(val.(string)) 796 } 797 798 loggingStatus.LoggingEnabled = loggingEnabled 799 } 800 801 i := &s3.PutBucketLoggingInput{ 802 Bucket: aws.String(bucket), 803 BucketLoggingStatus: loggingStatus, 804 } 805 log.Printf("[DEBUG] S3 put bucket logging: %#v", i) 806 807 _, err := s3conn.PutBucketLogging(i) 808 if err != nil { 809 return fmt.Errorf("Error putting S3 logging: %s", err) 810 } 811 812 return nil 813 } 814 815 func normalizeJson(jsonString interface{}) string { 816 if jsonString == nil { 817 return "" 818 } 819 j := make(map[string]interface{}) 820 err := json.Unmarshal([]byte(jsonString.(string)), &j) 821 if err != nil { 822 return fmt.Sprintf("Error parsing JSON: %s", err) 823 } 824 b, _ := json.Marshal(j) 825 return string(b[:]) 826 } 827 828 func normalizeRegion(region string) string { 829 // Default to us-east-1 if the bucket doesn't have a region: 830 // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETlocation.html 831 if region == "" { 832 region = "us-east-1" 833 } 834 835 return region 836 } 837 838 type S3Website struct { 839 Endpoint, Domain string 840 }