github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/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 (%q) must contain from 1 to 20 alphanumeric characters or hyphens", k, value)) 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 (%q)", k, value)) 44 } 45 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 46 errors = append(errors, fmt.Errorf( 47 "first character of %q (%q) must be a letter", k, value)) 48 } 49 if regexp.MustCompile(`--`).MatchString(value) { 50 errors = append(errors, fmt.Errorf( 51 "%q (%q) cannot contain two consecutive hyphens", k, value)) 52 } 53 if regexp.MustCompile(`-$`).MatchString(value) { 54 errors = append(errors, fmt.Errorf( 55 "%q (%q) cannot end with a hyphen", k, value)) 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 validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$" 645 646 value := strings.ToLower(v.(string)) 647 if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) { 648 errors = append(errors, fmt.Errorf( 649 "%q must satisfy the format of \"ddd:hh24:mi-ddd:hh24:mi\".", k)) 650 } 651 return 652 } 653 654 func validateOnceADayWindowFormat(v interface{}, k string) (ws []string, errors []error) { 655 // valid time format is "hh24:mi" 656 validTimeFormat := "([0-1][0-9]|2[0-3]):([0-5][0-9])" 657 validTimeFormatConsolidated := "^(" + validTimeFormat + "-" + validTimeFormat + "|)$" 658 659 value := v.(string) 660 if !regexp.MustCompile(validTimeFormatConsolidated).MatchString(value) { 661 errors = append(errors, fmt.Errorf( 662 "%q must satisfy the format of \"hh24:mi-hh24:mi\".", k)) 663 } 664 return 665 } 666 667 func validateRoute53RecordType(v interface{}, k string) (ws []string, errors []error) { 668 // Valid Record types 669 // SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA 670 validTypes := map[string]struct{}{ 671 "SOA": {}, 672 "A": {}, 673 "TXT": {}, 674 "NS": {}, 675 "CNAME": {}, 676 "MX": {}, 677 "NAPTR": {}, 678 "PTR": {}, 679 "SRV": {}, 680 "SPF": {}, 681 "AAAA": {}, 682 } 683 684 value := v.(string) 685 if _, ok := validTypes[value]; !ok { 686 errors = append(errors, fmt.Errorf( 687 "%q must be one of [SOA, A, TXT, NS, CNAME, MX, NAPTR, PTR, SRV, SPF, AAAA]", k)) 688 } 689 return 690 } 691 692 // Validates that ECS Placement Constraints are set correctly 693 // Takes type, and expression as strings 694 func validateAwsEcsPlacementConstraint(constType, constExpr string) error { 695 switch constType { 696 case "distinctInstance": 697 // Expression can be nil for distinctInstance 698 return nil 699 case "memberOf": 700 if constExpr == "" { 701 return fmt.Errorf("Expression cannot be nil for 'memberOf' type") 702 } 703 default: 704 return fmt.Errorf("Unknown type provided: %q", constType) 705 } 706 return nil 707 } 708 709 // Validates that an Ecs placement strategy is set correctly 710 // Takes type, and field as strings 711 func validateAwsEcsPlacementStrategy(stratType, stratField string) error { 712 switch stratType { 713 case "random": 714 // random does not need the field attribute set, could error, but it isn't read at the API level 715 return nil 716 case "spread": 717 // For the spread placement strategy, valid values are instanceId 718 // (or host, which has the same effect), or any platform or custom attribute 719 // that is applied to a container instance 720 // stratField is already cased to a string 721 return nil 722 case "binpack": 723 if stratField != "cpu" && stratField != "memory" { 724 return fmt.Errorf("Binpack type requires the field attribute to be either 'cpu' or 'memory'. Got: %s", 725 stratField) 726 } 727 default: 728 return fmt.Errorf("Unknown type %s. Must be one of 'random', 'spread', or 'binpack'.", stratType) 729 } 730 return nil 731 } 732 733 func validateAwsEmrEbsVolumeType(v interface{}, k string) (ws []string, errors []error) { 734 validTypes := map[string]struct{}{ 735 "gp2": {}, 736 "io1": {}, 737 "standard": {}, 738 } 739 740 value := v.(string) 741 742 if _, ok := validTypes[value]; !ok { 743 errors = append(errors, fmt.Errorf( 744 "%q must be one of ['gp2', 'io1', 'standard']", k)) 745 } 746 return 747 } 748 749 func validateSfnActivityName(v interface{}, k string) (ws []string, errors []error) { 750 value := v.(string) 751 if len(value) > 80 { 752 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 753 } 754 755 return 756 } 757 758 func validateSfnStateMachineDefinition(v interface{}, k string) (ws []string, errors []error) { 759 value := v.(string) 760 if len(value) > 1048576 { 761 errors = append(errors, fmt.Errorf("%q cannot be longer than 1048576 characters", k)) 762 } 763 return 764 } 765 766 func validateSfnStateMachineName(v interface{}, k string) (ws []string, errors []error) { 767 value := v.(string) 768 if len(value) > 80 { 769 errors = append(errors, fmt.Errorf("%q cannot be longer than 80 characters", k)) 770 } 771 772 if !regexp.MustCompile(`^[a-zA-Z0-9-_]+$`).MatchString(value) { 773 errors = append(errors, fmt.Errorf( 774 "%q must be composed with only these characters [a-zA-Z0-9-_]: %v", k, value)) 775 } 776 return 777 } 778 779 func validateDmsCertificateId(v interface{}, k string) (ws []string, es []error) { 780 val := v.(string) 781 782 if len(val) > 255 { 783 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 784 } 785 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 786 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 787 } 788 if strings.Contains(val, "--") { 789 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 790 } 791 if strings.HasSuffix(val, "-") { 792 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 793 } 794 795 return 796 } 797 798 func validateDmsEndpointId(v interface{}, k string) (ws []string, es []error) { 799 val := v.(string) 800 801 if len(val) > 255 { 802 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 803 } 804 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 805 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 806 } 807 if strings.Contains(val, "--") { 808 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 809 } 810 if strings.HasSuffix(val, "-") { 811 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 812 } 813 814 return 815 } 816 817 func validateDmsReplicationInstanceId(v interface{}, k string) (ws []string, es []error) { 818 val := v.(string) 819 820 if len(val) > 63 { 821 es = append(es, fmt.Errorf("%q must not be longer than 63 characters", k)) 822 } 823 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 824 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 825 } 826 if strings.Contains(val, "--") { 827 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 828 } 829 if strings.HasSuffix(val, "-") { 830 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 831 } 832 833 return 834 } 835 836 func validateDmsReplicationSubnetGroupId(v interface{}, k string) (ws []string, es []error) { 837 val := v.(string) 838 839 if val == "default" { 840 es = append(es, fmt.Errorf("%q must not be default", k)) 841 } 842 if len(val) > 255 { 843 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 844 } 845 if !regexp.MustCompile(`^[a-zA-Z0-9. _-]+$`).MatchString(val) { 846 es = append(es, fmt.Errorf("%q must only contain alphanumeric characters, periods, spaces, underscores and hyphens", k)) 847 } 848 849 return 850 } 851 852 func validateDmsReplicationTaskId(v interface{}, k string) (ws []string, es []error) { 853 val := v.(string) 854 855 if len(val) > 255 { 856 es = append(es, fmt.Errorf("%q must not be longer than 255 characters", k)) 857 } 858 if !regexp.MustCompile("^[a-zA-Z][a-zA-Z0-9-]+$").MatchString(val) { 859 es = append(es, fmt.Errorf("%q must start with a letter, only contain alphanumeric characters and hyphens", k)) 860 } 861 if strings.Contains(val, "--") { 862 es = append(es, fmt.Errorf("%q must not contain consecutive hyphens", k)) 863 } 864 if strings.HasSuffix(val, "-") { 865 es = append(es, fmt.Errorf("%q must not end in a hyphen", k)) 866 } 867 868 return 869 } 870 871 func validateAppautoscalingScalableDimension(v interface{}, k string) (ws []string, errors []error) { 872 value := v.(string) 873 dimensions := map[string]bool{ 874 "ecs:service:DesiredCount": true, 875 "ec2:spot-fleet-request:TargetCapacity": true, 876 } 877 878 if !dimensions[value] { 879 errors = append(errors, fmt.Errorf("%q must be a valid scalable dimension value: %q", k, value)) 880 } 881 return 882 } 883 884 func validateAppautoscalingServiceNamespace(v interface{}, k string) (ws []string, errors []error) { 885 value := v.(string) 886 namespaces := map[string]bool{ 887 "ecs": true, 888 "ec2": true, 889 } 890 891 if !namespaces[value] { 892 errors = append(errors, fmt.Errorf("%q must be a valid service namespace value: %q", k, value)) 893 } 894 return 895 } 896 897 func validateConfigRuleSourceOwner(v interface{}, k string) (ws []string, errors []error) { 898 validOwners := []string{ 899 "CUSTOM_LAMBDA", 900 "AWS", 901 } 902 owner := v.(string) 903 for _, o := range validOwners { 904 if owner == o { 905 return 906 } 907 } 908 errors = append(errors, fmt.Errorf( 909 "%q contains an invalid owner %q. Valid owners are %q.", 910 k, owner, validOwners)) 911 return 912 } 913 914 func validateConfigExecutionFrequency(v interface{}, k string) (ws []string, errors []error) { 915 validFrequencies := []string{ 916 "One_Hour", 917 "Three_Hours", 918 "Six_Hours", 919 "Twelve_Hours", 920 "TwentyFour_Hours", 921 } 922 frequency := v.(string) 923 for _, f := range validFrequencies { 924 if frequency == f { 925 return 926 } 927 } 928 errors = append(errors, fmt.Errorf( 929 "%q contains an invalid freqency %q. Valid frequencies are %q.", 930 k, frequency, validFrequencies)) 931 return 932 }