Skip to content

Commit

Permalink
Support app service application logs blob storage (#3520)
Browse files Browse the repository at this point in the history
  • Loading branch information
hdpe authored and katbyte committed Jun 26, 2019
1 parent 437fc8b commit fa99ccf
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 1 deletion.
129 changes: 129 additions & 0 deletions azurerm/helpers/azure/app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,57 @@ func SchemaAppServiceSiteConfig() *schema.Schema {
}
}

func SchemaAppServiceLogsConfig() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"application_logs": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"azure_blob_storage": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"level": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(web.Error),
string(web.Information),
string(web.Off),
string(web.Verbose),
string(web.Warning),
}, false),
},
"sas_url": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
"retention_in_days": {
Type: schema.TypeInt,
Required: true,
},
},
},
},
},
},
},
},
},
}
}

func SchemaAppServiceDataSourceSiteConfig() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Expand Down Expand Up @@ -1014,6 +1065,84 @@ func FlattenAppServiceAuthSettings(input *web.SiteAuthSettingsProperties) []inte
return append(results, result)
}

func FlattenAppServiceLogs(input *web.SiteLogsConfigProperties) []interface{} {
results := make([]interface{}, 0)
if input == nil {
return results
}

result := make(map[string]interface{})

if input.ApplicationLogs != nil {
appLogs := make([]interface{}, 0)

appLogsItem := make(map[string]interface{})

if blobStorageInput := input.ApplicationLogs.AzureBlobStorage; blobStorageInput != nil {
blobStorage := make([]interface{}, 0)

blobStorageItem := make(map[string]interface{})

blobStorageItem["level"] = string(blobStorageInput.Level)

if blobStorageInput.SasURL != nil {
blobStorageItem["sas_url"] = *blobStorageInput.SasURL
}

if blobStorageInput.RetentionInDays != nil {
blobStorageItem["retention_in_days"] = *blobStorageInput.RetentionInDays
}

blobStorage = append(blobStorage, blobStorageItem)

appLogsItem["azure_blob_storage"] = blobStorage
}

appLogs = append(appLogs, appLogsItem)

result["application_logs"] = appLogs
}

return append(results, result)
}

func ExpandAppServiceLogs(input interface{}) web.SiteLogsConfigProperties {
configs := input.([]interface{})
logs := web.SiteLogsConfigProperties{}

if len(configs) == 0 {
return logs
}

config := configs[0].(map[string]interface{})

if v, ok := config["application_logs"]; ok {
appLogsConfigs := v.([]interface{})

for _, config := range appLogsConfigs {
appLogsConfig := config.(map[string]interface{})

logs.ApplicationLogs = &web.ApplicationLogsConfig{}

if v, ok := appLogsConfig["azure_blob_storage"]; ok {
storageConfigs := v.([]interface{})

for _, config := range storageConfigs {
storageConfig := config.(map[string]interface{})

logs.ApplicationLogs.AzureBlobStorage = &web.AzureBlobStorageApplicationLogsConfig{
Level: web.LogLevel(storageConfig["level"].(string)),
SasURL: utils.String(storageConfig["sas_url"].(string)),
RetentionInDays: utils.Int32(int32(storageConfig["retention_in_days"].(int))),
}
}
}
}
}

return logs
}

func ExpandAppServiceSiteConfig(input interface{}) web.SiteConfig {
configs := input.([]interface{})
siteConfig := web.SiteConfig{}
Expand Down
43 changes: 42 additions & 1 deletion azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ func resourceArmAppService() *schema.Resource {

"auth_settings": azure.SchemaAppServiceAuthSettings(),

"logs": azure.SchemaAppServiceLogsConfig(),

"client_affinity_enabled": {
Type: schema.TypeBool,
Optional: true,
Expand Down Expand Up @@ -292,6 +294,16 @@ func resourceArmAppServiceCreate(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error updating auth settings for App Service %q (Resource Group %q): %+s", name, resGroup, err)
}

logsConfig := azure.ExpandAppServiceLogs(d.Get("logs"))

logs := web.SiteLogsConfig{
ID: read.ID,
SiteLogsConfigProperties: &logsConfig}

if _, err := client.UpdateDiagnosticLogsConfig(ctx, resGroup, name, logs); err != nil {
return fmt.Errorf("Error updating diagnostic logs config for App Service %q (Resource Group %q): %+s", name, resGroup, err)
}

return resourceArmAppServiceUpdate(d, meta)
}

Expand Down Expand Up @@ -365,6 +377,19 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error
}
}

