Merge branch 'master' into f-aws-flow-logs

* master:
  Update CHANGELOG.md
  Update CHANGELOG.md
  Added affinity group resource.
  update link to actually work
  provider/azure: Fix SQL client name to match upstream
  add warning message to explain scenario of conflicting rules
  typo
  remove debugging
  Update CHANGELOG.md
  provider/aws: Add docs for autoscaling_policy + cloudwatch_metric_alarm
  provider/aws: Add autoscaling_policy
  provider/aws: Add cloudwatch_metric_alarm
  rename method, update docs
  clean up some conflicts with
  clean up old, incompatible test
  update tests with another example
  update test
  remove meta usage, stub test
  fix existing tests
  Consider security groups with source security groups when hashing
This commit is contained in:
Clint Shryock 2015-06-22 09:33:42 -05:00
commit cc43ae8c4b
21 changed files with 1450 additions and 31 deletions

View File

@ -2,8 +2,10 @@
FEATURES:
* **New provider: `azure`** [GH-2052, GH-2053, GH-2372, GH-2380]
* **New provider: `azure`** [GH-2052, GH-2053, GH-2372, GH-2380, GH-2394]
* **New resource: `aws_autoscaling_notification`** [GH-2197]
* **New resource: `aws_autoscaling_policy`** [GH-2201]
* **New resource: `aws_cloudwatch_metric_alarm`** [GH-2201]
* **New resource: `aws_dynamodb_table`** [GH-2121]
* **New resource: `aws_ecs_cluster`** [GH-1803]
* **New resource: `aws_ecs_service`** [GH-1803]
@ -35,6 +37,8 @@ BUG FIXES:
* command/apply: prevent output duplication when reporting errors [GH-2267]
* provider/aws: fix panic when route has no cidr_block [GH-2215]
* provider/aws: fix issue preventing destruction of IAM Roles [GH-2177]
* provider/aws: fix issue where Security Group Rules could collide and fail
to save to the state file correctly [GH-2376]
* provider/aws: fix issue preventing destruction self referencing Securtity
Group Rules [GH-2305]
* provider/aws: fix issue causing perpetual diff on ELB listeners

View File

