Skip to content

Commit

Permalink
Add virtual network subnet id to ip restrictions
Browse files Browse the repository at this point in the history
  • Loading branch information
mbfrahry committed Aug 28, 2019
1 parent aa80259 commit 77c29e1
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 28 deletions.
76 changes: 56 additions & 20 deletions azurerm/helpers/azure/app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,12 +300,22 @@ func SchemaAppServiceSiteConfig() *schema.Schema {
Schema: map[string]*schema.Schema{
"ip_address": {
Type: schema.TypeString,
Required: true,
Optional: true,
},
"virtual_network_subnet_id": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validate.NoEmptyStrings,
},
"subnet_mask": {
Type: schema.TypeString,
Optional: true,
Default: "255.255.255.255",
Computed: true,
// This attribute was made with the assumption that `ip_address` was the only valid option
// but `virtual_network_subnet_id` is being added and doesn't need a `subnet_mask`.
// We'll assume a default of "255.255.255.255" in the expand code when `ip_address` is specified
// and `subnet_mask` is not.
// Default: "255.255.255.255",
},
},
},
Expand Down Expand Up @@ -646,6 +656,10 @@ func SchemaAppServiceDataSourceSiteConfig() *schema.Schema {
Type: schema.TypeString,
Computed: true,
},
"virtual_network_subnet_id": {
Type: schema.TypeString,
Computed: true,
},
"subnet_mask": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -1294,12 +1308,12 @@ func FlattenAppServiceIdentity(identity *web.ManagedServiceIdentity) []interface
return []interface{}{result}
}

func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
func ExpandAppServiceSiteConfig(input interface{}) (web.SiteConfig, error) {
configs := input.([]interface{})
siteConfig := web.SiteConfig{}

if len(configs) == 0 {
return siteConfig
return siteConfig, nil
}

config := configs[0].(map[string]interface{})
Expand Down Expand Up @@ -1354,26 +1368,45 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
if v, ok := config["ip_restriction"]; ok {
ipSecurityRestrictions := v.([]interface{})
restrictions := make([]web.IPSecurityRestriction, 0)
for _, ipSecurityRestriction := range ipSecurityRestrictions {
for i, ipSecurityRestriction := range ipSecurityRestrictions {
restriction := ipSecurityRestriction.(map[string]interface{})

ipAddress := restriction["ip_address"].(string)
mask := restriction["subnet_mask"].(string)
// the 2018-02-01 API expects a blank subnet mask and an IP address in CIDR format: a.b.c.d/x
// so translate the IP and mask if necessary
restrictionMask := ""
cidrAddress := ipAddress
if mask != "" {
ipNet := net.IPNet{IP: net.ParseIP(ipAddress), Mask: net.IPMask(net.ParseIP(mask))}
cidrAddress = ipNet.String()
} else if !strings.Contains(ipAddress, "/") {
cidrAddress += "/32"
vNetSubnetID := restriction["virtual_network_subnet_id"].(string)
if vNetSubnetID != "" && ipAddress != "" {
return siteConfig, fmt.Errorf(fmt.Sprintf("only one of `ip_address` or `virtual_network_subnet_id` can set set for `site_config.0.ip_restriction.%d`", i))
}

if vNetSubnetID == "" && ipAddress == "" {
return siteConfig, fmt.Errorf(fmt.Sprintf("one of `ip_address` or `virtual_network_subnet_id` must be set set for `site_config.0.ip_restriction.%d`", i))
}

restrictions = append(restrictions, web.IPSecurityRestriction{
IPAddress: &cidrAddress,
SubnetMask: &restrictionMask,
})
ipSecurityRestriction := web.IPSecurityRestriction{}
if ipAddress != "" {
mask := restriction["subnet_mask"].(string)
if mask == "" {
mask = "255.255.255.255"
}
// the 2018-02-01 API expects a blank subnet mask and an IP address in CIDR format: a.b.c.d/x
// so translate the IP and mask if necessary
restrictionMask := ""
cidrAddress := ipAddress
if mask != "" {
ipNet := net.IPNet{IP: net.ParseIP(ipAddress), Mask: net.IPMask(net.ParseIP(mask))}
cidrAddress = ipNet.String()
} else if !strings.Contains(ipAddress, "/") {
cidrAddress += "/32"
}
ipSecurityRestriction.IPAddress = &cidrAddress
ipSecurityRestriction.SubnetMask = &restrictionMask
}

if vNetSubnetID != "" {
ipSecurityRestriction.VnetSubnetResourceID = &vNetSubnetID
}

restrictions = append(restrictions, ipSecurityRestriction)

}
siteConfig.IPSecurityRestrictions = &restrictions
}
Expand Down Expand Up @@ -1432,7 +1465,7 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
siteConfig.Cors = &expand
}

return siteConfig
return siteConfig, nil
}

func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} {
Expand Down Expand Up @@ -1500,6 +1533,9 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} {
if subnet := v.SubnetMask; subnet != nil {
block["subnet_mask"] = *subnet
}
if vNetSubnetID := v.VnetSubnetResourceID; vNetSubnetID != nil {
block["virtual_network_subnet_id"] = *vNetSubnetID
}
restrictions = append(restrictions, block)
}
}
Expand Down
16 changes: 13 additions & 3 deletions azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,10 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error
httpsOnly := d.Get("https_only").(bool)
t := d.Get("tags").(map[string]interface{})

siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service %q (Resource Group %q): %s", name, resGroup, err)
}

siteEnvelope := web.Site{
Location: &location,
Expand Down Expand Up @@ -316,7 +319,11 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error
httpsOnly := d.Get("https_only").(bool)
t := d.Get("tags").(map[string]interface{})

siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service %q (Resource Group %q): %s", name, resGroup, err)
}

siteEnvelope := web.Site{
Location: &location,
Tags: tags.Expand(t),
Expand Down Expand Up @@ -344,7 +351,10 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error

if d.HasChange("site_config") {
// update the main configuration
siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service %q (Resource Group %q): %s", name, resGroup, err)
}
siteConfigResource := web.SiteConfigResource{
SiteConfig: &siteConfig,
}
Expand Down
15 changes: 12 additions & 3 deletions azurerm/resource_arm_app_service_slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ func resourceArmAppServiceSlotCreate(d *schema.ResourceData, meta interface{}) e
t := d.Get("tags").(map[string]interface{})
affinity := d.Get("client_affinity_enabled").(bool)

siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service Slot %q (Resource Group %q): %s", slot, resourceGroup, err)
}
siteEnvelope := web.Site{
Location: &location,
Tags: tags.Expand(t),
Expand Down Expand Up @@ -230,7 +233,10 @@ func resourceArmAppServiceSlotUpdate(d *schema.ResourceData, meta interface{}) e

location := azure.NormalizeLocation(d.Get("location").(string))
appServicePlanId := d.Get("app_service_plan_id").(string)
siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service Slot %q (Resource Group %q): %s", slot, resourceGroup, err)
}
enabled := d.Get("enabled").(bool)
httpsOnly := d.Get("https_only").(bool)
t := d.Get("tags").(map[string]interface{})
Expand Down Expand Up @@ -261,7 +267,10 @@ func resourceArmAppServiceSlotUpdate(d *schema.ResourceData, meta interface{}) e

if d.HasChange("site_config") {
// update the main configuration
siteConfig := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
siteConfig, err := azure.ExpandAppServiceSiteConfig(d.Get("site_config"))
if err != nil {
return fmt.Errorf("Error expanding `site_config` for App Service Slot %q (Resource Group %q): %s", slot, resourceGroup, err)
}
siteConfigResource := web.SiteConfigResource{
SiteConfig: &siteConfig,
}
Expand Down
70 changes: 70 additions & 0 deletions azurerm/resource_arm_app_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,31 @@ func TestAccAzureRMAppService_oneIpRestriction(t *testing.T) {
})
}

func TestAccAzureRMAppService_oneVNetSubnetIpRestriction(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
config := testAccAzureRMAppService_oneVNetSubnetIpRestriction(ri, testLocation())

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMAppServiceDestroy,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMAppServiceExists(resourceName),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMAppService_zeroedIpRestriction(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
Expand Down Expand Up @@ -3026,6 +3051,51 @@ resource "azurerm_app_service" "test" {
`, rInt, location, rInt, rInt)
}

func testAccAzureRMAppService_oneVNetSubnetIpRestriction(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_virtual_network" "test" {
name = "acctestvirtnet%d"
address_space = ["10.0.0.0/16"]
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
subnet {
name = "subnet1"
address_prefix = "10.0.1.0/24"
}
}
resource "azurerm_app_service_plan" "test" {
name = "acctestASP-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm_app_service" "test" {
name = "acctestAS-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
app_service_plan_id = "${azurerm_app_service_plan.test.id}"
site_config {
ip_restriction {
virtual_network_subnet_id = "${azurerm_virtual_network.test.subnet.*.id}"[0]
}
}
}
`, rInt, location, rInt, rInt, rInt)
}

func testAccAzureRMAppService_manyIpRestrictions(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down
4 changes: 3 additions & 1 deletion website/docs/r/app_service.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,9 @@ A `google` block supports the following:

A `ip_restriction` block supports the following:

* `ip_address` - (Required) The IP Address used for this IP Restriction.
* `ip_address` - (Optional) The IP Address used for this IP Restriction. One of `ip_address` or `virtual_network_subnet_id` must be specified.

* `virtual_network_subnet_id` - (Optional.The Virtual Network Subnet ID used for this IP Restriction. One of `ip_address` or `virtual_network_subnet_id` must be specified.

* `subnet_mask` - (Optional) The Subnet mask used for this IP Restriction. Defaults to `255.255.255.255`.

Expand Down
5 changes: 4 additions & 1 deletion website/docs/r/app_service_slot.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,10 @@ A `google` block supports the following:

A `ip_restriction` block supports the following:

* `ip_address` - (Required) The IP Address used for this IP Restriction.
* `ip_address` - (Optional) The IP Address used for this IP Restriction. One of `ip_address` or `virtual_network_subnet_id` must be specified.

* `virtual_network_subnet_id` - (Optional.The Virtual Network Subnet ID used for this IP Restriction. One of `ip_address` or `virtual_network_subnet_id` must be specified.


* `subnet_mask` - (Optional) The Subnet mask used for this IP Restriction. Defaults to `255.255.255.255`.

Expand Down

0 comments on commit 77c29e1

Please sign in to comment.