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