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

Support for Windows Container in Azure App Service #3566

Merged
6 changes: 6 additions & 0 deletions azurerm/data_source_app_service_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ func dataSourceAppServicePlan() *schema.Resource {
Computed: true,
},

"is_xenon": {
Type: schema.TypeBool,
Computed: true,
},

"tags": tagsForDataSourceSchema(),
},
}
Expand All @@ -99,6 +104,7 @@ func dataSourceAppServicePlanRead(d *schema.ResourceData, meta interface{}) erro
d.Set("name", name)
d.Set("resource_group_name", resourceGroup)
d.Set("kind", resp.Kind)
d.Set("is_xenon", resp.IsXenon)

if location := resp.Location; location != nil {
d.Set("location", azureRMNormalizeLocation(*location))
Expand Down
50 changes: 50 additions & 0 deletions azurerm/data_source_app_service_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ func TestAccDataSourceAzureRMAppServicePlan_complete(t *testing.T) {
})
}

func TestAccDataSourceAzureRMAppServicePlan_basicWindowsContainer(t *testing.T) {
dataSourceName := "data.azurerm_app_service_plan.test"
rInt := tf.AccRandTimeInt()
location := testLocation()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAppServicePlan_basicWindowsContainer(rInt, location),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "kind", "xenon"),
resource.TestCheckResourceAttr(dataSourceName, "sku.#", "1"),
resource.TestCheckResourceAttr(dataSourceName, "sku.0.tier", "PremiumContainer"),
resource.TestCheckResourceAttr(dataSourceName, "sku.0.size", "PC2"),
resource.TestCheckResourceAttr(dataSourceName, "is_xenon", "true"),
),
},
},
})
}

func testAccDataSourceAppServicePlan_basic(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down Expand Up @@ -119,3 +142,30 @@ data "azurerm_app_service_plan" "test" {
}
`, rInt, location, rInt)
}

func testAccDataSourceAppServicePlan_basicWindowsContainer(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_app_service_plan" "test" {
name = "acctestASP-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
is_xenon = true
kind = "xenon"

sku {
tier = "PremiumContainer"
size = "PC2"
}
}

data "azurerm_app_service_plan" "test" {
name = "${azurerm_app_service_plan.test.name}"
resource_group_name = "${azurerm_app_service_plan.test.resource_group_name}"
}
`, rInt, location, rInt)
}
32 changes: 32 additions & 0 deletions azurerm/data_source_app_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,26 @@ func TestAccDataSourceAzureRMAppService_minTls(t *testing.T) {
})
}

func TestAccDataSourceAzureRMAppService_basicWindowsContainer(t *testing.T) {
dataSourceName := "data.azurerm_app_service.test"
rInt := tf.AccRandTimeInt()
location := testLocation()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccDataSourceAppService_basicWindowsContainer(rInt, location),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "site_config.0.windows_fx_version", "DOCKER|mcr.microsoft.com/azure-app-service/samples/aspnethelloworld:latest"),
resource.TestCheckResourceAttr(dataSourceName, "app_settings.DOCKER_REGISTRY_SERVER_URL", "https://mcr.microsoft.com"),
),
},
},
})
}

func testAccDataSourceAppService_basic(rInt int, location string) string {
config := testAccAzureRMAppService_basic(rInt, location)
return fmt.Sprintf(`
Expand Down Expand Up @@ -296,3 +316,15 @@ data "azurerm_app_service" "test" {
}
`, config)
}

func testAccDataSourceAppService_basicWindowsContainer(rInt int, location string) string {
config := testAccAzureRMAppService_basicWindowsContainer(rInt, location)
return fmt.Sprintf(`
%s

data "azurerm_app_service" "test" {
name = "${azurerm_app_service.test.name}"
resource_group_name = "${azurerm_app_service.test.resource_group_name}"
}
`, config)
}
20 changes: 20 additions & 0 deletions azurerm/helpers/azure/app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,19 @@ func SchemaAppServiceSiteConfig() *schema.Schema {
string(web.FtpsOnly),
}, false),
},

"linux_fx_version": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"windows_fx_version": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"min_tls_version": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -564,6 +571,11 @@ func SchemaAppServiceDataSourceSiteConfig() *schema.Schema {
Computed: true,
},

"windows_fx_version": {
Type: schema.TypeString,
Computed: true,
},

"min_tls_version": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -1051,6 +1063,10 @@ func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
siteConfig.LinuxFxVersion = utils.String(v.(string))
}

if v, ok := config["windows_fx_version"]; ok {
siteConfig.WindowsFxVersion = utils.String(v.(string))
}

if v, ok := config["http2_enabled"]; ok {
siteConfig.HTTP20Enabled = utils.Bool(v.(bool))
}
Expand Down Expand Up @@ -1239,6 +1255,10 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} {
result["linux_fx_version"] = *input.LinuxFxVersion
}

if input.WindowsFxVersion != nil {
result["windows_fx_version"] = *input.WindowsFxVersion
}

