From 8de09eaf2798969132cbfa26fd3b84aebc84d23d Mon Sep 17 00:00:00 2001 From: Sayan Saha Date: Tue, 13 Apr 2021 20:45:56 +0530 Subject: [PATCH 1/4] Add table azure_sql_database. closes #89 --- azure/plugin.go | 1 + azure/table_azure_sql_database.go | 371 ++++++++++++++++++++++++++++++ go.sum | 1 + 3 files changed, 373 insertions(+) create mode 100644 azure/table_azure_sql_database.go diff --git a/azure/plugin.go b/azure/plugin.go index e4f2fdbe..e84471eb 100644 --- a/azure/plugin.go +++ b/azure/plugin.go @@ -57,6 +57,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azure_role_definition": tableAzureIamRoleDefinition(ctx), "azure_route_table": tableAzureRouteTable(ctx), "azure_sql_server": tableAzureSQLServer(ctx), + "azure_sql_database": tableAzureSqlDatabase(ctx), "azure_storage_account": tableAzureStorageAccount(ctx), "azure_storage_blob_service": tableAzureStorageBlobService(ctx), "azure_storage_container": tableAzureStorageContainer(ctx), diff --git a/azure/table_azure_sql_database.go b/azure/table_azure_sql_database.go new file mode 100644 index 00000000..80386d50 --- /dev/null +++ b/azure/table_azure_sql_database.go @@ -0,0 +1,371 @@ +package azure + +import ( + "context" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/2017-03-01-preview/sql" + "github.com/turbot/steampipe-plugin-sdk/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/plugin/transform" + + "github.com/turbot/steampipe-plugin-sdk/plugin" +) + +//// TABLE DEFINITION //// + +func tableAzureSqlDatabase(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "azure_sql_database", + Description: "Azure SQL Database", + Get: &plugin.GetConfig{ + KeyColumns: plugin.AllColumns([]string{"name", "server_name", "resource_group"}), + Hydrate: getSqlDatabase, + }, + List: &plugin.ListConfig{ + ParentHydrate: listSQLServer, + Hydrate: listSqlDatabases, + }, + Columns: []*plugin.Column{ + { + Name: "name", + Type: proto.ColumnType_STRING, + Description: "The friendly name that identifies the database.", + }, + { + Name: "id", + Description: "Contains ID to identify a database uniquely.", + Type: proto.ColumnType_STRING, + Transform: transform.FromGo(), + }, + { + Name: "server_name", + Description: "The name of the parent server of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.From(idToServerName), + }, + { + Name: "status", + Description: "The status of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.Status"), + }, + { + Name: "type", + Description: "Type of the database.", + Type: proto.ColumnType_STRING, + }, + { + Name: "collation", + Description: "The collation of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.Collation"), + }, + { + Name: "containment_state", + Description: "The containment state of the database.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("DatabaseProperties.ContainmentState"), + }, + { + Name: "creation_date", + Description: "The creation date of the database.", + Type: proto.ColumnType_TIMESTAMP, + Transform: transform.FromField("DatabaseProperties.CreationDate").Transform(convertDateToTime), + }, + { + Name: "current_service_objective_id", + Description: "The current service level objective ID of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.CurrentServiceObjectiveID"), + }, + { + Name: "database_id", + Description: "The ID of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.DatabaseID"), + }, + { + Name: "default_secondary_location", + Description: "The default secondary region for this database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.DefaultSecondaryLocation"), + }, + { + Name: "earliest_restore_date", + Description: "This records the earliest start date and time that restore is available for this database.", + Type: proto.ColumnType_TIMESTAMP, + Transform: transform.FromField("DatabaseProperties.EarliestRestoreDate").Transform(convertDateToTime), + }, + { + Name: "elastic_pool_name", + Description: "The name of the elastic pool the database is in.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.ElasticPoolName"), + }, + { + Name: "failover_group_id", + Description: "The resource identifier of the failover group containing this database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.FailoverGroupID"), + }, + { + Name: "kind", + Description: "Kind of the database.", + Type: proto.ColumnType_STRING, + }, + { + Name: "location", + Description: "Location of the database.", + Type: proto.ColumnType_STRING, + }, + { + Name: "max_size_bytes", + Description: "The max size of the database expressed in bytes.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.MaxSizeBytes"), + }, + { + Name: "recovery_services_recovery_point_resource_id", + Description: "Specifies the resource ID of the recovery point to restore from if createMode is RestoreLongTermRetentionBackup.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.RecoveryServicesRecoveryPointResourceID"), + }, + { + Name: "requested_service_objective_id", + Description: "The configured service level objective ID of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.RequestedServiceObjectiveID"), + }, + { + Name: "restore_point_in_time", + Description: "Specifies the point in time of the source database that will be restored to create the new database.", + Type: proto.ColumnType_TIMESTAMP, + Transform: transform.FromField("DatabaseProperties.RestorePointInTime").Transform(convertDateToTime), + }, + { + Name: "source_database_deletion_date", + Description: "Specifies the time that the database was deleted when createMode is Restore and sourceDatabaseId is the deleted database's original resource id.", + Type: proto.ColumnType_TIMESTAMP, + Transform: transform.FromField("DatabaseProperties.SourceDatabaseDeletionDate").Transform(convertDateToTime), + }, + { + Name: "source_database_id", + Description: "Specifies the resource ID of the source database if createMode is Copy, NonReadableSecondary, OnlineSecondary, PointInTimeRestore, Recovery, or Restore.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.SourceDatabaseID"), + }, + { + Name: "zone_redundant", + Description: "Indicates if the database is zone redundant or not.", + Type: proto.ColumnType_BOOL, + Transform: transform.FromField("DatabaseProperties.ZoneRedundant"), + }, + { + Name: "create_mode", + Description: "Specifies the mode of database creation.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.CreateMode"), + }, + { + Name: "edition", + Description: "The edition of the database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.Edition"), + }, + { + Name: "read_scale", + Description: "ReadScale indicates whether read-only connections are allowed to this database or not if the database is a geo-secondary.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.ReadScale"), + }, + { + Name: "recommended_index", + Description: "The recommended indices for this database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.RecommendedIndex"), + }, + { + Name: "requested_service_objective_name", + Description: "The name of the configured service level objective of the database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.RequestedServiceObjectiveName"), + }, + { + Name: "sample_name", + Description: "Indicates the name of the sample schema to apply when creating this database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.SampleName"), + }, + { + Name: "service_level_objective", + Description: "The current service level objective of the database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.ServiceLevelObjective"), + }, + { + Name: "service_tier_advisors", + Description: "The list of service tier advisors for this database.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("DatabaseProperties.ServiceTierAdvisors"), + }, + { + Name: "transparent_data_encryption", + Description: "The transparent data encryption info for this database.", + Type: proto.ColumnType_JSON, + Hydrate: getSqlDatabaseTransparentDataEncryption, + Transform: transform.FromField("TransparentDataEncryptionProperties"), + }, + + // Standard columns + { + Name: "title", + Description: ColumnDescriptionTitle, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, + { + Name: "tags", + Description: ColumnDescriptionTags, + Type: proto.ColumnType_JSON, + Hydrate: getSqlDatabase, + }, + { + Name: "akas", + Description: ColumnDescriptionAkas, + Type: proto.ColumnType_JSON, + Transform: transform.FromField("ID").Transform(idToAkas), + }, + { + Name: "region", + Description: ColumnDescriptionRegion, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Location").Transform(toLower), + }, + { + Name: "resource_group", + Description: ColumnDescriptionResourceGroup, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ID").Transform(extractResourceGroupFromID), + }, + { + Name: "subscription_id", + Description: ColumnDescriptionSubscription, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ID").Transform(idToSubscriptionID), + }, + }, + } +} + +//// FETCH FUNCTIONS //// + +func listSqlDatabases(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + + client := sql.NewDatabasesClient(subscriptionID) + client.Authorizer = session.Authorizer + + server := h.Item.(sql.Server) + resourceGroupName := strings.Split(string(*server.ID), "/")[4] + + result, err := client.ListByServer(ctx, resourceGroupName, *server.Name, "", "") + if err != nil { + return nil, err + } + + for _, database := range *result.Value { + d.StreamLeafListItem(ctx, database) + } + + return nil, err +} + +//// HYDRATE FUNCTIONS //// + +func getSqlDatabase(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getSqlDatabase") + + var serverName, databaseName, resourceGroupName string + if h.Item != nil { + database := h.Item.(sql.Database) + serverName = strings.Split(*database.ID, "/")[8] + databaseName = *database.Name + resourceGroupName = strings.Split(string(*database.ID), "/")[4] + } else { + serverName = d.KeyColumnQuals["server_name"].GetStringValue() + databaseName = d.KeyColumnQuals["name"].GetStringValue() + resourceGroupName = d.KeyColumnQuals["resource_group"].GetStringValue() + } + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + + client := sql.NewDatabasesClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.Get(ctx, resourceGroupName, serverName, databaseName, "") + if err != nil { + return nil, err + } + + // In some cases resource does not give any notFound error + // instead of notFound error, it returns empty data + if op.ID != nil { + return op, nil + } + + return nil, nil +} + +func getSqlDatabaseTransparentDataEncryption(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getSqlDatabaseTransparentDataEncryption") + + var serverName, databaseName, resourceGroupName string + if h.Item != nil { + database := h.Item.(sql.Database) + serverName = strings.Split(*database.ID, "/")[8] + databaseName = *database.Name + resourceGroupName = strings.Split(string(*database.ID), "/")[4] + } else { + serverName = d.KeyColumnQuals["server_name"].GetStringValue() + databaseName = d.KeyColumnQuals["name"].GetStringValue() + resourceGroupName = d.KeyColumnQuals["resource_group"].GetStringValue() + } + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + + client := sql.NewTransparentDataEncryptionsClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.Get(ctx, resourceGroupName, serverName, databaseName) + if err != nil { + return nil, err + } + + // In some cases resource does not give any notFound error + // instead of notFound error, it returns empty data + if op.ID != nil { + return op, nil + } + + return nil, nil +} + +//// Transform Functions + +func idToServerName(ctx context.Context, d *transform.TransformData) (interface{}, error) { + data := d.HydrateItem.(sql.Database) + serverName := strings.Split(string(*data.ID), "/")[8] + return serverName, nil +} diff --git a/go.sum b/go.sum index 0fbaf60d..05ccf824 100644 --- a/go.sum +++ b/go.sum @@ -5,6 +5,7 @@ github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo github.com/Azure/azure-sdk-for-go v47.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v50.1.0+incompatible h1:SUR6Y194mjyNkNbEzDHyYX8Butfa+Om9fcGSIy0ffhk= github.com/Azure/azure-sdk-for-go v50.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v53.1.0+incompatible h1:f2h0KLVGa3zIaMDMHBe5Lazc0FT5+L78z0B8K9PmDyg= github.com/Azure/azure-storage-blob-go v0.12.0 h1:7bFXA1QB+lOK2/ASWHhp6/vnxjaeeZq6t8w1Jyp0Iaw= github.com/Azure/azure-storage-blob-go v0.12.0/go.mod h1:A0u4VjtpgZJ7Y7um/+ix2DHBuEKFC6sEIlj0xc13a4Q= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= From 6cf01f3f89e03b1e2a5d71b95fddfa0c3aa67bc8 Mon Sep 17 00:00:00 2001 From: Sayan Saha Date: Thu, 15 Apr 2021 19:31:19 +0530 Subject: [PATCH 2/4] Added docs & integration test --- .../tests/azure_sql_database/dependencies.txt | 0 .../azure_sql_database/test-get-expected.json | 33 ++++++ .../azure_sql_database/test-get-query.sql | 6 ++ .../test-hydrate-expected.json | 10 ++ .../azure_sql_database/test-hydrate-query.sql | 3 + .../test-list-expected.json | 9 ++ .../azure_sql_database/test-list-query.sql | 3 + .../test-not-found-expected.json | 1 + .../test-not-found-query.sql | 3 + .../test-turbot-expected.json | 13 +++ .../azure_sql_database/test-turbot-query.sql | 3 + .../tests/azure_sql_database/variables.json | 1 + .../tests/azure_sql_database/variables.tf | 99 +++++++++++++++++ azure/table_azure_sql_database.go | 20 ++-- docs/tables/azure_sql_database.md | 100 ++++++++++++++++++ 15 files changed, 294 insertions(+), 10 deletions(-) create mode 100644 azure-test/tests/azure_sql_database/dependencies.txt create mode 100644 azure-test/tests/azure_sql_database/test-get-expected.json create mode 100644 azure-test/tests/azure_sql_database/test-get-query.sql create mode 100644 azure-test/tests/azure_sql_database/test-hydrate-expected.json create mode 100644 azure-test/tests/azure_sql_database/test-hydrate-query.sql create mode 100644 azure-test/tests/azure_sql_database/test-list-expected.json create mode 100644 azure-test/tests/azure_sql_database/test-list-query.sql create mode 100644 azure-test/tests/azure_sql_database/test-not-found-expected.json create mode 100644 azure-test/tests/azure_sql_database/test-not-found-query.sql create mode 100644 azure-test/tests/azure_sql_database/test-turbot-expected.json create mode 100644 azure-test/tests/azure_sql_database/test-turbot-query.sql create mode 100644 azure-test/tests/azure_sql_database/variables.json create mode 100644 azure-test/tests/azure_sql_database/variables.tf create mode 100644 docs/tables/azure_sql_database.md diff --git a/azure-test/tests/azure_sql_database/dependencies.txt b/azure-test/tests/azure_sql_database/dependencies.txt new file mode 100644 index 00000000..e69de29b diff --git a/azure-test/tests/azure_sql_database/test-get-expected.json b/azure-test/tests/azure_sql_database/test-get-expected.json new file mode 100644 index 00000000..d2dc0288 --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-get-expected.json @@ -0,0 +1,33 @@ +[ + { + "akas": [ + "{{ output.resource_aka.value }}", + "{{ output.resource_aka_lower.value }}" + ], + "containment_state": 2, + "default_secondary_location": "{{ output.resource_default_secondary_location.value }}", + "earliest_restore_date": null, + "edition": "GeneralPurpose", + "elastic_pool_name": null, + "id": "{{ output.resource_id.value }}", + "location": "{{ output.resource_location.value }}", + "max_size_bytes": "34359738368", + "name": "{{ resourceName }}", + "region": "{{ output.resource_region.value }}", + "requested_service_objective_name": "GP_Gen5_2", + "resource_group": "{{ resourceName }}", + "server_name": "{{ resourceName }}", + "service_level_objective": "GP_Gen5_2", + "status": "Online", + "subscription_id": "{{ output.subscription_id.value }}", + "tags": { + "foo": "bar" + }, + "title": "{{ resourceName }}", + "transparent_data_encryption": { + "status": "Enabled" + }, + "type": "Microsoft.Sql/servers/databases", + "zone_redundant": false + } +] diff --git a/azure-test/tests/azure_sql_database/test-get-query.sql b/azure-test/tests/azure_sql_database/test-get-query.sql new file mode 100644 index 00000000..8fec39ee --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-get-query.sql @@ -0,0 +1,6 @@ +select name, id, server_name, status, type, containment_state, default_secondary_location, earliest_restore_date, edition, elastic_pool_name, location,max_size_bytes, zone_redundant, requested_service_objective_name, service_level_objective,transparent_data_encryption, title, tags, akas, region, resource_group, subscription_id +from + azure.azure_sql_database +where + name = '{{ resourceName }}' + and resource_group = '{{ resourceName }}'; \ No newline at end of file diff --git a/azure-test/tests/azure_sql_database/test-hydrate-expected.json b/azure-test/tests/azure_sql_database/test-hydrate-expected.json new file mode 100644 index 00000000..fdb8088f --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-hydrate-expected.json @@ -0,0 +1,10 @@ +[ + { + "id": "{{ output.resource_id.value }}", + "name": "{{ resourceName }}", + "server_name": "{{ resourceName }}", + "transparent_data_encryption": { + "status": "Enabled" + } + } +] diff --git a/azure-test/tests/azure_sql_database/test-hydrate-query.sql b/azure-test/tests/azure_sql_database/test-hydrate-query.sql new file mode 100644 index 00000000..cc3111de --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-hydrate-query.sql @@ -0,0 +1,3 @@ +select name, id, server_name, transparent_data_encryption +from azure.azure_sql_database +where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}'; \ No newline at end of file diff --git a/azure-test/tests/azure_sql_database/test-list-expected.json b/azure-test/tests/azure_sql_database/test-list-expected.json new file mode 100644 index 00000000..9095c0fd --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-list-expected.json @@ -0,0 +1,9 @@ +[ + { + "id": "{{ output.resource_id.value }}", + "location": "{{ output.resource_location.value }}", + "name": "{{ resourceName }}", + "resource_group": "{{ resourceName }}", + "server_name": "{{ resourceName }}" + } +] diff --git a/azure-test/tests/azure_sql_database/test-list-query.sql b/azure-test/tests/azure_sql_database/test-list-query.sql new file mode 100644 index 00000000..0821d3ce --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-list-query.sql @@ -0,0 +1,3 @@ +select id, name, location, server_name, resource_group +from azure.azure_sql_database +where akas::text = '["{{output.resource_aka.value}}", "{{output.resource_aka_lower.value}}"]'; \ No newline at end of file diff --git a/azure-test/tests/azure_sql_database/test-not-found-expected.json b/azure-test/tests/azure_sql_database/test-not-found-expected.json new file mode 100644 index 00000000..19765bd5 --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-not-found-expected.json @@ -0,0 +1 @@ +null diff --git a/azure-test/tests/azure_sql_database/test-not-found-query.sql b/azure-test/tests/azure_sql_database/test-not-found-query.sql new file mode 100644 index 00000000..72677817 --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-not-found-query.sql @@ -0,0 +1,3 @@ +select * +from azure.azure_sql_database +where name = 'test-0000' and resource_group = 'test-0000'; \ No newline at end of file diff --git a/azure-test/tests/azure_sql_database/test-turbot-expected.json b/azure-test/tests/azure_sql_database/test-turbot-expected.json new file mode 100644 index 00000000..88906e2e --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-turbot-expected.json @@ -0,0 +1,13 @@ +[ + { + "akas": [ + "{{ output.resource_aka.value }}", + "{{ output.resource_aka_lower.value }}" + ], + "name": "{{ resourceName }}", + "tags": { + "foo": "bar" + }, + "title": "{{ resourceName }}" + } +] diff --git a/azure-test/tests/azure_sql_database/test-turbot-query.sql b/azure-test/tests/azure_sql_database/test-turbot-query.sql new file mode 100644 index 00000000..707e2f9e --- /dev/null +++ b/azure-test/tests/azure_sql_database/test-turbot-query.sql @@ -0,0 +1,3 @@ +select name, akas, title, tags +from azure.azure_sql_database +where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}'; \ No newline at end of file diff --git a/azure-test/tests/azure_sql_database/variables.json b/azure-test/tests/azure_sql_database/variables.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/azure-test/tests/azure_sql_database/variables.json @@ -0,0 +1 @@ +{} diff --git a/azure-test/tests/azure_sql_database/variables.tf b/azure-test/tests/azure_sql_database/variables.tf new file mode 100644 index 00000000..f76ffd8c --- /dev/null +++ b/azure-test/tests/azure_sql_database/variables.tf @@ -0,0 +1,99 @@ + +variable "resource_name" { + type = string + default = "turbot-test-20200125-create-update" + description = "Name of the resource used throughout the test." +} + +variable "azure_environment" { + type = string + default = "public" + description = "Azure environment used for the test." +} + +variable "azure_subscription" { + type = string + default = "3510ae4d-530b-497d-8f30-53b9616fc6c1" + description = "Azure subscription used for the test." +} + +variable "azure_location" { + type = string + default = "East US" + description = "Azure location where the resource will be created." +} + +provider "azurerm" { + # Cannot be passed as a variable + features {} + environment = var.azure_environment + subscription_id = var.azure_subscription +} + +data "azurerm_client_config" "current" {} + +data "null_data_source" "resource" { + inputs = { + scope = "azure:///subscriptions/${data.azurerm_client_config.current.subscription_id}" + } +} + +resource "azurerm_resource_group" "named_test_resource" { + name = var.resource_name + location = var.azure_location +} + +resource "azurerm_sql_server" "named_test_resource" { + name = var.resource_name + resource_group_name = azurerm_resource_group.named_test_resource.name + location = azurerm_resource_group.named_test_resource.location + version = "12.0" + administrator_login = "mradministrator" + administrator_login_password = "thisIsDog11" +} + +resource "azurerm_sql_database" "named_test_resource" { + name = var.resource_name + resource_group_name = azurerm_resource_group.named_test_resource.name + location = azurerm_resource_group.named_test_resource.location + server_name = azurerm_sql_server.named_test_resource.name + tags = { + foo = "bar" + } +} + +output "resource_aka" { + value = "azure://${azurerm_sql_database.named_test_resource.id}" +} + +output "resource_aka_lower" { + value = "azure://${lower(azurerm_sql_database.named_test_resource.id)}" +} + +output "resource_name" { + value = var.resource_name +} + +output "resource_id" { + value = azurerm_sql_database.named_test_resource.id +} + +output "resource_location" { + value = var.azure_location +} + +output "resource_region" { + value = lower(var.azure_location) +} + +output "resource_creation_date" { + value = azurerm_sql_database.named_test_resource.creation_date +} + +output "resource_default_secondary_location" { + value = azurerm_sql_database.named_test_resource.default_secondary_location +} + +output "subscription_id" { + value = var.azure_subscription +} diff --git a/azure/table_azure_sql_database.go b/azure/table_azure_sql_database.go index 80386d50..0cef4b5f 100644 --- a/azure/table_azure_sql_database.go +++ b/azure/table_azure_sql_database.go @@ -11,7 +11,7 @@ import ( "github.com/turbot/steampipe-plugin-sdk/plugin" ) -//// TABLE DEFINITION //// +//// TABLE DEFINITION func tableAzureSqlDatabase(_ context.Context) *plugin.Table { return &plugin.Table{ @@ -96,6 +96,12 @@ func tableAzureSqlDatabase(_ context.Context) *plugin.Table { Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("DatabaseProperties.EarliestRestoreDate").Transform(convertDateToTime), }, + { + Name: "edition", + Description: "The edition of the database.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("DatabaseProperties.Edition"), + }, { Name: "elastic_pool_name", Description: "The name of the elastic pool the database is in.", @@ -166,12 +172,6 @@ func tableAzureSqlDatabase(_ context.Context) *plugin.Table { Type: proto.ColumnType_JSON, Transform: transform.FromField("DatabaseProperties.CreateMode"), }, - { - Name: "edition", - Description: "The edition of the database.", - Type: proto.ColumnType_JSON, - Transform: transform.FromField("DatabaseProperties.Edition"), - }, { Name: "read_scale", Description: "ReadScale indicates whether read-only connections are allowed to this database or not if the database is a geo-secondary.", @@ -257,7 +257,7 @@ func tableAzureSqlDatabase(_ context.Context) *plugin.Table { } } -//// FETCH FUNCTIONS //// +//// LIST FUNCTION func listSqlDatabases(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { session, err := GetNewSession(ctx, d, "MANAGEMENT") @@ -284,7 +284,7 @@ func listSqlDatabases(ctx context.Context, d *plugin.QueryData, h *plugin.Hydrat return nil, err } -//// HYDRATE FUNCTIONS //// +//// HYDRATE FUNCTIONS func getSqlDatabase(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { plugin.Logger(ctx).Trace("getSqlDatabase") @@ -362,7 +362,7 @@ func getSqlDatabaseTransparentDataEncryption(ctx context.Context, d *plugin.Quer return nil, nil } -//// Transform Functions +//// TRANSFORM FUNCTIONS func idToServerName(ctx context.Context, d *transform.TransformData) (interface{}, error) { data := d.HydrateItem.(sql.Database) diff --git a/docs/tables/azure_sql_database.md b/docs/tables/azure_sql_database.md new file mode 100644 index 00000000..4e1ac70e --- /dev/null +++ b/docs/tables/azure_sql_database.md @@ -0,0 +1,100 @@ +# Table: azure_sql_database + +An Azure SQL Database is an intelligent, scalable, relational database service built for the cloud. Optimise performance and durability with automated, AI-powered features that are always up to date. + +## Examples + +### Basic info + +```sql +select + name, + id, + server_name, + location, + edition +from + azure_sql_database; +``` + + +### List databases for a specific server + +```sql +select + name, + id, + server_name, + location, + edition +from + azure_sql_database +where + server_name = 'test-steampipe'; +``` + + +### List databases having system edition + +```sql +select + name, + id, + server_name, + location, + edition +from + azure_sql_database +where + edition = 'Basic'; +``` + + +### List databases which are online + +```sql +select + name, + id, + server_name, + location, + edition, + status +from + azure_sql_database +where + status = 'Online'; +``` + + +### List databases which are encrypted + +```sql +select + name, + id, + server_name, + location, + edition, + transparent_data_encryption ->> 'status' as encryption_status +from + azure_sql_database +where + transparent_data_encryption ->> 'status' = 'Enabled'; +``` + + +### List databases created before last 7 days + +```sql +select + name, + id, + server_name, + location, + creation_date +from + azure_sql_database +where + creation_date < (current_date - 7); +``` \ No newline at end of file From 0e6234c78944a901decd9482870d9d89ebe3239f Mon Sep 17 00:00:00 2001 From: Sayan Saha Date: Fri, 16 Apr 2021 19:40:27 +0530 Subject: [PATCH 3/4] Made review changes --- azure/plugin.go | 2 +- azure/table_azure_sql_database.go | 2 +- docs/tables/azure_sql_database.md | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/azure/plugin.go b/azure/plugin.go index e84471eb..852dd938 100644 --- a/azure/plugin.go +++ b/azure/plugin.go @@ -57,7 +57,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azure_role_definition": tableAzureIamRoleDefinition(ctx), "azure_route_table": tableAzureRouteTable(ctx), "azure_sql_server": tableAzureSQLServer(ctx), - "azure_sql_database": tableAzureSqlDatabase(ctx), + "azure_sql_database": tableAzureSqlDatabase(ctx), "azure_storage_account": tableAzureStorageAccount(ctx), "azure_storage_blob_service": tableAzureStorageBlobService(ctx), "azure_storage_container": tableAzureStorageContainer(ctx), diff --git a/azure/table_azure_sql_database.go b/azure/table_azure_sql_database.go index 0cef4b5f..bd05e0ee 100644 --- a/azure/table_azure_sql_database.go +++ b/azure/table_azure_sql_database.go @@ -362,7 +362,7 @@ func getSqlDatabaseTransparentDataEncryption(ctx context.Context, d *plugin.Quer return nil, nil } -//// TRANSFORM FUNCTIONS +//// TRANSFORM FUNCTION func idToServerName(ctx context.Context, d *transform.TransformData) (interface{}, error) { data := d.HydrateItem.(sql.Database) diff --git a/docs/tables/azure_sql_database.md b/docs/tables/azure_sql_database.md index 4e1ac70e..c366c4b8 100644 --- a/docs/tables/azure_sql_database.md +++ b/docs/tables/azure_sql_database.md @@ -1,6 +1,6 @@ # Table: azure_sql_database -An Azure SQL Database is an intelligent, scalable, relational database service built for the cloud. Optimise performance and durability with automated, AI-powered features that are always up to date. +An Azure SQL Database is an intelligent, scalable, relational database service built for the cloud. ## Examples @@ -50,7 +50,7 @@ where ``` -### List databases which are online +### List databases which are not online ```sql select @@ -63,11 +63,11 @@ select from azure_sql_database where - status = 'Online'; + status != 'Online'; ``` -### List databases which are encrypted +### List databases which are not encrypted ```sql select @@ -80,7 +80,7 @@ select from azure_sql_database where - transparent_data_encryption ->> 'status' = 'Enabled'; + transparent_data_encryption ->> 'status' != 'Enabled'; ``` From 086e05a3ec49bce593942bac98b5480519129668 Mon Sep 17 00:00:00 2001 From: cbruno10 Date: Thu, 22 Apr 2021 15:04:43 -0400 Subject: [PATCH 4/4] Update examples --- docs/tables/azure_sql_database.md | 52 ++----------------------------- 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/docs/tables/azure_sql_database.md b/docs/tables/azure_sql_database.md index c366c4b8..314ee12a 100644 --- a/docs/tables/azure_sql_database.md +++ b/docs/tables/azure_sql_database.md @@ -18,39 +18,7 @@ from ``` -### List databases for a specific server - -```sql -select - name, - id, - server_name, - location, - edition -from - azure_sql_database -where - server_name = 'test-steampipe'; -``` - - -### List databases having system edition - -```sql -select - name, - id, - server_name, - location, - edition -from - azure_sql_database -where - edition = 'Basic'; -``` - - -### List databases which are not online +### List databases that are not online ```sql select @@ -67,7 +35,7 @@ where ``` -### List databases which are not encrypted +### List databases that are not encrypted ```sql select @@ -82,19 +50,3 @@ from where transparent_data_encryption ->> 'status' != 'Enabled'; ``` - - -### List databases created before last 7 days - -```sql -select - name, - id, - server_name, - location, - creation_date -from - azure_sql_database -where - creation_date < (current_date - 7); -``` \ No newline at end of file