github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/azurerm/resource_arm_container_service.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 7 "net/http" 8 9 "time" 10 11 "bytes" 12 13 "github.com/Azure/azure-sdk-for-go/arm/containerservice" 14 "github.com/hashicorp/terraform/helper/hashcode" 15 "github.com/hashicorp/terraform/helper/resource" 16 "github.com/hashicorp/terraform/helper/schema" 17 ) 18 19 func resourceArmContainerService() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceArmContainerServiceCreate, 22 Read: resourceArmContainerServiceRead, 23 Update: resourceArmContainerServiceCreate, 24 Delete: resourceArmContainerServiceDelete, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 }, 32 33 "location": locationSchema(), 34 35 "resource_group_name": { 36 Type: schema.TypeString, 37 Required: true, 38 ForceNew: true, 39 }, 40 41 "orchestration_platform": { 42 Type: schema.TypeString, 43 Required: true, 44 ForceNew: true, 45 ValidateFunc: validateArmContainerServiceOrchestrationPlatform, 46 }, 47 48 "master_profile": { 49 Type: schema.TypeSet, 50 Required: true, 51 MaxItems: 1, 52 Elem: &schema.Resource{ 53 Schema: map[string]*schema.Schema{ 54 "count": { 55 Type: schema.TypeInt, 56 Optional: true, 57 Default: 1, 58 ValidateFunc: validateArmContainerServiceMasterProfileCount, 59 }, 60 61 "dns_prefix": { 62 Type: schema.TypeString, 63 Required: true, 64 }, 65 66 "fqdn": { 67 Type: schema.TypeString, 68 Computed: true, 69 }, 70 }, 71 }, 72 Set: resourceAzureRMContainerServiceMasterProfileHash, 73 }, 74 75 "linux_profile": { 76 Type: schema.TypeSet, 77 Required: true, 78 MaxItems: 1, 79 Elem: &schema.Resource{ 80 Schema: map[string]*schema.Schema{ 81 "admin_username": { 82 Type: schema.TypeString, 83 Required: true, 84 }, 85 "ssh_key": { 86 Type: schema.TypeSet, 87 Required: true, 88 MaxItems: 1, 89 Elem: &schema.Resource{ 90 Schema: map[string]*schema.Schema{ 91 "key_data": { 92 Type: schema.TypeString, 93 Required: true, 94 }, 95 }, 96 }, 97 }, 98 }, 99 }, 100 Set: resourceAzureRMContainerServiceLinuxProfilesHash, 101 }, 102 103 "agent_pool_profile": { 104 Type: schema.TypeSet, 105 Required: true, 106 MaxItems: 1, 107 Elem: &schema.Resource{ 108 Schema: map[string]*schema.Schema{ 109 "name": { 110 Type: schema.TypeString, 111 Required: true, 112 ForceNew: true, 113 }, 114 115 "count": { 116 Type: schema.TypeInt, 117 Optional: true, 118 Default: 1, 119 ValidateFunc: validateArmContainerServiceAgentPoolProfileCount, 120 }, 121 122 "dns_prefix": { 123 Type: schema.TypeString, 124 Required: true, 125 ForceNew: true, 126 }, 127 128 "fqdn": { 129 Type: schema.TypeString, 130 Computed: true, 131 }, 132 133 "vm_size": { 134 Type: schema.TypeString, 135 Required: true, 136 }, 137 }, 138 }, 139 Set: resourceAzureRMContainerServiceAgentPoolProfilesHash, 140 }, 141 142 "service_principal": { 143 Type: schema.TypeSet, 144 Optional: true, 145 MaxItems: 1, 146 Elem: &schema.Resource{ 147 Schema: map[string]*schema.Schema{ 148 "client_id": { 149 Type: schema.TypeString, 150 Required: true, 151 }, 152 153 "client_secret": { 154 Type: schema.TypeString, 155 Required: true, 156 Sensitive: true, 157 }, 158 }, 159 }, 160 Set: resourceAzureRMContainerServiceServicePrincipalProfileHash, 161 }, 162 163 "diagnostics_profile": { 164 Type: schema.TypeSet, 165 Required: true, 166 MaxItems: 1, 167 Elem: &schema.Resource{ 168 Schema: map[string]*schema.Schema{ 169 "enabled": { 170 Type: schema.TypeBool, 171 Required: true, 172 }, 173 174 "storage_uri": { 175 Type: schema.TypeString, 176 Computed: true, 177 }, 178 }, 179 }, 180 Set: resourceAzureRMContainerServiceDiagnosticProfilesHash, 181 }, 182 183 "tags": tagsSchema(), 184 }, 185 } 186 } 187 188 func resourceArmContainerServiceCreate(d *schema.ResourceData, meta interface{}) error { 189 client := meta.(*ArmClient) 190 containerServiceClient := client.containerServicesClient 191 192 log.Printf("[INFO] preparing arguments for Azure ARM Container Service creation.") 193 194 resGroup := d.Get("resource_group_name").(string) 195 name := d.Get("name").(string) 196 location := d.Get("location").(string) 197 198 orchestrationPlatform := d.Get("orchestration_platform").(string) 199 200 masterProfile := expandAzureRmContainerServiceMasterProfile(d) 201 linuxProfile := expandAzureRmContainerServiceLinuxProfile(d) 202 agentProfiles := expandAzureRmContainerServiceAgentProfiles(d) 203 diagnosticsProfile := expandAzureRmContainerServiceDiagnostics(d) 204 205 tags := d.Get("tags").(map[string]interface{}) 206 207 parameters := containerservice.ContainerService{ 208 Name: &name, 209 Location: &location, 210 Properties: &containerservice.Properties{ 211 MasterProfile: &masterProfile, 212 LinuxProfile: &linuxProfile, 213 OrchestratorProfile: &containerservice.OrchestratorProfile{ 214 OrchestratorType: containerservice.OchestratorTypes(orchestrationPlatform), 215 }, 216 AgentPoolProfiles: &agentProfiles, 217 DiagnosticsProfile: &diagnosticsProfile, 218 }, 219 Tags: expandTags(tags), 220 } 221 222 servicePrincipalProfile := expandAzureRmContainerServiceServicePrincipal(d) 223 if servicePrincipalProfile != nil { 224 parameters.ServicePrincipalProfile = servicePrincipalProfile 225 } 226 227 _, err := containerServiceClient.CreateOrUpdate(resGroup, name, parameters, make(chan struct{})) 228 if err != nil { 229 return err 230 } 231 232 read, err := containerServiceClient.Get(resGroup, name) 233 if err != nil { 234 return err 235 } 236 237 if read.ID == nil { 238 return fmt.Errorf("Cannot read Container Service %s (resource group %s) ID", name, resGroup) 239 } 240 241 log.Printf("[DEBUG] Waiting for Container Service (%s) to become available", d.Get("name")) 242 stateConf := &resource.StateChangeConf{ 243 Pending: []string{"Updating", "Creating"}, 244 Target: []string{"Succeeded"}, 245 Refresh: containerServiceStateRefreshFunc(client, resGroup, name), 246 Timeout: 30 * time.Minute, 247 MinTimeout: 15 * time.Second, 248 } 249 if _, err := stateConf.WaitForState(); err != nil { 250 return fmt.Errorf("Error waiting for Container Service (%s) to become available: %s", d.Get("name"), err) 251 } 252 253 d.SetId(*read.ID) 254 255 return resourceArmContainerServiceRead(d, meta) 256 } 257 258 func resourceArmContainerServiceRead(d *schema.ResourceData, meta interface{}) error { 259 containerServiceClient := meta.(*ArmClient).containerServicesClient 260 261 id, err := parseAzureResourceID(d.Id()) 262 if err != nil { 263 return err 264 } 265 resGroup := id.ResourceGroup 266 name := id.Path["containerServices"] 267 268 resp, err := containerServiceClient.Get(resGroup, name) 269 if err != nil { 270 return fmt.Errorf("Error making Read request on Azure Container Service %s: %s", name, err) 271 } 272 if resp.StatusCode == http.StatusNotFound { 273 d.SetId("") 274 return nil 275 } 276 277 d.Set("name", resp.Name) 278 d.Set("location", azureRMNormalizeLocation(*resp.Location)) 279 d.Set("resource_group_name", resGroup) 280 281 d.Set("orchestration_platform", string(resp.Properties.OrchestratorProfile.OrchestratorType)) 282 283 masterProfiles := flattenAzureRmContainerServiceMasterProfile(*resp.Properties.MasterProfile) 284 d.Set("master_profile", &masterProfiles) 285 286 linuxProfile := flattenAzureRmContainerServiceLinuxProfile(*resp.Properties.LinuxProfile) 287 d.Set("linux_profile", &linuxProfile) 288 289 agentPoolProfiles := flattenAzureRmContainerServiceAgentPoolProfiles(resp.Properties.AgentPoolProfiles) 290 d.Set("agent_pool_profile", &agentPoolProfiles) 291 292 servicePrincipal := flattenAzureRmContainerServiceServicePrincipalProfile(resp.Properties.ServicePrincipalProfile) 293 if servicePrincipal != nil { 294 d.Set("service_principal", servicePrincipal) 295 } 296 297 diagnosticProfile := flattenAzureRmContainerServiceDiagnosticsProfile(resp.Properties.DiagnosticsProfile) 298 if diagnosticProfile != nil { 299 d.Set("diagnostics_profile", diagnosticProfile) 300 } 301 302 flattenAndSetTags(d, resp.Tags) 303 304 return nil 305 } 306 307 func resourceArmContainerServiceDelete(d *schema.ResourceData, meta interface{}) error { 308 client := meta.(*ArmClient) 309 containerServiceClient := client.containerServicesClient 310 311 id, err := parseAzureResourceID(d.Id()) 312 if err != nil { 313 return err 314 } 315 resGroup := id.ResourceGroup 316 name := id.Path["containerServices"] 317 318 resp, err := containerServiceClient.Delete(resGroup, name, make(chan struct{})) 319 if err != nil { 320 return err 321 } 322 323 if resp.StatusCode != http.StatusOK { 324 return fmt.Errorf("Error issuing Azure ARM delete request of Container Service '%s': %s", name, err) 325 } 326 327 return nil 328 329 } 330 331 func flattenAzureRmContainerServiceMasterProfile(profile containerservice.MasterProfile) *schema.Set { 332 masterProfiles := &schema.Set{ 333 F: resourceAzureRMContainerServiceMasterProfileHash, 334 } 335 336 masterProfile := make(map[string]interface{}, 2) 337 338 masterProfile["count"] = int(*profile.Count) 339 masterProfile["dns_prefix"] = *profile.DNSPrefix 340 341 masterProfiles.Add(masterProfile) 342 343 return masterProfiles 344 } 345 346 func flattenAzureRmContainerServiceLinuxProfile(profile containerservice.LinuxProfile) *schema.Set { 347 profiles := &schema.Set{ 348 F: resourceAzureRMContainerServiceLinuxProfilesHash, 349 } 350 351 values := map[string]interface{}{} 352 353 sshKeys := &schema.Set{ 354 F: resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash, 355 } 356 for _, ssh := range *profile.SSH.PublicKeys { 357 keys := map[string]interface{}{} 358 keys["key_data"] = *ssh.KeyData 359 sshKeys.Add(keys) 360 } 361 362 values["admin_username"] = *profile.AdminUsername 363 values["ssh_key"] = &sshKeys 364 profiles.Add(values) 365 366 return profiles 367 } 368 369 func flattenAzureRmContainerServiceAgentPoolProfiles(profiles *[]containerservice.AgentPoolProfile) *schema.Set { 370 agentPoolProfiles := &schema.Set{ 371 F: resourceAzureRMContainerServiceAgentPoolProfilesHash, 372 } 373 374 for _, profile := range *profiles { 375 agentPoolProfile := map[string]interface{}{} 376 agentPoolProfile["count"] = int(*profile.Count) 377 agentPoolProfile["dns_prefix"] = *profile.DNSPrefix 378 agentPoolProfile["fqdn"] = *profile.Fqdn 379 agentPoolProfile["name"] = *profile.Name 380 agentPoolProfile["vm_size"] = string(profile.VMSize) 381 agentPoolProfiles.Add(agentPoolProfile) 382 } 383 384 return agentPoolProfiles 385 } 386 387 func flattenAzureRmContainerServiceServicePrincipalProfile(profile *containerservice.ServicePrincipalProfile) *schema.Set { 388 389 if profile == nil { 390 return nil 391 } 392 393 servicePrincipalProfiles := &schema.Set{ 394 F: resourceAzureRMContainerServiceServicePrincipalProfileHash, 395 } 396 397 values := map[string]interface{}{} 398 399 values["client_id"] = *profile.ClientID 400 if profile.Secret != nil { 401 values["client_secret"] = *profile.Secret 402 } 403 404 servicePrincipalProfiles.Add(values) 405 406 return servicePrincipalProfiles 407 } 408 409 func flattenAzureRmContainerServiceDiagnosticsProfile(profile *containerservice.DiagnosticsProfile) *schema.Set { 410 diagnosticProfiles := &schema.Set{ 411 F: resourceAzureRMContainerServiceDiagnosticProfilesHash, 412 } 413 414 values := map[string]interface{}{} 415 416 values["enabled"] = *profile.VMDiagnostics.Enabled 417 if profile.VMDiagnostics.StorageURI != nil { 418 values["storage_uri"] = *profile.VMDiagnostics.StorageURI 419 } 420 diagnosticProfiles.Add(values) 421 422 return diagnosticProfiles 423 } 424 425 func expandAzureRmContainerServiceDiagnostics(d *schema.ResourceData) containerservice.DiagnosticsProfile { 426 configs := d.Get("diagnostics_profile").(*schema.Set).List() 427 profile := containerservice.DiagnosticsProfile{} 428 429 data := configs[0].(map[string]interface{}) 430 431 enabled := data["enabled"].(bool) 432 433 profile = containerservice.DiagnosticsProfile{ 434 VMDiagnostics: &containerservice.VMDiagnostics{ 435 Enabled: &enabled, 436 }, 437 } 438 439 return profile 440 } 441 442 func expandAzureRmContainerServiceLinuxProfile(d *schema.ResourceData) containerservice.LinuxProfile { 443 profiles := d.Get("linux_profile").(*schema.Set).List() 444 config := profiles[0].(map[string]interface{}) 445 446 adminUsername := config["admin_username"].(string) 447 448 linuxKeys := config["ssh_key"].(*schema.Set).List() 449 sshPublicKeys := []containerservice.SSHPublicKey{} 450 451 key := linuxKeys[0].(map[string]interface{}) 452 keyData := key["key_data"].(string) 453 454 sshPublicKey := containerservice.SSHPublicKey{ 455 KeyData: &keyData, 456 } 457 458 sshPublicKeys = append(sshPublicKeys, sshPublicKey) 459 460 profile := containerservice.LinuxProfile{ 461 AdminUsername: &adminUsername, 462 SSH: &containerservice.SSHConfiguration{ 463 PublicKeys: &sshPublicKeys, 464 }, 465 } 466 467 return profile 468 } 469 470 func expandAzureRmContainerServiceMasterProfile(d *schema.ResourceData) containerservice.MasterProfile { 471 configs := d.Get("master_profile").(*schema.Set).List() 472 config := configs[0].(map[string]interface{}) 473 474 count := int32(config["count"].(int)) 475 dnsPrefix := config["dns_prefix"].(string) 476 477 profile := containerservice.MasterProfile{ 478 Count: &count, 479 DNSPrefix: &dnsPrefix, 480 } 481 482 return profile 483 } 484 485 func expandAzureRmContainerServiceServicePrincipal(d *schema.ResourceData) *containerservice.ServicePrincipalProfile { 486 487 value, exists := d.GetOk("service_principal") 488 if !exists { 489 return nil 490 } 491 492 configs := value.(*schema.Set).List() 493 494 config := configs[0].(map[string]interface{}) 495 496 clientId := config["client_id"].(string) 497 clientSecret := config["client_secret"].(string) 498 499 principal := containerservice.ServicePrincipalProfile{ 500 ClientID: &clientId, 501 Secret: &clientSecret, 502 } 503 504 return &principal 505 } 506 507 func expandAzureRmContainerServiceAgentProfiles(d *schema.ResourceData) []containerservice.AgentPoolProfile { 508 configs := d.Get("agent_pool_profile").(*schema.Set).List() 509 config := configs[0].(map[string]interface{}) 510 profiles := make([]containerservice.AgentPoolProfile, 0, len(configs)) 511 512 name := config["name"].(string) 513 count := int32(config["count"].(int)) 514 dnsPrefix := config["dns_prefix"].(string) 515 vmSize := config["vm_size"].(string) 516 517 profile := containerservice.AgentPoolProfile{ 518 Name: &name, 519 Count: &count, 520 VMSize: containerservice.VMSizeTypes(vmSize), 521 DNSPrefix: &dnsPrefix, 522 } 523 524 profiles = append(profiles, profile) 525 526 return profiles 527 } 528 529 func containerServiceStateRefreshFunc(client *ArmClient, resourceGroupName string, containerServiceName string) resource.StateRefreshFunc { 530 return func() (interface{}, string, error) { 531 res, err := client.containerServicesClient.Get(resourceGroupName, containerServiceName) 532 if err != nil { 533 return nil, "", fmt.Errorf("Error issuing read request in containerServiceStateRefreshFunc to Azure ARM for Container Service '%s' (RG: '%s'): %s", containerServiceName, resourceGroupName, err) 534 } 535 536 return res, *res.Properties.ProvisioningState, nil 537 } 538 } 539 540 func resourceAzureRMContainerServiceMasterProfileHash(v interface{}) int { 541 var buf bytes.Buffer 542 m := v.(map[string]interface{}) 543 544 count := m["count"].(int) 545 dnsPrefix := m["dns_prefix"].(string) 546 547 buf.WriteString(fmt.Sprintf("%d-", count)) 548 buf.WriteString(fmt.Sprintf("%s-", dnsPrefix)) 549 550 return hashcode.String(buf.String()) 551 } 552 553 func resourceAzureRMContainerServiceLinuxProfilesHash(v interface{}) int { 554 var buf bytes.Buffer 555 m := v.(map[string]interface{}) 556 557 adminUsername := m["admin_username"].(string) 558 559 buf.WriteString(fmt.Sprintf("%s-", adminUsername)) 560 561 return hashcode.String(buf.String()) 562 } 563 564 func resourceAzureRMContainerServiceLinuxProfilesSSHKeysHash(v interface{}) int { 565 var buf bytes.Buffer 566 m := v.(map[string]interface{}) 567 568 keyData := m["key_data"].(string) 569 570 buf.WriteString(fmt.Sprintf("%s-", keyData)) 571 572 return hashcode.String(buf.String()) 573 } 574 575 func resourceAzureRMContainerServiceAgentPoolProfilesHash(v interface{}) int { 576 var buf bytes.Buffer 577 m := v.(map[string]interface{}) 578 579 count := m["count"].(int) 580 dnsPrefix := m["dns_prefix"].(string) 581 name := m["name"].(string) 582 vm_size := m["vm_size"].(string) 583 584 buf.WriteString(fmt.Sprintf("%d-", count)) 585 buf.WriteString(fmt.Sprintf("%s-", dnsPrefix)) 586 buf.WriteString(fmt.Sprintf("%s-", name)) 587 buf.WriteString(fmt.Sprintf("%s-", vm_size)) 588 589 return hashcode.String(buf.String()) 590 } 591 592 func resourceAzureRMContainerServiceServicePrincipalProfileHash(v interface{}) int { 593 var buf bytes.Buffer 594 m := v.(map[string]interface{}) 595 596 clientId := m["client_id"].(string) 597 buf.WriteString(fmt.Sprintf("%s-", clientId)) 598 599 return hashcode.String(buf.String()) 600 } 601 602 func resourceAzureRMContainerServiceDiagnosticProfilesHash(v interface{}) int { 603 var buf bytes.Buffer 604 m := v.(map[string]interface{}) 605 606 enabled := m["enabled"].(bool) 607 608 buf.WriteString(fmt.Sprintf("%t", enabled)) 609 610 return hashcode.String(buf.String()) 611 } 612 613 func validateArmContainerServiceOrchestrationPlatform(v interface{}, k string) (ws []string, errors []error) { 614 value := v.(string) 615 capacities := map[string]bool{ 616 "DCOS": true, 617 "Kubernetes": true, 618 "Swarm": true, 619 } 620 621 if !capacities[value] { 622 errors = append(errors, fmt.Errorf("Container Service: Orchestration Platgorm can only be DCOS / Kubernetes / Swarm")) 623 } 624 return 625 } 626 627 func validateArmContainerServiceMasterProfileCount(v interface{}, k string) (ws []string, errors []error) { 628 value := v.(int) 629 capacities := map[int]bool{ 630 1: true, 631 3: true, 632 5: true, 633 } 634 635 if !capacities[value] { 636 errors = append(errors, fmt.Errorf("The number of master nodes must be 1, 3 or 5.")) 637 } 638 return 639 } 640 641 func validateArmContainerServiceAgentPoolProfileCount(v interface{}, k string) (ws []string, errors []error) { 642 value := v.(int) 643 if value > 100 || 0 >= value { 644 errors = append(errors, fmt.Errorf("The Count for an Agent Pool Profile can only be between 1 and 100.")) 645 } 646 return 647 }