if input.VnetName != nil {
result["virtual_network_name"] = *input.VnetName
}
Expand Down
16 changes: 16 additions & 0 deletions azurerm/resource_arm_app_service_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ func resourceArmAppServicePlan() *schema.Resource {
ValidateFunc: validation.StringInSlice([]string{
// @tombuildsstuff: I believe `app` is the older representation of `Windows`
// thus we need to support it to be able to import resources without recreating them.
// @jcorioland: new SKU and kind 'xenon' have been added for Windows Containers support
// https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-windows-container-support-in-azure-app-service/
"App",
"elastic",
"FunctionApp",
"Linux",
"Windows",
"xenon",
}, true),
DiffSuppressFunc: suppress.CaseDifference,
},
Expand Down Expand Up @@ -141,6 +144,11 @@ func resourceArmAppServicePlan() *schema.Resource {
Computed: true,
},

"is_xenon": {
Type: schema.TypeBool,
Optional: true,
},

"tags": tagsSchema(),
},
}
Expand Down Expand Up @@ -175,6 +183,13 @@ func resourceArmAppServicePlanCreateUpdate(d *schema.ResourceData, meta interfac
sku := expandAzureRmAppServicePlanSku(d)
properties := expandAppServicePlanProperties(d)

isXenon := d.Get("is_xenon").(bool)
properties.IsXenon = &isXenon

if kind == "xenon" && !isXenon {
return fmt.Errorf("Creating or updating App Service Plan %q (Resource Group %q): when kind is set to xenon, is_xenon property should be set to true", name, resGroup)
}

appServicePlan := web.AppServicePlan{
Location: &location,
Kind: &kind,
Expand Down Expand Up @@ -257,6 +272,7 @@ func resourceArmAppServicePlanRead(d *schema.ResourceData, meta interface{}) err
d.Set("location", azureRMNormalizeLocation(*location))
}
d.Set("kind", resp.Kind)
d.Set("is_xenon", resp.IsXenon)

if props := resp.AppServicePlanProperties; props != nil {
if err := d.Set("properties", flattenAppServiceProperties(props)); err != nil {
Expand Down
50 changes: 50 additions & 0 deletions azurerm/resource_arm_app_service_plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,34 @@ func TestAccAzureRMAppServicePlan_premiumConsumptionPlan(t *testing.T) {
})
}

func TestAccAzureRMAppServicePlan_basicWindowsContainer(t *testing.T) {
resourceName := "azurerm_app_service_plan.test"
ri := tf.AccRandTimeInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMAppServicePlanDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMAppServicePlan_basicWindowsContainer(ri, testLocation()),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMAppServicePlanExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "kind", "xenon"),
resource.TestCheckResourceAttr(resourceName, "is_xenon", "true"),
resource.TestCheckResourceAttr(resourceName, "sku.0.tier", "PremiumContainer"),
resource.TestCheckResourceAttr(resourceName, "sku.0.size", "PC2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testCheckAzureRMAppServicePlanDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*ArmClient).appServicePlansClient

Expand Down Expand Up @@ -595,3 +623,25 @@ resource "azurerm_app_service_plan" "test" {
}
`, rInt, location, rInt)
}

func testAccAzureRMAppServicePlan_basicWindowsContainer(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_app_service_plan" "test" {
name = "acctestASP-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
kind = "xenon"
is_xenon = true

sku {
tier = "PremiumContainer"
size = "PC2"
}
}
`, rInt, location, rInt)
}
66 changes: 66 additions & 0 deletions azurerm/resource_arm_app_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1721,6 +1721,33 @@ func TestAccAzureRMAppService_multiAuthSettings(t *testing.T) {
})
}

func TestAccAzureRMAppService_basicWindowsContainer(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
config := testAccAzureRMAppService_basicWindowsContainer(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),
resource.TestCheckResourceAttr(resourceName, "site_config.0.windows_fx_version", "DOCKER|mcr.microsoft.com/azure-app-service/samples/aspnethelloworld:latest"),
resource.TestCheckResourceAttr(resourceName, "app_settings.DOCKER_REGISTRY_SERVER_URL", "https://mcr.microsoft.com"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testCheckAzureRMAppServiceDestroy(s *terraform.State) error {
client := testAccProvider.Meta().(*ArmClient).appServicesClient

Expand Down Expand Up @@ -3626,3 +3653,42 @@ resource "azurerm_app_service" "test" {
}
`, rInt, location, rInt, rInt, tenantID, web.AzureActiveDirectory)
}

func testAccAzureRMAppService_basicWindowsContainer(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_app_service_plan" "test" {
name = "acctestASP-%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
is_xenon = true
kind = "xenon"

sku {
tier = "PremiumContainer"
size = "PC2"
}
}

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 {
windows_fx_version = "DOCKER|mcr.microsoft.com/azure-app-service/samples/aspnethelloworld:latest"
}

app_settings = {
"DOCKER_REGISTRY_SERVER_URL" = "https://mcr.microsoft.com",
"DOCKER_REGISTRY_SERVER_USERNAME" = "",
"DOCKER_REGISTRY_SERVER_PASSWORD" = "",
}
}
`, rInt, location, rInt, rInt)
}
3 changes: 3 additions & 0 deletions examples/app-service/windows-container/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Example: App Service configured for Windows Container

This example provisions an App Service inside an App Service Plan which is configured to run a Windows Container.
Loading