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 Plan resource #1

Merged

Conversation

lstolyarov
Copy link
Contributor

No description provided.

@lstolyarov
Copy link
Contributor Author

@tombuildsstuff I have migrated the code in pull requests hashicorp/terraform#12001 and hashicorp/terraform#13634 to this repository.

Copy link
Contributor

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

Hi @lstolyarov

Thanks for this contribution / porting this over to the new repo - I've reviewed and left some comments in-line - but this is off to a good start :)

Thanks!

client := meta.(*ArmClient)
AppServicePlanClient := client.appServicePlansClient

log.Printf("[INFO] preparing arguments for Azure App Service Plan 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 update this to AzureRM for consistency?

Type: schema.TypeString,
Required: true,
},
"location": {
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 switch this to use locationSchema()?


func resourceArmAppServicePlanCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient)
AppServicePlanClient := client.appServicePlansClient
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 client

Create: resourceArmAppServicePlanCreateUpdate,
Read: resourceArmAppServicePlanRead,
Update: resourceArmAppServicePlanCreateUpdate,
Delete: resourceArmAppServicePlanDelete,
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 test for it, can we add import support here:

Importer: &schema.ResourceImporter{
    State: schema.ImportStatePassthrough,
},

},
"maximum_number_of_workers": {
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.

should this also be Computed?

}

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.

can we set the other field values here?

}
}

