Skip to content
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_private_link_endpoint New Data Source: azurerm_private_link_endpoint_connection and expose attibute in azurerm_subnet #4493

Merged
merged 45 commits into from
Dec 3, 2019
Merged
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
64bb60e
[WIP] Private Endpoint
WodansSon Oct 3, 2019
d445539
[WIP] Update datasource to network_interface_ids
WodansSon Oct 3, 2019
cf252b9
[WIP] Update documentation with correct value
WodansSon Oct 3, 2019
266fa1b
Rename resource and flatten schema
WodansSon Oct 8, 2019
3840307
Merge branch 'master' of https://github.com/terraform-providers/terra…
WodansSon Oct 9, 2019
7ce24b8
Update for terraform-plugin-sdk
WodansSon Oct 9, 2019
71133ca
Gofmt and some PR comment updates
WodansSon Oct 9, 2019
f965560
Some documentation updates
WodansSon Oct 9, 2019
87cb012
More doc updates
WodansSon Oct 10, 2019
3a4b263
Merge branch 'master' of https://github.com/terraform-providers/terra…
WodansSon Oct 12, 2019
fe67c8f
Update to include new terraform-plugin-sdk
WodansSon Oct 12, 2019
bd69625
Fixed lint error
WodansSon Oct 12, 2019
5ee333d
Update to endpoint
WodansSon Oct 23, 2019
57ce715
Refactor progress
WodansSon Oct 25, 2019
52fa425
Added private_ip_address as computed output
WodansSon Oct 26, 2019
61624f4
The resource is done, working on tests
WodansSon Oct 29, 2019
d5e8155
Merge branch 'master' into nr_private-end-point
WodansSon Oct 30, 2019
07452f5
A few more updates
WodansSon Oct 30, 2019
b24ac15
Working new data source
WodansSon Nov 7, 2019
0d8dcd9
Merge branch 'master' into nr_private-end-point
WodansSon Nov 7, 2019
39eef7d
Fixed documentation
WodansSon Nov 7, 2019
69bb2c0
Merge branch 'nr_private-end-point' of https://github.com/terraform-p…
WodansSon Nov 7, 2019
f0f071d
gofmt
WodansSon Nov 7, 2019
e9300b9
Fix lint errors
WodansSon Nov 7, 2019
a676a2f
Partial update for consistency
WodansSon Nov 12, 2019
255957d
Update subnet and documentation
WodansSon Nov 12, 2019
06990fc
Merge branch 'master' of https://github.com/terraform-providers/terra…
WodansSon Nov 21, 2019
227ebc9
Some fixes
WodansSon Nov 21, 2019
e0fde44
Commit changes for rebase with master
WodansSon Nov 22, 2019
c0a3c8e
Merge branch 'master' of https://github.com/terraform-providers/terra…
WodansSon Nov 22, 2019
4ba6e4e
Updates per PR comments
WodansSon Nov 22, 2019
817ca0f
Remove dead code
WodansSon Nov 22, 2019
33e7fdd
Updates per PR comments
WodansSon Nov 22, 2019
e73e10e
Merge branch 'nr_private-end-point' of https://github.com/terraform-p…
WodansSon Nov 22, 2019
b209b87
Updates per PR comments
WodansSon Nov 27, 2019
bb8a136
Remove endpoint data source from provider
WodansSon Nov 28, 2019
ae82096
Update service endpoint connections
WodansSon Nov 28, 2019
7ab6a68
Update to documentation
WodansSon Nov 28, 2019
261c704
Fix sample code
WodansSon Nov 28, 2019
541694f
data source update
WodansSon Nov 28, 2019
6d6a38f
Merge branch 'master' of https://github.com/terraform-providers/terra…
WodansSon Dec 2, 2019
d818bc6
Removed service connections datasource
WodansSon Dec 3, 2019
017b56c
Add the service connections datasource back in
WodansSon Dec 3, 2019
a8e0eac
Update test case
WodansSon Dec 3, 2019
527a2f1
make fmt and adjust comment location
katbyte Dec 3, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Refactor progress
WodansSon committed Oct 25, 2019
commit 57ce7158b9ce9dc75f87d2b5aa08ee4e11821d5c
4 changes: 2 additions & 2 deletions azurerm/data_source_subnet.go
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ func dataSourceArmSubnet() *schema.Resource {
},
},

