github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/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/apigateway" 11 "github.com/aws/aws-sdk-go/service/s3" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func validateRdsId(v interface{}, k string) (ws []string, errors []error) { 16 value := v.(string) 17 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 18 errors = append(errors, fmt.Errorf( 19 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 20 } 21 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 22 errors = append(errors, fmt.Errorf( 23 "first character of %q must be a letter", k)) 24 } 25 if regexp.MustCompile(`--`).MatchString(value) { 26 errors = append(errors, fmt.Errorf( 27 "%q cannot contain two consecutive hyphens", k)) 28 } 29 if regexp.MustCompile(`-$`).MatchString(value) { 30 errors = append(errors, fmt.Errorf( 31 "%q cannot end with a hyphen", k)) 32 } 33 return 34 } 35 36 func validateElastiCacheClusterId(v interface{}, k string) (ws []string, errors []error) { 37 value := v.(string) 38 if (len(value) < 1) || (len(value) > 20) { 39 errors = append(errors, fmt.Errorf( 40 "%q (%q) must contain from 1 to 20 alphanumeric characters or hyphens", k, value)) 41 } 42 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 43 errors = append(errors, fmt.Errorf( 44 "only lowercase alphanumeric characters and hyphens allowed in %q (%q)", k, value)) 45 } 46 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 47 errors = append(errors, fmt.Errorf( 48 "first character of %q (%q) must be a letter", k, value)) 49 } 50 if regexp.MustCompile(`--`).MatchString(value) { 51 errors = append(errors, fmt.Errorf( 52 "%q (%q) cannot contain two consecutive hyphens", k, value)) 53 } 54 if regexp.MustCompile(`-$`).MatchString(value) { 55 errors = append(errors, fmt.Errorf( 56 "%q (%q) cannot end with a hyphen", k, value)) 57 } 58 return 59 } 60 61 func validateASGScheduleTimestamp(v interface{}, k string) (ws []string, errors []error) { 62 value := v.(string) 63 _, err := time.Parse(awsAutoscalingScheduleTimeLayout, value) 64 if err != nil { 65 errors = append(errors, fmt.Errorf( 66 "%q cannot be parsed as iso8601 Timestamp Format", value)) 67 } 68 69 return 70 } 71 72 // validateTagFilters confirms the "value" component of a tag filter is one of 73 // AWS's three allowed types. 74 func validateTagFilters(v interface{}, k string) (ws []string, errors []error) { 75 value := v.(string) 76 if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" { 77 errors = append(errors, fmt.Errorf( 78 "%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k)) 79 } 80 return 81 } 82 83 func validateDbParamGroupName(v interface{}, k string) (ws []string, errors []error) { 84 value := v.(string) 85 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 86 errors = append(errors, fmt.Errorf( 87 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 88 } 89 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 90 errors = append(errors, fmt.Errorf( 91 "first character of %q must be a letter", k)) 92 } 93 if regexp.MustCompile(`--`).MatchString(value) { 94 errors = append(errors, fmt.Errorf( 95 "%q cannot contain two consecutive hyphens", k)) 96 } 97 if regexp.MustCompile(`-$`).MatchString(value) { 98 errors = append(errors, fmt.Errorf( 99 "%q cannot end with a hyphen", k)) 100 } 101 if len(value) > 255 { 102 errors = append(errors, fmt.Errorf( 103 "%q cannot be greater than 255 characters", k)) 104 } 105 return 106 107 } 108 109 func validateStreamViewType(v interface{}, k string) (ws []string, errors []error) { 110 value := v.(string) 111 viewTypes := map[string]bool{ 112 "KEYS_ONLY": true, 113 "NEW_IMAGE": true, 114 "OLD_IMAGE": true, 115 "NEW_AND_OLD_IMAGES": true, 116 } 117 118 if !viewTypes[value] { 119 errors = append(errors, fmt.Errorf("%q be a valid DynamoDB StreamViewType", k)) 120 } 121 return 122 } 123 124 func validateElbName(v interface{}, k string) (ws []string, errors []error) { 125 value := v.(string) 126 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 127 errors = append(errors, fmt.Errorf( 128 "only alphanumeric characters and hyphens allowed in %q: %q", 129 k, value)) 130 } 131 if len(value) > 32 { 132 errors = append(errors, fmt.Errorf( 133 "%q cannot be longer than 32 characters: %q", k, value)) 134 } 135 if regexp.MustCompile(`^-`).MatchString(value) { 136 errors = append(errors, fmt.Errorf( 137 "%q cannot begin with a hyphen: %q", k, value)) 138 } 139 if regexp.MustCompile(`-$`).MatchString(value) { 140 errors = append(errors, fmt.Errorf( 141 "%q cannot end with a hyphen: %q", k, value)) 142 } 143 return 144 } 145 146 func validateElbNamePrefix(v interface{}, k string) (ws []string, errors []error) { 147 value := v.(string) 148 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 149 errors = append(errors, fmt.Errorf( 150 "only alphanumeric characters and hyphens allowed in %q: %q", 151 k, value)) 152 } 153 if len(value) > 6 { 154 errors = append(errors, fmt.Errorf( 155 "%q cannot be longer than 6 characters: %q", k, value)) 156 } 157 if regexp.MustCompile(`^-`).MatchString(value) { 158 errors = append(errors, fmt.Errorf( 159 "%q cannot begin with a hyphen: %q", k, value)) 160 } 161 return 162 } 163 164 func validateEcrRepositoryName(v interface{}, k string) (ws []string, errors []error) { 165 value := v.(string) 166 if len(value) < 2 { 167 errors = append(errors, fmt.Errorf( 168 "%q must be at least 2 characters long: %q", k, value)) 169 } 170 if len(value) > 256 { 171 errors = append(errors, fmt.Errorf( 172 "%q cannot be longer than 256 characters: %q", k, value)) 173 } 174 175 // http://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_CreateRepository.html 176 pattern := `^(?:[a-z0-9]+(?:[._-][a-z0-9]+)*/)*[a-z0-9]+(?:[._-][a-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 validateCloudWatchEventRuleName(v interface{}, k string) (ws []string, errors []error) { 187 value := v.(string) 188 if len(value) > 64 { 189 errors = append(errors, fmt.Errorf( 190 "%q cannot be longer than 64 characters: %q", k, value)) 191 } 192 193 // http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_PutRule.html 194 pattern := `^[\.\-_A-Za-z0-9]+$` 195 if !regexp.MustCompile(pattern).MatchString(value) { 196 errors = append(errors, fmt.Errorf( 197 "%q doesn't comply with restrictions (%q): %q", 198 k, pattern, value)) 199 } 200 201 return 202 } 203 204 func validateMaxLength(length int) schema.SchemaValidateFunc { 205 return func(v interface{}, k string) (ws []string, errors []error) { 206 value := v.(string) 207 if len(value) > length { 208 errors = append(errors, fmt.Errorf( 209 "%q cannot be longer than %d characters: %q", k, length, value)) 210 } 211 return 212 } 213 } 214 215 func validateIntegerInRange(min, max int) schema.SchemaValidateFunc { 216 return func(v interface{}, k string) (ws []string, errors []error) { 217 value := v.(int) 218 if value < min { 219 errors = append(errors, fmt.Errorf( 220 "%q cannot be lower than %d: %d", k, min, value)) 221 } 222 if value > max { 223 errors = append(errors, fmt.Errorf( 224 "%q cannot be higher than %d: %d", k, max, value)) 225 } 226 return 227 } 228 } 229 230 func validateCloudWatchEventTargetId(v interface{}, k string) (ws []string, errors []error) { 231 value := v.(string) 232 if len(value) > 64 { 233 errors = append(errors, fmt.Errorf( 234 "%q cannot be longer than 64 characters: %q", k, value)) 235 } 236 237 // http://docs.aws.amazon.com/AmazonCloudWatchEvents/latest/APIReference/API_Target.html 238 pattern := `^[\.\-_A-Za-z0-9]+$` 239 if !regexp.MustCompile(pattern).MatchString(value) { 240 errors = append(errors, fmt.Errorf( 241 "%q doesn't comply with restrictions (%q): %q", 242 k, pattern, value)) 243 } 244 245 return 246 } 247 248 func validateLambdaFunctionName(v interface{}, k string) (ws []string, errors []error) { 249 value := v.(string) 250 if len(value) > 140 { 251 errors = append(errors, fmt.Errorf( 252 "%q cannot be longer than 140 characters: %q", k, value)) 253 } 254 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 255 pattern := `^(arn:[\w-]+:lambda:)?([a-z]{2}-[a-z]+-\d{1}:)?(\d{12}:)?(function:)?([a-zA-Z0-9-_]+)(:(\$LATEST|[a-zA-Z0-9-_]+))?$` 256 if !regexp.MustCompile(pattern).MatchString(value) { 257 errors = append(errors, fmt.Errorf( 258 "%q doesn't comply with restrictions (%q): %q", 259 k, pattern, value)) 260 } 261 262 return 263 } 264 265 func validateLambdaQualifier(v interface{}, k string) (ws []string, errors []error) { 266 value := v.(string) 267 if len(value) > 128 { 268 errors = append(errors, fmt.Errorf( 269 "%q cannot be longer than 128 characters: %q", k, value)) 270 } 271 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 272 pattern := `^[a-zA-Z0-9$_-]+$` 273 if !regexp.MustCompile(pattern).MatchString(value) { 274 errors = append(errors, fmt.Errorf( 275 "%q doesn't comply with restrictions (%q): %q", 276 k, pattern, value)) 277 } 278 279 return 280 } 281 282 func validateLambdaPermissionAction(v interface{}, k string) (ws []string, errors []error) { 283 value := v.(string) 284 285 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 286 pattern := `^(lambda:[*]|lambda:[a-zA-Z]+|[*])$` 287 if !regexp.MustCompile(pattern).MatchString(value) { 288 errors = append(errors, fmt.Errorf( 289 "%q doesn't comply with restrictions (%q): %q", 290 k, pattern, value)) 291 } 292 293 return 294 } 295 296 func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) { 297 value := v.(string) 298 299 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 300 pattern := `^\d{12}$` 301 if !regexp.MustCompile(pattern).MatchString(value) { 302 errors = append(errors, fmt.Errorf( 303 "%q doesn't look like AWS Account ID (exactly 12 digits): %q", 304 k, value)) 305 } 306 307 return 308 } 309 310 func validateArn(v interface{}, k string) (ws []string, errors []error) { 311 value := v.(string) 312 313 if value == "" { 314 return 315 } 316 317 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 318 pattern := `^arn:[\w-]+:([a-zA-Z0-9\-])+:([a-z]{2}-[a-z]+-\d{1})?:(\d{12})?:(.*)$` 319 if !regexp.MustCompile(pattern).MatchString(value) { 320 errors = append(errors, fmt.Errorf( 321 "%q doesn't look like a valid ARN (%q): %q", 322 k, pattern, value)) 323 } 324 325 return 326 } 327 328 func validatePolicyStatementId(v interface{}, k string) (ws []string, errors []error) { 329 value := v.(string) 330 331 if len(value) > 100 { 332 errors = append(errors, fmt.Errorf( 333 "%q cannot be longer than 100 characters: %q", k, value)) 334 } 335 336 // http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html 337 pattern := `^[a-zA-Z0-9-_]+$` 338 if !regexp.MustCompile(pattern).MatchString(value) { 339 errors = append(errors, fmt.Errorf( 340 "%q doesn't look like a valid statement ID (%q): %q", 341 k, pattern, value)) 342 } 343 344 return 345 } 346 347 // validateCIDRNetworkAddress ensures that the string value is a valid CIDR that 348 // represents a network address - it adds an error otherwise 349 func validateCIDRNetworkAddress(v interface{}, k string) (ws []string, errors []error) { 350 value := v.(string) 351 _, ipnet, err := net.ParseCIDR(value) 352 if err != nil { 353 errors = append(errors, fmt.Errorf( 354 "%q must contain a valid CIDR, got error parsing: %s", k, err)) 355 return 356 } 357 358 if ipnet == nil || value != ipnet.String() { 359 errors = append(errors, fmt.Errorf( 360 "%q must contain a valid network CIDR, expected %q, got %q", 361 k, ipnet, value)) 362 } 363 364 return 365 } 366 367 func validateHTTPMethod(v interface{}, k string) (ws []string, errors []error) { 368 value := v.(string) 369 370 validMethods := map[string]bool{ 371 "ANY": true, 372 "DELETE": true, 373 "GET": true, 374 "HEAD": true, 375 "OPTIONS": true, 376 "PATCH": true, 377 "POST": true, 378 "PUT": true, 379 } 380 381 if _, ok := validMethods[value]; !ok { 382 errors = append(errors, fmt.Errorf( 383 "%q contains an invalid method %q. Valid methods are either %q, %q, %q, %q, %q, %q, %q, or %q.", 384 k, value, "ANY", "DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT")) 385 } 386 return 387 } 388 389 func validateLogMetricFilterName(v interface{}, k string) (ws []string, errors []error) { 390 value := v.(string) 391 392 if len(value) > 512 { 393 errors = append(errors, fmt.Errorf( 394 "%q cannot be longer than 512 characters: %q", k, value)) 395 } 396 397 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_PutMetricFilter.html 398 pattern := `^[^:*]+$` 399 if !regexp.MustCompile(pattern).MatchString(value) { 400 errors = append(errors, fmt.Errorf( 401 "%q isn't a valid log metric name (must not contain colon nor asterisk): %q", 402 k, value)) 403 } 404 405 return 406 } 407 408 func validateLogMetricFilterTransformationName(v interface{}, k string) (ws []string, errors []error) { 409 value := v.(string) 410 411 if len(value) > 255 { 412 errors = append(errors, fmt.Errorf( 413 "%q cannot be longer than 255 characters: %q", k, value)) 414 } 415 416 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_MetricTransformation.html 417 pattern := `^[^:*$]*$` 418 if !regexp.MustCompile(pattern).MatchString(value) { 419 errors = append(errors, fmt.Errorf( 420 "%q isn't a valid log metric transformation name (must not contain"+ 421 " colon, asterisk nor dollar sign): %q", 422 k, value)) 423 } 424 425 return 426 } 427 428 func validateLogGroupName(v interface{}, k string) (ws []string, errors []error) { 429 value := v.(string) 430 431 if len(value) > 512 { 432 errors = append(errors, fmt.Errorf( 433 "%q cannot be longer than 512 characters: %q", k, value)) 434 } 435 436 // http://docs.aws.amazon.com/AmazonCloudWatchLogs/latest/APIReference/API_CreateLogGroup.html 437 pattern := `^[\.\-_/#A-Za-z0-9]+$` 438 if !regexp.MustCompile(pattern).MatchString(value) { 439 errors = append(errors, fmt.Errorf( 440 "%q isn't a valid log group name (alphanumeric characters, underscores,"+ 441 " hyphens, slashes, hash signs and dots are allowed): %q", 442 k, value)) 443 } 444 445 return 446 } 447 448 func validateS3BucketLifecycleTimestamp(v interface{}, k string) (ws []string, errors []error) { 449 value := v.(string) 450 _, err := time.Parse(time.RFC3339, fmt.Sprintf("%sT00:00:00Z", value)) 451 if err != nil { 452 errors = append(errors, fmt.Errorf( 453 "%q cannot be parsed as RFC3339 Timestamp Format", value)) 454 } 455 456 return 457 } 458 459 func validateS3BucketLifecycleStorageClass(v interface{}, k string) (ws []string, errors []error) { 460 value := v.(string) 461 if value != s3.TransitionStorageClassStandardIa && value != s3.TransitionStorageClassGlacier { 462 errors = append(errors, fmt.Errorf( 463 "%q must be one of '%q', '%q'", k, s3.TransitionStorageClassStandardIa, s3.TransitionStorageClassGlacier)) 464 } 465 466 return 467 } 468 469 func validateS3BucketReplicationRuleId(v interface{}, k string) (ws []string, errors []error) { 470 value := v.(string) 471 if len(value) > 255 { 472 errors = append(errors, fmt.Errorf( 473 "%q cannot be longer than 255 characters: %q", k, value)) 474 } 475 476 return 477 } 478 479 func validateS3BucketReplicationRulePrefix(v interface{}, k string) (ws []string, errors []error) { 480 value := v.(string) 481 if len(value) > 1024 { 482 errors = append(errors, fmt.Errorf( 483 "%q cannot be longer than 1024 characters: %q", k, value)) 484 } 485 486 return 487 } 488 489 func validateS3BucketReplicationDestinationStorageClass(v interface{}, k string) (ws []string, errors []error) { 490 value := v.(string) 491 if value != s3.StorageClassStandard && value != s3.StorageClassStandardIa && value != s3.StorageClassReducedRedundancy { 492 errors = append(errors, fmt.Errorf( 493 "%q must be one of '%q', '%q' or '%q'", k, s3.StorageClassStandard, s3.StorageClassStandardIa, s3.StorageClassReducedRedundancy)) 494 } 495 496 return 497 } 498 499 func validateS3BucketReplicationRuleStatus(v interface{}, k string) (ws []string, errors []error) { 500 value := v.(string) 501 if value != s3.ReplicationRuleStatusEnabled && value != s3.ReplicationRuleStatusDisabled { 502 errors = append(errors, fmt.Errorf( 503 "%q must be one of '%q' or '%q'", k, s3.ReplicationRuleStatusEnabled, s3.ReplicationRuleStatusDisabled)) 504 } 505 506 return 507 } 508 509 func validateS3BucketLifecycleRuleId(v interface{}, k string) (ws []string, errors []error) { 510 value := v.(string) 511 if len(value) > 255 { 512 errors = append(errors, fmt.Errorf( 513 "%q cannot exceed 255 characters", k)) 514 } 515 return 516 } 517 518 func validateDbEventSubscriptionName(v interface{}, k string) (ws []string, errors []error) { 519 value := v.(string) 520 if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { 521 errors = append(errors, fmt.Errorf( 522 "only alphanumeric characters and hyphens allowed in %q", k)) 523 } 524 if len(value) > 255 { 525 errors = append(errors, fmt.Errorf( 526 "%q cannot be longer than 255 characters", k)) 527 } 528 return 529 } 530 531 func validateApiGatewayIntegrationPassthroughBehavior(v interface{}, k string) (ws []string, errors []error) { 532 value := v.(string) 533 if value != "WHEN_NO_MATCH" && value != "WHEN_NO_TEMPLATES" && value != "NEVER" { 534 errors = append(errors, fmt.Errorf( 535 "%q must be one of 'WHEN_NO_MATCH', 'WHEN_NO_TEMPLATES', 'NEVER'", k)) 536 } 537 return 538 } 539 540 func validateJsonString(v interface{}, k string) (ws []string, errors []error) { 541 if _, err := normalizeJsonString(v); err != nil { 542 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 543 } 544 return 545 } 546 547 func validateCloudFormationTemplate(v interface{}, k string) (ws []string, errors []error) { 548 if looksLikeJsonString(v) { 549 if _, err := normalizeJsonString(v); err != nil { 550 errors = append(errors, fmt.Errorf("%q contains an invalid JSON: %s", k, err)) 551 } 552 } else { 553 if _, err := checkYamlString(v); err != nil { 554 errors = append(errors, fmt.Errorf("%q contains an invalid YAML: %s", k, err)) 555 } 556 } 557 return 558 } 559 560 func validateApiGatewayIntegrationType(v interface{}, k string) (ws []string, errors []error) { 561 value := v.(string) 562 563 validTypes := map[string]bool{ 564 "AWS": true, 565 "AWS_PROXY": true, 566 "HTTP": true, 567 "HTTP_PROXY": true, 568 "MOCK": true, 569 } 570 571 if _, ok := validTypes[value]; !ok { 572 errors = append(errors, fmt.Errorf( 573 "%q contains an invalid integration type %q. Valid types are either %q, %q, %q, %q, or %q.", 574 k, value, "AWS", "AWS_PROXY", "HTTP", "HTTP_PROXY", "MOCK")) 575 } 576 return 577 } 578 579 func validateApiGatewayIntegrationContentHandling(v interface{}, k string) (ws []string, errors []error) { 580 value := v.(string) 581 582 validTypes := map[string]bool{ 583 "CONVERT_TO_BINARY": true, 584 "CONVERT_TO_TEXT": true, 585 } 586 587 if _, ok := validTypes[value]; !ok { 588 errors = append(errors, fmt.Errorf( 589 "%q contains an invalid integration type %q. Valid types are either %q or %q.", 590 k, value, "CONVERT_TO_BINARY", "CONVERT_TO_TEXT")) 591 } 592 return 593 } 594 595 func validateSQSQueueName(v interface{}, k string) (errors []error) { 596 value := v.(string) 597 if len(value) > 80 { 598 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 599 } 600 601 if !regexp.MustCompile(`^[0-9A-Za-z-_]+$`).MatchString(value) { 602 errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k)) 603 } 604 return 605 } 606 607 func validateSQSFifoQueueName(v interface{}, k string) (errors []error) { 608 value := v.(string) 609 610 if len(value) > 80 { 611 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 612 } 613 614 if !regexp.MustCompile(`^[0-9A-Za-z-_.]+$`).MatchString(value) { 615 errors = append(errors, fmt.Errorf("only alphanumeric characters and hyphens allowed in %q", k)) 616 } 617 618 if regexp.MustCompile(`^[^a-zA-Z0-9-_]`).MatchString(value) { 619 errors = append(errors, fmt.Errorf("FIFO queue name must start with one of these characters [a-zA-Z0-9-_]: %v", value)) 620 } 621 622 if !regexp.MustCompile(`\.fifo$`).MatchString(value) { 623 errors = append(errors, fmt.Errorf("FIFO queue name should ends with \".fifo\": %v", value)) 624 } 625 626 return 627 } 628 629 func validateSNSSubscriptionProtocol(v interface{}, k string) (ws []string, errors []error) { 630 value := strings.ToLower(v.(string)) 631 forbidden := []string{"email", "sms"} 632 for _, f := range forbidden { 633 if strings.Contains(value, f) { 634 errors = append( 635 errors, 636 fmt.Errorf("Unsupported protocol (%s) for SNS Topic", value), 637 ) 638 } 639 } 640 return 641 } 642 643 func validateSecurityRuleType(v interface{}, k string) (ws []string, errors []error) { 644 value := strings.ToLower(v.(string)) 645 646 validTypes := map[string]bool{ 647 "ingress": true, 648 "egress": true, 649 } 650 651 if _, ok := validTypes[value]; !ok { 652 errors = append(errors, fmt.Errorf( 653 "%q contains an invalid Security Group Rule type %q. Valid types are either %q or %q.", 654 k, value, "ingress", "egress")) 655 } 656 return 657 } 658 659 func validateOnceAWeekWindowFormat(v interface{}, k string) (ws []string, errors []error) { 660 // valid time format is "ddd:hh24:mi" 661 validTimeFormat := "(sun|mon|tue|wed|thu|fri|sat):([0-1][0-9]|2[0-3]):([0-5][0-9])" 662 validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$" 663 664 value := strings.ToLower(v.(string)) 665 if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) { 666 errors = append(errors, fmt.Errorf( 667 "%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k)) 668 } 669 return 670 } 671 672 func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) { 673 // valid time format is "hh24:mi" 674 validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])" 675 validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$" 676 677 value := v.(string) 678 if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) { 679 errors = append(errors, fmt.Errorf( 680 "%q must satisfy the format of \"hh24:mi-hh24:mi\".", k)) 681 } 682 return 683 } 684 685 func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) { 686 // Valid Record types 687 // SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA 688 validTypes := map[string]struct{}{ 689 "SOA": {}, 690 "A": {}, 691 "TXT": {}, 692 "NS": {}, 693 "CNAME": {}, 694 "MX": {}, 695 "NAPTR": {}, 696 "PTR": {}, 697 "SRV": {}, 698 "SPF": {}, 699 "AAAA": {}, 700 } 701 702 value := v.(string) 703 if _, ok := validTypes[value]; !ok { 704 errors = append(errors, fmt.Errorf( 705 "%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k)) 706 } 707 return 708 } 709 710 // Validates that ECS Placement Constraints are set correctly 711 // Takes type, and expression as strings 712 func validateAwsEcsPlacementConstraint(constType, constExpr string) error { 713 switch constType { 714 case "distinctInstance": 715 // Expression can be nil for distinctInstance 716 return nil 717 case "memberOf": 718 if constExpr == "" { 719 return fmt.Errorf("Expression cannot be nil for 'memberOf' type") 720 } 721 default: 722 return fmt.Errorf("Unknown type provided: %q", constType) 723 } 724 return nil 725 } 726 727 // Validates that an Ecs placement strategy is set correctly 728 // Takes type, and field as strings 729 func validateAwsEcsPlacementStrategy(stratType, stratField string) error { 730 switch stratType { 731 case "random": 732 // random does not need the field attribute set, could error, but it isn't read at the API level 733 return nil 734 case "spread": 735 // For the spread placement strategy, valid values are instanceId 736 // (or host, which has the same effect), or any platform or custom attribute 737 // that is applied to a container instance 738 // stratField is already cased to a string 739 return nil 740 case "binpack": 741 if stratField != "cpu" && stratField != "memory" { 742 return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s", 743 stratField) 744 } 745 default: 746 return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType) 747 } 748 return nil 749 } 750 751 func validateAwsEmrEbsVolumeType(v interface{}, k string) (ws []string, errors []error) { 752 validTypes := map[string]struct{}{ 753 "gp2": {}, 754 "io1": {}, 755 "standard": {}, 756 } 757 758 value := v.(string) 759 760 if _, ok := validTypes[value]; !ok { 761 errors = append(errors, fmt.Errorf( 762 "%q must be one of ['gp2', 'io1', 'standard']", k)) 763 } 764 return 765 } 766 767 func validateSfnActivityName(v interface{}, k string) (ws []string, errors []error) { 768 value := v.(string) 769 if len(value) > 80 { 770 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 771 } 772 773 return 774 } 775 776 func validateSfnStateMachineDefinition(v interface{}, k string) (ws []string, errors []error) { 777 value := v.(string) 778 if len(value) > 1048576 { 779 errors = append(errors, fmt.Errorf("%q cannot be longer than 1048576 characters", k)) 780 } 781 return 782 } 783 784 func validateSfnStateMachineName(v interface{}, k string) (ws []string, errors []error) { 785 value := v.(string) 786 if len(value) > 80 { 787 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 788 } 789 790 if !regexp.MustCompile(`^[a-zA-Z0-9-_]+$`).MatchString(value) { 791 errors = append(errors, fmt.Errorf( 792 "%q must be composed with only these characters [a-zA-Z0-9-_]: %v", k, value)) 793 } 794 return 795 } 796 797 func validateDmsCertificateId(v interface{}, k string) (ws []string, es []error) { 798 val := v.(string) 799 800 if len(val) > 255 { 801 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 802 } 803 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 804 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 805 } 806 if strings.Contains(val, "--") { 807 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 808 } 809 if strings.HasSuffix(val, "-") { 810 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 811 } 812 813 return 814 } 815 816 func validateDmsEndpointId(v interface{}, k string) (ws []string, es []error) { 817 val := v.(string) 818 819 if len(val) > 255 { 820 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 821 } 822 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 823 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 824 } 825 if strings.Contains(val, "--") { 826 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 827 } 828 if strings.HasSuffix(val, "-") { 829 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 830 } 831 832 return 833 } 834 835 func validateDmsReplicationInstanceId(v interface{}, k string) (ws []string, es []error) { 836 val := v.(string) 837 838 if len(val) > 63 { 839 es = append(es, fmt.Errorf("%q must not be longer than 63 characters", k)) 840 } 841 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 842 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 843 } 844 if strings.Contains(val, "--") { 845 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 846 } 847 if strings.HasSuffix(val, "-") { 848 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 849 } 850 851 return 852 } 853 854 func validateDmsReplicationSubnetGroupId(v interface{}, k string) (ws []string, es []error) { 855 val := v.(string) 856 857 if val == "default" { 858 es = append(es, fmt.Errorf("%q must not be default", k)) 859 } 860 if len(val) > 255 { 861 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 862 } 863 if !regexp.MustCompile(`^[a-zA-Z0-9. _-]+$`).MatchString(val) { 864 es = append(es, fmt.Errorf("%q must only contain alphanumeric characters, periods, spaces, underscores and hyphens", k)) 865 } 866 867 return 868 } 869 870 func validateDmsReplicationTaskId(v interface{}, k string) (ws []string, es []error) { 871 val := v.(string) 872 873 if len(val) > 255 { 874 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 875 } 876 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 877 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 878 } 879 if strings.Contains(val, "--") { 880 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 881 } 882 if strings.HasSuffix(val, "-") { 883 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 884 } 885 886 return 887 } 888 889 func validateAppautoscalingScalableDimension(v interface{}, k string) (ws []string, errors []error) { 890 value := v.(string) 891 dimensions := map[string]bool{ 892 "ecs:service:DesiredCount": true, 893 "ec2:spot-fleet-request:TargetCapacity": true, 894 } 895 896 if !dimensions[value] { 897 errors = append(errors, fmt.Errorf("%q must be a valid scalable dimension value: %q", k, value)) 898 } 899 return 900 } 901 902 func validateAppautoscalingServiceNamespace(v interface{}, k string) (ws []string, errors []error) { 903 value := v.(string) 904 namespaces := map[string]bool{ 905 "ecs": true, 906 "ec2": true, 907 } 908 909 if !namespaces[value] { 910 errors = append(errors, fmt.Errorf("%q must be a valid service namespace value: %q", k, value)) 911 } 912 return 913 } 914 915 func validateConfigRuleSourceOwner(v interface{}, k string) (ws []string, errors []error) { 916 validOwners := []string{ 917 "CUSTOM_LAMBDA", 918 "AWS", 919 } 920 owner := v.(string) 921 for _, o := range validOwners { 922 if owner == o { 923 return 924 } 925 } 926 errors = append(errors, fmt.Errorf( 927 "%q contains an invalid owner %q. Valid owners are %q.", 928 k, owner, validOwners)) 929 return 930 } 931 932 func validateConfigExecutionFrequency(v interface{}, k string) (ws []string, errors []error) { 933 validFrequencies := []string{ 934 "One_Hour", 935 "Three_Hours", 936 "Six_Hours", 937 "Twelve_Hours", 938 "TwentyFour_Hours", 939 } 940 frequency := v.(string) 941 for _, f := range validFrequencies { 942 if frequency == f { 943 return 944 } 945 } 946 errors = append(errors, fmt.Errorf( 947 "%q contains an invalid frequency %q. Valid frequencies are %q.", 948 k, frequency, validFrequencies)) 949 return 950 } 951 952 func validateAccountAlias(v interface{}, k string) (ws []string, es []error) { 953 val := v.(string) 954 955 if (len(val) < 3) || (len(val) > 63) { 956 es = append(es, fmt.Errorf("%q must contain from 3 to 63 alphanumeric characters or hyphens", k)) 957 } 958 if !regexp.MustCompile("^[a-z0-9][a-z0-9-]+$").MatchString(val) { 959 es = append(es, fmt.Errorf("%q must start with an alphanumeric character and only contain lowercase alphanumeric characters and hyphens", k)) 960 } 961 if strings.Contains(val, "--") { 962 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 963 } 964 if strings.HasSuffix(val, "-") { 965 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 966 } 967 return 968 } 969 970 func validateApiGatewayApiKeyValue(v interface{}, k string) (ws []string, errors []error) { 971 value := v.(string) 972 if len(value) < 30 { 973 errors = append(errors, fmt.Errorf( 974 "%q must be at least 30 characters long", k)) 975 } 976 if len(value) > 128 { 977 errors = append(errors, fmt.Errorf( 978 "%q cannot be longer than 128 characters", k)) 979 } 980 return 981 } 982 983 func validateIamRolePolicyName(v interface{}, k string) (ws []string, errors []error) { 984 // https://github.com/boto/botocore/blob/2485f5c/botocore/data/iam/2010-05-08/service-2.json#L8291-L8296 985 value := v.(string) 986 if len(value) > 128 { 987 errors = append(errors, fmt.Errorf( 988 "%q cannot be longer than 128 characters", k)) 989 } 990 if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) { 991 errors = append(errors, fmt.Errorf("%q must match [\\w+=,.@-]", k)) 992 } 993 return 994 } 995 996 func validateIamRolePolicyNamePrefix(v interface{}, k string) (ws []string, errors []error) { 997 value := v.(string) 998 if len(value) > 100 { 999 errors = append(errors, fmt.Errorf( 1000 "%q cannot be longer than 100 characters", k)) 1001 } 1002 if !regexp.MustCompile("^[\\w+=,.@-]+$").MatchString(value) { 1003 errors = append(errors, fmt.Errorf("%q must match [\\w+=,.@-]", k)) 1004 } 1005 return 1006 } 1007 1008 func validateApiGatewayUsagePlanQuotaSettingsPeriod(v interface{}, k string) (ws []string, errors []error) { 1009 validPeriods := []string{ 1010 apigateway.QuotaPeriodTypeDay, 1011 apigateway.QuotaPeriodTypeWeek, 1012 apigateway.QuotaPeriodTypeMonth, 1013 } 1014 period := v.(string) 1015 for _, f := range validPeriods { 1016 if period == f { 1017 return 1018 } 1019 } 1020 errors = append(errors, fmt.Errorf( 1021 "%q contains an invalid period %q. Valid period are %q.", 1022 k, period, validPeriods)) 1023 return 1024 } 1025 1026 func validateApiGatewayUsagePlanQuotaSettings(v map[string]interface{}) (errors []error) { 1027 period := v["period"].(string) 1028 offset := v["offset"].(int) 1029 1030 if period == apigateway.QuotaPeriodTypeDay && offset != 0 { 1031 errors = append(errors, fmt.Errorf("Usage Plan quota offset must be zero in the DAY period")) 1032 } 1033 1034 if period == apigateway.QuotaPeriodTypeWeek && (offset < 0 || offset > 6) { 1035 errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 6 inclusive in the WEEK period")) 1036 } 1037 1038 if period == apigateway.QuotaPeriodTypeMonth && (offset < 0 || offset > 27) { 1039 errors = append(errors, fmt.Errorf("Usage Plan quota offset must be between 0 and 27 inclusive in the MONTH period")) 1040 } 1041 1042 return 1043 }