@ -10,6 +10,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
@ -37,6 +38,7 @@ type Config struct {
}
type AWSClient struct {
cloudwatchconn *cloudwatch.CloudWatch
dynamodbconn *dynamodb.DynamoDB
ec2conn *ec2.EC2
ecsconn *ecs.ECS
@ -143,6 +145,9 @@ func (c *Config) Client() (interface{}, error) {
log.Println("[INFO] Initializing Lambda Connection")
client.lambdaconn = lambda.New(awsConfig)
log.Println("[INFO] Initializing CloudWatch SDK connection")
client.cloudwatchconn = cloudwatch.New(awsConfig)
}
if len(errs) > 0 {

View File

@ -86,6 +86,8 @@ func Provider() terraform.ResourceProvider {
"aws_app_cookie_stickiness_policy": resourceAwsAppCookieStickinessPolicy(),
"aws_autoscaling_group": resourceAwsAutoscalingGroup(),
"aws_autoscaling_notification": resourceAwsAutoscalingNotification(),
"aws_autoscaling_policy": resourceAwsAutoscalingPolicy(),
"aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(),
"aws_customer_gateway": resourceAwsCustomerGateway(),
"aws_db_instance": resourceAwsDbInstance(),
"aws_db_parameter_group": resourceAwsDbParameterGroup(),

View File

@ -408,7 +408,7 @@ func waitForASGCapacity(d *schema.ResourceData, meta interface{}) error {
}
wantELB := d.Get("min_elb_capacity").(int)
log.Printf("[DEBUG] Wanting for capacity: %d ASG, %d ELB", wantASG, wantELB)
log.Printf("[DEBUG] Waiting for capacity: %d ASG, %d ELB", wantASG, wantELB)
return resource.Retry(waitForASGCapacityTimeout, func() error {
g, err := getAwsAutoscalingGroup(d, meta)

View File

@ -0,0 +1,181 @@
package aws
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsAutoscalingPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsAutoscalingPolicyCreate,
Read: resourceAwsAutoscalingPolicyRead,
Update: resourceAwsAutoscalingPolicyUpdate,
Delete: resourceAwsAutoscalingPolicyDelete,
Schema: map[string]*schema.Schema{
"arn": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"adjustment_type": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"autoscaling_group_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"cooldown": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"min_adjustment_step": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
},
"scaling_adjustment": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
},
}
}
func resourceAwsAutoscalingPolicyCreate(d *schema.ResourceData, meta interface{}) error {
autoscalingconn := meta.(*AWSClient).autoscalingconn
params := getAwsAutoscalingPutScalingPolicyInput(d)
log.Printf("[DEBUG] AutoScaling PutScalingPolicy: %#v", params)
resp, err := autoscalingconn.PutScalingPolicy(&params)
if err != nil {
return fmt.Errorf("Error putting scaling policy: %s", err)
}
d.Set("arn", resp.PolicyARN)
d.SetId(d.Get("name").(string))
log.Printf("[INFO] AutoScaling Scaling PolicyARN: %s", d.Get("arn").(string))
return resourceAwsAutoscalingPolicyRead(d, meta)
}
func resourceAwsAutoscalingPolicyRead(d *schema.ResourceData, meta interface{}) error {
p, err := getAwsAutoscalingPolicy(d, meta)
if err != nil {
return err
}
if p == nil {
d.SetId("")
return nil
}
log.Printf("[DEBUG] Read Scaling Policy: ASG: %s, SP: %s, Obj: %#v", d.Get("autoscaling_group_name"), d.Get("name"), p)
d.Set("adjustment_type", p.AdjustmentType)
d.Set("autoscaling_group_name", p.AutoScalingGroupName)
d.Set("cooldown", p.Cooldown)
d.Set("min_adjustment_step", p.MinAdjustmentStep)
d.Set("arn", p.PolicyARN)
d.Set("name", p.PolicyName)
d.Set("scaling_adjustment", p.ScalingAdjustment)
return nil
}
func resourceAwsAutoscalingPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
autoscalingconn := meta.(*AWSClient).autoscalingconn
params := getAwsAutoscalingPutScalingPolicyInput(d)
log.Printf("[DEBUG] Autoscaling Update Scaling Policy: %#v", params)
_, err := autoscalingconn.PutScalingPolicy(&params)
if err != nil {
return err
}
return resourceAwsAutoscalingPolicyRead(d, meta)
}
func resourceAwsAutoscalingPolicyDelete(d *schema.ResourceData, meta interface{}) error {
autoscalingconn := meta.(*AWSClient).autoscalingconn
p, err := getAwsAutoscalingPolicy(d, meta)
if err != nil {
return err
}
if p == nil {
return nil
}
params := autoscaling.DeletePolicyInput{
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
PolicyName: aws.String(d.Get("name").(string)),
}
if _, err := autoscalingconn.DeletePolicy(&params); err != nil {
return fmt.Errorf("Autoscaling Scaling Policy: %s ", err)
}
d.SetId("")
return nil
}
// PutScalingPolicy seems to require all params to be resent, so create and update can share this common function
func getAwsAutoscalingPutScalingPolicyInput(d *schema.ResourceData) autoscaling.PutScalingPolicyInput {
var params = autoscaling.PutScalingPolicyInput{
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
PolicyName: aws.String(d.Get("name").(string)),
}
if v, ok := d.GetOk("adjustment_type"); ok {
params.AdjustmentType = aws.String(v.(string))
}
if v, ok := d.GetOk("cooldown"); ok {
params.Cooldown = aws.Long(int64(v.(int)))
}
if v, ok := d.GetOk("scaling_adjustment"); ok {
params.ScalingAdjustment = aws.Long(int64(v.(int)))
}
if v, ok := d.GetOk("min_adjustment_step"); ok {
params.MinAdjustmentStep = aws.Long(int64(v.(int)))
}
return params
}
func getAwsAutoscalingPolicy(d *schema.ResourceData, meta interface{}) (*autoscaling.ScalingPolicy, error) {
autoscalingconn := meta.(*AWSClient).autoscalingconn
params := autoscaling.DescribePoliciesInput{
AutoScalingGroupName: aws.String(d.Get("autoscaling_group_name").(string)),
PolicyNames: []*string{aws.String(d.Get("name").(string))},
}
log.Printf("[DEBUG] AutoScaling Scaling Policy Describe Params: %#v", params)
resp, err := autoscalingconn.DescribePolicies(&params)
if err != nil {
return nil, fmt.Errorf("Error retrieving scaling policies: %s", err)
}
// find scaling policy
name := d.Get("name")
for idx, sp := range resp.ScalingPolicies {
if *sp.PolicyName == name {
return resp.ScalingPolicies[idx], nil
}
}
// policy not found
return nil, nil
}

View File

@ -0,0 +1,115 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSAutoscalingPolicy_basic(t *testing.T) {
var policy autoscaling.ScalingPolicy
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSAutoscalingPolicyDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSAutoscalingPolicyConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckScalingPolicyExists("aws_autoscaling_policy.foobar", &policy),
resource.TestCheckResourceAttr("aws_autoscaling_policy.foobar", "adjustment_type", "ChangeInCapacity"),
resource.TestCheckResourceAttr("aws_autoscaling_policy.foobar", "cooldown", "300"),
),
},
},
})
}
func testAccCheckScalingPolicyExists(n string, policy *autoscaling.ScalingPolicy) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
rs = rs
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
params := &autoscaling.DescribePoliciesInput{
AutoScalingGroupName: aws.String(rs.Primary.Attributes["autoscaling_group_name"]),
PolicyNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribePolicies(params)
if err != nil {
return err
}
if len(resp.ScalingPolicies) == 0 {
return fmt.Errorf("ScalingPolicy not found")
}
return nil
}
}
func testAccCheckAWSAutoscalingPolicyDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).autoscalingconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_autoscaling_group" {
continue
}
params := autoscaling.DescribePoliciesInput{
AutoScalingGroupName: aws.String(rs.Primary.Attributes["autoscaling_group_name"]),
PolicyNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribePolicies(&params)
if err == nil {
if len(resp.ScalingPolicies) != 0 &&
*resp.ScalingPolicies[0].PolicyName == rs.Primary.ID {
return fmt.Errorf("Scaling Policy Still Exists: %s", rs.Primary.ID)
}
}
}
return nil
}
var testAccAWSAutoscalingPolicyConfig = fmt.Sprintf(`
resource "aws_launch_configuration" "foobar" {
name = "terraform-test-foobar5"
image_id = "ami-21f78e11"
instance_type = "t1.micro"
}
resource "aws_autoscaling_group" "foobar" {
availability_zones = ["us-west-2a"]
name = "terraform-test-foobar5"
max_size = 5
min_size = 2
health_check_grace_period = 300
health_check_type = "ELB"
force_delete = true
termination_policies = ["OldestInstance"]
launch_configuration = "${aws_launch_configuration.foobar.name}"
tag {
key = "Foo"
value = "foo-bar"
propagate_at_launch = true
}
}
resource "aws_autoscaling_policy" "foobar" {
name = "foobar"
scaling_adjustment = 4
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = "${aws_autoscaling_group.foobar.name}"
}
`)

View File