if d.HasChange("logs") {
logs := azure.ExpandAppServiceLogs(d.Get("logs"))
id := d.Id()
logsResource := web.SiteLogsConfig{
ID: &id,
SiteLogsConfigProperties: &logs,
}

if _, err := client.UpdateDiagnosticLogsConfig(ctx, resGroup, name, logsResource); err != nil {
return fmt.Errorf("Error updating Diagnostics Logs for App Service %q: %+v", name, err)
}
}

if d.HasChange("client_affinity_enabled") {

affinity := d.Get("client_affinity_enabled").(bool)
Expand Down Expand Up @@ -465,6 +490,11 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error retrieving the AuthSettings for App Service %q (Resource Group %q): %+v", name, resGroup, err)
}

logsResp, err := client.GetDiagnosticLogsConfiguration(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error retrieving the DiagnosticsLogsConfiguration for App Service %q (Resource Group %q): %+v", name, resGroup, err)
}

appSettingsResp, err := client.ListApplicationSettings(ctx, resGroup, name)
if err != nil {
if utils.ResponseWasNotFound(appSettingsResp.Response) {
Expand Down Expand Up @@ -515,7 +545,13 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
d.Set("possible_outbound_ip_addresses", props.PossibleOutboundIPAddresses)
}

if err := d.Set("app_settings", flattenAppServiceAppSettings(appSettingsResp.Properties)); err != nil {
appSettings := flattenAppServiceAppSettings(appSettingsResp.Properties)

// remove DIAGNOSTICS* settings - Azure will sync these, so just maintain the logs block equivalents in the state
delete(appSettings, "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL")
delete(appSettings, "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS")

if err := d.Set("app_settings", appSettings); err != nil {
return err
}

Expand All @@ -533,6 +569,11 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error setting `auth_settings`: %s", err)
}

logs := azure.FlattenAppServiceLogs(logsResp.SiteLogsConfigProperties)
if err := d.Set("logs", logs); err != nil {
return fmt.Errorf("Error setting `logs`: %s", err)
}

scm := flattenAppServiceSourceControl(scmResp.SiteSourceControlProperties)
if err := d.Set("source_control", scm); err != nil {
return err
Expand Down
65 changes: 65 additions & 0 deletions azurerm/resource_arm_app_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,34 @@ func TestAccAzureRMAppService_localMySql(t *testing.T) {
})
}

func TestAccAzureRMAppService_applicationBlobStorageLogs(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
config := testAccAzureRMAppService_applicationBlobStorageLogs(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, "logs.0.application_logs.0.azure_blob_storage.0.level", "Information"),
resource.TestCheckResourceAttr(resourceName, "logs.0.application_logs.0.azure_blob_storage.0.sas_url", "http://x.com/"),
resource.TestCheckResourceAttr(resourceName, "logs.0.application_logs.0.azure_blob_storage.0.retention_in_days", "3"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMAppService_managedPipelineMode(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
Expand Down Expand Up @@ -2632,6 +2660,43 @@ resource "azurerm_app_service" "test" {
`, rInt, location, rInt, rInt)
}

func testAccAzureRMAppService_applicationBlobStorageLogs(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}"
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}"
logs {
application_logs {
azure_blob_storage {
level = "Information"
sas_url = "http://x.com/"
retention_in_days = 3
}
}
}
}
`, rInt, location, rInt, rInt)
}

func testAccAzureRMAppService_managedPipelineMode(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
Expand Down
24 changes: 24 additions & 0 deletions website/docs/r/app_service.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ The following arguments are supported:

* `https_only` - (Optional) Can the App Service only be accessed via HTTPS? Defaults to `false`.

* `logs` - (Optional) A `logs` block as defined below.

* `site_config` - (Optional) A `site_config` block as defined below.

* `tags` - (Optional) A mapping of tags to assign to the resource.
Expand All @@ -109,6 +111,28 @@ A `identity` block supports the following:

---

A `logs` block supports the following:

* `application_logs` - (Optional) An `application_logs` block as defined below.

---

An `application_logs` block supports the following:

* `azure_blob_storage` - (Optional) An `azure_blob_storage` block as defined below.

---

An `azure_blob_storage` block supports the following:

* `level` - (Required) The level at which to log. Possible values include `Error`, `Warning`, `Information`, `Verbose` and `Off`.

* `sas_url` - (Required) The URL to the storage container, with a Service SAS token appended. **NOTE:** there is currently no means of generating Service SAS tokens with the `azurerm` provider.

* `retention_in_days` - (Required) The number of days to retain logs for.

---

A `site_config` block supports the following:

* `always_on` - (Optional) Should the app be loaded at all times? Defaults to `false`.
Expand Down

0 comments on commit fa99ccf

Please sign in to comment.