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

Create azure App Service resource #2

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
9 changes: 9 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/sql"
"github.com/Azure/azure-sdk-for-go/arm/storage"
"github.com/Azure/azure-sdk-for-go/arm/trafficmanager"
"github.com/Azure/azure-sdk-for-go/arm/web"
mainStorage "github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
Expand Down Expand Up @@ -104,6 +105,8 @@ type ArmClient struct {
keyVaultClient keyvault.VaultsClient

sqlElasticPoolsClient sql.ElasticPoolsClient

appsClient web.AppsClient
}

func withRequestLogging() autorest.SendDecorator {
Expand Down Expand Up @@ -476,6 +479,12 @@ func (c *Config) getArmClient() (*ArmClient, error) {
sqlepc.Sender = autorest.CreateSender(withRequestLogging())
client.sqlElasticPoolsClient = sqlepc

ac := web.NewAppsClientWithBaseURI(endpoint, c.SubscriptionID)
setUserAgent(&ac.Client)
ac.Authorizer = auth
ac.Sender = autorest.CreateSender(withRequestLogging())
client.appsClient = ac

return &client, nil
}

Expand Down
2 changes: 2 additions & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ func Provider() terraform.ResourceProvider {
"azurerm_virtual_network": resourceArmVirtualNetwork(),
"azurerm_virtual_network_peering": resourceArmVirtualNetworkPeering(),

"azurerm_app_service": resourceArmAppService(),

// These resources use the Riviera SDK
"azurerm_dns_a_record": resourceArmDnsARecord(),
"azurerm_dns_aaaa_record": resourceArmDnsAAAARecord(),
Expand Down
165 changes: 165 additions & 0 deletions azurerm/resource_arm_app_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
package azurerm

import (
"fmt"
"log"
"net/http"

"github.com/Azure/azure-sdk-for-go/arm/web"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmAppService() *schema.Resource {
return &schema.Resource{
Create: resourceArmAppServiceCreateUpdate,
Read: resourceArmAppServiceRead,
Update: resourceArmAppServiceCreateUpdate,
Delete: resourceArmAppServiceDelete,

Schema: map[string]*schema.Schema{
"resource_group_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"skip_dns_registration": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"skip_custom_domain_verification": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
"force_dns_registration": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"ttl_in_seconds": {
Type: schema.TypeString,
Copy link
Contributor

Choose a reason for hiding this comment

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

Would we be better to have this as an Int we cast to/from a String?

Optional: true,
Default: "",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we want to remove this line, we can instead then do:

if v, ok := d.GetOk("ttl_in_seconds"); ok {
  # assume we have ttl_in_seconds
  ttl := v.(int)
}

},
"app_service_plan_id": {
Type: schema.TypeString,
Optional: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

does this need to be Required and ForceNew?

},
"always_on": {
Copy link
Contributor

Choose a reason for hiding this comment

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

given there's a ton of possible options here, it'd be nice to pull this out into a nested structure:

resource "azurerm_app_service" "test" {
  name = "foo"
  site_config {
    always_on = true
    app_service_plan_id = "..."
    foo = "bar"
  }
}

Type: schema.TypeBool,
Optional: true,
},
"location": locationSchema(),
"tags": tagsSchema(),
},
}
}

func resourceArmAppServiceCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient)
appClient := client.appsClient
Copy link
Contributor

Choose a reason for hiding this comment

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

minor: we'd generally combine these into one variable rather than splitting it out


log.Printf("[INFO] preparing arguments for Azure ARM Web App creation.")
Copy link
Contributor

Choose a reason for hiding this comment

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

can we rename Web App -> App Service?


resGroup := d.Get("resource_group_name").(string)
name := d.Get("name").(string)
location := d.Get("location").(string)
skipDNSRegistration := d.Get("skip_dns_registration").(bool)
skipCustomDomainVerification := d.Get("skip_custom_domain_verification").(bool)
forceDNSRegistration := d.Get("force_dns_registration").(bool)
ttlInSeconds := d.Get("ttl_in_seconds").(string)
tags := d.Get("tags").(map[string]interface{})

siteConfig := web.SiteConfig{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the available fields, it feels like it might be worth splitting this out into a separate block?

if v, ok := d.GetOk("always_on"); ok {
alwaysOn := v.(bool)
siteConfig.AlwaysOn = &alwaysOn
}

siteProps := web.SiteProperties{
SiteConfig: &siteConfig,
}
if v, ok := d.GetOk("app_service_plan_id"); ok {
serverFarmID := v.(string)
siteProps.ServerFarmID = &serverFarmID
}

siteEnvelope := web.Site{
Location: &location,
Tags: expandTags(tags),
SiteProperties: &siteProps,
}

_, error := appClient.CreateOrUpdate(resGroup, name, siteEnvelope, &skipDNSRegistration, &skipCustomDomainVerification, &forceDNSRegistration, ttlInSeconds, make(chan struct{}))
err := <-error
if err != nil {
return err
}

read, err := appClient.Get(resGroup, name)
if err != nil {
return err
}
if read.ID == nil {
return fmt.Errorf("Cannot read App Service %s (resource group %s) ID", name, resGroup)
}

d.SetId(*read.ID)

return resourceArmAppServiceRead(d, meta)
}

func resourceArmAppServiceRead(d *schema.ResourceData, meta interface{}) error {
appClient := meta.(*ArmClient).appsClient

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}

log.Printf("[DEBUG] Reading App Service details %s", id)

resGroup := id.ResourceGroup
name := id.Path["sites"]

resp, err := appClient.Get(resGroup, name)
if err != nil {
if resp.StatusCode == http.StatusNotFound {
d.SetId("")
return nil
}
return fmt.Errorf("Error making Read request on AzureRM App Service %s: %s", name, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs to be %+v to output the error correctly e.g.

Error making Read request on AzureRM App Service '%s': %+v

}

d.Set("name", name)
d.Set("resource_group_name", resGroup)
Copy link
Contributor

Choose a reason for hiding this comment

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

could we set the other fields here too?


return nil
}

func resourceArmAppServiceDelete(d *schema.ResourceData, meta interface{}) error {
appClient := meta.(*ArmClient).appsClient

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroup := id.ResourceGroup
name := id.Path["sites"]

log.Printf("[DEBUG] Deleting App Service %s: %s", resGroup, name)

deleteMetrics := true
deleteEmptyServerFarm := true
skipDNSRegistration := true
Copy link
Contributor

Choose a reason for hiding this comment

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

we can pull the user specified values from d.Get("foo").(string) in this case, rather than assuming them :)

(although, FWIW I think we shouldn't be giving the option to delete an empty server farm, since that's managed as a separate resource?)


_, err = appClient.Delete(resGroup, name, &deleteMetrics, &deleteEmptyServerFarm, &skipDNSRegistration)

return err
}
Loading