diff --git a/azurerm/data_source_function.go b/azurerm/data_source_function.go new file mode 100644 index 000000000000..e693d963bef7 --- /dev/null +++ b/azurerm/data_source_function.go @@ -0,0 +1,108 @@ +package azurerm + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/web" + + webmgmt "github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web" +) + +func dataSourceArmFunction() *schema.Resource { + return &schema.Resource{ + Read: dataSourceArmFunctionRead, + + Schema: map[string]*schema.Schema{ + "function_app_name": { + Type: schema.TypeString, + Required: true, + }, + + "resource_group_name": azure.SchemaResourceGroupNameForDataSource(), + + "function_name": { + Type: schema.TypeString, + Required: true, + }, + + "key": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, + }, + "trigger_url": { + Type: schema.TypeString, + Computed: true, + Sensitive: true, // Note the trigger url contains the key + }, + }, + } +} + +func dataSourceArmFunctionRead(d *schema.ResourceData, meta interface{}) error { + webClient := meta.(*ArmClient).Web + ctx := meta.(*ArmClient).StopContext + + name := d.Get("function_app_name").(string) + functionName := d.Get("function_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + log.Printf("[DEBUG] Waiting for Function %q in Function app %q (Resource Group %q) to become available", functionName, name, resourceGroup) + stateConf := &resource.StateChangeConf{ + Pending: []string{"pending"}, + Target: []string{"available"}, + Refresh: functionAvailabilityChecker(ctx, name, functionName, resourceGroup, webClient), + Timeout: 10 * time.Minute, + Delay: 30 * time.Second, + PollInterval: 10 * time.Second, + } + + resp, err := stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for Function %q in Function app %q (Resource Group %q) to become available", functionName, name, resourceGroup) + } + + functionSecret, cast := resp.(webmgmt.FunctionSecrets) + if !cast { + return fmt.Errorf("Error retrieving key for Function %q in Function app %q (Resource Group %q). functionSecret returned nil from API", functionName, name, resourceGroup) + } + + if functionSecret.TriggerURL == nil { + return fmt.Errorf("Error retrieving key for Function %q in Function app %q (Resource Group %q). TriggerURL returned nil from API", functionName, name, resourceGroup) + } + if functionSecret.Key == nil { + return fmt.Errorf("Error retrieving key for Function %q in Function app %q (Resource Group %q). Key returned nil from API", functionName, name, resourceGroup) + } + + triggerUrlHash := sha256.Sum256([]byte(*functionSecret.TriggerURL)) + d.SetId(hex.EncodeToString(triggerUrlHash[:])) + + d.Set("trigger_url", functionSecret.TriggerURL) + d.Set("key", functionSecret.Key) + + return nil +} + +func functionAvailabilityChecker(ctx context.Context, name, functionName, resourceGroup string, client *web.Client) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking to see if Function %q is available..", functionName) + + function, err := client.AppServicesClient.ListFunctionSecrets(ctx, resourceGroup, name, functionName) + + if err != nil || function.StatusCode != 200 { + log.Printf("[DEBUG] Didn't find Function at %q", name) + return nil, "pending", err + } + + log.Printf("[DEBUG] Found function at %q", functionName) + return function, "available", nil + } +} diff --git a/azurerm/data_source_function_test.go b/azurerm/data_source_function_test.go new file mode 100644 index 000000000000..72b1d726036b --- /dev/null +++ b/azurerm/data_source_function_test.go @@ -0,0 +1,119 @@ +package azurerm + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccDataSourceAzureRMFunction_basic(t *testing.T) { + dataSourceName := "data.azurerm_function.test" + rInt := tf.AccRandTimeInt() + location := testLocation() + rs := strings.ToLower(acctest.RandString(11)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceFunction_basic(rInt, location, rs), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "trigger_url"), + resource.TestCheckResourceAttrSet(dataSourceName, "key"), + ), + }, + }, + }) +} + +func TestAccDataSourceAzureRMFunction_wait(t *testing.T) { + dataSourceName := "data.azurerm_function.test" + rInt := tf.AccRandTimeInt() + location := testLocation() + rs := strings.ToLower(acctest.RandString(11)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMFirewallDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceFunction_basic(rInt, location, rs), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(dataSourceName, "trigger_url"), + resource.TestCheckResourceAttrSet(dataSourceName, "key"), + ), + }, + }, + }) +} + +func testAccDataSourceFunction_basic(rInt int, location, storage string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestsa%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_storage_container" "test" { + name = "function-releases" + + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "container" +} + +resource "azurerm_storage_blob" "javazip" { + name = "testfunc.zip" + + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + + type = "block" + source = "testdata/testfunc.zip" +} + +resource "azurerm_function_app" "test" { + name = "acctest-%[1]d-func" + 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_connection_string = "${azurerm_storage_account.test.primary_connection_string}" + + app_settings = { + WEBSITE_RUN_FROM_PACKAGE = "https://${azurerm_storage_account.test.name}.blob.core.windows.net/${azurerm_storage_container.test.name}/testfunc.zip" + FUNCTIONS_WORKER_RUNTIME = "node" + } +} + +data "azurerm_function" "test" { + function_app_name = "${azurerm_function_app.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + function_name = "testfunc" +} +`, rInt, location, storage) +} diff --git a/azurerm/provider.go b/azurerm/provider.go index 80d94676fad4..400528bfb141 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -88,6 +88,7 @@ func Provider() terraform.ResourceProvider { "azurerm_eventhub_namespace": dataSourceEventHubNamespace(), "azurerm_express_route_circuit": dataSourceArmExpressRouteCircuit(), "azurerm_firewall": dataSourceArmFirewall(), + "azurerm_function": dataSourceArmFunction(), "azurerm_image": dataSourceArmImage(), "azurerm_hdinsight_cluster": dataSourceArmHDInsightSparkCluster(), "azurerm_healthcare_service": dataSourceArmHealthcareService(), diff --git a/azurerm/testdata/testfunc.zip b/azurerm/testdata/testfunc.zip new file mode 100644 index 000000000000..61a29006da18 Binary files /dev/null and b/azurerm/testdata/testfunc.zip differ diff --git a/go.sum b/go.sum index ed0914d943ea..b6bcb0c0345a 100644 --- a/go.sum +++ b/go.sum @@ -10,15 +10,6 @@ cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbf cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= contrib.go.opencensus.io/exporter/ocagent v0.5.0 h1:TKXjQSRS0/cCDrP7KvkgU6SmILtF/yV2TOs/02K/WZQ= contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= -github.com/Azure/azure-sdk-for-go v21.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v32.5.0+incompatible h1:Hn/DsObfmw0M7dMGS/c0MlVrJuGFzHzOpBWL89acR68= -github.com/Azure/azure-sdk-for-go v32.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v34.1.0+incompatible h1:uW/dgSzmRQEPXwaRUN8WzBHJy5J2cp8cw1ea908uFj0= -github.com/Azure/azure-sdk-for-go v34.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v35.0.0+incompatible h1:PkmdmQUmeSdQQ5258f4SyCf2Zcz0w67qztEg37cOR7U= -github.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v36.2.0+incompatible h1:09cv2WoH0g6jl6m2iT+R9qcIPZKhXEL0sbmLhxP895s= -github.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest v10.15.4+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v13.0.0+incompatible h1:56c11ykhsFSPNNQuS73Ri8h/ezqVhr2h6t9LJIEKVO0= github.com/Azure/go-autorest v13.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= @@ -194,8 +185,6 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/aws-sdk-go-base v0.3.0/go.mod h1:ZIWACGGi0N7a4DZbf15yuE1JQORmWLtBcVM6F5SXNFU= -github.com/hashicorp/azure-sdk-for-go v36.2.1-hashi+incompatible h1:JuiGr8Ys+Gtp/O0EFi37se0Z5rY7PIqWNnUwjASWVEM= -github.com/hashicorp/azure-sdk-for-go v36.2.1-hashi+incompatible/go.mod h1:9MzMWzN0gJiWgGzEfgaJaj/sHWDDDwU0LUYAnE+GbHQ= github.com/hashicorp/azure-sdk-for-go v36.2.2-hashi+incompatible h1:iWg/0ZhDQ4lmvTyGarUXqkcgVbtSXb+Bsqa5vuHD08E= github.com/hashicorp/azure-sdk-for-go v36.2.2-hashi+incompatible/go.mod h1:9MzMWzN0gJiWgGzEfgaJaj/sHWDDDwU0LUYAnE+GbHQ= github.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI= @@ -207,8 +196,6 @@ github.com/hashicorp/go-azure-helpers v0.4.1 h1:aEWYW4hxAVVmxmq7nPXGK8F44A6HBXQ4 github.com/hashicorp/go-azure-helpers v0.4.1/go.mod h1:lu62V//auUow6k0IykxLK2DCNW8qTmpm8KqhYVWattA= github.com/hashicorp/go-azure-helpers v0.7.0 h1:wxGpOyWYp15bjBMeL3pXKP5X3oFLZbThAMcJcU6x4FA= github.com/hashicorp/go-azure-helpers v0.7.0/go.mod h1:3xdjhbL7qs69rnwxA0UENOzkPJjtTFIRb5aRyrEpbCU= -github.com/hashicorp/go-azure-helpers v0.9.0 h1:KERW4n9AukvQ6kXGJdqXLaR0S2yxH3Xwj+rio/3/uLI= -github.com/hashicorp/go-azure-helpers v0.9.0/go.mod h1:3xdjhbL7qs69rnwxA0UENOzkPJjtTFIRb5aRyrEpbCU= github.com/hashicorp/go-azure-helpers v0.10.0 h1:KhjDnQhCqEMKlt4yH00MCevJQPJ6LkHFdSveXINO6vE= github.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= diff --git a/vendor/modules.txt b/vendor/modules.txt index bec16dcf70cf..40dd6383d3c3 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -6,7 +6,7 @@ cloud.google.com/go/internal/optional cloud.google.com/go/internal/trace cloud.google.com/go/internal/version cloud.google.com/go/storage -# github.com/Azure/azure-sdk-for-go v36.2.0+incompatible => github.com/hashicorp/azure-sdk-for-go v36.2.2-hashi+incompatible +github.com/Azure/azure-sdk-for-go v36.2.0+incompatible => github.com/hashicorp/azure-sdk-for-go v36.2.2-hashi+incompatible github.com/Azure/azure-sdk-for-go/profiles/2017-03-09/resources/mgmt/resources github.com/Azure/azure-sdk-for-go/services/analysisservices/mgmt/2017-08-01/analysisservices github.com/Azure/azure-sdk-for-go/services/apimanagement/mgmt/2018-01-01/apimanagement diff --git a/website/docs/d/function.html.markdown b/website/docs/d/function.html.markdown new file mode 100644 index 000000000000..5eb870003543 --- /dev/null +++ b/website/docs/d/function.html.markdown @@ -0,0 +1,36 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_function" +sidebar_current: "docs-azurerm-datasource-function" +description: |- + Gets information about an Azure Function hosted in an FunctionApp. +--- + +# Data Source: azurerm_function + +Use this data source to access information about an Azure Function hosted in a Function App. + +## Example Usage + +```hcl +data "azurerm_functions_key" "test" { + function_app_name = "function_app_name_here" + resource_group_name = "resource_group_here" + function_name = "name_of_function_you_want_key_for_here" +} + +output "key" { + value = "${data.azurerm_function.test.key}" +} +``` + +## Argument Reference + +* `function_app_name` - (Required) Specifies the name of the Function App in which the Function is hosted. +* `function_name` - (Required) Specifies the name of the Function. +* `resource_group_name` - (Required) Specifies the name of the resource group the Function App is located. + +## Attributes Reference + +* `key` - The Function Key +* `trigger_url` - The full URL with Function Key used to trigger the function