var testAccAzureRMAppServicePlan_standard = `
Copy link
Contributor

Choose a reason for hiding this comment

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

for newer resources we're tending to make this a function which returns a formatted string:

func testAccAzureRMAppServicePlan_standard(rInt int) string {
  return fmt.Sprintf(`foo`, rInt)
}

"github.com/hashicorp/terraform/terraform"
)

func TestAccAzureRMAppServicePlan_standard(t *testing.T) {
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 add another test case with all fields set?

"tier": {
Type: schema.TypeString,
Required: true,
},
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 SkuDescription object:

// SkuDescription is description of a SKU for a scalable resource.
type SkuDescription struct {
	Name         *string       `json:"name,omitempty"`
	Tier         *string       `json:"tier,omitempty"`
	Size         *string       `json:"size,omitempty"`
	Family       *string       `json:"family,omitempty"`
	Capacity     *int32        `json:"capacity,omitempty"`
	SkuCapacity  *SkuCapacity  `json:"skuCapacity,omitempty"`
	Locations    *[]string     `json:"locations,omitempty"`
	Capabilities *[]Capability `json:"capabilities,omitempty"`
}

I think it makes sense to separate this out into it's own block i.e.:

sku {
  name = "Standard_S0"
  tier = "Standard"
  size = "S0"
  ...
}

Here's an example of the Schema usage - expanding/parsing it out and flattening/writing it back - which I'm hoping makes sense?

"github.com/hashicorp/terraform/helper/schema"
)

func resourceArmAppServicePlan() *schema.Resource {
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 add some documentation for this resource too?

@lstolyarov
Copy link
Contributor Author

Hey @tombuildsstuff, I am facing a problem when running the acceptance test for the App Service Plan. I get the following error: Cannot find Web space acctestRG-2152939972729330496-WestEuropewebspace for subscription xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. Have you ever seen this problem before?

@tombuildsstuff
Copy link
Contributor

Hey @lstolyarov

Taking a look at the request made in Debug mode, I can confirm I'm seeing the same response - which appears to be an Azure API / Service error:

PUT /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-212/providers/Microsoft.Web/serverfarms/acctestAppServicePlan-test?api-version=2016-09-01 HTTP/1.1
Host: management.azure.com
User-Agent: HashiCorp-Terraform-v0.9.8
Content-Length: 91
Content-Type: application/json
Accept-Encoding: gzip

{
	"location": "westeurope",
	"properties": {},
	"sku": {
		"name": "S0",
		"tier": "Standard",
		"size": "S0"
	}
}

Which returns the response:

HTTP/1.1 404 Not Found
Content-Length: 667
...

{
	"Code": "NotFound",
	"Message": "Cannot find Web space acctestRG-212-WestEuropewebspace for subscription 00000000-0000-0000-0000-000000000000.",
	"Target": null,
	"Details": [{
		"Message": "Cannot find Web space acctestRG-212-WestEuropewebspace for subscription 00000000-0000-0000-0000-000000000000."
	}, {
		"Code": "NotFound"
	}, {
		"ErrorEntity": {
			"ExtendedCode": "03005",
			"MessageTemplate": "Cannot find Web space {0} for subscription {1}.",
			"Parameters": ["acctestRG-212-WestEuropewebspace", "00000000-0000-0000-0000-000000000000"],
			"Code": "NotFound",
			"Message": "Cannot find Web space acctestRG-212-WestEuropewebspace for subscription 00000000-0000-0000-0000-000000000000."
		}
	}],
	"Innererror": null
}

I've also tested in an HTTP client submitting via a POST rather than a PUT to check if this allowed is to be created - but unfortunately this doesn't appear to be an option. As such, I believe this is a service error, and the next step would be to open an issue over on the Azure Rest API Specs Repository which would allow the team to investigate further.

Hope that helps? :)

@echuvyrov
Copy link
Contributor

Hey @lstolyarov thanks for all your work on this. I'd love to help you resolve the issue so we can move your change along. I did a bit of looking and testing and it looks like you have "S0" as the plan for your tests. Using your code, I changed it to "S1" and the tests almost passed :) (got a different, hopefully familiar error). Here are a couple of links to take a look at:

App Service Plans: https://azure.microsoft.com/en-us/pricing/details/app-service/
Gist creating a resource group + web app plan using Go SDK (portions, including logging borrowed from TF): https://gist.github.com/echuvyrov/cd8f9c2fcbd90a01e7bace2c523bf915

Let me know if this helps or if there's something else going on here.

@tombuildsstuff
Copy link
Contributor

👋 hey @lstolyarov - have you had a chance to look into @echuvyrov's comments? :)

@lstolyarov
Copy link
Contributor Author

Hi @tombuildsstuff,

Apologies, I haven't had a chance yet.

@echuvyrov thanks for the suggestion. I am aiming to try it out this week.

Thanks

@echuvyrov
Copy link
Contributor

@lstolyarov let me know if you could use a helping hand - I think this is one of the key resources for Azure :)

@lstolyarov
Copy link
Contributor Author

Hey guys,

I just had a look at this and the app service plan does get created with the sku size set to "S1". However, the tests now fail when resources are being destroyed. The destroy command cannot execute because the resource still has a create operation running on it:

{
  "Code": "Conflict",
  "Message": "Cannot modify this web hosting plan because another operation is in progress. Details: Id: 096c1fa1-b248-4e67-98c5-f0e018b1b261, OperationName: Create, CreatedTime: 7/18/2017 5:42:06 PM, RequestId: 09e22889-8fb2-40f0-8446-2739e820f135, EntityType: 9",
  "Target": null,
  "Details": [
    {
      "Message": "Cannot modify this web hosting plan because another operation is in progress. Details: Id: 096c1fa1-b248-4e67-98c5-f0e018b1b261, OperationName: Create, CreatedTime: 7/18/2017 5:42:06 PM, RequestId: 09e22889-8fb2-40f0-8446-2739e820f135, EntityType: 9"
    },
    {
      "Code": "Conflict"
    },
    {
      "ErrorEntity": {
        "ExtendedCode": "59205",
        "MessageTemplate": "Cannot modify this web hosting plan because another operation is in progress. Details: {0}",
        "Parameters": [
          "Id: 096c1fa1-b248-4e67-98c5-f0e018b1b261, OperationName: Create, CreatedTime: 7/18/2017 5:42:06 PM, RequestId: 09e22889-8fb2-40f0-8446-2739e820f135, EntityType: 9"
        ],
        "Code": "Conflict",
        "Message": "Cannot modify this web hosting plan because another operation is in progress. Details: Id: 096c1fa1-b248-4e67-98c5-f0e018b1b261, OperationName: Create, CreatedTime: 7/18/2017 5:42:06 PM, RequestId: 09e22889-8fb2-40f0-8446-2739e820f135, EntityType: 9"
      }
    }
  ],
  "Innererror": null
}

@tombuildsstuff have you come across this before?

@tombuildsstuff
Copy link
Contributor

Hey @lstolyarov

From what you've said above, I'd guess the SDK is returning from the CreateOrUpdate method when the resource creation has been submitted/requested, rather than polling for it's completion and returning once it's finished creating. In general I'd put that down to a Swagger bug, where the status code isn't right - let's ignore that for now, but we should probably open a bug about it on the azure-rest-api-specs repository at some point

To work around that - in some resources we have functions which poll to check the resource has been created - which is what I'd suggest adding to the Create method.

Hopefully that should solve the issue you're seeing here :)

Thanks!

@lstolyarov
Copy link
Contributor Author

Hi @tombuildsstuff,

Thanks for the suggestion. I added the function but it doesn't help. The ProvisioningState is updated to "Succeeded" when the app service is created. I also tested this manually and I am only able to delete the app service plan about 5 minutes after it has been provisioned so there is definitely something else happening. I have got an issue open here updated with the latest problems.

@echuvyrov
Copy link
Contributor

hey @lstolyarov @tombuildsstuff I am running into the same issue you are. Looking into this into internally and hope to have an answer very soon.

apparentlymart pushed a commit that referenced this pull request Jul 22, 2017
* added .travis.yml and deploy.sh
* added deploy script, updated travis.yml to build topic- branches
* generate random string for hostname
* plan now produces output plan, apply now consumes outputted plan
* cleanup; sane defaults
* explicit build dirs
@tombuildsstuff
Copy link
Contributor

Hey @echuvyrov

Did you manage to get anywhere with this internally? :)

Thanks!

@georgeedwards
Copy link

There's been an update on the SDK issue. Are we sure that S0 is a valid tier?

Sku: &sku,
}

_, error := AppServicePlanClient.CreateOrUpdate(resGroup, name, appServicePlan, make(chan struct{}))
Copy link

Choose a reason for hiding this comment

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

You may as well update this to be:

_, error := AppServicePlanClient.CreateOrUpdate(resGroup, name, appServicePlan, nil)

Instantiating a channel like this on the fly doesn't allow you a way to cancel and just adds a little garbage to be cleaned up later. :) Notice in this playground example nil is passed to wait(...) but never triggers the cancel select case.

@georgeedwards
Copy link

Hey @lstolyarov, Did you get a chance to look at the comments? Let me know if you need a hand - I would be really keen to see this as a supported resource.

@echuvyrov
Copy link
Contributor

hey @georgeedwards @lstolyarov @tombuildsstuff we should have this unblocked this week, will keep you posted. I will test at the end of the week or at the beginning of the next week to confirm.

@echuvyrov
Copy link
Contributor

hey @lstolyarov @tombuildsstuff @georgeedwards - our team reporting the acceptance tests passing now, can you please confirm that you are unblocked when you get a chance?

@lstolyarov
Copy link
Contributor Author

Hi @echuvyrov, thanks a lot for having a look at this. The tests are now passing:

$ TF_ACC=1 envchain azurecloudops go test ./azurerm -v -timeout 300m -parallel 5 -run TestAccAzureRMAppServicePlan
=== RUN   TestAccAzureRMAppServicePlan_importBasic
--- PASS: TestAccAzureRMAppServicePlan_importBasic (63.80s)
=== RUN   TestAccAzureRMAppServicePlan_importStandard
--- PASS: TestAccAzureRMAppServicePlan_importStandard (77.62s)
=== RUN   TestAccAzureRMAppServicePlan_importPremium
--- PASS: TestAccAzureRMAppServicePlan_importPremium (74.71s)
=== RUN   TestAccAzureRMAppServicePlan_basic
--- PASS: TestAccAzureRMAppServicePlan_basic (57.62s)
=== RUN   TestAccAzureRMAppServicePlan_standard
--- PASS: TestAccAzureRMAppServicePlan_standard (63.03s)
=== RUN   TestAccAzureRMAppServicePlan_premium
--- PASS: TestAccAzureRMAppServicePlan_premium (66.64s)
PASS

@tombuildsstuff I have added a couple more tests and updated the documentation. Is there anything else that needs to be done before this can be merged?

Thanks,
Leo

Copy link
Contributor

@tombuildsstuff tombuildsstuff left a comment

Choose a reason for hiding this comment

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

Hey @lstolyarov

Thanks for the continued effort on this PR :)

I've taken a look through and left some comments, but they're mostly minor - if we can fix those up this otherwise looks good to merge :)

Thanks!

Set: resourceAzureRMAppServicePlanSkuHash,
},
"maximum_number_of_workers": {
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.

I think this should be an int rather than a String?


return res, string(res.AppServicePlanProperties.ProvisioningState), nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

minor could we move this into the resource_arm_app_service_plan.go file?

Timeout: 10 * time.Minute,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for App Service Plan (%s) to become available: %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.

minor can we make this second formatting argument %+v rather than %s to show the full error?

return func() (interface{}, string, error) {
res, err := client.appServicePlansClient.Get(resourceGroupName, appserviceplan)
if err != nil {
return nil, "", fmt.Errorf("Error issuing read request in appServicePlanStateRefreshFunc to Azure ARM for App Service Plan '%s' (RG: '%s'): %s", appserviceplan, resourceGroupName, err)
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 make the final formatting argument %+v here to return the full error?

"github.com/hashicorp/terraform/helper/resource"
)

func appServicePlanStateRefreshFunc(client *ArmClient, resourceGroupName string, appserviceplan string) resource.StateRefreshFunc {
Copy link
Contributor

Choose a reason for hiding this comment

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

really minor it'd be good to call appserviceplan -> name for consistency across these methods, if possible

size = "B1"
}
}
`, rInt, rInt)
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 add location as the second argument here?

