-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Resources: azurerm_firewall
& azurerm_firewall_network_rule_collection
#1627
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @hbuckle
Thanks for this PR :)
I've taken a look through and left some comments inline but this is off to a good start. I'd agree this makes sense split out as sub-resources - whilst it takes a little longer to provision it's a better user experience; do you think it'd make sense to do the same thing with the Rule resource too? I've held off reviewing the docs/tests for the moment whilst we hash out the design, but this is looking good 👍
Thanks!
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
var azureFirewallResourceName = "azurerm_azure_firewall" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should be able to make this azurerm_firewall
"ip_configuration": { | ||
Type: schema.TypeList, | ||
Required: true, | ||
MinItems: 1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor since this is Required - MinItems
is inferred so we could remove this
if resp.IPConfigurations != nil { | ||
ipConfigs := flattenArmAzureFirewallIPConfigurations(resp.IPConfigurations) | ||
d.Set("ip_configuration", ipConfigs) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so there's a couple of things here:
- we should set this all the time (even if it's nil / when the API's broken) - since that allows changes to be detected
- since
ip_configurations
is a complex object - we should check for errors when setting it
as such can we update this to be:
ipConfigs := flattenArmAzureFirewallIPConfigurations(resp.IPConfigurations)
if err := d.Set("ip_configuration", ipConfigs); err != nil {
return fmt.Errorf("Error setting `ip_configuration`: %+v", err)
}
(we can then check if resp.IPConfigurations
is nil within the flatten function, and return an empty list there if needed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on a separate note - is IPConfigurations
available within the [XX]Properties
object? the API's generally return these in the Properties block, which would be better to use if it's an option
|
||
func flattenArmAzureFirewallIPConfigurations(ipConfigs *[]network.AzureFirewallIPConfiguration) []interface{} { | ||
result := make([]interface{}, 0, len(*ipConfigs)) | ||
for _, ipConfig := range *ipConfigs { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a couple of potential crashes here; can we make this:
result := make([]interface{}, 0)
if ipConfigs == nil {
return result
}
result := make([]interface{}, 0, len(*ipConfigs)) | ||
for _, ipConfig := range *ipConfigs { | ||
afIPConfig := make(map[string]interface{}) | ||
props := ipConfig.AzureFirewallIPConfigurationPropertiesFormat |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given props
can be nil here - I think we want a nil-check after this? e.g.
if props == nil {
continue
}
collections := *firewall.AzureFirewallPropertiesFormat.NetworkRuleCollections | ||
for i, collection := range collections { | ||
if collection.Name != nil && *collection.Name == name { | ||
index = i |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may also be possible to call delete(collections, collection)
here, since we're breaking anyway?
ruleToAdd := network.AzureFirewallNetworkRule{ | ||
Name: &name, | ||
} | ||
if description := rule["description"].(string); description != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we're safe to assign this regardless (since if the key's nil in TF, we should reset the description?)
ruleToAdd.Protocols = &protocols | ||
} | ||
rules = append(rules, ruleToAdd) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(same for all of these) if the lists are nil can we set them anyway, since this'll reset the values to an empty list as needed
if rules != nil { | ||
for _, rule := range *rules { | ||
fwRule := make(map[string]interface{}) | ||
fwRule["name"] = *rule.Name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we nil-check this?
func flattenArmAzureFirewallNetworkRules(rules *[]network.AzureFirewallNetworkRule) []map[string]interface{} { | ||
result := make([]map[string]interface{}, 0) | ||
|
||
if rules != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor we could probably invert this to make it more readable, but it's not a blocker by any means :)
Thanks for the review so far - I'll carry on and add the application rules as a third resource. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hey @hbuckle
Thanks for pushing those changes - I've taken a look through and reviewed the other half of this (Network Rule Collections). Regarding the other new resource, since this PR's already quite large / whilst it's related - would you mind if we pulled that out into a separate PR?
Thanks!
azurerm/provider.go
Outdated
@@ -138,6 +138,8 @@ func Provider() terraform.ResourceProvider { | |||
"azurerm_automation_schedule": resourceArmAutomationSchedule(), | |||
"azurerm_autoscale_setting": resourceArmAutoScaleSetting(), | |||
"azurerm_availability_set": resourceArmAvailabilitySet(), | |||
"azurerm_azure_firewall": resourceArmAzureFirewall(), | |||
"azurerm_azure_firewall_network_rule_collection": resourceArmAzureFirewallNetworkRuleCollection(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we make this azurerm_firewall
and azurerm_firewall_network_rule
respectively? I think the extra azure
is redundant in each case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although it looks a little unwieldy Azure Firewall is the name of the product, so the naming is consistent. Lots of things have firewalls in Azure so I think this makes it clear exactly which product is being managed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's a fair point - but in general we ditch the word Azure
from the front (e.g. Azure Kubernetes Service is azurerm_kubernetes_cluster
/ Azure Managed Database for MySQL becomes azurerm_mysql_server
); so I think we should ditch it to be consistent with the other resources (the sole exception to this is azurerm_azuread
, because that's how it's commonly referred too).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tombuildsstuff OK, i've renamed everything to azurerm_firewall
name := d.Get("name").(string) | ||
location := azureRMNormalizeLocation(d.Get("location").(string)) | ||
tags := d.Get("tags").(map[string]interface{}) | ||
ipConfigs, subnetToLock, vnetToLock, sgErr := expandArmAzureFirewallIPConfigurations(d) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor we can rename this from sgErr
to err
and reuse the variable below, rather than defining a specific error type
return fmt.Errorf("Error making Read request on Azure Firewall %q (Resource Group %q): %+v", name, resourceGroup, err) | ||
} | ||
|
||
ipConfigs := flattenArmAzureFirewallIPConfigurations(firewall.AzureFirewallPropertiesFormat.IPConfigurations) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's possible that firewall.AzureFirewallPropertiesFormat
could be nil here, either through a bad API response or invalid Swagger in future; can we make this:
if props := firewall.AzureFirewallPropertiesFormat; props != nil {
ipConfigs := flattenArmAzureFirewallIPConfigurations(firewall.AzureFirewallPropertiesFormat.IPConfigurations)
if err := d.Set("ip_configuration", ipConfigs); err != nil {
return fmt.Errorf("Error setting `ip_configuration`: %+v", err)
}
}
return err | ||
} | ||
|
||
func expandArmAzureFirewallIPConfigurations(d *schema.ResourceData) ([]network.AzureFirewallIPConfiguration, *[]string, *[]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can return a pointer to a list here (*[]network.AzureFirewallIPConfiguration
) which would allow us to return nil below where we currently return an empty list. the benefit of doing this is it means any unexpected code paths/accesses of this returned value would crash (which will be caught during testing)
} | ||
} | ||
|
||
if ipAddress := ipConfig.PrivateIPAddress; ipAddress != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we access this via props
instead of via ipConfig
?
ruleToAdd.Description = &description | ||
ruleToAdd.SourceAddresses = expandArmAzureFirewallSet(sourceAddresses) | ||
ruleToAdd.DestinationAddresses = expandArmAzureFirewallSet(destinationAddresses) | ||
ruleToAdd.DestinationPorts = expandArmAzureFirewallSet(destinationPorts) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor can we in-line these? (also we can use utils.String(val)
rather than &
for consistency with the other resources) e.g.
ruleToAdd := network.AzureFirewallNetworkRule{
Name: &name,
Description: utils.String(description),
SourceAddresses: expandArmAzureFirewallSet(sourceAddresses),
DestinationAddresses: expandArmAzureFirewallSet(destinationAddresses),
DestinationPorts: expandArmAzureFirewallSet(destinationPorts),
}
page_title: "Azure Resource Manager: azurerm_azure_firewall" | ||
sidebar_current: "docs-azurerm-resource-azurefirewall-x" | ||
description: |- | ||
Create an Azure Firewall. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor can we make this Manages an Azure Firewall
|
||
Create an Azure Firewall. | ||
|
||
~> **NOTE** This resource is currently in public preview. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this in invite-only preview, or can customers opt-in?
|
||
* `name` - (Required) Specifies the name of the ip configuration. | ||
* `subnet_id` - (Required) Reference to the subnet associated with the ip configuration. | ||
* `internal_public_ip_address_id` - (Required) Reference to the public IP address associated with the firewall. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add the above note below this resource?
~> **NOTE** The public IP must have a `Static` allocation and `Standard` sku
`ip_configuration` supports the following: | ||
|
||
* `name` - (Required) Specifies the name of the ip configuration. | ||
* `subnet_id` - (Required) Reference to the subnet associated with the ip configuration. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add the above note below this resource?
~> **NOTE** The firewall subnet must be called `AzureFirewallSubnet` and the subnet mask must be at least `/25`
@tombuildsstuff Thanks, think I have fixed everything up. I'll review all the tests today then look at a separate PR to add application rule collections |
@hbuckle hey sorry for the delayed re-review here - I'll take another look through this today |
dismissing since I've pushed changes
- Locking on the Firewall Name - Handling resources being deleted outside of Terraform - Removing some crash points - Making the Protocol and Action type case-sensitive - Refactoring the virtual resource to allow for - Parsing the ID rather than using the config for the delete and read functions (so delete's are successful when the config's gone) - Rewriting some of the tests for the Network Rule Collections, to check the resource's state rather than the object - Updating the documentation (and including Import support for Network Rule Collections)
d289b1e
to
a25e3d1
Compare
@hbuckle I thought I'd posted this message yesterday - apologies. I ended up rebasing this yesterday and taking a look into what's needed to get this merged - and I've pushed a commit with some changes to finish this off (I hope you don't mind!) - in particular I've updated the Read and Delete functions to parse the ID of the Network Rule Collection rather than reading from the config (since this isn't always present during Import/Delete's) and updated some of the tests. Since I've pushed some larger changes here (since this is a couple of resources) - I'm going to as @katbyte to take a look into this - but I'm going to kick off the tests now. Thanks! |
azurerm_firewall
& azurerm_firewall_network_rule_collection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This pretty much LGTM 👍 All the comments I have left inline are pretty minor and not blockers so i'm going to approve this.
My main concern is TestAccAzureRMFirewall_importBasic
being separate, while the rule collection is part of the standard tests. I think it should be merged in to the standard TestAccAzureRMFirewall
tests as well.
``` $ acctests azurerm TestValidateFirewallName === RUN TestValidateFirewallName --- PASS: TestValidateFirewallName (0.00s) PASS ok github.com/terraform-providers/terraform-provider-azurerm/azurerm 0.344s ```
@tombuildsstuff Thanks for the update, I'm sure my go skills still leave something to be desired. I'll have another look through this today. I've started the application rule collection on another branch as well. |
1 similar comment
@tombuildsstuff Thanks for the update, I'm sure my go skills still leave something to be desired. I'll have another look through this today. I've started the application rule collection on another branch as well. |
Not at all, thanks for getting this most of the way there - apologies this sat for a while, we're still trying to work through the open PR's.
That's awesome 👍 - thanks!
Cool, that's good to know - I'll keep an eye on the SDK release and add a TODO for that Thanks! |
3d16742
to
8167fc7
Compare
I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 [email protected]. Thanks! |
I'm opening this to get some feedback on the design - so far I've split network rules into a separate resource from the firewall to make them easier to manage.
This does have a couple of drawbacks, first it makes the code more complicated because there isn't a separate client for rules, everything has to be done through the AzureFirewalls client. Second it's slow, because all the rules have to be added or updated individually and one at a time.
The alternative is to implement the whole thing as one resource including rules as a property, but this could be harder to manage from the user's perspective.
Thoughts?