Skip to content

Commit

Permalink
azurerm_app_service: support for storage mounts (#3792)
Browse files Browse the repository at this point in the history
  • Loading branch information
Joakim Bakke Hellum authored and katbyte committed Jul 5, 2019
1 parent 9ebf8ae commit d8a4bb6
Show file tree
Hide file tree
Showing 4 changed files with 289 additions and 0 deletions.
101 changes: 101 additions & 0 deletions azurerm/helpers/azure/app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,56 @@ func SchemaAppServiceLogsConfig() *schema.Schema {
}
}

func SchemaAppServiceStorageAccounts() *schema.Schema {
return &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},

"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
string(web.AzureBlob),
string(web.AzureFiles),
}, false),
},

"account_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},

"share_name": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},

"access_key": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
ValidateFunc: validate.NoEmptyStrings,
},

"mount_path": {
Type: schema.TypeString,
Optional: true,
},
},
},
}
}

func SchemaAppServiceDataSourceSiteConfig() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Expand Down Expand Up @@ -1400,3 +1450,54 @@ func FlattenAppServiceSiteConfig(input *web.SiteConfig) []interface{} {

return append(results, result)
}

func ExpandAppServiceStorageAccounts(d *schema.ResourceData) map[string]*web.AzureStorageInfoValue {
input := d.Get("storage_account").(*schema.Set).List()
output := make(map[string]*web.AzureStorageInfoValue, len(input))

for _, v := range input {
vals := v.(map[string]interface{})

saName := vals["name"].(string)
saType := vals["type"].(string)
saAccountName := vals["account_name"].(string)
saShareName := vals["share_name"].(string)
saAccessKey := vals["access_key"].(string)
saMountPath := vals["mount_path"].(string)

output[saName] = &web.AzureStorageInfoValue{
Type: web.AzureStorageType(saType),
AccountName: utils.String(saAccountName),
ShareName: utils.String(saShareName),
AccessKey: utils.String(saAccessKey),
MountPath: utils.String(saMountPath),
}
}

return output
}

func FlattenAppServiceStorageAccounts(input map[string]*web.AzureStorageInfoValue) []interface{} {
results := make([]interface{}, 0)

for k, v := range input {
result := make(map[string]interface{})
result["name"] = k
result["type"] = string(v.Type)
if v.AccountName != nil {
result["account_name"] = *v.AccountName
}
if v.ShareName != nil {
result["share_name"] = *v.ShareName
}
if v.AccessKey != nil {
result["access_key"] = *v.AccessKey
}
if v.MountPath != nil {
result["mount_path"] = *v.MountPath
}
results = append(results, result)
}

return results
}
22 changes: 22 additions & 0 deletions azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func resourceArmAppService() *schema.Resource {
Computed: true,
},

"storage_account": azure.SchemaAppServiceStorageAccounts(),

"connection_string": {
Type: schema.TypeSet,
Optional: true,
Expand Down Expand Up @@ -418,6 +420,17 @@ func resourceArmAppServiceUpdate(d *schema.ResourceData, meta interface{}) error
}
}

if d.HasChange("storage_account") {
storageAccounts := azure.ExpandAppServiceStorageAccounts(d)
properties := web.AzureStoragePropertyDictionaryResource{
Properties: storageAccounts,
}

if _, err := client.UpdateAzureStorageAccounts(ctx, resGroup, name, properties); err != nil {
return fmt.Errorf("Error updating Storage Accounts for App Service %q: %+v", name, err)
}
}

if d.HasChange("connection_string") {
// update the ConnectionStrings
connectionStrings := expandAppServiceConnectionStrings(d)
Expand Down Expand Up @@ -505,6 +518,11 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error making Read request on AzureRM App Service AppSettings %q: %+v", name, err)
}

storageAccountsResp, err := client.ListAzureStorageAccounts(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error making Read request on AzureRM App Service Storage Accounts %q: %+v", name, err)
}

connectionStringsResp, err := client.ListConnectionStrings(ctx, resGroup, name)
if err != nil {
return fmt.Errorf("Error making Read request on AzureRM App Service ConnectionStrings %q: %+v", name, err)
Expand Down Expand Up @@ -555,6 +573,10 @@ func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
return err
}

if err := d.Set("storage_account", azure.FlattenAppServiceStorageAccounts(storageAccountsResp.Properties)); err != nil {
return err
}

if err := d.Set("connection_string", flattenAppServiceConnectionStrings(connectionStringsResp.Properties)); err != nil {
return err
}
Expand Down
148 changes: 148 additions & 0 deletions azurerm/resource_arm_app_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,39 @@ func TestAccAzureRMAppService_connectionStrings(t *testing.T) {
})
}

func TestAccAzureRMAppService_storageAccounts(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
location := testLocation()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMAppServiceDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMAppService_storageAccounts(ri, location),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMAppServiceExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "storage_account.#", "1"),
),
},
{
Config: testAccAzureRMAppService_storageAccountsUpdated(ri, location),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMAppServiceExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "storage_account.#", "2"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMAppService_oneIpRestriction(t *testing.T) {
resourceName := "azurerm_app_service.test"
ri := tf.AccRandTimeInt()
Expand Down Expand Up @@ -2454,6 +2487,121 @@ resource "azurerm_app_service" "test" {
`, rInt, location, rInt, rInt)
}

func testAccAzureRMAppService_storageAccounts(rInt int, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_storage_account" "test" {
name = "acct%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "test" {
name = "acctestcontainer"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
}
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}"
storage_account {
name = "blobs"
type = "AzureBlob"
account_name = "${azurerm_storage_account.test.name}"
share_name = "${azurerm_storage_container.test.name}"
access_key = "${azurerm_storage_account.test.primary_access_key}"
mount_path = "/blobs"
}
}
`, rInt, location, rInt, rInt, rInt)
}

func testAccAzureRMAppService_storageAccountsUpdated(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_storage_account" "test" {
name = "acct%d"
location = "${azurerm_resource_group.test.location}"
resource_group_name = "${azurerm_resource_group.test.name}"
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_storage_container" "test" {
name = "acctestcontainer"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
}
resource "azurerm_storage_share" "test" {
name = "acctestshare"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
}
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}"
storage_account {
name = "blobs"
type = "AzureBlob"
account_name = "${azurerm_storage_account.test.name}"
share_name = "${azurerm_storage_container.test.name}"
access_key = "${azurerm_storage_account.test.primary_access_key}"
mount_path = "/blobs"
}
storage_account {
name = "files"
type = "AzureFiles"
account_name = "${azurerm_storage_account.test.name}"
share_name = "${azurerm_storage_share.test.name}"
access_key = "${azurerm_storage_account.test.primary_access_key}"
mount_path = "/files"
}
}
`, rInt, location, rInt, rInt, rInt)
}

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

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

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

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

* `client_affinity_enabled` - (Optional) Should the App Service send session affinity cookies, which route client requests in the same session to the same instance?
Expand All @@ -93,6 +95,22 @@ The following arguments are supported:

---

A `storage_account` block supports the following:

* `name` - (Required) The name of the storage account identifier.

* `type` - (Required) The type of storage. Possible values are `AzureBlob` and `AzureFiles`.

* `account_name` - (Required) The name of the storage account.

* `share_name` - (Required) The name of the file share (container name, for Blob storage).

* `access_key` - (Required) The access key for the storage account.

* `mount_path` - (Optional) The path to mount the storage within the site's runtime environment.

---

A `connection_string` block supports the following:

* `name` - (Required) The name of the Connection String.
Expand Down

0 comments on commit d8a4bb6

Please sign in to comment.