"disable_private_endpoint_network_policies": {
"disable_private_link_endpoint_network_policies": {
Type: schema.TypeBool,
Computed: true,
},
@@ -98,7 +98,7 @@ func dataSourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {
d.Set("address_prefix", props.AddressPrefix)

if privateEndpointNetworkPolicies := props.PrivateEndpointNetworkPolicies; privateEndpointNetworkPolicies != nil {
if err := d.Set("disable_private_endpoint_network_policies", *privateEndpointNetworkPolicies == "Disabled"); err != nil {
if err := d.Set("disable_private_link_endpoint_network_policies", *privateEndpointNetworkPolicies == "Disabled"); err != nil {
return err
}
}
23 changes: 23 additions & 0 deletions azurerm/internal/services/network/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package network

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func ValidatePrivateLinkEndpointSettings(d *schema.ResourceData) error {
privateServiceConnections := d.Get("private_service_connection").([]interface{})

for _, psc := range privateServiceConnections {
privateServiceConnection := psc.(map[string]interface{})
name := privateServiceConnection["name"].(string)

// If this is not a manule connection and the message is set return an error since this does not make sense.
if !privateServiceConnection["is_manual_connection"].(bool) && privateServiceConnection["request_message"].(string) != "" {
return fmt.Errorf(`"private_service_connection":%q is invalid, the "request_message" attribute cannot be set if the "is_manual_connection" attribute is "false"`, name)
}
}

return nil
}
149 changes: 136 additions & 13 deletions azurerm/resource_arm_private_link_endpoint.go
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features"
aznet "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/network"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)
@@ -44,8 +45,55 @@ func resourceArmPrivateLinkEndpoint() *schema.Resource {
ValidateFunc: validate.NoEmptyStrings,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we validate this is a proper ID?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

},

// the 'manual_private_link_service_connection' attribute has been removed
// as the behavior does not make sense in a terraform resource
"private_service_connection": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use regex to validate this?

},
"is_manual_connection": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"private_connection_resource_id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

COuld we validate this is an ID?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we validate this with RegEx?

},
"subresource_names": {
Type: schema.TypeList,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use a TypeSet here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a TypeSet

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a type set?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a type set?

Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validate.NoEmptyStrings,
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we validate this is an ID?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not validating its an ID?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same thing?

},
"request_message": {
// Only valid if is_manual_connection == true. Restricted to 140 chars.
Type: schema.TypeString,
Optional: true,
ValidateFunc: validate.PrivateLinkEnpointRequestMessage,
},
"provisioning_state": {
// ProvisioningState - The provisioning state of the Private Link Endpoint. Possible values include: 'Succeeded', 'Updating', 'Deleting', 'Failed'
// PrivateLinkServiceConnectionProperties.ProvisioningState
Type: schema.TypeString,
Computed: true,
},
"status": {
// Status - Indicates whether the connection has been Approved/Rejected/Removed by the owner of the service.
// PrivateLinkServiceConnection.PrivateLinkServiceConnectionProperties.PrivateLinkServiceConnectionState.Status
Type: schema.TypeString,
Computed: true,
},
},
},
},

