Merge branch 'master' of https://github.com/GiovanniPaoloGibilisco/terraform
This commit is contained in:
commit
7bb7508c86
|
@ -6,6 +6,8 @@ FEATURES:
|
|||
* **New Resource:** `aws_load_balancer_policy` [GH-7458]
|
||||
* **New Resource:** `aws_load_balancer_backend_server_policy` [GH-7458]
|
||||
* **New Resource:** `aws_load_balancer_listener_policy` [GH-7458]
|
||||
* **New Data Source:** `aws_ip_ranges` [GH-7984]
|
||||
* **New Data Source:** `fastly_ip_ranges` [GH-7984]
|
||||
|
||||
IMPROVEMENTS
|
||||
* provider/aws: Introduce `aws_elasticsearch_domain` `elasticsearch_version` field (to specify ES version) [GH-7860]
|
||||
|
@ -14,6 +16,8 @@ IMPROVEMENTS
|
|||
* provider/aws: Add support for Elasticsearch destination to firehose delivery streams [GH-7839]
|
||||
* provider/aws: Retry AttachInternetGateway and increase timeout on `aws_internet_gateway` [GH-7891]
|
||||
* provider/aws: Add support for Enhanced monitoring to `aws_rds_cluster_instance` [GH-8038]
|
||||
* provider/aws: Add ability to set Requests Payer in `aws_s3_bucket` [GH-8065]
|
||||
* provider/aws: Add ability to set canned ACL in `aws_s3_bucket_object` [GH-8091]
|
||||
* provider/azurerm: Adds support for uploading blobs to azure storage from local source [GH-7994]
|
||||
* provider/google: allows atomic Cloud DNS record changes [GH-6575]
|
||||
* provider/google: Move URLMap hosts to TypeSet from TypeList [GH-7472]
|
||||
|
@ -30,7 +34,9 @@ BUG FIXES:
|
|||
* provider/aws: Retry association of IAM Role & instance profile [GH-7938]
|
||||
* provider/aws: Fix `aws_s3_bucket` resource `redirect_all_requests_to` action [GH-7883]
|
||||
* provider/aws: Fix issue updating ElasticBeanstalk Environment Settings [GH-7777]
|
||||
* providers/aws: `aws_rds_cluster` creation timeout bumped to 40 minutes [GH-8052]
|
||||
* provider/aws: `aws_rds_cluster` creation timeout bumped to 40 minutes [GH-8052]
|
||||
* provider/aws: Fix line ending errors/diffs with IAM Server Certs [GH-8074]
|
||||
* provider/aws: Fixing IAM data source policy generation to prevent spurious diffs [GH-6956]
|
||||
* provider/google: Use resource specific project when making queries/changes [GH-7029]
|
||||
* provider/google: Fix read for the backend service resource [GH-7476]
|
||||
|
||||
|
|
|
@ -150,12 +150,19 @@ func dataSourceAwsIamPolicyDocumentRead(d *schema.ResourceData, meta interface{}
|
|||
return nil
|
||||
}
|
||||
|
||||
func dataSourceAwsIamPolicyDocumentReplaceVarsInList(in []string) []string {
|
||||
out := make([]string, len(in))
|
||||
for i, item := range in {
|
||||
out[i] = dataSourceAwsIamPolicyDocumentVarReplacer.Replace(item)
|
||||
func dataSourceAwsIamPolicyDocumentReplaceVarsInList(in interface{}) interface{} {
|
||||
switch v := in.(type) {
|
||||
case string:
|
||||
return dataSourceAwsIamPolicyDocumentVarReplacer.Replace(v)
|
||||
case []string:
|
||||
out := make([]string, len(v))
|
||||
for i, item := range v {
|
||||
out[i] = dataSourceAwsIamPolicyDocumentVarReplacer.Replace(item)
|
||||
}
|
||||
return out
|
||||
default:
|
||||
panic("dataSourceAwsIamPolicyDocumentReplaceVarsInList: input not string nor []string")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func dataSourceAwsIamPolicyDocumentMakeConditions(in []interface{}) IAMPolicyStatementConditionSet {
|
||||
|
|
|
@ -75,7 +75,6 @@ data "aws_iam_policy_document" "test" {
|
|||
test = "StringLike"
|
||||
variable = "s3:prefix"
|
||||
values = [
|
||||
"",
|
||||
"home/",
|
||||
"home/&{aws:username}/",
|
||||
]
|
||||
|
@ -118,59 +117,45 @@ var testAccAWSIAMPolicyDocumentExpectedJSON = `{
|
|||
"Sid": "1",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:ListAllMyBuckets"
|
||||
"s3:ListAllMyBuckets",
|
||||
"s3:GetBucketLocation"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
"Resource": "arn:aws:s3:::*"
|
||||
},
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:ListBucket"
|
||||
],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::foo"
|
||||
],
|
||||
"Action": "s3:ListBucket",
|
||||
"Resource": "arn:aws:s3:::foo",
|
||||
"NotPrincipal": {
|
||||
"AWS": [
|
||||
"arn:blahblah:example"
|
||||
]
|
||||
"AWS": "arn:blahblah:example"
|
||||
},
|
||||
"Condition": {
|
||||
"StringLike": {
|
||||
"s3:prefix": [
|
||||
"",
|
||||
"home/",
|
||||
"home/${aws:username}/"
|
||||
"home/${aws:username}/",
|
||||
"home/"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:*"
|
||||
],
|
||||
"Action": "s3:*",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::foo/home/${aws:username}/*",
|
||||
"arn:aws:s3:::foo/home/${aws:username}"
|
||||
],
|
||||
"Principal": {
|
||||
"AWS": [
|
||||
"arn:blahblah:example"
|
||||
]
|
||||
"AWS": "arn:blahblah:example"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Deny",
|
||||
"NotAction": [
|
||||
"s3:*"
|
||||
],
|
||||
"NotResource": [
|
||||
"arn:aws:s3:::*"
|
||||
]
|
||||
"NotAction": "s3:*",
|
||||
"NotResource": "arn:aws:s3:::*"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
type dataSourceAwsIPRangesResult struct {
|
||||
CreateDate string
|
||||
Prefixes []dataSourceAwsIPRangesPrefix
|
||||
SyncToken string
|
||||
}
|
||||
|
||||
type dataSourceAwsIPRangesPrefix struct {
|
||||
IpPrefix string `json:"ip_prefix"`
|
||||
Region string
|
||||
Service string
|
||||
}
|
||||
|
||||
func dataSourceAwsIPRanges() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceAwsIPRangesRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cidr_blocks": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
"create_date": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
"regions": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
Optional: true,
|
||||
},
|
||||
"services": &schema.Schema{
|
||||
Type: schema.TypeSet,
|
||||
Required: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
"sync_token": &schema.Schema{
|
||||
Type: schema.TypeInt,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceAwsIPRangesRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
conn := cleanhttp.DefaultClient()
|
||||
|
||||
log.Printf("[DEBUG] Reading IP ranges")
|
||||
|
||||
res, err := conn.Get("https://ip-ranges.amazonaws.com/ip-ranges.json")
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error listing IP ranges: %s", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading response body: %s", err)
|
||||
}
|
||||
|
||||
result := new(dataSourceAwsIPRangesResult)
|
||||
|
||||
if err := json.Unmarshal(data, result); err != nil {
|
||||
return fmt.Errorf("Error parsing result: %s", err)
|
||||
}
|
||||
|
||||
if err := d.Set("create_date", result.CreateDate); err != nil {
|
||||
return fmt.Errorf("Error setting create date: %s", err)
|
||||
}
|
||||
|
||||
syncToken, err := strconv.Atoi(result.SyncToken)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while converting sync token: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(result.SyncToken)
|
||||
|
||||
if err := d.Set("sync_token", syncToken); err != nil {
|
||||
return fmt.Errorf("Error setting sync token: %s", err)
|
||||
}
|
||||
|
||||
get := func(key string) *schema.Set {
|
||||
|
||||
set := d.Get(key).(*schema.Set)
|
||||
|
||||
for _, e := range set.List() {
|
||||
|
||||
s := e.(string)
|
||||
|
||||
set.Remove(s)
|
||||
set.Add(strings.ToLower(s))
|
||||
|
||||
}
|
||||
|
||||
return set
|
||||
|
||||
}
|
||||
|
||||
var (
|
||||
regions = get("regions")
|
||||
services = get("services")
|
||||
noRegionFilter = regions.Len() == 0
|
||||
prefixes []string
|
||||
)
|
||||
|
||||
for _, e := range result.Prefixes {
|
||||
|
||||
var (
|
||||
matchRegion = noRegionFilter || regions.Contains(strings.ToLower(e.Region))
|
||||
matchService = services.Contains(strings.ToLower(e.Service))
|
||||
)
|
||||
|
||||
if matchRegion && matchService {
|
||||
prefixes = append(prefixes, e.IpPrefix)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(prefixes) == 0 {
|
||||
return fmt.Errorf(" No IP ranges result from filters")
|
||||
}
|
||||
|
||||
sort.Strings(prefixes)
|
||||
|
||||
if err := d.Set("cidr_blocks", prefixes); err != nil {
|
||||
return fmt.Errorf("Error setting ip ranges: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccAWSIPRanges(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSIPRangesConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccAWSIPRanges("data.aws_ip_ranges.some"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccAWSIPRanges(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
r := s.RootModule().Resources[n]
|
||||
a := r.Primary.Attributes
|
||||
|
||||
var (
|
||||
cidrBlockSize int
|
||||
createDate time.Time
|
||||
err error
|
||||
syncToken int
|
||||
)
|
||||
|
||||
if cidrBlockSize, err = strconv.Atoi(a["cidr_blocks.#"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cidrBlockSize < 10 {
|
||||
return fmt.Errorf("cidr_blocks for eu-west-1 seem suspiciously low: %d", cidrBlockSize)
|
||||
}
|
||||
|
||||
if createDate, err = time.Parse("2006-01-02-15-04-05", a["create_date"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncToken, err = strconv.Atoi(a["sync_token"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if syncToken != int(createDate.Unix()) {
|
||||
return fmt.Errorf("sync_token %d does not match create_date %s", syncToken, createDate)
|
||||
}
|
||||
|
||||
var cidrBlocks sort.StringSlice = make([]string, cidrBlockSize)
|
||||
|
||||
for i := range make([]string, cidrBlockSize) {
|
||||
|
||||
block := a[fmt.Sprintf("cidr_blocks.%d", i)]
|
||||
|
||||
if _, _, err := net.ParseCIDR(block); err != nil {
|
||||
return fmt.Errorf("malformed CIDR block %s: %s", block, err)
|
||||
}
|
||||
|
||||
cidrBlocks[i] = block
|
||||
|
||||
}
|
||||
|
||||
if !sort.IsSorted(cidrBlocks) {
|
||||
return fmt.Errorf("unexpected order of cidr_blocks: %s", cidrBlocks)
|
||||
}
|
||||
|
||||
var (
|
||||
regionMember = regexp.MustCompile(`regions\.\d+`)
|
||||
regions, services int
|
||||
serviceMember = regexp.MustCompile(`services\.\d+`)
|
||||
)
|
||||
|
||||
for k, v := range a {
|
||||
|
||||
if regionMember.MatchString(k) {
|
||||
|
||||
if !(v == "eu-west-1" || v == "EU-central-1") {
|
||||
return fmt.Errorf("unexpected region %s", v)
|
||||
}
|
||||
|
||||
regions = regions + 1
|
||||
|
||||
}
|
||||
|
||||
if serviceMember.MatchString(k) {
|
||||
|
||||
if v != "EC2" {
|
||||
return fmt.Errorf("unexpected service %s", v)
|
||||
}
|
||||
|
||||
services = services + 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if regions != 2 {
|
||||
return fmt.Errorf("unexpected number of regions: %d", regions)
|
||||
}
|
||||
|
||||
if services != 1 {
|
||||
return fmt.Errorf("unexpected number of services: %d", services)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccAWSIPRangesConfig = `
|
||||
data "aws_ip_ranges" "some" {
|
||||
regions = [ "eu-west-1", "EU-central-1" ]
|
||||
services = [ "EC2" ]
|
||||
}
|
||||
`
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type IAMPolicyDoc struct {
|
||||
|
@ -11,12 +12,12 @@ type IAMPolicyDoc struct {
|
|||
}
|
||||
|
||||
type IAMPolicyStatement struct {
|
||||
Sid string `json:",omitempty"`
|
||||
Sid string
|
||||
Effect string `json:",omitempty"`
|
||||
Actions []string `json:"Action,omitempty"`
|
||||
NotActions []string `json:"NotAction,omitempty"`
|
||||
Resources []string `json:"Resource,omitempty"`
|
||||
NotResources []string `json:"NotResource,omitempty"`
|
||||
Actions interface{} `json:"Action,omitempty"`
|
||||
NotActions interface{} `json:"NotAction,omitempty"`
|
||||
Resources interface{} `json:"Resource,omitempty"`
|
||||
NotResources interface{} `json:"NotResource,omitempty"`
|
||||
Principals IAMPolicyStatementPrincipalSet `json:"Principal,omitempty"`
|
||||
NotPrincipals IAMPolicyStatementPrincipalSet `json:"NotPrincipal,omitempty"`
|
||||
Conditions IAMPolicyStatementConditionSet `json:"Condition,omitempty"`
|
||||
|
@ -24,51 +25,71 @@ type IAMPolicyStatement struct {
|
|||
|
||||
type IAMPolicyStatementPrincipal struct {
|
||||
Type string
|
||||
Identifiers []string
|
||||
Identifiers interface{}
|
||||
}
|
||||
|
||||
type IAMPolicyStatementCondition struct {
|
||||
Test string
|
||||
Variable string
|
||||
Values []string
|
||||
Values interface{}
|
||||
}
|
||||
|
||||
type IAMPolicyStatementPrincipalSet []IAMPolicyStatementPrincipal
|
||||
type IAMPolicyStatementConditionSet []IAMPolicyStatementCondition
|
||||
|
||||
func (ps IAMPolicyStatementPrincipalSet) MarshalJSON() ([]byte, error) {
|
||||
raw := map[string][]string{}
|
||||
raw := map[string]interface{}{}
|
||||
|
||||
for _, p := range ps {
|
||||
if _, ok := raw[p.Type]; !ok {
|
||||
raw[p.Type] = make([]string, 0, len(p.Identifiers))
|
||||
switch i := p.Identifiers.(type) {
|
||||
case []string:
|
||||
if _, ok := raw[p.Type]; !ok {
|
||||
raw[p.Type] = make([]string, 0, len(i))
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(i)))
|
||||
raw[p.Type] = append(raw[p.Type].([]string), i...)
|
||||
case string:
|
||||
raw[p.Type] = i
|
||||
default:
|
||||
panic("Unsupported data type for IAMPolicyStatementPrincipalSet")
|
||||
}
|
||||
raw[p.Type] = append(raw[p.Type], p.Identifiers...)
|
||||
}
|
||||
|
||||
return json.Marshal(&raw)
|
||||
}
|
||||
|
||||
func (cs IAMPolicyStatementConditionSet) MarshalJSON() ([]byte, error) {
|
||||
raw := map[string]map[string][]string{}
|
||||
raw := map[string]map[string]interface{}{}
|
||||
|
||||
for _, c := range cs {
|
||||
if _, ok := raw[c.Test]; !ok {
|
||||
raw[c.Test] = map[string][]string{}
|
||||
raw[c.Test] = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := raw[c.Test][c.Variable]; !ok {
|
||||
raw[c.Test][c.Variable] = make([]string, 0, len(c.Values))
|
||||
switch i := c.Values.(type) {
|
||||
case []string:
|
||||
if _, ok := raw[c.Test][c.Variable]; !ok {
|
||||
raw[c.Test][c.Variable] = make([]string, 0, len(i))
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(i)))
|
||||
raw[c.Test][c.Variable] = append(raw[c.Test][c.Variable].([]string), i...)
|
||||
case string:
|
||||
raw[c.Test][c.Variable] = i
|
||||
default:
|
||||
panic("Unsupported data type for IAMPolicyStatementConditionSet")
|
||||
}
|
||||
raw[c.Test][c.Variable] = append(raw[c.Test][c.Variable], c.Values...)
|
||||
}
|
||||
|
||||
return json.Marshal(&raw)
|
||||
}
|
||||
|
||||
func iamPolicyDecodeConfigStringList(lI []interface{}) []string {
|
||||
func iamPolicyDecodeConfigStringList(lI []interface{}) interface{} {
|
||||
if len(lI) == 1 {
|
||||
return lI[0].(string)
|
||||
}
|
||||
ret := make([]string, len(lI))
|
||||
for i, vI := range lI {
|
||||
ret[i] = vI.(string)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(ret)))
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
)
|
||||
|
||||
func TestAccAWSRole_importBasic(t *testing.T) {
|
||||
resourceName := "aws_iam_role.role"
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSRoleDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSRoleConfig,
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
ResourceName: resourceName,
|
||||
ImportState: true,
|
||||
ImportStateVerify: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -114,6 +114,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"aws_ami": dataSourceAwsAmi(),
|
||||
"aws_availability_zones": dataSourceAwsAvailabilityZones(),
|
||||
"aws_iam_policy_document": dataSourceAwsIamPolicyDocument(),
|
||||
"aws_ip_ranges": dataSourceAwsIPRanges(),
|
||||
"aws_s3_bucket_object": dataSourceAwsS3BucketObject(),
|
||||
"aws_ecs_container_definition": dataSourceAwsEcsContainerDefinition(),
|
||||
},
|
||||
|
|
|
@ -120,8 +120,8 @@ func testAccCheckAWSInstanceProfileExists(n string, res *iam.GetInstanceProfileO
|
|||
|
||||
const testAccAwsIamInstanceProfileConfig = `
|
||||
resource "aws_iam_role" "test" {
|
||||
name = "test"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||
name = "test"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "test" {
|
||||
|
@ -132,8 +132,8 @@ resource "aws_iam_instance_profile" "test" {
|
|||
|
||||
const testAccAWSInstanceProfilePrefixNameConfig = `
|
||||
resource "aws_iam_role" "test" {
|
||||
name = "test"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||
name = "test"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_instance_profile" "test" {
|
||||
|
|
|
@ -113,22 +113,8 @@ resource "aws_iam_user" "user" {
|
|||
name = "test-user"
|
||||
}
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "test-role"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
name = "test-role"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_group" "group" {
|
||||
|
@ -174,61 +160,16 @@ resource "aws_iam_user" "user3" {
|
|||
name = "test-user3"
|
||||
}
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "test-role"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
name = "test-role"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
resource "aws_iam_role" "role2" {
|
||||
name = "test-role2"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
name = "test-role2"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
resource "aws_iam_role" "role3" {
|
||||
name = "test-role3"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
name = "test-role3"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
resource "aws_iam_group" "group" {
|
||||
name = "test-group"
|
||||
|
|
|
@ -2,6 +2,7 @@ package aws
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
|
@ -20,6 +21,10 @@ func resourceAwsIamRole() *schema.Resource {
|
|||
Update: resourceAwsIamRoleUpdate,
|
||||
Delete: resourceAwsIamRoleDelete,
|
||||
|
||||
Importer: &schema.ResourceImporter{
|
||||
State: schema.ImportStatePassthrough,
|
||||
},
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"arn": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
|
@ -174,6 +179,10 @@ func resourceAwsIamRoleReadResult(d *schema.ResourceData, role *iam.Role) error
|
|||
if err := d.Set("unique_id", role.RoleId); err != nil {
|
||||
return err
|
||||
}
|
||||
policy, _ := url.QueryUnescape(*role.AssumeRolePolicyDocument)
|
||||
if err := d.Set("assume_role_policy", aws.String(policy)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -90,22 +90,8 @@ func testAccCheckAWSRolePolicyAttachmentAttributes(policies []string, out *iam.L
|
|||
|
||||
const testAccAWSRolePolicyAttachConfig = `
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "test-role"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
name = "test-role"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_policy" "policy" {
|
||||
|
@ -135,22 +121,8 @@ resource "aws_iam_role_policy_attachment" "test-attach" {
|
|||
|
||||
const testAccAWSRolePolicyAttachConfigUpdate = `
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "test-role"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
name = "test-role"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_policy" "policy" {
|
||||
|
|
|
@ -113,15 +113,15 @@ func testAccCheckIAMRolePolicy(
|
|||
func testAccIAMRolePolicyConfig(role, policy1 string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "tf_test_role_%s"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Effect\":\"Allow\",\"Sid\":\"\"}]}"
|
||||
name = "tf_test_role_%s"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "foo" {
|
||||
name = "tf_test_policy_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
name = "tf_test_policy_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
}
|
||||
`, role, policy1)
|
||||
}
|
||||
|
@ -129,21 +129,21 @@ resource "aws_iam_role_policy" "foo" {
|
|||
func testAccIAMRolePolicyConfigUpdate(role, policy1, policy2 string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "tf_test_role_%s"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":\"sts:AssumeRole\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Effect\":\"Allow\",\"Sid\":\"\"}]}"
|
||||
name = "tf_test_role_%s"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "foo" {
|
||||
name = "tf_test_policy_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
name = "tf_test_policy_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "bar" {
|
||||
name = "tf_test_policy_2_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
name = "tf_test_policy_2_%s"
|
||||
role = "${aws_iam_role.role.name}"
|
||||
policy = "{\"Version\":\"2012-10-17\",\"Statement\":{\"Effect\":\"Allow\",\"Action\":\"*\",\"Resource\":\"*\"}}"
|
||||
}
|
||||
`, role, policy1, policy2)
|
||||
}
|
||||
|
|
|
@ -165,39 +165,24 @@ func testAccCheckAWSRoleAttributes(role *iam.GetRoleOutput) resource.TestCheckFu
|
|||
|
||||
const testAccAWSRoleConfig = `
|
||||
resource "aws_iam_role" "role" {
|
||||
name = "test-role"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||
name = "test-role"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSRolePrefixNameConfig = `
|
||||
resource "aws_iam_role" "role" {
|
||||
name_prefix = "test-role-"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"ec2.amazonaws.com\"]},\"Action\":[\"sts:AssumeRole\"]}]}"
|
||||
name_prefix = "test-role-"
|
||||
path = "/"
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
`
|
||||
|
||||
const testAccAWSRolePre = `
|
||||
resource "aws_iam_role" "role_update_test" {
|
||||
name = "tf_old_name"
|
||||
path = "/test/"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "role_update_test" {
|
||||
|
@ -232,21 +217,7 @@ const testAccAWSRolePost = `
|
|||
resource "aws_iam_role" "role_update_test" {
|
||||
name = "tf_new_name"
|
||||
path = "/test/"
|
||||
assume_role_policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Action": "sts:AssumeRole",
|
||||
"Principal": {
|
||||
"Service": "ec2.amazonaws.com"
|
||||
},
|
||||
"Effect": "Allow",
|
||||
"Sid": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
assume_role_policy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"ec2.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "role_update_test" {
|
||||
|
|
|
@ -201,14 +201,30 @@ func normalizeCert(cert interface{}) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
var rawCert string
|
||||
switch cert.(type) {
|
||||
case string:
|
||||
hash := sha1.Sum([]byte(strings.TrimSpace(cert.(string))))
|
||||
return hex.EncodeToString(hash[:])
|
||||
rawCert = cert.(string)
|
||||
case *string:
|
||||
hash := sha1.Sum([]byte(strings.TrimSpace(*cert.(*string))))
|
||||
return hex.EncodeToString(hash[:])
|
||||
rawCert = *cert.(*string)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
cleanVal := sha1.Sum(stripCR([]byte(strings.TrimSpace(rawCert))))
|
||||
return hex.EncodeToString(cleanVal[:])
|
||||
}
|
||||
|
||||
// strip CRs from raw literals. Lifted from go/scanner/scanner.go
|
||||
// See https://github.com/golang/go/blob/release-branch.go1.6/src/go/scanner/scanner.go#L479
|
||||
func stripCR(b []byte) []byte {
|
||||
c := make([]byte, len(b))
|
||||
i := 0
|
||||
for _, ch := range b {
|
||||
if ch != '\r' {
|
||||
c[i] = ch
|
||||
i++
|
||||
}
|
||||
}
|
||||
return c[:i]
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/iam"
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
@ -86,6 +87,35 @@ func TestAccAWSIAMServerCertificate_disappears(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSIAMServerCertificate_file(t *testing.T) {
|
||||
var cert iam.ServerCertificate
|
||||
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckIAMServerCertificateDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-unix-line-endings"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
testAccCheckAWSServerCertAttributes(&cert),
|
||||
),
|
||||
},
|
||||
|
||||
resource.TestStep{
|
||||
Config: testAccIAMServerCertConfig_file(rInt, "iam-ssl-windows-line-endings"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckCertExists("aws_iam_server_certificate.test_cert", &cert),
|
||||
testAccCheckAWSServerCertAttributes(&cert),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckCertExists(n string, cert *iam.ServerCertificate) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
@ -285,3 +315,43 @@ dg+Sd4Wjm89UQoUUoiIcstY7FPbqfBtYKfh4RYHAHV2BwDFqzZCM
|
|||
EOF
|
||||
}
|
||||
`
|
||||
|
||||
// iam-ssl-unix-line-endings
|
||||
func testAccIAMServerCertConfig_file(rInt int, fName string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_iam_server_certificate" "test_cert" {
|
||||
name = "terraform-test-cert-%d"
|
||||
certificate_body = "${file("test-fixtures/%s.pem")}"
|
||||
|
||||
private_key = <<EOF
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAv4sWWwIkbm3FmnjK676VCe5SuAFdEb6KtCpxNeVX3UGnmGmZ
|
||||
EjKGcYK1bHEMFQvGuHhY3dVX/SMPMpP3vqguA7LeGPnoJ2p/4mVnWqAw3lscHeab
|
||||
dpVaeuewtsGx/BlIRuTcqG39wTWcra2g1Vb6vOdLeb56hXdmf8NQ3AaVmvjaEY2z
|
||||
oROfaNRFjvwROpGZbjvG+N/zp4iju9QsclpjrHJqs+qQvwNlrM6a9jTqFQa8bAYG
|
||||
fLuC6B9eoMb4jydw8GwYkZICYeZVXyjREIH1SNj5O9IxBGkQOhWW/Ksa6CWwPy2j
|
||||
/j7tRyy4MaVFg3qOYg4LfuY6L+PBF9YvVhRrgwIDAQABAoIBAFqJ4h1Om+3e0WK8
|
||||
6h4YzdYN4ue7LUTv7hxPW4gASlH5cMDoWURywX3yLNN/dBiWom4b5NWmvJqY8dwU
|
||||
eSyTznxNFhJ0PjozaxOWnw4FXlQceOPhV2bsHgKudadNU1Y4lSN9lpe+tg2Xy+GE
|
||||
ituM66RTKCf502w3DioiJpx6OEkxuhrnsQAWNcGB0MnTukm2f+629V+04R5MT5V1
|
||||
nY+5Phx2BpHgYzWBKh6Px1puu7xFv5SMQda1ndlPIKb4cNp0yYn+1lHNjbOE7QL/
|
||||
oEpWgrauS5Zk/APK33v/p3wVYHrKocIFHlPiCW0uIJJLsOZDY8pQXpTlc+/xGLLy
|
||||
WBu4boECgYEA6xO+1UNh6ndJ3xGuNippH+ucTi/uq1+0tG1bd63v+75tn5l4LyY2
|
||||
CWHRaWVlVn+WnDslkQTJzFD68X+9M7Cc4oP6WnhTyPamG7HlGv5JxfFHTC9GOKmz
|
||||
sSc624BDmqYJ7Xzyhe5kc3iHzqG/L72ZF1aijZdrodQMSY1634UX6aECgYEA0Jdr
|
||||
cBPSN+mgmEY6ogN5h7sO5uNV3TQQtW2IslfWZn6JhSRF4Rf7IReng48CMy9ZhFBy
|
||||
Q7H2I1pDGjEC9gQHhgVfm+FyMSVqXfCHEW/97pvvu9ougHA0MhPep1twzTGrqg+K
|
||||
f3PLW8hVkGyCrTfWgbDlPsHgsocA/wTaQOheaqMCgYBat5z+WemQfQZh8kXDm2xE
|
||||
KD2Cota9BcsLkeQpdFNXWC6f167cqydRSZFx1fJchhJOKjkeFLX3hgzBY6VVLEPu
|
||||
2jWj8imLNTv3Fhiu6RD5NVppWRkFRuAUbmo1SPNN2+Oa5YwGCXB0a0Alip/oQYex
|
||||
zPogIB4mLlmrjNCtL4SB4QKBgCEHKMrZSJrz0irqS9RlanPUaZqjenAJE3A2xMNA
|
||||
Z0FZXdsIEEyA6JGn1i1dkoKaR7lMp5sSbZ/RZfiatBZSMwLEjQv4mYUwoHP5Ztma
|
||||
+wEyDbaX6G8L1Sfsv3+OWgETkVPfHBXsNtH0mZ/BnrtgsQVeBh52wmZiPAUlNo26
|
||||
fWCzAoGBAJOjqovLelLWzyQGqPFx/MwuI56UFXd1CmFlCIvF2WxCFmk3tlExoCN1
|
||||
HqSpt92vsgYgV7+lAb4U7Uy/v012gwiU1LK+vyAE9geo3pTjG73BNzG4H547xtbY
|
||||
dg+Sd4Wjm89UQoUUoiIcstY7FPbqfBtYKfh4RYHAHV2BwDFqzZCM
|
||||
-----END RSA PRIVATE KEY-----
|
||||
EOF
|
||||
}
|
||||
`, rInt, fName)
|
||||
}
|
||||
|
|
|
@ -286,8 +286,6 @@ func resourceAwsS3Bucket() *schema.Resource {
|
|||
},
|
||||
},
|
||||
|
||||
"tags": tagsSchema(),
|
||||
|
||||
"force_destroy": &schema.Schema{
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
|
@ -300,6 +298,15 @@ func resourceAwsS3Bucket() *schema.Resource {
|
|||
Computed: true,
|
||||
ValidateFunc: validateS3BucketAccelerationStatus,
|
||||
},
|
||||
|
||||
"request_payer": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Computed: true,
|
||||
ValidateFunc: validateS3BucketRequestPayerType,
|
||||
},
|
||||
|
||||
"tags": tagsSchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -408,6 +415,12 @@ func resourceAwsS3BucketUpdate(d *schema.ResourceData, meta interface{}) error {
|
|||
}
|
||||
}
|
||||
|
||||
if d.HasChange("request_payer") {
|
||||
if err := resourceAwsS3BucketRequestPayerUpdate(s3conn, d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return resourceAwsS3BucketRead(d, meta)
|
||||
}
|
||||
|
||||
|
@ -568,6 +581,20 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
d.Set("acceleration_status", accelerate.Status)
|
||||
}
|
||||
|
||||
// Read the request payer configuration.
|
||||
payer, err := s3conn.GetBucketRequestPayment(&s3.GetBucketRequestPaymentInput{
|
||||
Bucket: aws.String(d.Id()),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("[DEBUG] S3 Bucket: %s, read request payer: %v", d.Id(), payer)
|
||||
if payer.Payer != nil {
|
||||
if err := d.Set("request_payer", *payer.Payer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Read the logging configuration
|
||||
logging, err := s3conn.GetBucketLogging(&s3.GetBucketLoggingInput{
|
||||
Bucket: aws.String(d.Id()),
|
||||
|
@ -575,6 +602,7 @@ func resourceAwsS3BucketRead(d *schema.ResourceData, meta interface{}) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] S3 Bucket: %s, logging: %v", d.Id(), logging)
|
||||
if v := logging.LoggingEnabled; v != nil {
|
||||
lcl := make([]map[string]interface{}, 0, 1)
|
||||
|
@ -1163,6 +1191,26 @@ func resourceAwsS3BucketAccelerationUpdate(s3conn *s3.S3, d *schema.ResourceData
|
|||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsS3BucketRequestPayerUpdate(s3conn *s3.S3, d *schema.ResourceData) error {
|
||||
bucket := d.Get("bucket").(string)
|
||||
payer := d.Get("request_payer").(string)
|
||||
|
||||
i := &s3.PutBucketRequestPaymentInput{
|
||||
Bucket: aws.String(bucket),
|
||||
RequestPaymentConfiguration: &s3.RequestPaymentConfiguration{
|
||||
Payer: aws.String(payer),
|
||||
},
|
||||
}
|
||||
log.Printf("[DEBUG] S3 put bucket request payer: %#v", i)
|
||||
|
||||
_, err := s3conn.PutBucketRequestPayment(i)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error putting S3 request payer: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceAwsS3BucketLifecycleUpdate(s3conn *s3.S3, d *schema.ResourceData) error {
|
||||
bucket := d.Get("bucket").(string)
|
||||
|
||||
|
@ -1370,6 +1418,16 @@ func validateS3BucketAccelerationStatus(v interface{}, k string) (ws []string, e
|
|||
return
|
||||
}
|
||||
|
||||
func validateS3BucketRequestPayerType(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
if value != s3.PayerRequester && value != s3.PayerBucketOwner {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q contains an invalid Request Payer type %q. Valid types are either %q or %q",
|
||||
k, value, s3.PayerRequester, s3.PayerBucketOwner))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func expirationHash(v interface{}) int {
|
||||
var buf bytes.Buffer
|
||||
m := v.(map[string]interface{})
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
|
@ -30,6 +31,13 @@ func resourceAwsS3BucketObject() *schema.Resource {
|
|||
ForceNew: true,
|
||||
},
|
||||
|
||||
"acl": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Default: "private",
|
||||
Optional: true,
|
||||
ValidateFunc: validateS3BucketObjectAclType,
|
||||
},
|
||||
|
||||
"cache_control": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
|
@ -101,6 +109,7 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
|
|||
|
||||
bucket := d.Get("bucket").(string)
|
||||
key := d.Get("key").(string)
|
||||
acl := d.Get("acl").(string)
|
||||
var body io.ReadSeeker
|
||||
|
||||
if v, ok := d.GetOk("source"); ok {
|
||||
|
@ -131,6 +140,7 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
|
|||
putInput := &s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(key),
|
||||
ACL: aws.String(acl),
|
||||
Body: body,
|
||||
}
|
||||
|
||||
|
@ -251,3 +261,39 @@ func resourceAwsS3BucketObjectDelete(d *schema.ResourceData, meta interface{}) e
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateS3BucketObjectAclType(v interface{}, k string) (ws []string, errors []error) {
|
||||
value := v.(string)
|
||||
|
||||
cannedAcls := map[string]bool{
|
||||
s3.ObjectCannedACLPrivate: true,
|
||||
s3.ObjectCannedACLPublicRead: true,
|
||||
s3.ObjectCannedACLPublicReadWrite: true,
|
||||
s3.ObjectCannedACLAuthenticatedRead: true,
|
||||
s3.ObjectCannedACLAwsExecRead: true,
|
||||
s3.ObjectCannedACLBucketOwnerRead: true,
|
||||
s3.ObjectCannedACLBucketOwnerFullControl: true,
|
||||
}
|
||||
|
||||
sentenceJoin := func(m map[string]bool) string {
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, fmt.Sprintf("%q", k))
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
length := len(keys)
|
||||
words := make([]string, length)
|
||||
copy(words, keys)
|
||||
|
||||
words[length-1] = fmt.Sprintf("or %s", words[length-1])
|
||||
return strings.Join(words, ", ")
|
||||
}
|
||||
|
||||
if _, ok := cannedAcls[value]; !ok {
|
||||
errors = append(errors, fmt.Errorf(
|
||||
"%q contains an invalid canned ACL type %q. Valid types are either %s",
|
||||
k, value, sentenceJoin(cannedAcls)))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/acctest"
|
||||
|
@ -265,6 +267,104 @@ func TestAccAWSS3BucketObject_kms(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSS3BucketObject_acl(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
var obj s3.GetObjectOutput
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketObjectConfig_acl(rInt, "private"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketObjectExists(
|
||||
"aws_s3_bucket_object.object", &obj),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket_object.object",
|
||||
"acl",
|
||||
"private"),
|
||||
testAccCheckAWSS3BucketObjectAcl(
|
||||
"aws_s3_bucket_object.object",
|
||||
[]string{"FULL_CONTROL"}),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketObjectConfig_acl(rInt, "public-read"),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketObjectExists(
|
||||
"aws_s3_bucket_object.object",
|
||||
&obj),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket_object.object",
|
||||
"acl",
|
||||
"public-read"),
|
||||
testAccCheckAWSS3BucketObjectAcl(
|
||||
"aws_s3_bucket_object.object",
|
||||
[]string{"FULL_CONTROL", "READ"}),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceAWSS3BucketObjectAcl_validation(t *testing.T) {
|
||||
_, errors := validateS3BucketObjectAclType("incorrect", "acl")
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("Expected to trigger a validation error")
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
Value string
|
||||
ErrCount int
|
||||
}{
|
||||
{
|
||||
Value: "public-read",
|
||||
ErrCount: 0,
|
||||
},
|
||||
{
|
||||
Value: "public-read-write",
|
||||
ErrCount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, errors := validateS3BucketObjectAclType(tc.Value, "acl")
|
||||
if len(errors) != tc.ErrCount {
|
||||
t.Fatalf("Expected not to trigger a validation error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketObjectAcl(n string, expectedPerms []string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
s3conn := testAccProvider.Meta().(*AWSClient).s3conn
|
||||
|
||||
out, err := s3conn.GetObjectAcl(&s3.GetObjectAclInput{
|
||||
Bucket: aws.String(rs.Primary.Attributes["bucket"]),
|
||||
Key: aws.String(rs.Primary.Attributes["key"]),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetObjectAcl error: %v", err)
|
||||
}
|
||||
|
||||
var perms []string
|
||||
for _, v := range out.Grants {
|
||||
perms = append(perms, *v.Permission)
|
||||
}
|
||||
sort.Strings(perms)
|
||||
|
||||
if !reflect.DeepEqual(perms, expectedPerms) {
|
||||
return fmt.Errorf("Expected ACL permissions to be %v, got %v", expectedPerms, perms)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketObjectConfigSource(randInt int, source string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "object_bucket" {
|
||||
|
@ -358,3 +458,17 @@ resource "aws_s3_bucket_object" "object" {
|
|||
}
|
||||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketObjectConfig_acl(randInt int, acl string) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "object_bucket" {
|
||||
bucket = "tf-object-test-bucket-%d"
|
||||
}
|
||||
resource "aws_s3_bucket_object" "object" {
|
||||
bucket = "${aws_s3_bucket.object_bucket.bucket}"
|
||||
key = "test-key"
|
||||
content = "some_bucket_content"
|
||||
acl = "%s"
|
||||
}
|
||||
`, randInt, acl)
|
||||
}
|
||||
|
|
|
@ -77,6 +77,72 @@ func TestAccAWSS3Bucket_acceleration(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestAccAWSS3Bucket_RequestPayer(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckAWSS3BucketDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketConfigRequestPayerBucketOwner(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket.bucket",
|
||||
"request_payer",
|
||||
"BucketOwner"),
|
||||
testAccCheckAWSS3RequestPayer(
|
||||
"aws_s3_bucket.bucket",
|
||||
"BucketOwner"),
|
||||
),
|
||||
},
|
||||
resource.TestStep{
|
||||
Config: testAccAWSS3BucketConfigRequestPayerRequester(rInt),
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckAWSS3BucketExists("aws_s3_bucket.bucket"),
|
||||
resource.TestCheckResourceAttr(
|
||||
"aws_s3_bucket.bucket",
|
||||
"request_payer",
|
||||
"Requester"),
|
||||
testAccCheckAWSS3RequestPayer(
|
||||
"aws_s3_bucket.bucket",
|
||||
"Requester"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestResourceAWSS3BucketRequestPayer_validation(t *testing.T) {
|
||||
_, errors := validateS3BucketRequestPayerType("incorrect", "request_payer")
|
||||
if len(errors) == 0 {
|
||||
t.Fatalf("Expected to trigger a validation error")
|
||||
}
|
||||
|
||||
var testCases = []struct {
|
||||
Value string
|
||||
ErrCount int
|
||||
}{
|
||||
{
|
||||
Value: "Requester",
|
||||
ErrCount: 0,
|
||||
},
|
||||
{
|
||||
Value: "BucketOwner",
|
||||
ErrCount: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
_, errors := validateS3BucketRequestPayerType(tc.Value, "request_payer")
|
||||
if len(errors) != tc.ErrCount {
|
||||
t.Fatalf("Expected not to trigger a validation error")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccAWSS3Bucket_Policy(t *testing.T) {
|
||||
rInt := acctest.RandInt()
|
||||
|
||||
|
@ -689,6 +755,28 @@ func testAccCheckAWSS3BucketCors(n string, corsRules []*s3.CORSRule) resource.Te
|
|||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3RequestPayer(n, expectedPayer string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
conn := testAccProvider.Meta().(*AWSClient).s3conn
|
||||
|
||||
out, err := conn.GetBucketRequestPayment(&s3.GetBucketRequestPaymentInput{
|
||||
Bucket: aws.String(rs.Primary.ID),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetBucketRequestPayment error: %v", err)
|
||||
}
|
||||
|
||||
if *out.Payer != expectedPayer {
|
||||
return fmt.Errorf("bad error request payer type, expected: %v, got %v",
|
||||
expectedPayer, out.Payer)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckAWSS3BucketLogging(n, b, p string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, _ := s.RootModule().Resources[n]
|
||||
|
@ -844,6 +932,26 @@ resource "aws_s3_bucket" "bucket" {
|
|||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketConfigRequestPayerBucketOwner(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "tf-test-bucket-%d"
|
||||
acl = "public-read"
|
||||
request_payer = "BucketOwner"
|
||||
}
|
||||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketConfigRequestPayerRequester(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
bucket = "tf-test-bucket-%d"
|
||||
acl = "public-read"
|
||||
request_payer = "Requester"
|
||||
}
|
||||
`, randInt)
|
||||
}
|
||||
|
||||
func testAccAWSS3BucketConfigWithPolicy(randInt int) string {
|
||||
return fmt.Sprintf(`
|
||||
resource "aws_s3_bucket" "bucket" {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe4CCQCGWwBmOiHQdTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
|
||||
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMB4XDTE2MDYyMTE2MzM0MVoXDTE3MDYyMTE2MzM0MVowRTELMAkG
|
||||
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
|
||||
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AL+LFlsCJG5txZp4yuu+lQnuUrgBXRG+irQqcTXlV91Bp5hpmRIyhnGCtWxxDBUL
|
||||
xrh4WN3VV/0jDzKT976oLgOy3hj56Cdqf+JlZ1qgMN5bHB3mm3aVWnrnsLbBsfwZ
|
||||
SEbk3Kht/cE1nK2toNVW+rznS3m+eoV3Zn/DUNwGlZr42hGNs6ETn2jURY78ETqR
|
||||
mW47xvjf86eIo7vULHJaY6xyarPqkL8DZazOmvY06hUGvGwGBny7gugfXqDG+I8n
|
||||
cPBsGJGSAmHmVV8o0RCB9UjY+TvSMQRpEDoVlvyrGuglsD8to/4+7UcsuDGlRYN6
|
||||
jmIOC37mOi/jwRfWL1YUa4MCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAPDxTH0oQ
|
||||
JjKXoJgkmQxurB81RfnK/NrswJVzWbOv6ejcbhwh+/ZgJTMc15BrYcxU6vUW1V/i
|
||||
Z7APU0qJ0icECACML+a2fRI7YdLCTiPIOmY66HY8MZHAn3dGjU5TeiUflC0n0zkP
|
||||
mxKJe43kcYLNDItbfvUDo/GoxTXrC3EFVZyU0RhFzoVJdODlTHXMVFCzcbQEBrBJ
|
||||
xKdShCEc8nFMneZcGFeEU488ntZoWzzms8/QpYrKa5S0Sd7umEU2Kwu4HTkvUFg/
|
||||
CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4
|
||||
GnSud83VUo9G9w==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe4CCQCGWwBmOiHQdTANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJB
|
||||
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMB4XDTE2MDYyMTE2MzM0MVoXDTE3MDYyMTE2MzM0MVowRTELMAkG
|
||||
A1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0
|
||||
IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AL+LFlsCJG5txZp4yuu+lQnuUrgBXRG+irQqcTXlV91Bp5hpmRIyhnGCtWxxDBUL
|
||||
xrh4WN3VV/0jDzKT976oLgOy3hj56Cdqf+JlZ1qgMN5bHB3mm3aVWnrnsLbBsfwZ
|
||||
SEbk3Kht/cE1nK2toNVW+rznS3m+eoV3Zn/DUNwGlZr42hGNs6ETn2jURY78ETqR
|
||||
mW47xvjf86eIo7vULHJaY6xyarPqkL8DZazOmvY06hUGvGwGBny7gugfXqDG+I8n
|
||||
cPBsGJGSAmHmVV8o0RCB9UjY+TvSMQRpEDoVlvyrGuglsD8to/4+7UcsuDGlRYN6
|
||||
jmIOC37mOi/jwRfWL1YUa4MCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAPDxTH0oQ
|
||||
JjKXoJgkmQxurB81RfnK/NrswJVzWbOv6ejcbhwh+/ZgJTMc15BrYcxU6vUW1V/i
|
||||
Z7APU0qJ0icECACML+a2fRI7YdLCTiPIOmY66HY8MZHAn3dGjU5TeiUflC0n0zkP
|
||||
mxKJe43kcYLNDItbfvUDo/GoxTXrC3EFVZyU0RhFzoVJdODlTHXMVFCzcbQEBrBJ
|
||||
xKdShCEc8nFMneZcGFeEU488ntZoWzzms8/QpYrKa5S0Sd7umEU2Kwu4HTkvUFg/
|
||||
CqDUFjhydXxYRsxXBBrEiLOE5BdtJR1sH/QHxIJe23C9iHI2nS1NbLziNEApLwC4
|
||||
GnSud83VUo9G9w==
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,70 @@
|
|||
package fastly
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
"github.com/hashicorp/terraform/helper/hashcode"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
type dataSourceFastlyIPRangesResult struct {
|
||||
Addresses []string
|
||||
}
|
||||
|
||||
func dataSourceFastlyIPRanges() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Read: dataSourceFastlyIPRangesRead,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"cidr_blocks": &schema.Schema{
|
||||
Type: schema.TypeList,
|
||||
Computed: true,
|
||||
Elem: &schema.Schema{Type: schema.TypeString},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func dataSourceFastlyIPRangesRead(d *schema.ResourceData, meta interface{}) error {
|
||||
|
||||
conn := cleanhttp.DefaultClient()
|
||||
|
||||
log.Printf("[DEBUG] Reading IP ranges")
|
||||
|
||||
res, err := conn.Get("https://api.fastly.com/public-ip-list")
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error listing IP ranges: %s", err)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(res.Body)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error reading response body: %s", err)
|
||||
}
|
||||
|
||||
d.SetId(strconv.Itoa(hashcode.String(string(data))))
|
||||
|
||||
result := new(dataSourceFastlyIPRangesResult)
|
||||
|
||||
if err := json.Unmarshal(data, result); err != nil {
|
||||
return fmt.Errorf("Error parsing result: %s", err)
|
||||
}
|
||||
|
||||
sort.Strings(result.Addresses)
|
||||
|
||||
if err := d.Set("cidr_blocks", result.Addresses); err != nil {
|
||||
return fmt.Errorf("Error setting ip ranges: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package fastly
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccFastlyIPRanges(t *testing.T) {
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccFastlyIPRangesConfig,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccFastlyIPRanges("data.fastly_ip_ranges.some"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccFastlyIPRanges(n string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
r := s.RootModule().Resources[n]
|
||||
a := r.Primary.Attributes
|
||||
|
||||
var (
|
||||
cidrBlockSize int
|
||||
err error
|
||||
)
|
||||
|
||||
if cidrBlockSize, err = strconv.Atoi(a["cidr_blocks.#"]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cidrBlockSize < 10 {
|
||||
return fmt.Errorf("cidr_blocks seem suspiciously low: %d", cidrBlockSize)
|
||||
}
|
||||
|
||||
var cidrBlocks sort.StringSlice = make([]string, cidrBlockSize)
|
||||
|
||||
for i := range make([]string, cidrBlockSize) {
|
||||
|
||||
block := a[fmt.Sprintf("cidr_blocks.%d", i)]
|
||||
|
||||
if _, _, err := net.ParseCIDR(block); err != nil {
|
||||
return fmt.Errorf("malformed CIDR block %s: %s", block, err)
|
||||
}
|
||||
|
||||
cidrBlocks[i] = block
|
||||
|
||||
}
|
||||
|
||||
if !sort.IsSorted(cidrBlocks) {
|
||||
return fmt.Errorf("unexpected order of cidr_blocks: %s", cidrBlocks)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
const testAccFastlyIPRangesConfig = `
|
||||
data "fastly_ip_ranges" "some" {
|
||||
}
|
||||
`
|
|
@ -18,6 +18,9 @@ func Provider() terraform.ResourceProvider {
|
|||
Description: "Fastly API Key from https://app.fastly.com/#account",
|
||||
},
|
||||
},
|
||||
DataSourcesMap: map[string]*schema.Resource{
|
||||
"fastly_ip_ranges": dataSourceFastlyIPRanges(),
|
||||
},
|
||||
ResourcesMap: map[string]*schema.Resource{
|
||||
"fastly_service_v1": resourceServiceV1(),
|
||||
},
|
||||
|
|
|
@ -61,6 +61,7 @@ import (
|
|||
)
|
||||
|
||||
var InternalProviders = map[string]plugin.ProviderFunc{
|
||||
"archive": archiveprovider.Provider,
|
||||
"atlas": atlasprovider.Provider,
|
||||
"aws": awsprovider.Provider,
|
||||
"azure": azureprovider.Provider,
|
||||
|
@ -105,7 +106,6 @@ var InternalProviders = map[string]plugin.ProviderFunc{
|
|||
"ultradns": ultradnsprovider.Provider,
|
||||
"vcd": vcdprovider.Provider,
|
||||
"vsphere": vsphereprovider.Provider,
|
||||
"archive": archiveprovider.Provider,
|
||||
}
|
||||
|
||||
var InternalProvisioners = map[string]plugin.ProvisionerFunc{
|
||||
|
|
|
@ -27,9 +27,8 @@ will be rendered as a literal `${foo}`.
|
|||
variable name. For example, `${var.foo}` will interpolate the
|
||||
`foo` variable value. If the variable is a map, then you
|
||||
can reference static keys in the map with the syntax
|
||||
`var.MAP.KEY`. For example, `${var.amis.us-east-1}` would
|
||||
get the value of the `us-east-1` key within the `amis` variable
|
||||
that is a map.
|
||||
`var.MAP["KEY"]`. For example, `${var.amis["us-east-1"]` would
|
||||
get the value of the `us-east-1` key within the `amis` map variable.
|
||||
|
||||
**To reference attributes of your own resource**, the syntax is
|
||||
`self.ATTRIBUTE`. For example `${self.private_ip_address}` will
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_ip_ranges"
|
||||
sidebar_current: "docs-aws-datasource-ip_ranges"
|
||||
description: |-
|
||||
Get information on AWS IP ranges.
|
||||
---
|
||||
|
||||
# aws\_ip_ranges
|
||||
|
||||
Use this data source to get the [IP ranges][1] of various AWS products and services.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
data "aws_ip_ranges" "european_ec2" {
|
||||
regions = [ "eu-west-1", "eu-central-1" ]
|
||||
services = [ "ec2" ]
|
||||
}
|
||||
|
||||
resource "aws_security_group" "from_europe" {
|
||||
|
||||
name = "from_europe"
|
||||
|
||||
ingress {
|
||||
from_port = "443"
|
||||
to_port = "443"
|
||||
protocol = "tcp"
|
||||
cidr_blocks = [ "${data.aws_ip_ranges.european_ec2.blocks}" ]
|
||||
}
|
||||
|
||||
tags {
|
||||
CreateDate = "${data.aws_ip_ranges.european_ec2.create_date}"
|
||||
SyncToken = "${data.aws_ip_ranges.european_ec2.sync_token}"
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
* `regions` - (Optional) Filter IP ranges by regions (or include all regions, if
|
||||
omitted). Valid items are `global` (for `cloudfront`) as well as all AWS regions
|
||||
(e.g. `eu-central-1`)
|
||||
|
||||
* `services` - (Required) Filter IP ranges by services. Valid items are `amazon`
|
||||
(for amazon.com), `cloudfront`, `ec2`, `route53` and `route53_healthchecks`.
|
||||
|
||||
~> **NOTE:** If the specified combination of regions and services does not yield any
|
||||
CIDR blocks, Terraform will fail.
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
* `cidr_blocks` - The lexically ordered list of CIDR blocks.
|
||||
* `create_date` - The publication time of the IP ranges (e.g. `2016-08-03-23-46-05`).
|
||||
* `sync_token` - The publication time of the IP ranges, in Unix epoch time format
|
||||
(e.g. `1470267965`).
|
||||
|
||||
[1]: http://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html
|
|
@ -4,7 +4,7 @@ page_title: "AWS: aws_opsworks_permission"
|
|||
sidebar_current: "docs-aws-resource-opsworks-permission"
|
||||
description: |-
|
||||
Provides an OpsWorks permission resource.
|
||||
-------------------------------------------
|
||||
---
|
||||
|
||||
# aws\_opsworks\_permission
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
layout: "aws"
|
||||
page_title: "AWS: aws_opsworks_user_profile_"
|
||||
page_title: "AWS: aws_opsworks_user_profile"
|
||||
sidebar_current: "docs-aws-resource-opsworks-user-profile"
|
||||
description: |-
|
||||
Provides an OpsWorks User Profile resource.
|
||||
---------------------------------------------
|
||||
---
|
||||
|
||||
# aws\_opsworks\_user\_profile
|
||||
|
||||
|
|
|
@ -173,6 +173,10 @@ The following arguments are supported:
|
|||
* `logging` - (Optional) A settings of [bucket logging](https://docs.aws.amazon.com/AmazonS3/latest/UG/ManagingBucketLogging.html) (documented below).
|
||||
* `lifecycle_rule` - (Optional) A configuration of [object lifecycle management](http://docs.aws.amazon.com/AmazonS3/latest/dev/object-lifecycle-mgmt.html) (documented below).
|
||||
* `acceleration_status` - (Optional) Sets the accelerate configuration of an existing bucket. Can be `Enabled` or `Suspended`.
|
||||
* `request_payer` - (Optional) Specifies who should bear the cost of Amazon S3 data transfer.
|
||||
Can be either `BucketOwner` or `Requester`. By default, the owner of the S3 bucket would incur
|
||||
the costs of any data transfer. See [Requester Pays Buckets](http://docs.aws.amazon.com/AmazonS3/latest/dev/RequesterPaysBuckets.html)
|
||||
developer guide for more information.
|
||||
|
||||
~> **NOTE:** You cannot use `acceleration_status` in `cn-north-1` or `us-gov-west-1`
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ The following arguments are supported:
|
|||
* `key` - (Required) The name of the object once it is in the bucket.
|
||||
* `source` - (Required) The path to the source file being uploaded to the bucket.
|
||||
* `content` - (Required unless `source` given) The literal content being uploaded to the bucket.
|
||||
* `acl` - (Optional) The [canned ACL](https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl) to apply. Defaults to "private".
|
||||
* `cache_control` - (Optional) Specifies caching behavior along the request/reply chain Read [w3c cache_control](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9) for further details.
|
||||
* `content_disposition` - (Optional) Specifies presentational information for the object. Read [wc3 content_disposition](http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1) for further information.
|
||||
* `content_encoding` - (Optional) Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field. Read [w3c content encoding](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11) for further information.
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
layout: "fastly"
|
||||
page_title: "Fastly: fastly_ip_ranges"
|
||||
sidebar_current: "docs-fastly-datasource-ip_ranges"
|
||||
description: |-
|
||||
Get information on Fastly IP ranges.
|
||||
---
|
||||
|
||||
# fastly\_ip_ranges
|
||||
|
||||
Use this data source to get the [IP ranges][1] of Fastly edge nodes.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
data "fastly_ip_ranges" "fastly" {
|
||||
}
|
||||
|
||||
resource "aws_security_group" "from_fastly" {
|
||||
|
||||
name = "from_fastly"
|
||||
|
||||
ingress {
|
||||
from_port = "443"
|
||||
to_port = "443"
|
||||
protocol = "tcp"
|
||||
cidr_blocks = [ "${data.fastly_ip_ranges.fastly.cidr_blocks}" ]
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
* `cidr_blocks` - The lexically ordered list of CIDR blocks.
|
||||
|
||||
[1]: https://docs.fastly.com/guides/securing-communications/accessing-fastlys-ip-ranges
|
|
@ -51,7 +51,7 @@ The following arguments are supported:
|
|||
Keystone service. By specifying a token, you do not have to
|
||||
specify a username/password combination, since the token was
|
||||
already created by a username/password out of band of Terraform.
|
||||
If ommitted, the `OS_AUTH_TOKEN` environment variable is used.
|
||||
If omitted, the `OS_AUTH_TOKEN` environment variable is used.
|
||||
|
||||
* `api_key` - (Optional; Required if not using `password`) An API Key
|
||||
is issued by a cloud provider as alternative password. Unless
|
||||
|
@ -144,6 +144,8 @@ variables must also be set:
|
|||
|
||||
* `OS_NETWORK_ID` - The UUID of a network in your test environment.
|
||||
|
||||
* `OS_EXTGW_ID` - The UUID of the external gateway.
|
||||
|
||||
To make development easier, the `builtin/providers/openstack/devstack/deploy.sh`
|
||||
script will assist in installing and configuring a standardized
|
||||
[DevStack](http://docs.openstack.org/developer/devstack/) environment along with
|
||||
|
|
|
@ -15,6 +15,7 @@ Provides a PowerDNS record resource.
|
|||
Note that PowerDNS internally lowercases certain records (e.g. CNAME and AAAA), which can lead to resources being marked for a change in every singe plan.
|
||||
|
||||
For the v1 API (PowerDNS version 4):
|
||||
|
||||
```
|
||||
# Add a record to the zone
|
||||
resource "powerdns_record" "foobar" {
|
||||
|
@ -27,6 +28,7 @@ resource "powerdns_record" "foobar" {
|
|||
```
|
||||
|
||||
For the legacy API (PowerDNS version 3.4):
|
||||
|
||||
```
|
||||
# Add a record to the zone
|
||||
resource "powerdns_record" "foobar" {
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
<li<%= sidebar_current("docs-aws-datasource-iam-policy-document") %>>
|
||||
<a href="/docs/providers/aws/d/iam_policy_document.html">aws_iam_policy_document</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-ip_ranges") %>>
|
||||
<a href="/docs/providers/aws/d/ip_ranges.html">aws_ip_ranges</a>
|
||||
</li>
|
||||
<li<%= sidebar_current("docs-aws-datasource-s3-bucket-object") %>>
|
||||
<a href="/docs/providers/aws/d/s3_bucket_object.html">aws_s3_bucket_object</a>
|
||||
</li>
|
||||
|
@ -567,6 +570,10 @@
|
|||
<a href="#">OpsWorks Resources</a>
|
||||
<ul class="nav nav-visible">
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-application") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_application.html">aws_opsworks_application</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-custom-layer") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_custom_layer.html">aws_opsworks_custom_layer</a>
|
||||
</li>
|
||||
|
@ -599,6 +606,10 @@
|
|||
<a href="/docs/providers/aws/r/opsworks_nodejs_app_layer.html">aws_opsworks_nodejs_app_layer</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-permission") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_permission.html">aws_opsworks_permission</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-php-app-layer") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_php_app_layer.html">aws_opsworks_php_app_layer</a>
|
||||
</li>
|
||||
|
@ -615,8 +626,8 @@
|
|||
<a href="/docs/providers/aws/r/opsworks_static_web_layer.html">aws_opsworks_static_web_layer</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-application") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_application.html">aws_opsworks_application</a>
|
||||
<li<%= sidebar_current("docs-aws-resource-opsworks-user-profile") %>>
|
||||
<a href="/docs/providers/aws/r/opsworks_user_profile.html">aws_opsworks_user_profile</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
|
|
@ -10,6 +10,15 @@
|
|||
<a href="/docs/providers/fastly/index.html">Fastly Provider</a>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-fastly-datasource/) %>>
|
||||
<a href="#">Data Sources</a>
|
||||
<ul class="nav nav-visible">
|
||||
<li<%= sidebar_current("docs-fastly-datasource-ip_ranges") %>>
|
||||
<a href="/docs/providers/fastly/d/ip_ranges.html">fastly_ip_ranges</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li<%= sidebar_current(/^docs-fastly-resource/) %>>
|
||||
<a href="#">Resources</a>
|
||||
|
||||
|
|
Loading…
Reference in New Issue