@ -0,0 +1,288 @@
package aws
import (
"fmt"
"log"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
)
func resourceAwsCloudWatchMetricAlarm() *schema.Resource {
return &schema.Resource{
Create: resourceAwsCloudWatchMetricAlarmCreate,
Read: resourceAwsCloudWatchMetricAlarmRead,
Update: resourceAwsCloudWatchMetricAlarmUpdate,
Delete: resourceAwsCloudWatchMetricAlarmDelete,
Schema: map[string]*schema.Schema{
"alarm_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"comparison_operator": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"evaluation_periods": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"metric_name": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"namespace": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"period": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"statistic": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"threshold": &schema.Schema{
Type: schema.TypeFloat,
Required: true,
},
"actions_enabled": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"alarm_actions": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
"alarm_description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"dimensions": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
},
"insufficient_data_actions": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
"ok_actions": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
"unit": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
}
}
func resourceAwsCloudWatchMetricAlarmCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchconn
params := getAwsCloudWatchPutMetricAlarmInput(d)
log.Printf("[DEBUG] Creating CloudWatch Metric Alarm: %#v", params)
_, err := conn.PutMetricAlarm(&params)
if err != nil {
return fmt.Errorf("Creating metric alarm failed: %s", err)
}
d.SetId(d.Get("alarm_name").(string))
log.Println("[INFO] CloudWatch Metric Alarm created")
return resourceAwsCloudWatchMetricAlarmRead(d, meta)
}
func resourceAwsCloudWatchMetricAlarmRead(d *schema.ResourceData, meta interface{}) error {
a, err := getAwsCloudWatchMetricAlarm(d, meta)
if err != nil {
return err
}
if a == nil {
d.SetId("")
return nil
}
log.Printf("[DEBUG] Reading CloudWatch Metric Alarm: %s", d.Get("alarm_name"))
d.Set("actions_enabled", a.ActionsEnabled)
if err := d.Set("alarm_actions", _strArrPtrToList(a.AlarmActions)); err != nil {
log.Printf("[WARN] Error setting Alarm Actions: %s", err)
}
d.Set("alarm_description", a.AlarmDescription)
d.Set("alarm_name", a.AlarmName)
d.Set("comparison_operator", a.ComparisonOperator)
d.Set("dimensions", a.Dimensions)
d.Set("evaluation_periods", a.EvaluationPeriods)
if err := d.Set("insufficient_data_actions", _strArrPtrToList(a.InsufficientDataActions)); err != nil {
log.Printf("[WARN] Error setting Insufficient Data Actions: %s", err)
}
d.Set("metric_name", a.MetricName)
d.Set("namespace", a.Namespace)
if err := d.Set("ok_actions", _strArrPtrToList(a.OKActions)); err != nil {
log.Printf("[WARN] Error setting OK Actions: %s", err)
}
d.Set("period", a.Period)
d.Set("statistic", a.Statistic)
d.Set("threshold", a.Threshold)
d.Set("unit", a.Unit)
return nil
}
func resourceAwsCloudWatchMetricAlarmUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).cloudwatchconn
params := getAwsCloudWatchPutMetricAlarmInput(d)
log.Printf("[DEBUG] Updating CloudWatch Metric Alarm: %#v", params)
_, err := conn.PutMetricAlarm(&params)
if err != nil {
return fmt.Errorf("Updating metric alarm failed: %s", err)
}
log.Println("[INFO] CloudWatch Metric Alarm updated")
return resourceAwsCloudWatchMetricAlarmRead(d, meta)
}
func resourceAwsCloudWatchMetricAlarmDelete(d *schema.ResourceData, meta interface{}) error {
p, err := getAwsCloudWatchMetricAlarm(d, meta)
if err != nil {
return err
}
if p == nil {
log.Printf("[DEBUG] CloudWatch Metric Alarm %s is already gone", d.Id())
return nil
}
log.Printf("[INFO] Deleting CloudWatch Metric Alarm: %s", d.Id())
conn := meta.(*AWSClient).cloudwatchconn
params := cloudwatch.DeleteAlarmsInput{
AlarmNames: []*string{aws.String(d.Id())},
}
if _, err := conn.DeleteAlarms(&params); err != nil {
return fmt.Errorf("Error deleting CloudWatch Metric Alarm: %s", err)
}
log.Println("[INFO] CloudWatch Metric Alarm deleted")
d.SetId("")
return nil
}
func getAwsCloudWatchPutMetricAlarmInput(d *schema.ResourceData) cloudwatch.PutMetricAlarmInput {
params := cloudwatch.PutMetricAlarmInput{
AlarmName: aws.String(d.Get("alarm_name").(string)),
ComparisonOperator: aws.String(d.Get("comparison_operator").(string)),
EvaluationPeriods: aws.Long(int64(d.Get("evaluation_periods").(int))),
MetricName: aws.String(d.Get("metric_name").(string)),
Namespace: aws.String(d.Get("namespace").(string)),
Period: aws.Long(int64(d.Get("period").(int))),
Statistic: aws.String(d.Get("statistic").(string)),
Threshold: aws.Double(d.Get("threshold").(float64)),
}
if v := d.Get("actions_enabled"); v != nil {
params.ActionsEnabled = aws.Boolean(v.(bool))
}
if v, ok := d.GetOk("alarm_description"); ok {
params.AlarmDescription = aws.String(v.(string))
}
if v, ok := d.GetOk("unit"); ok {
params.Unit = aws.String(v.(string))
}
var alarmActions []*string
if v := d.Get("alarm_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
alarmActions = append(alarmActions, aws.String(str))
}
params.AlarmActions = alarmActions
}
var insufficientDataActions []*string
if v := d.Get("insufficient_data_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
insufficientDataActions = append(insufficientDataActions, aws.String(str))
}
params.InsufficientDataActions = insufficientDataActions
}
var okActions []*string
if v := d.Get("ok_actions"); v != nil {
for _, v := range v.(*schema.Set).List() {
str := v.(string)
okActions = append(okActions, aws.String(str))
}
params.OKActions = okActions
}
a := d.Get("dimensions").(map[string]interface{})
dimensions := make([]*cloudwatch.Dimension, 0, len(a))
for k, v := range a {
dimensions = append(dimensions, &cloudwatch.Dimension{
Name: aws.String(k),
Value: aws.String(v.(string)),
})
}
params.Dimensions = dimensions
return params
}
func getAwsCloudWatchMetricAlarm(d *schema.ResourceData, meta interface{}) (*cloudwatch.MetricAlarm, error) {
conn := meta.(*AWSClient).cloudwatchconn
params := cloudwatch.DescribeAlarmsInput{
AlarmNames: []*string{aws.String(d.Id())},
}
resp, err := conn.DescribeAlarms(&params)
if err != nil {
return nil, nil
}
// Find it and return it
for idx, ma := range resp.MetricAlarms {
if *ma.AlarmName == d.Id() {
return resp.MetricAlarms[idx], nil
}
}
return nil, nil
}
func _strArrPtrToList(strArrPtr []*string) []string {
var result []string
for _, elem := range strArrPtr {
result = append(result, *elem)
}
return result
}

View File

