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

New data source 'azurerm_resources' #3529

Merged
merged 11 commits into from
Oct 14, 2019
177 changes: 177 additions & 0 deletions azurerm/data_source_resources.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package azurerm

import (
"fmt"
"log"
"strings"

"github.com/google/uuid"
"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/tags"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
)

func dataSourceArmResources() *schema.Resource {
return &schema.Resource{
Read: dataSourceArmResourcesRead,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"resource_group_name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"required_tags": tags.Schema(),

"resources": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
},
"id": {
Type: schema.TypeString,
Computed: true,
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"location": azure.SchemaLocationForDataSource(),
"tags": tags.SchemaDataSource(),
},
},
},
},
}
}

func dataSourceArmResourcesRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).Resource.ResourcesClient
ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d)
defer cancel()

resourceGroupName := d.Get("resource_group_name").(string)
resourceName := d.Get("name").(string)
resourceType := d.Get("type").(string)
requiredTags := d.Get("required_tags").(map[string]interface{})

if resourceGroupName == "" && resourceName == "" && resourceType == "" {
return fmt.Errorf("At least one of `name`, `resource_group_name` or `type` must be specified")
}

var filter string

if resourceGroupName != "" {
v := fmt.Sprintf("resourceGroup eq '%s'", resourceGroupName)
filter = filter + v
}

if resourceName != "" {
if strings.Contains(filter, "eq") {
filter = filter + " and "
}
v := fmt.Sprintf("name eq '%s'", resourceName)
filter = filter + v
}

if resourceType != "" {
if strings.Contains(filter, "eq") {
filter = filter + " and "
}
v := fmt.Sprintf("resourceType eq '%s'", resourceType)
filter = filter + v
}

resources := make([]map[string]interface{}, 0)
resource, err := client.ListComplete(ctx, filter, "", nil)
if err != nil {
return fmt.Errorf("Error getting resources: %+v", err)
}

for resource.NotDone() {
res := resource.Value()
if res.ID == nil {
continue
}

// currently its not supported to use tags filter with other filters
// therefore we need to filter the resources manually.
tagMatches := 0
if res.Tags != nil {
for requiredTagName, requiredTagVal := range requiredTags {
for tagName, tagVal := range res.Tags {
if requiredTagName == tagName && requiredTagVal == *tagVal {
tiwood marked this conversation as resolved.
Show resolved Hide resolved
tagMatches++
}
}
}
}

if tagMatches == len(requiredTags) {
resName := ""
if res.Name != nil {
resName = *res.Name
}

resID := ""
if res.ID != nil {
resID = *res.ID
}

resType := ""
if res.Type != nil {
resType = *res.Type
}

resLocation := ""
if res.Location != nil {
resLocation = *res.Location
}

resTags := make(map[string]interface{})
if res.Tags != nil {
resTags = make(map[string]interface{}, len(res.Tags))
for key, value := range res.Tags {
resTags[key] = *value
tiwood marked this conversation as resolved.
Show resolved Hide resolved
}
}

resources = append(resources, map[string]interface{}{
"name": resName,
"id": resID,
"type": resType,
"location": resLocation,
"tags": resTags,
})
} else {
log.Printf("[DEBUG] azurerm_resources - resources %q (id: %q) skipped as a required tag is not set or has the wrong value.", *res.Name, *res.ID)
}

err = resource.NextWithContext(ctx)
if err != nil {
return fmt.Errorf("Error loading Resource List: %s", err)
}
}

d.SetId("resource-" + uuid.New().String())
if err := d.Set("resources", resources); err != nil {
return fmt.Errorf("Error setting `resources`: %+v", err)
}

return nil
}
144 changes: 144 additions & 0 deletions azurerm/data_source_resources_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package azurerm

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
)

func TestAccDataSourceAzureRMResources_ByName(t *testing.T) {
dataSourceName := "data.azurerm_resources.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(4)
location := testLocation()
config := testAccDataSourceAzureRMResources_ByName(ri, rs, location)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "resources.#", "1"),
),
},
},
})
}