"network_interface_ids": {
Type: schema.TypeList,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this a TypeSet?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THis should be a TypeSet

@@ -67,11 +115,15 @@ func resourceArmPrivateLinkEndpointCreateUpdate(d *schema.ResourceData, meta int
name := d.Get("name").(string)
resourceGroup := d.Get("resource_group_name").(string)

if err := aznet.ValidatePrivateLinkEndpointSettings(d); err != nil {
return fmt.Errorf("Error validating the configuration for the Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if features.ShouldResourcesBeImported() && d.IsNewResource() {
resp, err := client.Get(ctx, resourceGroup, name, "")
if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("Error checking for present of existing Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error checking for present of existing Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}
if !utils.ResponseWasNotFound(resp.Response) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect

Suggested change
if !utils.ResponseWasNotFound(resp.Response) {
if existing.ID != nil && *existing.ID != "" {

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -80,12 +132,15 @@ func resourceArmPrivateLinkEndpointCreateUpdate(d *schema.ResourceData, meta int
}

location := azure.NormalizeLocation(d.Get("location").(string))
privateServiceConnections := d.Get("private_service_connection").([]interface{})
subnetId := d.Get("subnet_id").(string)
t := d.Get("tags").(map[string]interface{})

parameters := network.PrivateEndpoint{
Location: utils.String(location),
PrivateEndpointProperties: &network.PrivateEndpointProperties{
PrivateLinkServiceConnections: expandArmPrivateLinkEndpointServiceConnection(privateServiceConnections, false),
ManualPrivateLinkServiceConnections: expandArmPrivateLinkEndpointServiceConnection(privateServiceConnections, true),
Subnet: &network.Subnet{
ID: utils.String(subnetId),
},
@@ -95,19 +150,19 @@ func resourceArmPrivateLinkEndpointCreateUpdate(d *schema.ResourceData, meta int

future, err := client.CreateOrUpdate(ctx, resourceGroup, name, parameters)
if err != nil {
return fmt.Errorf("Error creating Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error creating Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for creation of Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error waiting for creation of Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}

resp, err := client.Get(ctx, resourceGroup, name, "")
if err != nil {
return fmt.Errorf("Error retrieving Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error retrieving Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("API returns a nil/empty id on Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
if resp.ID == nil || *resp.ID == "" {
return fmt.Errorf("API returns a nil/empty id on Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
d.SetId(*resp.ID)

return resourceArmPrivateLinkEndpointRead(d, meta)
@@ -127,11 +182,11 @@ func resourceArmPrivateLinkEndpointRead(d *schema.ResourceData, meta interface{}
resp, err := client.Get(ctx, resourceGroup, name, "")
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[INFO] Private Endpoint %q does not exist - removing from state", d.Id())
log.Printf("[INFO] Private Link Endpoint %q does not exist - removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("Error reading Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error reading Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}

d.Set("name", resp.Name)
@@ -169,18 +224,86 @@ func resourceArmPrivateLinkEndpointDelete(d *schema.ResourceData, meta interface
if response.WasNotFound(future.Response()) {
return nil
}
return fmt.Errorf("Error deleting Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error deleting Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
if !response.WasNotFound(future.Response()) {
return fmt.Errorf("Error waiting for deleting Private Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
return fmt.Errorf("Error waiting for deleting Private Link Endpoint %q (Resource Group %q): %+v", name, resourceGroup, err)
}
}

return nil
}

func expandArmPrivateLinkEndpointServiceConnection(input []interface{}, parseManual bool) *[]network.PrivateLinkServiceConnection {
results := make([]network.PrivateLinkServiceConnection, 0)
for _, item := range input {
v := item.(map[string]interface{})
privateConnectonResourceId := v["private_connection_resource_id"].(string)
subresourceNames := v["subresource_names"].([]interface{})
requestMessage := v["request_message"].(string)
isManual := v["is_manual_connection"].(bool)
name := v["name"].(string)

if isManual == parseManual {
result := network.PrivateLinkServiceConnection{
Name: utils.String(name),
PrivateLinkServiceConnectionProperties: &network.PrivateLinkServiceConnectionProperties{
GroupIds: utils.ExpandStringSlice(subresourceNames),
PrivateLinkServiceID: utils.String(privateConnectonResourceId),
},
}

if requestMessage != "" {
result.PrivateLinkServiceConnectionProperties.RequestMessage = utils.String(requestMessage)
}

results = append(results, result)
}
}

return &results
}

func flattenArmPrivateLinkEndpointServiceConnection(input *[]network.PrivateLinkServiceConnection) []interface{} {
results := make([]interface{}, 0)
if input == nil {
return results
}

for _, item := range *input {
v := make(map[string]interface{})

if name := item.Name; name != nil {
v["name"] = *name
}
if props := item.PrivateLinkServiceConnectionProperties; props != nil {
if subresourceNames := props.GroupIds; subresourceNames != nil {
v["subresource_names"] = utils.FlattenStringSlice(subresourceNames)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this should be called group_ids rather than subresource_names?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the name because it was confusing as to what data it was expecting, here are the strings that are valid here:

Resource type Subresource name Subresource name
Sql DB/DW sqlServer
Storage Account blob blob_secondary
Storage Account table table_secondary
Storage Account queue queue_secondary
Storage Account file file_secondary
Storage Account web web_secondary
Data Lake File System Gen2 dfs dfs_secondary

}
if privateConnectionResourceId := props.PrivateLinkServiceID; privateConnectionResourceId != nil {
v["private_connection_resource_id"] = *privateConnectionResourceId
}
if requestMessage := props.RequestMessage; requestMessage != nil {
v["request_message"] = *requestMessage
}
if provisioningState := props.ProvisioningState; provisioningState != "" {
v["provisioning_state"] = provisioningState
}
katbyte marked this conversation as resolved.
Show resolved Hide resolved
if s := props.PrivateLinkServiceConnectionState; s != nil {
if status := s.Status; status != nil {
v["status"] = *status
}
}
katbyte marked this conversation as resolved.
Show resolved Hide resolved
}

results = append(results, v)
}

return results
}

func flattenArmPrivateLinkEndpointInterface(input *[]network.Interface) []interface{} {
if input == nil {
return make([]interface{}, 0)
2 changes: 1 addition & 1 deletion azurerm/resource_arm_private_link_endpoint_test.go
Original file line number Diff line number Diff line change
@@ -174,7 +174,7 @@ resource "azurerm_subnet" "test" {
address_prefix = "10.5.1.0/24"

disable_private_link_service_network_policies = true
disable_private_endpoint_network_policies = true
disable_private_link_endpoint_network_policies = true
}

resource "azurerm_public_ip" "test" {
6 changes: 3 additions & 3 deletions azurerm/resource_arm_subnet.go
Original file line number Diff line number Diff line change
@@ -82,7 +82,7 @@ func resourceArmSubnet() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
},

"disable_private_endpoint_network_policies": {
"disable_private_link_endpoint_network_policies": {
Type: schema.TypeBool,
Optional: true,
Default: false,
@@ -211,7 +211,7 @@ func resourceArmSubnetCreateUpdate(d *schema.ResourceData, meta interface{}) err
properties.RouteTable = nil
}

if v, ok := d.GetOk("disable_private_endpoint_network_policies"); ok {
if v, ok := d.GetOk("disable_private_link_endpoint_network_policies"); ok {
// This is strange logic, but to get the schema to make sense for the end user
// I exposed it with the same name that the Azure CLI does to be consistent
// between the tool sets, which means true == Disabled.
@@ -320,7 +320,7 @@ func resourceArmSubnetRead(d *schema.ResourceData, meta interface{}) error {
// subnet because Network policies like network security groups are not
// supported by private endpoints.
if privateEndpointNetworkPolicies := props.PrivateEndpointNetworkPolicies; privateEndpointNetworkPolicies != nil {
d.Set("disable_private_endpoint_network_policies", *privateEndpointNetworkPolicies == "Disabled")
d.Set("disable_private_link_endpoint_network_policies", *privateEndpointNetworkPolicies == "Disabled")
}

delegation := flattenSubnetDelegation(props.Delegations)
2 changes: 1 addition & 1 deletion website/docs/d/subnet.html.markdown
Original file line number Diff line number Diff line change
@@ -38,4 +38,4 @@ output "subnet_id" {
* `route_table_id` - The ID of the Route Table associated with this subnet.
* `ip_configurations` - The collection of IP Configurations with IPs within this subnet.
* `service_endpoints` - A list of Service Endpoints within this subnet.
* `disable_private_endpoint_network_policies` - Enable or Disable network policies for a Private Link Endpoint in the subnet.
* `disable_private_link_endpoint_network_policies` - Enable or Disable network policies for a Private Link Endpoint in the subnet.
2 changes: 1 addition & 1 deletion website/docs/r/private_link_endpoint.html.markdown
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ resource "azurerm_subnet" "example" {
address_prefix = "10.0.1.0/24"

disable_private_link_service_network_policies = true
disable_private_endpoint_network_policies = true
disable_private_link_endpoint_network_policies = true
}

resource "azurerm_public_ip" "example" {
4 changes: 2 additions & 2 deletions website/docs/r/subnet.html.markdown
Original file line number Diff line number Diff line change
@@ -71,9 +71,9 @@ The following arguments are supported:

* `delegation` - (Optional) One or more `delegation` blocks as defined below.

* `disable_private_endpoint_network_policies` - (Optional) Enable or Disable network policies on private end point in the subnet. Default valule is `false`.
* `disable_private_link_endpoint_network_policies` - (Optional) Enable or Disable network policies on private end point in the subnet. Default valule is `false`.

-> **NOTE:** Network policies, like network security groups (NSG), are not supported for `Private Link Endpoints`. In order to deploy a `Private Link Endpoint` on a given subnet, you must set the `disable_private_endpoint_network_policies` attribute to `true`. This setting is only applicable for the `Private Link Endpoint`, for all other resources in the subnet access is controlled based on the `network_security_group_id`.
-> **NOTE:** Network policies, like network security groups (NSG), are not supported for `Private Link Endpoints`. In order to deploy a `Private Link Endpoint` on a given subnet, you must set the `disable_private_link_endpoint_network_policies` attribute to `true`. This setting is only applicable for the `Private Link Endpoint`, for all other resources in the subnet access is controlled based on the `network_security_group_id`.

---