@@ -42,6 +42,17 @@
</ul>
</li>

<li<%= sidebar_current("docs-azurerm-resource-app-service") %>>
<a href="#">App Service Resources</a>
Copy link
Contributor

Choose a reason for hiding this comment

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

🤔 do you think it's worth making this "App Service (Web Apps) Resources", like we've done for CosmosDB/DocumentDB?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Yes, I think it is worth doing that.

<ul class="nav nav-visible">

<li<%= sidebar_current("docs-azurerm-resource-app-service-plan") %>>
<a href="/docs/providers/azurerm/r/app_service_pla.html">azurerm_app_service_plan</a>
Copy link
Contributor

Choose a reason for hiding this comment

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

app_service_pla.html -> app_service_plan.html

* `tier` - (Required) Specified the plan's pricing tier.
* `size` - (Required) Specified the plan's instance size.

* `maximum_number_of_workers` - (Optional) .
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 add a description for this field, here's the description from the Azure docs: "Maximum number of instances that can be assigned to this App Service plan."


resource "azurerm_app_service_plan" "test" {
name = "acctestASP-%d"
location = "West Europe"
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 make this ${azurerm_resource_group.test.location} to only insert the location formatting argument a single time?

@georgeedwards
Copy link

@lstolyarov Are ther any plans to support adding env variables to the configuration?

tombuildsstuff pushed a commit that referenced this pull request Sep 14, 2017
tombuildsstuff pushed a commit that referenced this pull request Oct 12, 2017
tombuildsstuff pushed a commit that referenced this pull request Oct 12, 2017
tombuildsstuff pushed a commit that referenced this pull request Mar 27, 2018
katbyte pushed a commit that referenced this pull request Jun 14, 2018
katbyte pushed a commit that referenced this pull request Jun 28, 2018
katbyte pushed a commit that referenced this pull request Aug 14, 2018
katbyte pushed a commit that referenced this pull request Sep 19, 2018
katbyte pushed a commit that referenced this pull request Oct 26, 2018
tombuildsstuff pushed a commit that referenced this pull request Jan 3, 2019
add _id to fields for clarity
tombuildsstuff pushed a commit that referenced this pull request Feb 14, 2019
Sync master from Terraform head
tombuildsstuff pushed a commit that referenced this pull request May 7, 2019
tombuildsstuff pushed a commit that referenced this pull request Sep 3, 2019
tombuildsstuff pushed a commit that referenced this pull request Sep 23, 2019
tombuildsstuff pushed a commit that referenced this pull request Oct 25, 2019
tombuildsstuff pushed a commit that referenced this pull request Feb 18, 2020
tombuildsstuff pushed a commit that referenced this pull request Feb 25, 2020
Add network interface and security group association
@ghost
Copy link

ghost commented Apr 1, 2020

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. If you feel I made an error 🤖 🙉 , please reach out to my human friends 👉 hashibot-feedback@hashicorp.com. Thanks!

@ghost ghost locked and limited conversation to collaborators Apr 1, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants