github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/builtin/providers/azurerm/resource_arm_loadbalancer_rule.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "time" 8 9 "github.com/Azure/azure-sdk-for-go/arm/network" 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/jen20/riviera/azure" 14 ) 15 16 func resourceArmLoadBalancerRule() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceArmLoadBalancerRuleCreate, 19 Read: resourceArmLoadBalancerRuleRead, 20 Update: resourceArmLoadBalancerRuleCreate, 21 Delete: resourceArmLoadBalancerRuleDelete, 22 Importer: &schema.ResourceImporter{ 23 State: loadBalancerSubResourceStateImporter, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 ValidateFunc: validateArmLoadBalancerRuleName, 32 }, 33 34 "location": { 35 Type: schema.TypeString, 36 ForceNew: true, 37 Optional: true, 38 StateFunc: azureRMNormalizeLocation, 39 DiffSuppressFunc: azureRMSuppressLocationDiff, 40 Deprecated: "location is no longer used", 41 }, 42 43 "resource_group_name": { 44 Type: schema.TypeString, 45 Required: true, 46 ForceNew: true, 47 }, 48 49 "loadbalancer_id": { 50 Type: schema.TypeString, 51 Required: true, 52 ForceNew: true, 53 }, 54 55 "frontend_ip_configuration_name": { 56 Type: schema.TypeString, 57 Required: true, 58 }, 59 60 "frontend_ip_configuration_id": { 61 Type: schema.TypeString, 62 Computed: true, 63 }, 64 65 "backend_address_pool_id": { 66 Type: schema.TypeString, 67 Optional: true, 68 Computed: true, 69 }, 70 71 "protocol": { 72 Type: schema.TypeString, 73 Required: true, 74 }, 75 76 "frontend_port": { 77 Type: schema.TypeInt, 78 Required: true, 79 }, 80 81 "backend_port": { 82 Type: schema.TypeInt, 83 Required: true, 84 }, 85 86 "probe_id": { 87 Type: schema.TypeString, 88 Optional: true, 89 Computed: true, 90 }, 91 92 "enable_floating_ip": { 93 Type: schema.TypeBool, 94 Optional: true, 95 Default: false, 96 }, 97 98 "idle_timeout_in_minutes": { 99 Type: schema.TypeInt, 100 Optional: true, 101 Computed: true, 102 }, 103 104 "load_distribution": { 105 Type: schema.TypeString, 106 Optional: true, 107 Computed: true, 108 }, 109 }, 110 } 111 } 112 113 func resourceArmLoadBalancerRuleCreate(d *schema.ResourceData, meta interface{}) error { 114 client := meta.(*ArmClient) 115 lbClient := client.loadBalancerClient 116 117 loadBalancerID := d.Get("loadbalancer_id").(string) 118 armMutexKV.Lock(loadBalancerID) 119 defer armMutexKV.Unlock(loadBalancerID) 120 121 loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) 122 if err != nil { 123 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 124 } 125 if !exists { 126 d.SetId("") 127 log.Printf("[INFO] LoadBalancer %q not found. Removing from state", d.Get("name").(string)) 128 return nil 129 } 130 131 newLbRule, err := expandAzureRmLoadBalancerRule(d, loadBalancer) 132 if err != nil { 133 return errwrap.Wrapf("Error Exanding LoadBalancer Rule {{err}}", err) 134 } 135 136 lbRules := append(*loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules, *newLbRule) 137 138 existingRule, existingRuleIndex, exists := findLoadBalancerRuleByName(loadBalancer, d.Get("name").(string)) 139 if exists { 140 if d.Get("name").(string) == *existingRule.Name { 141 // this rule is being updated/reapplied remove old copy from the slice 142 lbRules = append(lbRules[:existingRuleIndex], lbRules[existingRuleIndex+1:]...) 143 } 144 } 145 146 loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules = &lbRules 147 resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string)) 148 if err != nil { 149 return errwrap.Wrapf("Error Getting LoadBalancer Name and Group: {{err}}", err) 150 } 151 152 _, err = lbClient.CreateOrUpdate(resGroup, loadBalancerName, *loadBalancer, make(chan struct{})) 153 if err != nil { 154 return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err) 155 } 156 157 read, err := lbClient.Get(resGroup, loadBalancerName, "") 158 if err != nil { 159 return errwrap.Wrapf("Error Getting LoadBalancer {{err}}", err) 160 } 161 if read.ID == nil { 162 return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", loadBalancerName, resGroup) 163 } 164 165 var rule_id string 166 for _, LoadBalancingRule := range *(*read.LoadBalancerPropertiesFormat).LoadBalancingRules { 167 if *LoadBalancingRule.Name == d.Get("name").(string) { 168 rule_id = *LoadBalancingRule.ID 169 } 170 } 171 172 if rule_id != "" { 173 d.SetId(rule_id) 174 } else { 175 return fmt.Errorf("Cannot find created LoadBalancer Rule ID %q", rule_id) 176 } 177 178 log.Printf("[DEBUG] Waiting for LoadBalancer (%s) to become available", loadBalancerName) 179 stateConf := &resource.StateChangeConf{ 180 Pending: []string{"Accepted", "Updating"}, 181 Target: []string{"Succeeded"}, 182 Refresh: loadbalancerStateRefreshFunc(client, resGroup, loadBalancerName), 183 Timeout: 10 * time.Minute, 184 } 185 if _, err := stateConf.WaitForState(); err != nil { 186 return fmt.Errorf("Error waiting for LoadBalancer (%s) to become available: %s", loadBalancerName, err) 187 } 188 189 return resourceArmLoadBalancerRuleRead(d, meta) 190 } 191 192 func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) error { 193 id, err := parseAzureResourceID(d.Id()) 194 if err != nil { 195 return err 196 } 197 name := id.Path["loadBalancingRules"] 198 199 loadBalancer, exists, err := retrieveLoadBalancerById(d.Get("loadbalancer_id").(string), meta) 200 if err != nil { 201 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 202 } 203 if !exists { 204 d.SetId("") 205 log.Printf("[INFO] LoadBalancer %q not found. Removing from state", name) 206 return nil 207 } 208 209 config, _, exists := findLoadBalancerRuleByName(loadBalancer, name) 210 if !exists { 211 d.SetId("") 212 log.Printf("[INFO] LoadBalancer Rule %q not found. Removing from state", name) 213 return nil 214 } 215 216 d.Set("name", config.Name) 217 d.Set("resource_group_name", id.ResourceGroup) 218 219 d.Set("protocol", config.LoadBalancingRulePropertiesFormat.Protocol) 220 d.Set("frontend_port", config.LoadBalancingRulePropertiesFormat.FrontendPort) 221 d.Set("backend_port", config.LoadBalancingRulePropertiesFormat.BackendPort) 222 223 if config.LoadBalancingRulePropertiesFormat.EnableFloatingIP != nil { 224 d.Set("enable_floating_ip", config.LoadBalancingRulePropertiesFormat.EnableFloatingIP) 225 } 226 227 if config.LoadBalancingRulePropertiesFormat.IdleTimeoutInMinutes != nil { 228 d.Set("idle_timeout_in_minutes", config.LoadBalancingRulePropertiesFormat.IdleTimeoutInMinutes) 229 } 230 231 if config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration != nil { 232 fipID, err := parseAzureResourceID(*config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration.ID) 233 if err != nil { 234 return err 235 } 236 237 d.Set("frontend_ip_configuration_name", fipID.Path["frontendIPConfigurations"]) 238 d.Set("frontend_ip_configuration_id", config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration.ID) 239 } 240 241 if config.LoadBalancingRulePropertiesFormat.BackendAddressPool != nil { 242 d.Set("backend_address_pool_id", config.LoadBalancingRulePropertiesFormat.BackendAddressPool.ID) 243 } 244 245 if config.LoadBalancingRulePropertiesFormat.Probe != nil { 246 d.Set("probe_id", config.LoadBalancingRulePropertiesFormat.Probe.ID) 247 } 248 249 if config.LoadBalancingRulePropertiesFormat.LoadDistribution != "" { 250 d.Set("load_distribution", config.LoadBalancingRulePropertiesFormat.LoadDistribution) 251 } 252 253 return nil 254 } 255 256 func resourceArmLoadBalancerRuleDelete(d *schema.ResourceData, meta interface{}) error { 257 client := meta.(*ArmClient) 258 lbClient := client.loadBalancerClient 259 260 loadBalancerID := d.Get("loadbalancer_id").(string) 261 armMutexKV.Lock(loadBalancerID) 262 defer armMutexKV.Unlock(loadBalancerID) 263 264 loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) 265 if err != nil { 266 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 267 } 268 if !exists { 269 d.SetId("") 270 return nil 271 } 272 273 _, index, exists := findLoadBalancerRuleByName(loadBalancer, d.Get("name").(string)) 274 if !exists { 275 return nil 276 } 277 278 oldLbRules := *loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules 279 newLbRules := append(oldLbRules[:index], oldLbRules[index+1:]...) 280 loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules = &newLbRules 281 282 resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string)) 283 if err != nil { 284 return errwrap.Wrapf("Error Getting LoadBalancer Name and Group: {{err}}", err) 285 } 286 287 _, err = lbClient.CreateOrUpdate(resGroup, loadBalancerName, *loadBalancer, make(chan struct{})) 288 if err != nil { 289 return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err) 290 } 291 292 read, err := lbClient.Get(resGroup, loadBalancerName, "") 293 if err != nil { 294 return errwrap.Wrapf("Error Getting LoadBalancer {{err}}", err) 295 } 296 if read.ID == nil { 297 return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", loadBalancerName, resGroup) 298 } 299 300 return nil 301 } 302 303 func expandAzureRmLoadBalancerRule(d *schema.ResourceData, lb *network.LoadBalancer) (*network.LoadBalancingRule, error) { 304 305 properties := network.LoadBalancingRulePropertiesFormat{ 306 Protocol: network.TransportProtocol(d.Get("protocol").(string)), 307 FrontendPort: azure.Int32(int32(d.Get("frontend_port").(int))), 308 BackendPort: azure.Int32(int32(d.Get("backend_port").(int))), 309 EnableFloatingIP: azure.Bool(d.Get("enable_floating_ip").(bool)), 310 } 311 312 if v, ok := d.GetOk("idle_timeout_in_minutes"); ok { 313 properties.IdleTimeoutInMinutes = azure.Int32(int32(v.(int))) 314 } 315 316 if v := d.Get("load_distribution").(string); v != "" { 317 properties.LoadDistribution = network.LoadDistribution(v) 318 } 319 320 if v := d.Get("frontend_ip_configuration_name").(string); v != "" { 321 rule, _, exists := findLoadBalancerFrontEndIpConfigurationByName(lb, v) 322 if !exists { 323 return nil, fmt.Errorf("[ERROR] Cannot find FrontEnd IP Configuration with the name %s", v) 324 } 325 326 feip := network.SubResource{ 327 ID: rule.ID, 328 } 329 330 properties.FrontendIPConfiguration = &feip 331 } 332 333 if v := d.Get("backend_address_pool_id").(string); v != "" { 334 beAP := network.SubResource{ 335 ID: &v, 336 } 337 338 properties.BackendAddressPool = &beAP 339 } 340 341 if v := d.Get("probe_id").(string); v != "" { 342 pid := network.SubResource{ 343 ID: &v, 344 } 345 346 properties.Probe = &pid 347 } 348 349 lbRule := network.LoadBalancingRule{ 350 Name: azure.String(d.Get("name").(string)), 351 LoadBalancingRulePropertiesFormat: &properties, 352 } 353 354 return &lbRule, nil 355 } 356 357 func validateArmLoadBalancerRuleName(v interface{}, k string) (ws []string, errors []error) { 358 value := v.(string) 359 if !regexp.MustCompile(`^[a-zA-Z_0-9.-]+$`).MatchString(value) { 360 errors = append(errors, fmt.Errorf( 361 "only word characters, numbers, underscores, periods, and hyphens allowed in %q: %q", 362 k, value)) 363 } 364 365 if len(value) > 80 { 366 errors = append(errors, fmt.Errorf( 367 "%q cannot be longer than 80 characters: %q", k, value)) 368 } 369 370 if len(value) == 0 { 371 errors = append(errors, fmt.Errorf( 372 "%q cannot be an empty string: %q", k, value)) 373 } 374 if !regexp.MustCompile(`[a-zA-Z0-9_]$`).MatchString(value) { 375 errors = append(errors, fmt.Errorf( 376 "%q must end with a word character, number, or underscore: %q", k, value)) 377 } 378 379 if !regexp.MustCompile(`^[a-zA-Z0-9]`).MatchString(value) { 380 errors = append(errors, fmt.Errorf( 381 "%q must start with a word character or number: %q", k, value)) 382 } 383 384 return 385 }