-
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 Resource: azurerm_sql_virtual_network_rule
#978
New Resource: azurerm_sql_virtual_network_rule
#978
Conversation
hey @vlouwagie Thanks for this PR :) I've taken a look through and this is looking pretty good; however I'm wondering is it possible to have a SQL Server connected to multiple Virtual Networks? If it's not - I'm wondering if it'd make sense to add a
If you can add it to this PR that'd be great - but I'd suggest holding off for the moment until we've worked out if this'd be better located within the Thanks! |
@tombuildsstuff Yep, it is possible for a single SQL server to be connected to one or more subnets from one or more virtual networks. This resource has a lot in common with the SQL firewall rule resource, so I originally modelled it after that. Template for multiple rules
|
@vlouwagie cool, thanks for confirming that. Could we add a test to cover that scenario, perhaps using the configuration you've posted above? Thanks! |
@tombuildsstuff Sure! I will add a task for that in the pull request 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.
hey @vlouwagie
Thanks for this PR - I've taken a look through and left some minor comments but this mostly LGTM; if we can fix up the minor comments and add some documentation this should be good to merge :)
Thanks!
azurerm/config.go
Outdated
sqlVNRClient.Authorizer = auth | ||
sqlVNRClient.Sender = sender | ||
sqlVNRClient.SkipResourceProviderRegistration = c.skipProviderRegistration | ||
c.sqlVirtualNetworkRulesClient = sqlVNRClient |
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 switch this over to using the new registration syntax? e.g. `c.configureClient(&sqlVNRClient.Client, auth)
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.
✅
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
//TODO: Validation (invalid if non-numeric characters) |
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 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.
✅
|
||
"virtual_network_subnet_id": { | ||
Type: schema.TypeString, | ||
Required: true, |
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.
should this be ForceNew
or is it possible to migrate between Virtual Networks? Can we add a test for this if that's the 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.
@tombuildsstuff It does indeed look like you can change the subnets of a rule, and it does apply correctly. I will make a task to create a test case for that. 😄
On a side note, I also tested migrating from one SQL server to another without ForceNew
(just in case) , and it creates a rule on the 2nd target SQL server, but does not delete it on the first server, so the ForceNew
attribute is needed on the server_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.
✅
d.Set("resource_group_name", resourceGroup) | ||
d.Set("server_name", serverName) | ||
d.Set("virtual_network_subnet_id", resp.VirtualNetworkSubnetID) | ||
d.Set("ignore_missing_vnet_service_endpoint", resp.IgnoreMissingVnetServiceEndpoint) |
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.
for these two fields can we use the field in resp.[XXX]Properties
and nil-check it (e.g. to handle bad API responses); since that's what's recommended by Azure e.g.
if props := resp.[XXX]Properties; props != nil {
d.Set("virtual_network_subnet_id", props.VirtualNetworkSubnetID)
d.Set("ignore_missing_vnet_service_endpoint", props.IgnoreMissingVnetServiceEndpoint)
}
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.
✅
ForceNew: true, | ||
}, | ||
|
||
"virtual_network_subnet_id": { |
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.
🤔 do you think it'd be worth making this subnet_id
to be more consistent with the other Terraform resources?
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 Yes, that definitely makes sense.
On a similar note, what is your opinion on ignore_missing_vnet_service_endpoint
? Do you think it would be better if it was shorted a bit as well to ignore_missing_service_endpoint
or even ignore_missing_endpoint
?
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 the description (in the API Docs): Create firewall rule before the virtual network has vnet service endpoint enabled.
- I guess leaving it as ignore_missing_vnet_service_endpoint
would probably make the most sense? Whilst I don't feel that name's particularly descriptive, it's how the Azure API's chosen to label it, so it probably makes sense to match it incase it does extra stuff in the future
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.
✅
virtual_network_subnet_id = "${azurerm_subnet.test.id}" | ||
ignore_missing_vnet_service_endpoint = false | ||
} | ||
`, rInt, location, rInt, rInt, rInt, rInt, rInt) |
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 could we make the spacing consistent here with 2 spaces for indents?
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.
✅
server_name = "${azurerm_sql_server.test.name}" | ||
virtual_network_subnet_id = "${azurerm_subnet.test.id}" | ||
ignore_missing_vnet_service_endpoint = true | ||
} |
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 could we make the spacing consistent here with 2 spaces for indents?
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.
✅
server_name = "${azurerm_sql_server.test.name}" | ||
virtual_network_subnet_id = "${azurerm_subnet.test.id}" | ||
ignore_missing_vnet_service_endpoint = true | ||
} |
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 could we make the spacing consistent here with 2 spaces for indents?
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.
✅
server_name = "${azurerm_sql_server.test.name}" | ||
virtual_network_subnet_id = "${azurerm_subnet.test.id}" | ||
ignore_missing_vnet_service_endpoint = false | ||
} |
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 could we make the spacing consistent here with 2 spaces for indents?
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.
✅
server_name = "${azurerm_sql_server.test.name}" | ||
virtual_network_subnet_id = "${azurerm_subnet.vnet2_subnet1.id}" | ||
ignore_missing_vnet_service_endpoint = false | ||
} |
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 could we make the spacing consistent here with 2 spaces for indents?
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.
✅
Thanks for the feedback! I will investigate each one and get back to you. Also added the requested changes to the pull request description. 😄 |
postConfig := testAccAzureRMSqlVirtualNetworkRule_subnetSwitchPost(ri, testLocation()) | ||
|
||
// Create regex strings that will ensure that one subnet name exists, but not the other | ||
preConfigRegex := regexp.MustCompile(fmt.Sprintf("(subnet1%d)$|(subnet[^2]%d)$", ri, ri)) //subnet 1 but not 2 |
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 I ended up making a regex match test that checks if the subnet ID contains one subnet name but not the other in the preConfig
setup, and then check for the inverse on the postConfig
setup. I wasn't sure if there was a better way to check if a computed item was a different value from the previous step.
It still works, but let me know if there actually is a better way. 😄
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 works, the other options would be building up the ID by hand (e.g. /subscriptions/0000../foo/bar/subnets/mySubnet
) or just check the suffix is mySubnet
- but this LGTM :)
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.
or just check the suffix is
mySubnet
Sounds good. This is basically what I'm doing anyways, but with a little extra!
err = future.WaitForCompletion(ctx, client.Client) | ||
if err != nil { | ||
|
||
//Same deal as before. Just in 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.
to clarify this, this is the main code path - as the deletion of the SQL Network Rule's should be a long running operation (LRO); afaik :)
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 Could you elaborate a bit on this comment?
@tombuildsstuff I added the endpoint state as a computed fields since that is also available from the API. I also added the documentation. I will be removing the WIP as all tasks (as of now) are complete and ready to be reviewed! 😄 |
azurerm_sql_virtual_network_rule
azurerm_sql_virtual_network_rule
hey @vlouwagie Thanks for pushing the updates - this now looks good to me. In running the tests I've noticed there appears to be an eventual consistency issue when creating this, where the API returns this as created but it doesn't exist until a few seconds later, which is causing the tests to fail intermittently:
We should be able to add a check/waiter function once the Network Rule's been created before retrieving it to ensure that this actually exists - something like this (modified from the IoTHub resource to add the
If we can add this check function that should solve the eventual consistency issue and then this should be good to merge :) Thanks! |
@tombuildsstuff I've been able to fail the tests myself as well. I think I might go one step further with your example, and check the provisioning state of the create/updated rule, similar to how the resource_arm_redis_cache.go (Lines 252, 463) log.Printf("[DEBUG] Waiting for Redis Instance (%s) to become available", d.Get("name"))
stateConf := &resource.StateChangeConf{
Pending: []string{"Updating", "Creating"},
Target: []string{"Succeeded"},
Refresh: redisStateRefreshFunc(ctx, client, resGroup, name),
Timeout: 60 * time.Minute,
MinTimeout: 15 * time.Second,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for Redis Instance (%s) to become available: %s", d.Get("name"), err)
} func redisStateRefreshFunc(ctx context.Context, client redis.Client, resourceGroupName string, sgName string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
res, err := client.Get(ctx, resourceGroupName, sgName)
if err != nil {
return nil, "", fmt.Errorf("Error issuing read request in redisStateRefreshFunc to Azure ARM for Redis Cache Instance '%s' (RG: '%s'): %s", sgName, resourceGroupName, err)
}
return res, *res.ProvisioningState, nil
}
} What do you think of this instead? |
…rule. Added consistency to error messages.
@vlouwagie yeah, sorry that's what I meant :) The only thing I'd suggest adding to that would be a Thanks! |
@tombuildsstuff I increased the Let me know if there is anything else I can do for this. 😄 @ |
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 @vlouwagie
Thanks for pushing the latest updates - this now LGTM and the tests pass 👍
I'm going to push one commit to remove the endpoint_state
field, since I don't believe it's necessary (we don't expose this on the other resources, for instance) - and then we can merge this :)
Thanks!
* Initializing - Indicates that the endpoint provisioning is in the initialization stage. | ||
* InProgress - Indicates that the endpoint is still bring provisioned. | ||
* Deleting - Indicates that the endpoint is currently being deleted. | ||
* Unknown - Indicates that the state of the endpoint is unknown. |
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'm going to push a commit to remove this field, since it shouldn't be being used by end-users
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.
(done)
@tombuildsstuff I suppose that field is not necessary, even more now since we added a check/wait function for the ready status. |
@vlouwagie yeah - we can always expose it if there's a use-case for it, but in general Terraform should only be returning from the Create method when it's ready/replicated (as in this case 😄). Thanks for this PR - this'll go out in v1.3.1 later today :) |
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! |
Summary
New resource that adds a SQL server to a subnet. (Azure Docs, relevant Azure GO SDK client)
Issue
#955
TODO
(From the main terraform's contrib guide)