@ -0,0 +1,95 @@
package aws
import (
"fmt"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAWSCloudWatchMetricAlarm_basic(t *testing.T) {
var alarm cloudwatch.MetricAlarm
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCloudWatchMetricAlarmDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAWSCloudWatchMetricAlarmConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckCloudWatchMetricAlarmExists("aws_cloudwatch_metric_alarm.foobar", &alarm),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "metric_name", "CPUUtilization"),
resource.TestCheckResourceAttr("aws_cloudwatch_metric_alarm.foobar", "statistic", "Average"),
),
},
},
})
}
func testAccCheckCloudWatchMetricAlarmExists(n string, alarm *cloudwatch.MetricAlarm) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn
params := cloudwatch.DescribeAlarmsInput{
AlarmNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribeAlarms(&params)
if err != nil {
return err
}
if len(resp.MetricAlarms) == 0 {
return fmt.Errorf("Alarm not found")
}
*alarm = *resp.MetricAlarms[0]
return nil
}
}
func testAccCheckAWSCloudWatchMetricAlarmDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).cloudwatchconn
for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_cloudwatch_metric_alarm" {
continue
}
params := cloudwatch.DescribeAlarmsInput{
AlarmNames: []*string{aws.String(rs.Primary.ID)},
}
resp, err := conn.DescribeAlarms(&params)
if err == nil {
if len(resp.MetricAlarms) != 0 &&
*resp.MetricAlarms[0].AlarmName == rs.Primary.ID {
return fmt.Errorf("Alarm Still Exists: %s", rs.Primary.ID)
}
}
}
return nil
}
var testAccAWSCloudWatchMetricAlarmConfig = fmt.Sprintf(`
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar5"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitor ec2 cpu utilization"
insufficient_data_actions = []
}
`)

View File

