github.com/pbthorste/terraform@v0.8.6-0.20170127005045-deb56bd93da2/builtin/providers/aws/validators.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"regexp"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/aws/aws-sdk-go/service/s3"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  )
    13  
    14  func validateRdsId(v interface{}, k string) (ws []string, errors []error) {
    15  	value := v.(string)
    16  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    17  		errors = append(errors, fmt.Errorf(
    18  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    19  	}
    20  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    21  		errors = append(errors, fmt.Errorf(
    22  			"first character of %q must be a letter", k))
    23  	}
    24  	if regexp.MustCompile(`--`).MatchString(value) {
    25  		errors = append(errors, fmt.Errorf(
    26  			"%q cannot contain two consecutive hyphens", k))
    27  	}
    28  	if regexp.MustCompile(`-$`).MatchString(value) {
    29  		errors = append(errors, fmt.Errorf(
    30  			"%q cannot end with a hyphen", k))
    31  	}
    32  	return
    33  }
    34  
    35  func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) {
    36  	value := v.(string)
    37  	if (len(value) < 1) || (len(value) > 20) {
    38  		errors = append(errors, fmt.Errorf(
    39  			"%q must contain from 1 to 20 alphanumeric characters or hyphens", k))
    40  	}
    41  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    42  		errors = append(errors, fmt.Errorf(
    43  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    44  	}
    45  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    46  		errors = append(errors, fmt.Errorf(
    47  			"first character of %q must be a letter", k))
    48  	}
    49  	if regexp.MustCompile(`--`).MatchString(value) {
    50  		errors = append(errors, fmt.Errorf(
    51  			"%q cannot contain two consecutive hyphens", k))
    52  	}
    53  	if regexp.MustCompile(`-$`).MatchString(value) {
    54  		errors = append(errors, fmt.Errorf(
    55  			"%q cannot end with a hyphen", k))
    56  	}
    57  	return
    58  }
    59  
    60  func validateASGScheduleTimestamp(v interface{}, k string) (ws []string, errors []error) {
    61  	value := v.(string)
    62  	_, err := time.Parse(awsAutoscalingScheduleTimeLayout, value)
    63  	if err != nil {
    64  		errors = append(errors, fmt.Errorf(
    65  			"%q cannot be parsed as iso8601 Timestamp Format", value))
    66  	}
    67  
    68  	return
    69  }
    70  
    71  // validateTagFilters confirms the "value" component of a tag filter is one of
    72  // AWS's three allowed types.
    73  func validateTagFilters(v interface{}, k string) (ws []string, errors []error) {
    74  	value := v.(string)
    75  	if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" {
    76  		errors = append(errors, fmt.Errorf(
    77  			"%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k))
    78  	}
    79  	return
    80  }
    81  
    82  func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) {
    83  	value := v.(string)
    84  	if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) {
    85  		errors = append(errors, fmt.Errorf(
    86  			"only lowercase alphanumeric characters and hyphens allowed in %q", k))
    87  	}
    88  	if !regexp.MustCompile(`^[a-z]`).MatchString(value) {
    89  		errors = append(errors, fmt.Errorf(
    90  			"first character of %q must be a letter", k))
    91  	}
    92  	if regexp.MustCompile(`--`).MatchString(value) {
    93  		errors = append(errors, fmt.Errorf(
    94  			"%q cannot contain two consecutive hyphens", k))
    95  	}
    96  	if regexp.MustCompile(`-$`).MatchString(value) {
    97  		errors = append(errors, fmt.Errorf(
    98  			"%q cannot end with a hyphen", k))
    99  	}
   100  	if len(value) > 255 {
   101  		errors = append(errors, fmt.Errorf(
   102  			"%q cannot be greater than 255 characters", k))
   103  	}
   104  	return
   105  
   106  }
   107  
   108  func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) {
   109  	value := v.(string)
   110  	viewTypes := map[string]bool{
   111  		"KEYS_ONLY":          true,
   112  		"NEW_IMAGE":          true,
   113  		"OLD_IMAGE":          true,
   114  		"NEW_AND_OLD_IMAGES": true,
   115  	}
   116  
   117  	if !viewTypes[value] {
   118  		errors = append(errors, fmt.Errorf("%q be a valid DynamoDB StreamViewType", k))
   119  	}
   120  	return
   121  }
   122  
   123  func validateElbName(v interface{}, k string) (ws []string, errors []error) {
   124  	value := v.(string)
   125  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   126  		errors = append(errors, fmt.Errorf(
   127  			"only alphanumeric characters and hyphens allowed in %q: %q",
   128  			k, value))
   129  	}
   130  	if len(value) > 32 {
   131  		errors = append(errors, fmt.Errorf(
   132  			"%q cannot be longer than 32 characters: %q", k, value))
   133  	}
   134  	if regexp.MustCompile(`^-`).MatchString(value) {
   135  		errors = append(errors, fmt.Errorf(
   136  			"%q cannot begin with a hyphen: %q", k, value))
   137  	}
   138  	if regexp.MustCompile(`-$`).MatchString(value) {
   139  		errors = append(errors, fmt.Errorf(
   140  			"%q cannot end with a hyphen: %q", k, value))
   141  	}
   142  	return
   143  
   144  }
   145  
   146  func validateEcrRepositoryName(v interface{}, k string) (ws []string, errors []error) {
   147  	value := v.(string)
   148  	if len(value) < 2 {
   149  		errors = append(errors, fmt.Errorf(
   150  			"%q must be at least 2 characters long: %q", k, value))
   151  	}
   152  	if len(value) > 256 {
   153  		errors = append(errors, fmt.Errorf(
   154  			"%q cannot be longer than 256 characters: %q", k, value))
   155  	}
   156  
   157  	// http://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_CreateRepository.html
   158  	pattern := `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-z0-9]+)*$`
   159  	if !regexp.MustCompile(pattern).MatchString(value) {
   160  		errors = append(errors, fmt.Errorf(
   161  			"%q doesn't comply with restrictions (%q): %q",
   162  			k, pattern, value))
   163  	}
   164  
   165  	return
   166  }
   167  
   168  func validateCloudWatchEventRuleName(v interface{}, k string) (ws []string, errors []error) {
   169  	value := v.(string)
   170  	if len(value) > 64 {
   171  		errors = append(errors, fmt.Errorf(
   172  			"%q cannot be longer than 64 characters: %q", k, value))
   173  	}
   174  
   175  	// http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html
   176  	pattern := `^[\.\-_A-Za-z0-9]+$`
   177  	if !regexp.MustCompile(pattern).MatchString(value) {
   178  		errors = append(errors, fmt.Errorf(
   179  			"%q doesn't comply with restrictions (%q): %q",
   180  			k, pattern, value))
   181  	}
   182  
   183  	return
   184  }
   185  
   186  func validateMaxLength(length int) schema.SchemaValidateFunc {
   187  	return func(v interface{}, k string) (ws []string, errors []error) {
   188  		value := v.(string)
   189  		if len(value) > length {
   190  			errors = append(errors, fmt.Errorf(
   191  				"%q cannot be longer than %d characters: %q", k, length, value))
   192  		}
   193  		return
   194  	}
   195  }
   196  
   197  func validateIntegerInRange(min, max int) schema.SchemaValidateFunc {
   198  	return func(v interface{}, k string) (ws []string, errors []error) {
   199  		value := v.(int)
   200  		if value < min {
   201  			errors = append(errors, fmt.Errorf(
   202  				"%q cannot be lower than %d: %d", k, min, value))
   203  		}
   204  		if value > max {
   205  			errors = append(errors, fmt.Errorf(
   206  				"%q cannot be higher than %d: %d", k, max, value))
   207  		}
   208  		return
   209  	}
   210  }
   211  
   212  func validateCloudWatchEventTargetId(v interface{}, k string) (ws []string, errors []error) {
   213  	value := v.(string)
   214  	if len(value) > 64 {
   215  		errors = append(errors, fmt.Errorf(
   216  			"%q cannot be longer than 64 characters: %q", k, value))
   217  	}
   218  
   219  	// http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_Target.html
   220  	pattern := `^[\.\-_A-Za-z0-9]+$`
   221  	if !regexp.MustCompile(pattern).MatchString(value) {
   222  		errors = append(errors, fmt.Errorf(
   223  			"%q doesn't comply with restrictions (%q): %q",
   224  			k, pattern, value))
   225  	}
   226  
   227  	return
   228  }
   229  
   230  func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []error) {
   231  	value := v.(string)
   232  	if len(value) > 140 {
   233  		errors = append(errors, fmt.Errorf(
   234  			"%q cannot be longer than 140 characters: %q", k, value))
   235  	}
   236  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   237  	pattern := `^(arn:[\w-]+:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$`
   238  	if !regexp.MustCompile(pattern).MatchString(value) {
   239  		errors = append(errors, fmt.Errorf(
   240  			"%q doesn't comply with restrictions (%q): %q",
   241  			k, pattern, value))
   242  	}
   243  
   244  	return
   245  }
   246  
   247  func validateLambdaQualifier(v interface{}, k string) (ws []string, errors []error) {
   248  	value := v.(string)
   249  	if len(value) > 128 {
   250  		errors = append(errors, fmt.Errorf(
   251  			"%q cannot be longer than 128 characters: %q", k, value))
   252  	}
   253  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   254  	pattern := `^[a-zA-Z0-9$_-]+$`
   255  	if !regexp.MustCompile(pattern).MatchString(value) {
   256  		errors = append(errors, fmt.Errorf(
   257  			"%q doesn't comply with restrictions (%q): %q",
   258  			k, pattern, value))
   259  	}
   260  
   261  	return
   262  }
   263  
   264  func validateLambdaPermissionAction(v interface{}, k string) (ws []string, errors []error) {
   265  	value := v.(string)
   266  
   267  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   268  	pattern := `^(lambda:[*]|lambda:[a-zA-Z]+|[*])$`
   269  	if !regexp.MustCompile(pattern).MatchString(value) {
   270  		errors = append(errors, fmt.Errorf(
   271  			"%q doesn't comply with restrictions (%q): %q",
   272  			k, pattern, value))
   273  	}
   274  
   275  	return
   276  }
   277  
   278  func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) {
   279  	value := v.(string)
   280  
   281  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   282  	pattern := `^\d{12}$`
   283  	if !regexp.MustCompile(pattern).MatchString(value) {
   284  		errors = append(errors, fmt.Errorf(
   285  			"%q doesn't look like AWS Account ID (exactly 12 digits): %q",
   286  			k, value))
   287  	}
   288  
   289  	return
   290  }
   291  
   292  func validateArn(v interface{}, k string) (ws []string, errors []error) {
   293  	value := v.(string)
   294  
   295  	if value == "" {
   296  		return
   297  	}
   298  
   299  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   300  	pattern := `^arn:[\w-]+:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$`
   301  	if !regexp.MustCompile(pattern).MatchString(value) {
   302  		errors = append(errors, fmt.Errorf(
   303  			"%q doesn't look like a valid ARN (%q): %q",
   304  			k, pattern, value))
   305  	}
   306  
   307  	return
   308  }
   309  
   310  func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) {
   311  	value := v.(string)
   312  
   313  	if len(value) > 100 {
   314  		errors = append(errors, fmt.Errorf(
   315  			"%q cannot be longer than 100 characters: %q", k, value))
   316  	}
   317  
   318  	// http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
   319  	pattern := `^[a-zA-Z0-9-_]+$`
   320  	if !regexp.MustCompile(pattern).MatchString(value) {
   321  		errors = append(errors, fmt.Errorf(
   322  			"%q doesn't look like a valid statement ID (%q): %q",
   323  			k, pattern, value))
   324  	}
   325  
   326  	return
   327  }
   328  
   329  // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that
   330  // represents a network address - it adds an error otherwise
   331  func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) {
   332  	value := v.(string)
   333  	_, ipnet, err := net.ParseCIDR(value)
   334  	if err != nil {
   335  		errors = append(errors, fmt.Errorf(
   336  			"%q must contain a valid CIDR, got error parsing: %s", k, err))
   337  		return
   338  	}
   339  
   340  	if ipnet == nil || value != ipnet.String() {
   341  		errors = append(errors, fmt.Errorf(
   342  			"%q must contain a valid network CIDR, expected %q, got %q",
   343  			k, ipnet, value))
   344  	}
   345  
   346  	return
   347  }
   348  
   349  func validateHTTPMethod(v interface{}, k string) (ws []string, errors []error) {
   350  	value := v.(string)
   351  
   352  	validMethods := map[string]bool{
   353  		"ANY":     true,
   354  		"DELETE":  true,
   355  		"GET":     true,
   356  		"HEAD":    true,
   357  		"OPTIONS": true,
   358  		"PATCH":   true,
   359  		"POST":    true,
   360  		"PUT":     true,
   361  	}
   362  
   363  	if _, ok := validMethods[value]; !ok {
   364  		errors = append(errors, fmt.Errorf(
   365  			"%q contains an invalid method %q. Valid methods are either %q, %q, %q, %q, %q, %q, %q, or %q.",
   366  			k, value, "ANY", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"))
   367  	}
   368  	return
   369  }
   370  
   371  func validateLogMetricFilterName(v interface{}, k string) (ws []string, errors []error) {
   372  	value := v.(string)
   373  
   374  	if len(value) > 512 {
   375  		errors = append(errors, fmt.Errorf(
   376  			"%q cannot be longer than 512 characters: %q", k, value))
   377  	}
   378  
   379  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutMetricFilter.html
   380  	pattern := `^[^:*]+$`
   381  	if !regexp.MustCompile(pattern).MatchString(value) {
   382  		errors = append(errors, fmt.Errorf(
   383  			"%q isn't a valid log metric name (must not contain colon nor asterisk): %q",
   384  			k, value))
   385  	}
   386  
   387  	return
   388  }
   389  
   390  func validateLogMetricFilterTransformationName(v interface{}, k string) (ws []string, errors []error) {
   391  	value := v.(string)
   392  
   393  	if len(value) > 255 {
   394  		errors = append(errors, fmt.Errorf(
   395  			"%q cannot be longer than 255 characters: %q", k, value))
   396  	}
   397  
   398  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_MetricTransformation.html
   399  	pattern := `^[^:*$]*$`
   400  	if !regexp.MustCompile(pattern).MatchString(value) {
   401  		errors = append(errors, fmt.Errorf(
   402  			"%q isn't a valid log metric transformation name (must not contain"+
   403  				" colon, asterisk nor dollar sign): %q",
   404  			k, value))
   405  	}
   406  
   407  	return
   408  }
   409  
   410  func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) {
   411  	value := v.(string)
   412  
   413  	if len(value) > 512 {
   414  		errors = append(errors, fmt.Errorf(
   415  			"%q cannot be longer than 512 characters: %q", k, value))
   416  	}
   417  
   418  	// http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html
   419  	pattern := `^[\.\-_/#A-Za-z0-9]+$`
   420  	if !regexp.MustCompile(pattern).MatchString(value) {
   421  		errors = append(errors, fmt.Errorf(
   422  			"%q isn't a valid log group name (alphanumeric characters, underscores,"+
   423  				" hyphens, slashes, hash signs and dots are allowed): %q",
   424  			k, value))
   425  	}
   426  
   427  	return
   428  }
   429  
   430  func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) {
   431  	value := v.(string)
   432  	_, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value))
   433  	if err != nil {
   434  		errors = append(errors, fmt.Errorf(
   435  			"%q cannot be parsed as RFC3339 Timestamp Format", value))
   436  	}
   437  
   438  	return
   439  }
   440  
   441  func validateS3BucketLifecycleStorageClass(v interface{}, k string) (ws []string, errors []error) {
   442  	value := v.(string)
   443  	if value != s3.TransitionStorageClassStandardIa && value != s3.TransitionStorageClassGlacier {
   444  		errors = append(errors, fmt.Errorf(
   445  			"%q must be one of '%q', '%q'", k, s3.TransitionStorageClassStandardIa, s3.TransitionStorageClassGlacier))
   446  	}
   447  
   448  	return
   449  }
   450  
   451  func validateS3BucketReplicationRuleId(v interface{}, k string) (ws []string, errors []error) {
   452  	value := v.(string)
   453  	if len(value) > 255 {
   454  		errors = append(errors, fmt.Errorf(
   455  			"%q cannot be longer than 255 characters: %q", k, value))
   456  	}
   457  
   458  	return
   459  }
   460  
   461  func validateS3BucketReplicationRulePrefix(v interface{}, k string) (ws []string, errors []error) {
   462  	value := v.(string)
   463  	if len(value) > 1024 {
   464  		errors = append(errors, fmt.Errorf(
   465  			"%q cannot be longer than 1024 characters: %q", k, value))
   466  	}
   467  
   468  	return
   469  }
   470  
   471  func validateS3BucketReplicationDestinationStorageClass(v interface{}, k string) (ws []string, errors []error) {
   472  	value := v.(string)
   473  	if value != s3.StorageClassStandard && value != s3.StorageClassStandardIa && value != s3.StorageClassReducedRedundancy {
   474  		errors = append(errors, fmt.Errorf(
   475  			"%q must be one of '%q', '%q' or '%q'", k, s3.StorageClassStandard, s3.StorageClassStandardIa, s3.StorageClassReducedRedundancy))
   476  	}
   477  
   478  	return
   479  }
   480  
   481  func validateS3BucketReplicationRuleStatus(v interface{}, k string) (ws []string, errors []error) {
   482  	value := v.(string)
   483  	if value != s3.ReplicationRuleStatusEnabled && value != s3.ReplicationRuleStatusDisabled {
   484  		errors = append(errors, fmt.Errorf(
   485  			"%q must be one of '%q' or '%q'", k, s3.ReplicationRuleStatusEnabled, s3.ReplicationRuleStatusDisabled))
   486  	}
   487  
   488  	return
   489  }
   490  
   491  func validateS3BucketLifecycleRuleId(v interface{}, k string) (ws []string, errors []error) {
   492  	value := v.(string)
   493  	if len(value) > 255 {
   494  		errors = append(errors, fmt.Errorf(
   495  			"%q cannot exceed 255 characters", k))
   496  	}
   497  	return
   498  }
   499  
   500  func validateDbEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) {
   501  	value := v.(string)
   502  	if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) {
   503  		errors = append(errors, fmt.Errorf(
   504  			"only alphanumeric characters and hyphens allowed in %q", k))
   505  	}
   506  	if len(value) > 255 {
   507  		errors = append(errors, fmt.Errorf(
   508  			"%q cannot be longer than 255 characters", k))
   509  	}
   510  	return
   511  }
   512  
   513  func validateApiGatewayIntegrationPassthroughBehavior(v interface{}, k string) (ws []string, errors []error) {
   514  	value := v.(string)
   515  	if value != "WHEN_NO_MATCH" && value != "WHEN_NO_TEMPLATES" && value != "NEVER" {
   516  		errors = append(errors, fmt.Errorf(
   517  			"%q must be one of 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES', 'NEVER'", k))
   518  	}
   519  	return
   520  }
   521  
   522  func validateJsonString(v interface{}, k string) (ws []string, errors []error) {
   523  	if _, err := normalizeJsonString(v); err != nil {
   524  		errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   525  	}
   526  	return
   527  }
   528  
   529  func validateCloudFormationTemplate(v interface{}, k string) (ws []string, errors []error) {
   530  	if looksLikeJsonString(v) {
   531  		if _, err := normalizeJsonString(v); err != nil {
   532  			errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err))
   533  		}
   534  	} else {
   535  		if _, err := checkYamlString(v); err != nil {
   536  			errors = append(errors, fmt.Errorf("%q contains an invalid YAML: %s", k, err))
   537  		}
   538  	}
   539  	return
   540  }
   541  
   542  func validateApiGatewayIntegrationType(v interface{}, k string) (ws []string, errors []error) {
   543  	value := v.(string)
   544  
   545  	validTypes := map[string]bool{
   546  		"AWS":        true,
   547  		"AWS_PROXY":  true,
   548  		"HTTP":       true,
   549  		"HTTP_PROXY": true,
   550  		"MOCK":       true,
   551  	}
   552  
   553  	if _, ok := validTypes[value]; !ok {
   554  		errors = append(errors, fmt.Errorf(
   555  			"%q contains an invalid integration type %q. Valid types are either %q, %q, %q, %q, or %q.",
   556  			k, value, "AWS", "AWS_PROXY", "HTTP", "HTTP_PROXY", "MOCK"))
   557  	}
   558  	return
   559  }
   560  
   561  func validateApiGatewayIntegrationContentHandling(v interface{}, k string) (ws []string, errors []error) {
   562  	value := v.(string)
   563  
   564  	validTypes := map[string]bool{
   565  		"CONVERT_TO_BINARY": true,
   566  		"CONVERT_TO_TEXT":   true,
   567  	}
   568  
   569  	if _, ok := validTypes[value]; !ok {
   570  		errors = append(errors, fmt.Errorf(
   571  			"%q contains an invalid integration type %q. Valid types are either %q or %q.",
   572  			k, value, "CONVERT_TO_BINARY", "CONVERT_TO_TEXT"))
   573  	}
   574  	return
   575  }
   576  
   577  func validateSQSQueueName(v interface{}, k string) (errors []error) {
   578  	value := v.(string)
   579  	if len(value) > 80 {
   580  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   581  	}
   582  
   583  	if !regexp.MustCompile(`^[0-9A-Za-z-_]+$`).MatchString(value) {
   584  		errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k))
   585  	}
   586  	return
   587  }
   588  
   589  func validateSQSFifoQueueName(v interface{}, k string) (errors []error) {
   590  	value := v.(string)
   591  
   592  	if len(value) > 80 {
   593  		errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k))
   594  	}
   595  
   596  	if !regexp.MustCompile(`^[0-9A-Za-z-_.]+$`).MatchString(value) {
   597  		errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k))
   598  	}
   599  
   600  	if regexp.MustCompile(`^[^a-zA-Z0-9-_]`).MatchString(value) {
   601  		errors = append(errors, fmt.Errorf("FIFO queue name must start with one of these characters [a-zA-Z0-9-_]: %v", value))
   602  	}
   603  
   604  	if !regexp.MustCompile(`\.fifo$`).MatchString(value) {
   605  		errors = append(errors, fmt.Errorf("FIFO queue name should ends with \".fifo\": %v", value))
   606  	}
   607  
   608  	return
   609  }
   610  
   611  func validateSNSSubscriptionProtocol(v interface{}, k string) (ws []string, errors []error) {
   612  	value := strings.ToLower(v.(string))
   613  	forbidden := []string{"email", "sms"}
   614  	for _, f := range forbidden {
   615  		if strings.Contains(value, f) {
   616  			errors = append(
   617  				errors,
   618  				fmt.Errorf("Unsupported protocol (%s) for SNS Topic", value),
   619  			)
   620  		}
   621  	}
   622  	return
   623  }
   624  
   625  func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) {
   626  	value := strings.ToLower(v.(string))
   627  
   628  	validTypes := map[string]bool{
   629  		"ingress": true,
   630  		"egress":  true,
   631  	}
   632  
   633  	if _, ok := validTypes[value]; !ok {
   634  		errors = append(errors, fmt.Errorf(
   635  			"%q contains an invalid Security Group Rule type %q. Valid types are either %q or %q.",
   636  			k, value, "ingress", "egress"))
   637  	}
   638  	return
   639  }
   640  
   641  func validateOnceAWeekWindowFormat(v interface{}, k string) (ws []string, errors []error) {
   642  	// valid time format is "ddd:hh24:mi"
   643  	validTimeFormat := "(sun|mon|tue|wed|thu|fri|sat):([0-1][0-9]|2[0-3]):([0-5][0-9])"
   644  
   645  	value := strings.ToLower(v.(string))
   646  	if !regexp.MustCompile(validTimeFormat + "-" + validTimeFormat).MatchString(value) {
   647  		errors = append(errors, fmt.Errorf(
   648  			"%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k))
   649  	}
   650  	return
   651  }
   652  
   653  func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) {
   654  	// valid time format is "hh24:mi"
   655  	validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])"
   656  
   657  	value := v.(string)
   658  	if !regexp.MustCompile(validTimeFormat + "-" + validTimeFormat).MatchString(value) {
   659  		errors = append(errors, fmt.Errorf(
   660  			"%q must satisfy the format of \"hh24:mi-hh24:mi\".", k))
   661  	}
   662  	return
   663  }
   664  
   665  func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) {
   666  	// Valid Record types
   667  	// SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA
   668  	validTypes := map[string]struct{}{
   669  		"SOA":   {},
   670  		"A":     {},
   671  		"TXT":   {},
   672  		"NS":    {},
   673  		"CNAME": {},
   674  		"MX":    {},
   675  		"NAPTR": {},
   676  		"PTR":   {},
   677  		"SRV":   {},
   678  		"SPF":   {},
   679  		"AAAA":  {},
   680  	}
   681  
   682  	value := v.(string)
   683  	if _, ok := validTypes[value]; !ok {
   684  		errors = append(errors, fmt.Errorf(
   685  			"%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k))
   686  	}
   687  	return
   688  }
   689  
   690  // Validates that ECS Placement Constraints are set correctly
   691  // Takes type, and expression as strings
   692  func validateAwsEcsPlacementConstraint(constType, constExpr string) error {
   693  	switch constType {
   694  	case "distinctInstance":
   695  		// Expression can be nil for distinctInstance
   696  		return nil
   697  	case "memberOf":
   698  		if constExpr == "" {
   699  			return fmt.Errorf("Expression cannot be nil for 'memberOf' type")
   700  		}
   701  	default:
   702  		return fmt.Errorf("Unknown type provided: %q", constType)
   703  	}
   704  	return nil
   705  }
   706  
   707  // Validates that an Ecs placement strategy is set correctly
   708  // Takes type, and field as strings
   709  func validateAwsEcsPlacementStrategy(stratType, stratField string) error {
   710  	switch stratType {
   711  	case "random":
   712  		// random does not need the field attribute set, could error, but it isn't read at the API level
   713  		return nil
   714  	case "spread":
   715  		//  For the spread placement strategy, valid values are instanceId
   716  		// (or host, which has the same effect), or any platform or custom attribute
   717  		// that is applied to a container instance
   718  		// stratField is already cased to a string
   719  		return nil
   720  	case "binpack":
   721  		if stratField != "cpu" && stratField != "memory" {
   722  			return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s",
   723  				stratField)
   724  		}
   725  	default:
   726  		return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType)
   727  	}
   728  	return nil
   729  }