func TestAccDataSourceAzureRMResources_ByResourceGroup(t *testing.T) {
dataSourceName := "data.azurerm_resources.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(4)
location := testLocation()
config := testAccDataSourceAzureRMResources_ByResourceGroup(ri, rs, location)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "resources.#", "1"),
),
},
},
})
}

func TestAccDataSourceAzureRMResources_ByResourceType(t *testing.T) {
dataSourceName := "data.azurerm_resources.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(4)
location := testLocation()
config := testAccDataSourceAzureRMResources_ByResourceType(ri, rs, location)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "resources.#", "1"),
),
},
},
})
}

func TestAccDataSourceAzureRMResources_FilteredByTags(t *testing.T) {
dataSourceName := "data.azurerm_resources.test"
ri := tf.AccRandTimeInt()
rs := acctest.RandString(4)
location := testLocation()
config := testAccDataSourceAzureRMResources_FilteredByTags(ri, rs, location)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: config,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(dataSourceName, "resources.#", "1"),
),
},
},
})
}

func testAccDataSourceAzureRMResources_ByName(rInt int, rString string, location string) string {
r := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location)
return fmt.Sprintf(`
%s

data "azurerm_resources" "test" {
name = "${azurerm_storage_account.test.name}"
}
`, r)
}

func testAccDataSourceAzureRMResources_ByResourceGroup(rInt int, rString string, location string) string {
r := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location)
return fmt.Sprintf(`
%s

data "azurerm_resources" "test" {
resource_group_name = "${azurerm_storage_account.test.resource_group_name}"
}
`, r)
}

func testAccDataSourceAzureRMResources_ByResourceType(rInt int, rString string, location string) string {
r := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location)
return fmt.Sprintf(`
%s

data "azurerm_resources" "test" {
resource_group_name = "${azurerm_storage_account.test.resource_group_name}"
type = "Microsoft.Storage/storageAccounts"
}
`, r)
}

func testAccDataSourceAzureRMResources_FilteredByTags(rInt int, rString string, location string) string {
r := testAccDataSourceAzureRMStorageAccount_basic(rInt, rString, location)
return fmt.Sprintf(`
%s

data "azurerm_resources" "test" {
name = "${azurerm_storage_account.test.name}"
resource_group_name = "${azurerm_storage_account.test.resource_group_name}"

required_tags = {
environment = "production"
}
}
`, r)
}
5 changes: 5 additions & 0 deletions azurerm/internal/services/resource/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type Client struct {
DeploymentsClient *resources.DeploymentsClient
LocksClient *locks.ManagementLocksClient
ProvidersClient *providers.ProvidersClient
ResourcesClient *resources.Client
}

func BuildClient(o *common.ClientOptions) *Client {
Expand All @@ -28,10 +29,14 @@ func BuildClient(o *common.ClientOptions) *Client {
ProvidersClient := providers.NewProvidersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&ProvidersClient.Client, o.ResourceManagerAuthorizer)

ResourcesClient := resources.NewClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&ResourcesClient.Client, o.ResourceManagerAuthorizer)

return &Client{
GroupsClient: &GroupsClient,
DeploymentsClient: &DeploymentsClient,
LocksClient: &LocksClient,
ProvidersClient: &ProvidersClient,
ResourcesClient: &ResourcesClient,
}
}
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_recovery_services_vault": dataSourceArmRecoveryServicesVault(),
"azurerm_recovery_services_protection_policy_vm": dataSourceArmRecoveryServicesProtectionPolicyVm(),
"azurerm_redis_cache": dataSourceArmRedisCache(),
"azurerm_resources": dataSourceArmResources(),
"azurerm_resource_group": dataSourceArmResourceGroup(),
"azurerm_role_definition": dataSourceArmRoleDefinition(),
"azurerm_route_table": dataSourceArmRouteTable(),
Expand Down
4 changes: 4 additions & 0 deletions website/azurerm.erb
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,10 @@
<a href="/docs/providers/azurerm/d/recovery_services_protection_policy_vm.html">azurerm_recovery_services_protection_policy_vm</a>
</li>

<li>
<a href="/docs/providers/azurerm/d/resources.html">azurerm_resources</a>
</li>

<li>
<a href="/docs/providers/azurerm/d/resource_group.html">azurerm_resource_group</a>
</li>
Expand Down
Loading