@ -21,6 +21,9 @@ func resourceAwsSecurityGroupRule() *schema.Resource {
Read: resourceAwsSecurityGroupRuleRead,
Delete: resourceAwsSecurityGroupRuleDelete,
SchemaVersion: 1,
MigrateState: resourceAwsSecurityGroupRuleMigrateState,
Schema: map[string]*schema.Schema{
"type": &schema.Schema{
Type: schema.TypeString,
@ -65,6 +68,7 @@ func resourceAwsSecurityGroupRule() *schema.Resource {
Optional: true,
ForceNew: true,
Computed: true,
ConflictsWith: []string{"cidr_blocks"},
},
"self": &schema.Schema{
@ -90,6 +94,7 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}
ruleType := d.Get("type").(string)
var autherr error
switch ruleType {
case "ingress":
log.Printf("[DEBUG] Authorizing security group %s %s rule: %s",
@ -105,13 +110,7 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}
req.GroupName = sg.GroupName
}
_, err := conn.AuthorizeSecurityGroupIngress(req)
if err != nil {
return fmt.Errorf(
"Error authorizing security group %s rules: %s",
"rules", err)
}
_, autherr = conn.AuthorizeSecurityGroupIngress(req)
case "egress":
log.Printf("[DEBUG] Authorizing security group %s %s rule: %#v",
@ -122,18 +121,28 @@ func resourceAwsSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}
IPPermissions: []*ec2.IPPermission{perm},
}
_, err = conn.AuthorizeSecurityGroupEgress(req)
if err != nil {
return fmt.Errorf(
"Error authorizing security group %s rules: %s",
"rules", err)
}
_, autherr = conn.AuthorizeSecurityGroupEgress(req)
default:
return fmt.Errorf("Security Group Rule must be type 'ingress' or type 'egress'")
}
if autherr != nil {
if awsErr, ok := autherr.(awserr.Error); ok {
if awsErr.Code() == "InvalidPermission.Duplicate" {
return fmt.Errorf(`[WARN] A duplicate Security Group rule was found. This may be
a side effect of a now-fixed Terraform issue causing two security groups with
identical attributes but different source_security_group_ids to overwrite each
other in the state. See https://github.com/hashicorp/terraform/pull/2376 for more
information and instructions for recovery. Error message: %s`, awsErr.Message())
}
}
return fmt.Errorf(
"Error authorizing security group rule type %s: %s",
ruleType, autherr)
}
d.SetId(ipPermissionIDHash(ruleType, perm))
return resourceAwsSecurityGroupRuleRead(d, meta)
@ -263,15 +272,32 @@ func findResourceSecurityGroup(conn *ec2.EC2, id string) (*ec2.SecurityGroup, er
return resp.SecurityGroups[0], nil
}
// ByGroupPair implements sort.Interface for []*ec2.UserIDGroupPairs based on
// GroupID or GroupName field (only one should be set).
type ByGroupPair []*ec2.UserIDGroupPair
func (b ByGroupPair) Len() int { return len(b) }
func (b ByGroupPair) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
func (b ByGroupPair) Less(i, j int) bool {
if b[i].GroupID != nil && b[j].GroupID != nil {
return *b[i].GroupID < *b[j].GroupID
}
if b[i].GroupName != nil && b[j].GroupName != nil {
return *b[i].GroupName < *b[j].GroupName
}
panic("mismatched security group rules, may be a terraform bug")
}
func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string {
var buf bytes.Buffer
// for egress rules, an TCP rule of -1 is automatically added, in which case
// the to and from ports will be nil. We don't record this rule locally.
if ip.IPProtocol != nil && *ip.IPProtocol != "-1" {
if ip.FromPort != nil && *ip.FromPort > 0 {
buf.WriteString(fmt.Sprintf("%d-", *ip.FromPort))
buf.WriteString(fmt.Sprintf("%d-", *ip.ToPort))
buf.WriteString(fmt.Sprintf("%s-", *ip.IPProtocol))
}
if ip.ToPort != nil && *ip.ToPort > 0 {
buf.WriteString(fmt.Sprintf("%d-", *ip.ToPort))
}
buf.WriteString(fmt.Sprintf("%s-", *ip.IPProtocol))
buf.WriteString(fmt.Sprintf("%s-", ruleType))
// We need to make sure to sort the strings below so that we always
@ -288,6 +314,22 @@ func ipPermissionIDHash(ruleType string, ip *ec2.IPPermission) string {
}
}
if len(ip.UserIDGroupPairs) > 0 {
sort.Sort(ByGroupPair(ip.UserIDGroupPairs))
for _, pair := range ip.UserIDGroupPairs {
if pair.GroupID != nil {
buf.WriteString(fmt.Sprintf("%s-", *pair.GroupID))
} else {
buf.WriteString("-")
}
if pair.GroupName != nil {
buf.WriteString(fmt.Sprintf("%s-", *pair.GroupName))
} else {
buf.WriteString("-")
}
}
}
return fmt.Sprintf("sg-%d", hashcode.String(buf.String()))
}

View File

@ -0,0 +1,101 @@
package aws
import (
"fmt"
"log"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/terraform"
)
func resourceAwsSecurityGroupRuleMigrateState(
v int, is *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
switch v {
case 0:
log.Println("[INFO] Found AWS Security Group State v0; migrating to v1")
return migrateSGRuleStateV0toV1(is)
default:
return is, fmt.Errorf("Unexpected schema version: %d", v)
}
return is, nil
}
func migrateSGRuleStateV0toV1(is *terraform.InstanceState) (*terraform.InstanceState, error) {
if is.Empty() {
log.Println("[DEBUG] Empty InstanceState; nothing to migrate.")
return is, nil
}
perm, err := migrateExpandIPPerm(is.Attributes)
if err != nil {
return nil, fmt.Errorf("[WARN] Error making new IP Permission in Security Group migration")
}
log.Printf("[DEBUG] Attributes before migration: %#v", is.Attributes)
newID := ipPermissionIDHash(is.Attributes["type"], perm)
is.Attributes["id"] = newID
is.ID = newID
log.Printf("[DEBUG] Attributes after migration: %#v, new id: %s", is.Attributes, newID)
return is, nil
}
func migrateExpandIPPerm(attrs map[string]string) (*ec2.IPPermission, error) {
var perm ec2.IPPermission
tp, err := strconv.Atoi(attrs["to_port"])
if err != nil {
return nil, fmt.Errorf("Error converting to_port in Security Group migration")
}
fp, err := strconv.Atoi(attrs["from_port"])
if err != nil {
return nil, fmt.Errorf("Error converting from_port in Security Group migration")
}
perm.ToPort = aws.Long(int64(tp))
perm.FromPort = aws.Long(int64(fp))
perm.IPProtocol = aws.String(attrs["protocol"])
groups := make(map[string]bool)
if attrs["self"] == "true" {
groups[attrs["security_group_id"]] = true
}
if attrs["source_security_group_id"] != "" {
groups[attrs["source_security_group_id"]] = true
}
if len(groups) > 0 {
perm.UserIDGroupPairs = make([]*ec2.UserIDGroupPair, len(groups))
// build string list of group name/ids
var gl []string
for k, _ := range groups {
gl = append(gl, k)
}
for i, name := range gl {
perm.UserIDGroupPairs[i] = &ec2.UserIDGroupPair{
GroupID: aws.String(name),
}
}
}
var cb []string
for k, v := range attrs {
if k != "cidr_blocks.#" && strings.HasPrefix(k, "cidr_blocks") {
cb = append(cb, v)
}
}
if len(cb) > 0 {
perm.IPRanges = make([]*ec2.IPRange, len(cb))
for i, v := range cb {
perm.IPRanges[i] = &ec2.IPRange{CIDRIP: aws.String(v)}
}
}
return &perm, nil
}

View File

@ -0,0 +1,67 @@
package aws
import (
"testing"
"github.com/hashicorp/terraform/terraform"
)
func TestAWSSecurityGroupRuleMigrateState(t *testing.T) {
cases := map[string]struct {
StateVersion int
ID string
Attributes map[string]string
Expected string
Meta interface{}
}{
"v0_1": {
StateVersion: 0,
ID: "sg-4235098228",
Attributes: map[string]string{
"self": "false",
"to_port": "0",
"security_group_id": "sg-13877277",
"cidr_blocks.#": "0",
"type": "ingress",
"protocol": "-1",
"from_port": "0",
"source_security_group_id": "sg-11877275",
},
Expected: "sg-3766347571",
},
"v0_2": {
StateVersion: 0,
ID: "sg-1021609891",
Attributes: map[string]string{
"security_group_id": "sg-0981746d",
"from_port": "0",
"to_port": "0",
"type": "ingress",
"self": "false",
"protocol": "-1",
"cidr_blocks.0": "172.16.1.0/24",
"cidr_blocks.1": "172.16.2.0/24",
"cidr_blocks.2": "172.16.3.0/24",
"cidr_blocks.3": "172.16.4.0/24",
"cidr_blocks.#": "4"},
Expected: "sg-4100229787",
},
}
for tn, tc := range cases {
is := &terraform.InstanceState{
ID: tc.ID,
Attributes: tc.Attributes,
}
is, err := resourceAwsSecurityGroupRuleMigrateState(
tc.StateVersion, is, tc.Meta)
if err != nil {
t.Fatalf("bad: %s, err: %#v", tn, err)
}
if is.ID != tc.Expected {
t.Fatalf("bad sg rule id: %s\n\n expected: %s", is.ID, tc.Expected)
}
}
}

View File

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
@ -44,6 +45,46 @@ func TestIpPermissionIDHash(t *testing.T) {
},
}
vpc_security_group_source := &ec2.IPPermission{
IPProtocol: aws.String("tcp"),
FromPort: aws.Long(int64(80)),
ToPort: aws.Long(int64(8000)),
UserIDGroupPairs: []*ec2.UserIDGroupPair{
&ec2.UserIDGroupPair{
UserID: aws.String("987654321"),
GroupID: aws.String("sg-12345678"),
},
&ec2.UserIDGroupPair{
UserID: aws.String("123456789"),
GroupID: aws.String("sg-987654321"),
},
&ec2.UserIDGroupPair{
UserID: aws.String("123456789"),
GroupID: aws.String("sg-12345678"),
},
},
}
security_group_source := &ec2.IPPermission{
IPProtocol: aws.String("tcp"),
FromPort: aws.Long(int64(80)),
ToPort: aws.Long(int64(8000)),
UserIDGroupPairs: []*ec2.UserIDGroupPair{
&ec2.UserIDGroupPair{
UserID: aws.String("987654321"),
GroupName: aws.String("my-security-group"),
},
&ec2.UserIDGroupPair{
UserID: aws.String("123456789"),
GroupName: aws.String("my-security-group"),
},
&ec2.UserIDGroupPair{
UserID: aws.String("123456789"),
GroupName: aws.String("my-other-security-group"),
},
},
}
// hardcoded hashes, to detect future change
cases := []struct {
Input *ec2.IPPermission
@ -52,13 +93,15 @@ func TestIpPermissionIDHash(t *testing.T) {
}{
{simple, "ingress", "sg-82613597"},
{egress, "egress", "sg-363054720"},
{egress_all, "egress", "sg-857124156"},
{egress_all, "egress", "sg-2766285362"},
{vpc_security_group_source, "egress", "sg-2661404947"},
{security_group_source, "egress", "sg-1841245863"},
}
for _, tc := range cases {
actual := ipPermissionIDHash(tc.Type, tc.Input)
if actual != tc.Output {
t.Fatalf("input: %s - %#v\noutput: %s", tc.Type, tc.Input, actual)
t.Errorf("input: %s - %s\noutput: %s", tc.Type, awsutil.StringValue(tc.Input), actual)
}
}
}
@ -141,9 +184,9 @@ func TestAccAWSSecurityGroupRule_MultiIngress(t *testing.T) {
var group ec2.SecurityGroup
testMultiRuleCount := func(*terraform.State) error {
if len(group.IPPermissions) != 3 {
if len(group.IPPermissions) != 2 {
return fmt.Errorf("Wrong Security Group rule count, expected %d, got %d",
3, len(group.IPPermissions))
2, len(group.IPPermissions))
}
var rule *ec2.IPPermission
@ -395,7 +438,6 @@ resource "aws_security_group_rule" "ingress_1" {
cidr_blocks = ["10.0.0.0/8"]
security_group_id = "${aws_security_group.web.id}"
source_security_group_id = "${aws_security_group.worker.id}"
}
resource "aws_security_group_rule" "ingress_2" {

View File

@ -6,6 +6,7 @@ import (
"sync"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/affinitygroup"
"github.com/Azure/azure-sdk-for-go/management/hostedservice"
"github.com/Azure/azure-sdk-for-go/management/networksecuritygroup"
"github.com/Azure/azure-sdk-for-go/management/osimage"
@ -31,13 +32,15 @@ type Config struct {
type Client struct {
mgmtClient management.Client
affinityGroupClient affinitygroup.AffinityGroupClient
hostedServiceClient hostedservice.HostedServiceClient
secGroupClient networksecuritygroup.SecurityGroupClient
osImageClient osimage.OSImageClient
sqlClient sql.SqlDatabaseClient
sqlClient sql.SQLDatabaseClient
storageServiceClient storageservice.StorageServiceClient
@ -107,6 +110,7 @@ func (c *Config) NewClientFromSettingsFile() (*Client, error) {
return &Client{
mgmtClient: mc,
affinityGroupClient: affinitygroup.NewClient(mc),
hostedServiceClient: hostedservice.NewClient(mc),
secGroupClient: networksecuritygroup.NewClient(mc),
osImageClient: osimage.NewClient(mc),
@ -130,6 +134,7 @@ func (c *Config) NewClient() (*Client, error) {
return &Client{
mgmtClient: mc,
affinityGroupClient: affinitygroup.NewClient(mc),
hostedServiceClient: hostedservice.NewClient(mc),
secGroupClient: networksecuritygroup.NewClient(mc),
osImageClient: osimage.NewClient(mc),

View File

@ -33,6 +33,7 @@ func Provider() terraform.ResourceProvider {
ResourcesMap: map[string]*schema.Resource{
"azure_instance": resourceAzureInstance(),
"azure_affinity_group": resourceAzureAffinityGroup(),
"azure_data_disk": resourceAzureDataDisk(),
"azure_sql_database_server": resourceAzureSqlDatabaseServer(),
"azure_sql_database_service": resourceAzureSqlDatabaseService(),

View File

@ -0,0 +1,168 @@
package azure
import (
"fmt"
"log"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/Azure/azure-sdk-for-go/management/affinitygroup"
"github.com/hashicorp/terraform/helper/schema"
)
// resourceAzureAffinityGroup returns the *schema.Resource associated to a
// resource affinity group on Azure.
func resourceAzureAffinityGroup() *schema.Resource {
return &schema.Resource{
Create: resourceAzureAffinityGroupCreate,
Read: resourceAzureAffinityGroupRead,
Update: resourceAzureAffinityGroupUpdate,
Exists: resourceAzureAffinityGroupExists,
Delete: resourceAzureAffinityGroupDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"location": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"label": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
}
}
// resourceAzureAffinityGroupCreate does all the necessary API calls to
// create an affinity group on Azure.
func resourceAzureAffinityGroupCreate(d *schema.ResourceData, meta interface{}) error {
affinityGroupClient := meta.(*Client).affinityGroupClient
log.Println("[INFO] Begun creating Azure Affinity Group creation request.")
name := d.Get("name").(string)
params := affinitygroup.CreateAffinityGroupParams{
Name: name,
Label: d.Get("label").(string),
Location: d.Get("location").(string),
}
if desc, ok := d.GetOk("description"); ok {
params.Description = desc.(string)
}
log.Println("[INFO] Sending Affinity Group creation request to Azure.")
err := affinityGroupClient.CreateAffinityGroup(params)
if err != nil {
return fmt.Errorf("Error issuing Azure Affinity Group creation: %s", err)
}
d.SetId(name)
return nil
}
// resourceAzureAffinityGroupRead does all the necessary API calls to
// read the state of the affinity group off Azure.
func resourceAzureAffinityGroupRead(d *schema.ResourceData, meta interface{}) error {
affinityGroupClient := meta.(*Client).affinityGroupClient
log.Println("[INFO] Issuing Azure Affinity Group list request.")
affinityGroups, err := affinityGroupClient.ListAffinityGroups()
if err != nil {
return fmt.Errorf("Error obtaining Affinity Group list off Azure: %s", err)
}
var found bool
name := d.Get("name").(string)
for _, group := range affinityGroups.AffinityGroups {
if group.Name == name {
found = true
d.Set("location", group.Location)
d.Set("label", group.Label)
d.Set("description", group.Description)
break
}
}
if !found {
// it means the affinity group has been deleted in the meantime, so we
// must stop tracking it:
d.SetId("")
}
return nil
}
// resourceAzureAffinityGroupUpdate does all the necessary API calls to
// update the state of the affinity group on Azure.
func resourceAzureAffinityGroupUpdate(d *schema.ResourceData, meta interface{}) error {
affinityGroupClient := meta.(*Client).affinityGroupClient
name := d.Get("name").(string)
clabel := d.HasChange("label")
cdesc := d.HasChange("description")
if clabel || cdesc {
log.Println("[INFO] Beginning Affinity Group update process.")
params := affinitygroup.UpdateAffinityGroupParams{}
if clabel {
params.Label = d.Get("label").(string)
}
if cdesc {
params.Description = d.Get("description").(string)
}
log.Println("[INFO] Sending Affinity Group update request to Azure.")
err := affinityGroupClient.UpdateAffinityGroup(name, params)
if err != nil {
return fmt.Errorf("Error updating Azure Affinity Group parameters: %s", err)
}
}
return nil
}
// resourceAzureAffinityGroupExists does all the necessary API calls to
// check for the existence of the affinity group on Azure.
func resourceAzureAffinityGroupExists(d *schema.ResourceData, meta interface{}) (bool, error) {
affinityGroupClient := meta.(*Client).affinityGroupClient
log.Println("[INFO] Issuing Azure Affinity Group get request.")
name := d.Get("name").(string)
_, err := affinityGroupClient.GetAffinityGroup(name)
if err != nil {
if management.IsResourceNotFoundError(err) {
// it means that the affinity group has been deleted in the
// meantime, so we must untrack it from the schema:
d.SetId("")
return false, nil
} else {
return false, fmt.Errorf("Error getting Affinity Group off Azure: %s", err)
}
}
return true, nil
}
// resourceAzureAffinityGroupDelete does all the necessary API calls to
// delete the affinity group off Azure.
func resourceAzureAffinityGroupDelete(d *schema.ResourceData, meta interface{}) error {
affinityGroupClient := meta.(*Client).affinityGroupClient
log.Println("[INFO] Sending Affinity Group deletion request to Azure.")
name := d.Get("name").(string)
err := affinityGroupClient.DeleteAffinityGroup(name)
if err != nil {
return fmt.Errorf("Error deleting Azure Affinity Group: %s", err)
}
return nil
}

View File

@ -0,0 +1,121 @@
package azure
import (
"fmt"
"testing"
"github.com/Azure/azure-sdk-for-go/management"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccAzureAffinityGroupBasic(t *testing.T) {
name := "azure_affinity_group.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureAffinityGroupDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureAffinityGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureAffinityGroupExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-group"),
resource.TestCheckResourceAttr(name, "location", "West US"),
resource.TestCheckResourceAttr(name, "label", "A nice label."),
resource.TestCheckResourceAttr(name, "description", "A nice description."),
),
},
},
})
}
func TestAccAzureAffinityGroupUpdate(t *testing.T) {
name := "azure_affinity_group.foo"
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAzureAffinityGroupDestroyed,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccAzureAffinityGroupConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureAffinityGroupExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-group"),
resource.TestCheckResourceAttr(name, "location", "West US"),
resource.TestCheckResourceAttr(name, "label", "A nice label."),
resource.TestCheckResourceAttr(name, "description", "A nice description."),
),
},
resource.TestStep{
Config: testAccAzureAffinityGroupUpdateConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckAzureAffinityGroupExists(name),
resource.TestCheckResourceAttr(name, "name", "terraform-testing-group"),
resource.TestCheckResourceAttr(name, "location", "West US"),
resource.TestCheckResourceAttr(name, "label", "An even nicer label."),
resource.TestCheckResourceAttr(name, "description", "An even nicer description."),
),
},
},
})
}
func testAccCheckAzureAffinityGroupExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
resource, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Affinity Group resource %q doesn't exist.", name)
}
if resource.Primary.ID == "" {
return fmt.Errorf("Affinity Group resource %q ID not set.", name)
}
affinityGroupClient := testAccProvider.Meta().(*Client).affinityGroupClient
_, err := affinityGroupClient.GetAffinityGroup(resource.Primary.ID)
return err
}
}
func testAccCheckAzureAffinityGroupDestroyed(s *terraform.State) error {
var err error
affinityGroupClient := testAccProvider.Meta().(*Client).affinityGroupClient
for _, resource := range s.RootModule().Resources {
if resource.Type != "azure_affinity_group" {
continue
}
if resource.Primary.ID == "" {
return fmt.Errorf("Affinity Group resource ID not set.")
}
_, err = affinityGroupClient.GetAffinityGroup(resource.Primary.ID)
if !management.IsResourceNotFoundError(err) {
return err
}
}
return nil
}
const testAccAzureAffinityGroupConfig = `
resource "azure_affinity_group" "foo" {
name = "terraform-testing-group"
location = "West US"
label = "A nice label."
description = "A nice description."
}
`
const testAccAzureAffinityGroupUpdateConfig = `
resource "azure_affinity_group" "foo" {
name = "terraform-testing-group"
location = "West US"
label = "An even nicer label."
description = "An even nicer description."
}
`

View File

@ -0,0 +1,53 @@
---
layout: "aws"
page_title: "AWS: aws_autoscaling_policy"
sidebar_current: "docs-aws-resource-autoscaling-policy"
description: |-
Provides an AutoScaling Scaling Group resource.
---
# aws\_autoscaling\_policy
Provides an AutoScaling Scaling Policy resource.
~> **NOTE:** You may want to omit `desired_capacity` attribute from attached `aws_autoscaling_group`
when using autoscaling policies. It's good practice to pick either
[manual](http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/as-manual-scaling.html)
or [dynamic](http://docs.aws.amazon.com/AutoScaling/latest/DeveloperGuide/as-scale-based-on-demand.html)
(policy-based) scaling.
## Example Usage
```
resource "aws_autoscaling_policy" "bat" {
name = "foobar3-terraform-test"
scaling_adjustment = 4
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = "${aws_autoscaling_group.bar.name}"
}
resource "aws_autoscaling_group" "bar" {
availability_zones = ["us-east-1a"]
name = "foobar3-terraform-test"
max_size = 5
min_size = 2
health_check_grace_period = 300
health_check_type = "ELB"
force_delete = true
launch_configuration = "${aws_launch_configuration.foo.name}"
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the policy.
* `autoscaling_group_name` - (Required) The name or ARN of the group.
* `adjustment_type` - (Required) Specifies whether the `scaling_adjustment` is an absolute number or a percentage of the current capacity. Valid values are `ChangeInCapacity`, `ExactCapacity`, and `PercentChangeInCapacity`.
* `scaling_adjustment` - (Required) The number of instances by which to scale. `adjustment_type` determines the interpretation of this number (e.g., as an absolute number or as a percentage of the existing Auto Scaling group size). A positive increment adds to the current capacity and a negative value removes from the current capacity.
* `cooldown` - (Optional) The amount of time, in seconds, after a scaling activity completes and before the next scaling activity can start.
* `min_adjustment_step` - (Optional) Used with `adjustment_type` with the value `PercentChangeInCapacity`, the scaling policy changes the `desired_capacity` of the Auto Scaling group by at least the number of instances specified in the value.
## Attribute Reference
* `arn` - The ARN assigned by AWS to the scaling policy.

View File

@ -0,0 +1,75 @@
---
layout: "aws"
page_title: "AWS: cloudwatch_metric_alarm"
sidebar_current: "docs-aws-resource-cloudwatch-metric-alarm"
description: |-
Provides an AutoScaling Scaling Group resource.
---
# aws\_cloudwatch\_metric\_alarm
Provides a CloudWatch Metric Alarm resource.
## Example Usage
```
resource "aws_cloudwatch_metric_alarm" "foobar" {
alarm_name = "terraform-test-foobar5"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitor ec2 cpu utilization"
insufficient_data_actions = []
}
```
## Example in Conjuction with Scaling Policies
```
resource "aws_autoscaling_policy" "bat" {
name = "foobar3-terraform-test"
scaling_adjustment = 4
adjustment_type = "ChangeInCapacity"
cooldown = 300
autoscaling_group_name = "${aws_autoscaling_group.bar.name}"
}
resource "aws_cloudwatch_metric_alarm" "bat" {
alarm_name = "terraform-test-foobar5"
comparison_operator = "GreaterThanOrEqualToThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "120"
statistic = "Average"
threshold = "80"
alarm_description = "This metric monitor ec2 cpu utilization"
alarm_actions = ["${aws_autoscaling_policy.bat.arn}"]
}
```
## Argument Reference
See [related part of AWS Docs](http://docs.aws.amazon.com/AmazonCloudWatch/latest/APIReference/API_PutMetricAlarm.html)
for details about valid values.
The following arguments are supported:
* `alarm_name` - (Required) The descriptive name for the alarm. This name must be unique within the user's AWS account
* `comparison_operator` - (Required) The arithmetic operation to use when comparing the specified Statistic and Threshold. The specified Statistic value is used as the first operand. Either of the following is supported: `GreaterThanOrEqualToThreshold`, `GreaterThanThreshold`, `LessThanThreshold`, `LessThanOrEqualToThreshold`.
* `evaluation_periods` - (Required) The number of periods over which data is compared to the specified threshold.
* `metric_name` - (Required) The name for the alarm's associated metric.
See docs for [supported metrics]([valid metrics](http://docs.aws.amazon.com/AmazonCloudWatch/latest/DeveloperGuide/CW_Support_For_AWS.html)).
* `namespace` - (Required) The namespace for the alarm's associated metric.
* `period` - (Required) The period in seconds over which the specified `statistic` is applied.
* `statistic` - (Required) The statistic to apply to the alarm's associated metric.
Either of the following is supported: `SampleCount`, `Average`, `Sum`, `Minimum`, `Maximum`
* `threshold` - (Required) The value against which the specified statistic is compared.
* `actions_enabled` - (Optional) Indicates whether or not actions should be executed during any changes to the alarm's state. Defaults to `true`.
* `alarm_actions` - (Optional) The list of actions to execute when this alarm transitions into an ALARM state from any other state. Each action is specified as an Amazon Resource Number (ARN).
* `alarm_description` - (Optional) The description for the alarm.
* `dimensions` - (Optional) The dimensions for the alarm's associated metric.
* `insufficient_data_actions` - (Optional) The list of actions to execute when this alarm transitions into an INSUFFICIENT_DATA state from any other state. Each action is specified as an Amazon Resource Number (ARN).
* `ok_actions` - (Optional) The list of actions to execute when this alarm transitions into an OK state from any other state. Each action is specified as an Amazon Resource Number (ARN).
* `unit` - (Optional) The unit for the alarm's associated metric.

View File

@ -0,0 +1,42 @@
---
layout: "azure"
page_title: "Azure: azure_affinity_group"
sidebar_current: "docs-azure-affinity-group"
description: |-
Creates a new affinity group on Azure.
---
# azure\_affinity\_group
Creates a new affinity group on Azure.
## Example Usage
```
resource "azure_affinity_group" "terraform-main-group" {
name = "terraform-group"
location = "North Europe"
label = "tf-group-01"
description = "Affinity group created by Terraform."
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the affinity group. Must be unique on your
Azure subscription.
* `location` - (Required) The location where the affinity group should be created.
For a list of all Azure locations, please consult [this link](http://azure.microsoft.com/en-us/regions/).
* `label` - (Required) A label to be used for tracking purposes.
* `description` - (Optional) A description for the affinity group.
## Attributes Reference
The following attributes are exported:
* `id` - The affinity group ID. Coincides with the given `name`.

View File

@ -21,6 +21,14 @@
<a href="/docs/providers/aws/r/autoscaling_notification.html">aws_autoscaling_notification</a>
</li>
<li<%= sidebar_current("docs-aws-resource-autoscaling-policy") %>>
<a href="/docs/providers/aws/r/autoscaling_policy.html">aws_autoscaling_policy</a>
</li>
<li<%= sidebar_current("docs-aws-resource-cloudwatch-metric-alarm") %>>
<a href="/docs/providers/aws/r/cloudwatch_metric_alarm.html">aws_cloudwatch_metric_alarm</a>
</li>
<li<%= sidebar_current("docs-aws-resource-customer-gateway") %>>
<a href="/docs/providers/aws/r/customer_gateway.html">aws_customer_gateway</a>
</li>

View File

@ -13,6 +13,10 @@
<li<%= sidebar_current(/^docs-azure-resource/) %>>
<a href="#">Resources</a>
<ul class="nav nav-visible">
<li<%= sidebar_current("docs-azure-resource-affinity-group") %>>
<a href="/docs/providers/azure/r/affinity_group.html">azure_affinity_group</a>
</li>
<li<%= sidebar_current("docs-azure-resource-data-disk") %>>
<a href="/docs/providers/azure/r/data_disk.html">azure_data_disk</a>
</li>