From e050bd6d8623c67bd1a7a701aebc950022e578e6 Mon Sep 17 00:00:00 2001 From: Subhajit Kumar Mondal Date: Fri, 26 Mar 2021 16:48:31 +0530 Subject: [PATCH 1/7] Add table azure_postgresql_server. Closes #63 --- azure/plugin.go | 1 + azure/table_azure_postgresql_server.go | 427 +++++++++++++++++++++++++ docs/tables/azure_postgresql_server.md | 81 +++++ 3 files changed, 509 insertions(+) create mode 100644 azure/table_azure_postgresql_server.go create mode 100644 docs/tables/azure_postgresql_server.md diff --git a/azure/plugin.go b/azure/plugin.go index 246b6c46..4c4d20a4 100644 --- a/azure/plugin.go +++ b/azure/plugin.go @@ -48,6 +48,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azure_network_interface": tableAzureNetworkInterface(ctx), "azure_network_security_group": tableAzureNetworkSecurityGroup(ctx), "azure_network_watcher": tableAzureNetworkWatcher(ctx), + "azure_postgresql_server": tableAzurePostgreSqlServer(ctx), "azure_provider": tableAzureProvider(ctx), "azure_public_ip": tableAzurePublicIP(ctx), "azure_resource_group": tableAzureResourceGroup(ctx), diff --git a/azure/table_azure_postgresql_server.go b/azure/table_azure_postgresql_server.go new file mode 100644 index 00000000..161457c5 --- /dev/null +++ b/azure/table_azure_postgresql_server.go @@ -0,0 +1,427 @@ +package azure + +import ( + "context" + "strings" + + "github.com/turbot/steampipe-plugin-sdk/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/plugin" + "github.com/turbot/steampipe-plugin-sdk/plugin/transform" + + "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2020-01-01/postgresql" +) + +//// TABLE DEFINITION + +func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "azure_postgresql_server", + Description: "Azure PostgreSQL Server", + Get: &plugin.GetConfig{ + KeyColumns: plugin.AllColumns([]string{"name", "resource_group"}), + Hydrate: getPostgreSqlServer, + ShouldIgnoreError: isNotFoundError([]string{"ResourceNotFound", "ResourceGroupNotFound", "404", "InvalidApiVersionParameter"}), + }, + List: &plugin.ListConfig{ + Hydrate: listPostgreSqlServers, + }, + Columns: []*plugin.Column{ + { + Name: "name", + Description: "The friendly name that identifies the server.", + Type: proto.ColumnType_STRING, + }, + { + Name: "id", + Description: "Contains ID to identify a server uniquely.", + Type: proto.ColumnType_STRING, + Transform: transform.FromGo(), + }, + { + Name: "type", + Description: "The resource type of the SQL server.", + Type: proto.ColumnType_STRING, + }, + { + Name: "version", + Description: "Specifies the version of the server.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.Version").Transform(transform.ToString), + }, + { + Name: "location", + Description: "The resource location.", + Type: proto.ColumnType_STRING, + }, + { + Name: "administrator_login", + Description: "Specifies the username of the Administrator for this server.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.AdministratorLogin"), + }, + { + Name: "byok_enforcement", + Description: "Status showing whether the server data encryption is enabled with customer-managed keys.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.ByokEnforcement"), + }, + { + Name: "earliest_restore_date", + Description: "Specifies the earliest restore point creation time.", + Type: proto.ColumnType_TIMESTAMP, + Transform: transform.FromField("ServerProperties.EarliestRestoreDate").Transform(convertDateToTime), + }, + { + Name: "fully_qualified_domain_name", + Description: "The fully qualified domain name of the server.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.FullyQualifiedDomainName"), + }, + { + Name: "infrastructure_encryption", + Description: "Status showing whether the server enabled infrastructure encryption. Possible values include: 'InfrastructureEncryptionEnabled', 'InfrastructureEncryptionDisabled'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.InfrastructureEncryption").Transform(transform.ToString), + }, + { + Name: "master_server_id", + Description: "The master server id of a replica server.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.MasterServerID"), + }, + { + Name: "minimal_tls_version", + Description: "Enforce a minimal Tls version for the server. Possible values include: 'TLS10', 'TLS11', 'TLS12', 'TLSEnforcementDisabled'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.MinimalTLSVersion").Transform(transform.ToString), + }, + { + Name: "public_network_access", + Description: "Indicates whether or not public network access is allowed for this server. Value is optional but if passed in, must be 'Enabled' or 'Disabled'. Possible values include: 'PublicNetworkAccessEnumEnabled', 'PublicNetworkAccessEnumDisabled'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.PublicNetworkAccess").Transform(transform.ToString), + }, + { + Name: "replica_capacity", + Description: "The maximum number of replicas that a master server can have.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("ServerProperties.ReplicaCapacity"), + }, + { + Name: "replication_role", + Description: "The replication role of the server.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.ReplicationRole"), + }, + { + Name: "sku_capacity", + Description: "The scale up/out capacity, representing server's compute units.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("Sku.Capacity"), + }, + { + Name: "sku_family", + Description: "The family of hardware.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Sku.Family"), + }, + { + Name: "sku_name", + Description: "The name of the sku. For example: 'B_Gen4_1', 'GP_Gen5_8'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Sku.Name"), + }, + { + Name: "sku_size", + Description: "The size code, to be interpreted by resource as appropriate.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Sku.Size"), + }, + { + Name: "sku_tier", + Description: "The tier of the particular SKU. Possible values include: 'Basic', 'GeneralPurpose', 'MemoryOptimized'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Sku.Tier").Transform(transform.ToString), + }, + { + Name: "ssl_enforcement", + Description: "Enable ssl enforcement or not when connect to server. Possible values include: 'Enabled', 'Disabled'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.SslEnforcement").Transform(transform.ToString), + }, + { + Name: "storage_profile_backup_retention_days", + Description: "Backup retention days for the server.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("ServerProperties.StorageProfile.BackupRetentionDays"), + }, + { + Name: "storage_profile_geo_redundant_backup", + Description: "Indicates whether Geo-redundant is enabled, or not for server backup.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.StorageProfile.GeoRedundantBackup").Transform(transform.ToString), + }, + { + Name: "storage_profile_storage_auto_grow", + Description: "Indicates whether storage auto grow is enabled, or not.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.StorageProfile.StorageAutogrow").Transform(transform.ToString), + }, + { + Name: "storage_profile_storage_mb", + Description: "Indicates max storage allowed for a server.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("ServerProperties.StorageProfile.StorageMB"), + }, + { + Name: "user_visible_state", + Description: "A state of a server that is visible to user. Possible values include: 'ServerStateReady', 'ServerStateDropping', 'ServerStateDisabled', 'ServerStateInaccessible'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.UserVisibleState").Transform(transform.ToString), + }, + { + Name: "private_endpoint_connections", + Description: "A list of private endpoint connections on a server.", + Type: proto.ColumnType_JSON, + Transform: transform.FromField("ServerProperties.PrivateEndpointConnections"), + }, + { + Name: "firewall_rules", + Description: "A list of firewall rules for a server.", + Type: proto.ColumnType_JSON, + Hydrate: getPostgreSQLServerFirewallRules, + Transform: transform.FromValue(), + }, + { + Name: "server_administrators", + Description: "A list of server administrators.", + Type: proto.ColumnType_JSON, + Hydrate: getPostgreSQLServerAdministrator, + Transform: transform.FromValue(), + }, + { + Name: "server_configurations", + Description: "A list of configurations for a server.", + Type: proto.ColumnType_JSON, + Hydrate: getPostgreSQLServerConfigurations, + Transform: transform.FromValue(), + }, + + // steampipe standard columns + { + Name: "tags", + Description: ColumnDescriptionTags, + Type: proto.ColumnType_JSON, + }, + { + Name: "akas", + Description: ColumnDescriptionAkas, + Type: proto.ColumnType_JSON, + Transform: transform.FromField("ID").Transform(idToAkas), + }, + { + Name: "title", + Description: ColumnDescriptionTitle, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, + + // azure standard columns + { + 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), + }, + }, + } +} + +//// LIST FUNCTION + +func listPostgreSqlServers(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + + client := postgresql.NewServersClient(subscriptionID) + client.Authorizer = session.Authorizer + + result, err := client.List(context.Background()) + if err != nil { + return nil, err + } + for _, server := range *result.Value { + d.StreamListItem(ctx, server) + } + return nil, err +} + +//// HYDRATE FUNCTIONS + +func getPostgreSqlServer(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getPostgreSqlServer") + + name := d.KeyColumnQuals["name"].GetStringValue() + resourceGroup := d.KeyColumnQuals["resource_group"].GetStringValue() + + // Error: postgresql.ServersClient#Get: Invalid input: autorest/validation: validation failed: parameter=resourceGroupName constraint=MinLength + // value="" details: value length must be greater than or equal to 1 + if len(resourceGroup) < 1 { + return nil, nil + } + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + + client := postgresql.NewServersClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.Get(ctx, resourceGroup, name) + 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 getPostgreSQLServerFirewallRules(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getPostgreSQLServerFirewallRules") + server := h.Item.(postgresql.Server) + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + resourceGroupName := strings.Split(string(*server.ID), "/")[4] + + client := postgresql.NewFirewallRulesClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.ListByServer(ctx, resourceGroupName, *server.Name) + if err != nil { + return nil, err + } + + var firewallRules []map[string]interface{} + for _, i := range *op.Value { + objectMap := make(map[string]interface{}) + if i.ID != nil { + objectMap["ID"] = i.ID + } + if i.Name != nil { + objectMap["Name"] = i.Name + } + if i.Type != nil { + objectMap["Type"] = i.Type + } + if i.FirewallRuleProperties != nil { + objectMap["FirewallRuleProperties"] = i.FirewallRuleProperties + } + firewallRules = append(firewallRules, objectMap) + } + + return firewallRules, nil +} + +func getPostgreSQLServerAdministrator(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getPostgreSQLServerAdministrator") + server := h.Item.(postgresql.Server) + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + resourceGroupName := strings.Split(string(*server.ID), "/")[4] + + client := postgresql.NewServerAdministratorsClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.List(ctx, resourceGroupName, *server.Name) + if err != nil { + return nil, err + } + + var serverAdministrators []map[string]interface{} + for _, i := range *op.Value { + objectMap := make(map[string]interface{}) + if i.ID != nil { + objectMap["ID"] = i.ID + } + if i.Name != nil { + objectMap["Name"] = i.Name + } + if i.Type != nil { + objectMap["Type"] = i.Type + } + if i.ServerAdministratorProperties != nil { + objectMap["ServerAdministratorProperties"] = i.ServerAdministratorProperties + } + serverAdministrators = append(serverAdministrators, objectMap) + } + return serverAdministrators, nil +} + +func getPostgreSQLServerConfigurations(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getPostgreSQLServerConfigurations") + server := h.Item.(postgresql.Server) + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + resourceGroupName := strings.Split(string(*server.ID), "/")[4] + + client := postgresql.NewConfigurationsClient(subscriptionID) + client.Authorizer = session.Authorizer + + op, err := client.ListByServer(ctx, resourceGroupName, *server.Name) + if err != nil { + return nil, err + } + + var serverParameters []map[string]interface{} + for _, i := range *op.Value { + objectMap := make(map[string]interface{}) + if i.ID != nil { + objectMap["ID"] = i.ID + } + if i.Name != nil { + objectMap["Name"] = i.Name + } + if i.Type != nil { + objectMap["Type"] = i.Type + } + if i.ConfigurationProperties != nil { + objectMap["ConfigurationProperties"] = i.ConfigurationProperties + } + serverParameters = append(serverParameters, objectMap) + } + return serverParameters, nil +} diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md new file mode 100644 index 00000000..f6e121cc --- /dev/null +++ b/docs/tables/azure_postgresql_server.md @@ -0,0 +1,81 @@ +# Table: azure_postgresql_server + +Azure Database for PostgreSQL is a relational database service based on the open-source Postgres database engine. It's a fully managed database-as-a-service that can handle mission-critical workloads with predictable performance, security, high availability, and dynamic scalability. + +## Examples + +### List servers for which Enforce SSL connection is enabled + +```sql +select + name, + id, + location, + ssl_enforcement +from + azure_postgresql_server +where + ssl_enforcement = 'Enabled'; +``` + +### List servers for which 'Allow access to Azure services' feature is disabled + +```sql +select + name, + id, + rule ->> 'Name' as rule_name, + rule ->> 'Type' as rule_type, + rule -> 'FirewallRuleProperties' ->> 'endIpAddress' as end_ip_address, + rule -> 'FirewallRuleProperties' ->> 'startIpAddress' as start_ip_address +from + azure_postgresql_server, + jsonb_array_elements(firewall_rules) as rule +where + rule ->> 'Name' = 'AllowAllAzureIps' + and rule -> 'FirewallRuleProperties' -> 'startIpAddress' = '0.0.0.0' + and rule -> 'FirewallRuleProperties' -> 'endIpAddress' = '0.0.0.0'; +``` + +## List servers for which Active Directory Admin is not configured + +```sql +select + name, + id, + location +from + azure_postgresql_server +where + server_administrators is null; +``` + +### List servers for which 'log_checkpoints' is set to 'OFF' + +```sql +select + name, + configurations ->> 'Name' as configuration_name, + configurations -> 'ConfigurationProperties' ->> 'value' as configuration_value +from + azure_postgresql_server, + jsonb_array_elements(server_configurations) as configurations +where + configurations ->> 'Name' = 'log_checkpoints' + and configurations -> 'ConfigurationProperties' ->> 'value' = 'OFF'; +``` + +### List servers for which 'log_retention_days' is greater than 3 days + +```sql +select + name, + configurations ->> 'Name' as configuration_name, + configurations -> 'ConfigurationProperties' ->> 'value' as configuration_value +from + azure_postgresql_server, + jsonb_array_elements(server_configurations) as configurations +where + configurations ->> 'Name' = 'log_retention_days' + and (configurations -> 'ConfigurationProperties' ->> 'value')::INTEGER > 3; +``` From 93b57238701c753a91653dc9daf69289d1e769c7 Mon Sep 17 00:00:00 2001 From: Subhajit Kumar Mondal Date: Sat, 27 Mar 2021 11:24:02 +0530 Subject: [PATCH 2/7] Add tests to validate the table outcomes --- .../azure_postgresql_server/dependencies.txt | 0 .../test-get-expected.json | 23 ++++ .../test-get-query.sql | 3 + .../test-hydrate-expected.json | 12 ++ .../test-hydrate-query.sql | 18 +++ .../test-list-expected.json | 7 ++ .../test-list-query.sql | 3 + .../test-not-found-expected.json | 1 + .../test-not-found-query.sql | 3 + .../test-turbot-expected.json | 13 ++ .../test-turbot-query.sql | 3 + .../azure_postgresql_server/variables.json | 1 + .../azure_postgresql_server/variables.tf | 113 ++++++++++++++++++ azure/table_azure_postgresql_server.go | 24 ++-- docs/tables/azure_postgresql_server.md | 6 +- 15 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 azure-test/tests/azure_postgresql_server/dependencies.txt create mode 100644 azure-test/tests/azure_postgresql_server/test-get-expected.json create mode 100644 azure-test/tests/azure_postgresql_server/test-get-query.sql create mode 100644 azure-test/tests/azure_postgresql_server/test-hydrate-expected.json create mode 100644 azure-test/tests/azure_postgresql_server/test-hydrate-query.sql create mode 100644 azure-test/tests/azure_postgresql_server/test-list-expected.json create mode 100644 azure-test/tests/azure_postgresql_server/test-list-query.sql create mode 100644 azure-test/tests/azure_postgresql_server/test-not-found-expected.json create mode 100644 azure-test/tests/azure_postgresql_server/test-not-found-query.sql create mode 100644 azure-test/tests/azure_postgresql_server/test-turbot-expected.json create mode 100644 azure-test/tests/azure_postgresql_server/test-turbot-query.sql create mode 100644 azure-test/tests/azure_postgresql_server/variables.json create mode 100644 azure-test/tests/azure_postgresql_server/variables.tf diff --git a/azure-test/tests/azure_postgresql_server/dependencies.txt b/azure-test/tests/azure_postgresql_server/dependencies.txt new file mode 100644 index 00000000..e69de29b diff --git a/azure-test/tests/azure_postgresql_server/test-get-expected.json b/azure-test/tests/azure_postgresql_server/test-get-expected.json new file mode 100644 index 00000000..475708e7 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-get-expected.json @@ -0,0 +1,23 @@ +[ + { + "administrator_login": "psqladminun", + "backup_retention_days": 7, + "fully_qualified_domain_name": "{{ output.server_fqdn.value }}", + "geo_redundant_backup": "Disabled", + "id": "{{ output.resource_id.value }}", + "location": "{{ output.location.value }}", + "minimal_tls_version": "TLS1_2", + "name": "{{ resourceName }}", + "public_network_access": "Enabled", + "region": "{{ output.location.value }}", + "resource_group": "{{ resourceName }}", + "sku_family": "Gen5", + "sku_name": "B_Gen5_1", + "sku_size": "", + "sku_tier": "Basic", + "ssl_enforcement": "Enabled", + "subscription_id": "{{ output.subscription_id.value }}", + "type": "Microsoft.DBforPostgreSQL/servers", + "version": "9.5" + } +] diff --git a/azure-test/tests/azure_postgresql_server/test-get-query.sql b/azure-test/tests/azure_postgresql_server/test-get-query.sql new file mode 100644 index 00000000..5788efd6 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-get-query.sql @@ -0,0 +1,3 @@ +select name, id, type, version, location, version, administrator_login, fully_qualified_domain_name, minimal_tls_version, public_network_access, sku_family, sku_name, sku_tier, sku_size, ssl_enforcement, backup_retention_days, geo_redundant_backup, resource_group, region, subscription_id +from azure.azure_postgresql_server +where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}'; diff --git a/azure-test/tests/azure_postgresql_server/test-hydrate-expected.json b/azure-test/tests/azure_postgresql_server/test-hydrate-expected.json new file mode 100644 index 00000000..eaef75ab --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-hydrate-expected.json @@ -0,0 +1,12 @@ +[ + { + "configuration_name": "log_checkpoints", + "configuration_value": "on", + "end_ip_address": "40.112.8.12", + "name": "{{ resourceName }}", + "rule_name": "{{ resourceName }}", + "rule_type": "Microsoft.DBforPostgreSQL/servers/firewallRules", + "server_admin_login_name": "ActiveDirectory", + "start_ip_address": "40.112.8.12" + } +] diff --git a/azure-test/tests/azure_postgresql_server/test-hydrate-query.sql b/azure-test/tests/azure_postgresql_server/test-hydrate-query.sql new file mode 100644 index 00000000..bb5f2fc7 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-hydrate-query.sql @@ -0,0 +1,18 @@ +select + name, + rule ->> 'Name' as rule_name, + rule ->> 'Type' as rule_type, + rule -> 'FirewallRuleProperties' ->> 'endIpAddress' as end_ip_address, + rule -> 'FirewallRuleProperties' ->> 'startIpAddress' as start_ip_address, + configurations ->> 'Name' as configuration_name, + configurations -> 'ConfigurationProperties' ->> 'value' as configuration_value, + server_admin ->> 'Name' as server_admin_login_name +from + azure_postgresql_server, + jsonb_array_elements(server_configurations) as configurations, + jsonb_array_elements(firewall_rules) as rule, + jsonb_array_elements(server_administrators) as server_admin +where + name = '{{ resourceName }}' + and resource_group = '{{ resourceName }}' + and configurations ->> 'Name' = 'log_checkpoints'; \ No newline at end of file diff --git a/azure-test/tests/azure_postgresql_server/test-list-expected.json b/azure-test/tests/azure_postgresql_server/test-list-expected.json new file mode 100644 index 00000000..441fbf0c --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-list-expected.json @@ -0,0 +1,7 @@ +[ + { + "id": "{{ output.resource_id.value }}", + "location": "{{ output.location.value }}", + "name": "{{ resourceName }}" + } +] diff --git a/azure-test/tests/azure_postgresql_server/test-list-query.sql b/azure-test/tests/azure_postgresql_server/test-list-query.sql new file mode 100644 index 00000000..70c5a991 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-list-query.sql @@ -0,0 +1,3 @@ +select id, name, location +from azure.azure_postgresql_server +where name = '{{ resourceName }}'; diff --git a/azure-test/tests/azure_postgresql_server/test-not-found-expected.json b/azure-test/tests/azure_postgresql_server/test-not-found-expected.json new file mode 100644 index 00000000..19765bd5 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-not-found-expected.json @@ -0,0 +1 @@ +null diff --git a/azure-test/tests/azure_postgresql_server/test-not-found-query.sql b/azure-test/tests/azure_postgresql_server/test-not-found-query.sql new file mode 100644 index 00000000..55794497 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-not-found-query.sql @@ -0,0 +1,3 @@ +select name, akas, title +from azure.azure_postgresql_server +where name = 'dummy-{{ resourceName }}' and resource_group = '{{ resourceName }}'; diff --git a/azure-test/tests/azure_postgresql_server/test-turbot-expected.json b/azure-test/tests/azure_postgresql_server/test-turbot-expected.json new file mode 100644 index 00000000..682002ac --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-turbot-expected.json @@ -0,0 +1,13 @@ +[ + { + "akas": [ + "{{ output.resource_aka.value }}", + "{{ output.resource_aka_lower.value }}" + ], + "name": "{{ resourceName }}", + "tags": { + "name": "{{ resourceName }}" + }, + "title": "{{ resourceName }}" + } +] diff --git a/azure-test/tests/azure_postgresql_server/test-turbot-query.sql b/azure-test/tests/azure_postgresql_server/test-turbot-query.sql new file mode 100644 index 00000000..bf9e921e --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/test-turbot-query.sql @@ -0,0 +1,3 @@ +select name, akas, title, tags +from azure.azure_postgresql_server +where name = '{{ resourceName }}' and resource_group = '{{ resourceName }}'; diff --git a/azure-test/tests/azure_postgresql_server/variables.json b/azure-test/tests/azure_postgresql_server/variables.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/variables.json @@ -0,0 +1 @@ +{} diff --git a/azure-test/tests/azure_postgresql_server/variables.tf b/azure-test/tests/azure_postgresql_server/variables.tf new file mode 100644 index 00000000..e290c577 --- /dev/null +++ b/azure-test/tests/azure_postgresql_server/variables.tf @@ -0,0 +1,113 @@ + +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." +} + +provider "azurerm" { + 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 = "East US" +} + +resource "azurerm_postgresql_server" "named_test_resource" { + name = var.resource_name + location = azurerm_resource_group.named_test_resource.location + resource_group_name = azurerm_resource_group.named_test_resource.name + + sku_name = "B_Gen5_1" + version = "9.5" + storage_mb = 5120 + + backup_retention_days = 7 + geo_redundant_backup_enabled = false + auto_grow_enabled = false + infrastructure_encryption_enabled = false + + administrator_login = "psqladminun" + administrator_login_password = "H@Sh1CoR3!" + + ssl_enforcement_enabled = true + ssl_minimal_tls_version_enforced = "TLS1_2" + + tags = { + name = var.resource_name + } +} + +resource "azurerm_postgresql_configuration" "named_test_resource" { + name = "log_checkpoints" + resource_group_name = azurerm_resource_group.named_test_resource.name + server_name = azurerm_postgresql_server.named_test_resource.name + value = "on" +} + +resource "azurerm_postgresql_firewall_rule" "named_test_resource" { + name = var.resource_name + resource_group_name = azurerm_resource_group.named_test_resource.name + server_name = azurerm_postgresql_server.named_test_resource.name + start_ip_address = "40.112.8.12" + end_ip_address = "40.112.8.12" +} + +resource "azurerm_postgresql_active_directory_administrator" "named_test_resource" { + server_name = azurerm_postgresql_server.named_test_resource.name + resource_group_name = azurerm_resource_group.named_test_resource.name + login = "sqladmin" + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id +} + +output "resource_aka" { + value = "azure://${azurerm_postgresql_server.named_test_resource.id}" +} + +output "resource_aka_lower" { + value = "azure://${lower(azurerm_postgresql_server.named_test_resource.id)}" +} + +output "resource_name" { + value = var.resource_name +} + +output "resource_id" { + value = azurerm_postgresql_server.named_test_resource.id +} + +output "location" { + value = lower(azurerm_resource_group.named_test_resource.location) +} + +output "subscription_id" { + value = var.azure_subscription +} + +output "server_fqdn" { + value = azurerm_postgresql_server.named_test_resource.fqdn +} diff --git a/azure/table_azure_postgresql_server.go b/azure/table_azure_postgresql_server.go index 161457c5..5ece5d4a 100644 --- a/azure/table_azure_postgresql_server.go +++ b/azure/table_azure_postgresql_server.go @@ -59,6 +59,12 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Type: proto.ColumnType_STRING, Transform: transform.FromField("ServerProperties.AdministratorLogin"), }, + { + Name: "backup_retention_days", + Description: "Backup retention days for the server.", + Type: proto.ColumnType_INT, + Transform: transform.FromField("ServerProperties.StorageProfile.BackupRetentionDays"), + }, { Name: "byok_enforcement", Description: "Status showing whether the server data encryption is enabled with customer-managed keys.", @@ -77,6 +83,12 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Type: proto.ColumnType_STRING, Transform: transform.FromField("ServerProperties.FullyQualifiedDomainName"), }, + { + Name: "geo_redundant_backup", + Description: "Indicates whether Geo-redundant is enabled, or not for server backup.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.StorageProfile.GeoRedundantBackup").Transform(transform.ToString), + }, { Name: "infrastructure_encryption", Description: "Status showing whether the server enabled infrastructure encryption. Possible values include: 'InfrastructureEncryptionEnabled', 'InfrastructureEncryptionDisabled'.", @@ -149,18 +161,6 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Type: proto.ColumnType_STRING, Transform: transform.FromField("ServerProperties.SslEnforcement").Transform(transform.ToString), }, - { - Name: "storage_profile_backup_retention_days", - Description: "Backup retention days for the server.", - Type: proto.ColumnType_INT, - Transform: transform.FromField("ServerProperties.StorageProfile.BackupRetentionDays"), - }, - { - Name: "storage_profile_geo_redundant_backup", - Description: "Indicates whether Geo-redundant is enabled, or not for server backup.", - Type: proto.ColumnType_STRING, - Transform: transform.FromField("ServerProperties.StorageProfile.GeoRedundantBackup").Transform(transform.ToString), - }, { Name: "storage_profile_storage_auto_grow", Description: "Indicates whether storage auto grow is enabled, or not.", diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md index f6e121cc..7ac5ed1a 100644 --- a/docs/tables/azure_postgresql_server.md +++ b/docs/tables/azure_postgresql_server.md @@ -32,9 +32,9 @@ from azure_postgresql_server, jsonb_array_elements(firewall_rules) as rule where - rule ->> 'Name' = 'AllowAllAzureIps' - and rule -> 'FirewallRuleProperties' -> 'startIpAddress' = '0.0.0.0' - and rule -> 'FirewallRuleProperties' -> 'endIpAddress' = '0.0.0.0'; + rule ->> 'Name' = 'AllowAllWindowsAzureIps' + and rule -> 'FirewallRuleProperties' ->> 'startIpAddress' = '0.0.0.0' + and rule -> 'FirewallRuleProperties' ->> 'endIpAddress' = '0.0.0.0'; ``` ## List servers for which Active Directory Admin is not configured From 6ff7fdfbfae3645db0b233672a19f79ac7b1c3df Mon Sep 17 00:00:00 2001 From: Subhajit Mondal <38218418+Subhajit97@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:20:51 +0530 Subject: [PATCH 3/7] Update the heading of the example added in docs --- docs/tables/azure_postgresql_server.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md index 7ac5ed1a..149523f0 100644 --- a/docs/tables/azure_postgresql_server.md +++ b/docs/tables/azure_postgresql_server.md @@ -18,7 +18,7 @@ where ssl_enforcement = 'Enabled'; ``` -### List servers for which 'Allow access to Azure services' feature is disabled +### List servers for which 'Allow access to Azure services' feature is enabled ```sql select From d275c62396c5d1d9e0b3e60f55ab6d2d07a26cbb Mon Sep 17 00:00:00 2001 From: Subhajit Kumar Mondal Date: Tue, 6 Apr 2021 12:06:04 +0530 Subject: [PATCH 4/7] Updated the order of the columns in table schema --- azure/table_azure_postgresql_server.go | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/azure/table_azure_postgresql_server.go b/azure/table_azure_postgresql_server.go index 5ece5d4a..c54da2e9 100644 --- a/azure/table_azure_postgresql_server.go +++ b/azure/table_azure_postgresql_server.go @@ -42,6 +42,12 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Description: "The resource type of the SQL server.", Type: proto.ColumnType_STRING, }, + { + Name: "user_visible_state", + Description: "A state of a server that is visible to user. Possible values include: 'ServerStateReady', 'ServerStateDropping', 'ServerStateDisabled', 'ServerStateInaccessible'.", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ServerProperties.UserVisibleState").Transform(transform.ToString), + }, { Name: "version", Description: "Specifies the version of the server.", @@ -55,7 +61,7 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { }, { Name: "administrator_login", - Description: "Specifies the username of the Administrator for this server.", + Description: "Specifies the username of the administrator for this server.", Type: proto.ColumnType_STRING, Transform: transform.FromField("ServerProperties.AdministratorLogin"), }, @@ -162,23 +168,17 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Transform: transform.FromField("ServerProperties.SslEnforcement").Transform(transform.ToString), }, { - Name: "storage_profile_storage_auto_grow", + Name: "storage_auto_grow", Description: "Indicates whether storage auto grow is enabled, or not.", Type: proto.ColumnType_STRING, Transform: transform.FromField("ServerProperties.StorageProfile.StorageAutogrow").Transform(transform.ToString), }, { - Name: "storage_profile_storage_mb", + Name: "storage_mb", Description: "Indicates max storage allowed for a server.", Type: proto.ColumnType_INT, Transform: transform.FromField("ServerProperties.StorageProfile.StorageMB"), }, - { - Name: "user_visible_state", - Description: "A state of a server that is visible to user. Possible values include: 'ServerStateReady', 'ServerStateDropping', 'ServerStateDisabled', 'ServerStateInaccessible'.", - Type: proto.ColumnType_STRING, - Transform: transform.FromField("ServerProperties.UserVisibleState").Transform(transform.ToString), - }, { Name: "private_endpoint_connections", Description: "A list of private endpoint connections on a server.", @@ -207,7 +207,13 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Transform: transform.FromValue(), }, - // steampipe standard columns + // Steampipe standard columns + { + Name: "title", + Description: ColumnDescriptionTitle, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, { Name: "tags", Description: ColumnDescriptionTags, @@ -219,14 +225,8 @@ func tableAzurePostgreSqlServer(_ context.Context) *plugin.Table { Type: proto.ColumnType_JSON, Transform: transform.FromField("ID").Transform(idToAkas), }, - { - Name: "title", - Description: ColumnDescriptionTitle, - Type: proto.ColumnType_STRING, - Transform: transform.FromField("Name"), - }, - // azure standard columns + // Azure standard columns { Name: "region", Description: ColumnDescriptionRegion, From ab8c45f2898a255f7409e4928dfcb85b21e957ff Mon Sep 17 00:00:00 2001 From: Subhajit Kumar Mondal Date: Tue, 6 Apr 2021 18:46:23 +0530 Subject: [PATCH 5/7] Added comments and updated the headings of the example queries --- azure/table_azure_postgresql_server.go | 6 ++++++ docs/tables/azure_postgresql_server.md | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/azure/table_azure_postgresql_server.go b/azure/table_azure_postgresql_server.go index c54da2e9..efd0d6f2 100644 --- a/azure/table_azure_postgresql_server.go +++ b/azure/table_azure_postgresql_server.go @@ -327,6 +327,8 @@ func getPostgreSQLServerFirewallRules(ctx context.Context, d *plugin.QueryData, return nil, err } + // If we return the API response directly, the output only gives + // the contents of FirewallRuleProperties var firewallRules []map[string]interface{} for _, i := range *op.Value { objectMap := make(map[string]interface{}) @@ -367,6 +369,8 @@ func getPostgreSQLServerAdministrator(ctx context.Context, d *plugin.QueryData, return nil, err } + // If we return the API response directly, the output only gives + // the contents of ServerAdministratorProperties var serverAdministrators []map[string]interface{} for _, i := range *op.Value { objectMap := make(map[string]interface{}) @@ -406,6 +410,8 @@ func getPostgreSQLServerConfigurations(ctx context.Context, d *plugin.QueryData, return nil, err } + // If we return the API response directly, the output only gives + // the contents of ConfigurationProperties var serverParameters []map[string]interface{} for _, i := range *op.Value { objectMap := make(map[string]interface{}) diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md index 149523f0..d54ec1ca 100644 --- a/docs/tables/azure_postgresql_server.md +++ b/docs/tables/azure_postgresql_server.md @@ -4,7 +4,7 @@ Azure Database for PostgreSQL is a relational database service based on the open ## Examples -### List servers for which Enforce SSL connection is enabled +### List servers for which SSL is enabled ```sql select From f8c876779cce2d6d5c1c3e20f0a3e9443e38621d Mon Sep 17 00:00:00 2001 From: Subhajit Kumar Mondal Date: Wed, 7 Apr 2021 14:01:58 +0530 Subject: [PATCH 6/7] Added example queries for geo-redundant backup and in-transit encryption --- docs/tables/azure_postgresql_server.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md index d54ec1ca..26f84c9e 100644 --- a/docs/tables/azure_postgresql_server.md +++ b/docs/tables/azure_postgresql_server.md @@ -4,7 +4,7 @@ Azure Database for PostgreSQL is a relational database service based on the open ## Examples -### List servers for which SSL is enabled +### List servers for which In-Transit Encryption is disabled ```sql select @@ -15,7 +15,7 @@ select from azure_postgresql_server where - ssl_enforcement = 'Enabled'; + ssl_enforcement = 'Disabled'; ``` ### List servers for which 'Allow access to Azure services' feature is enabled @@ -79,3 +79,17 @@ where configurations ->> 'Name' = 'log_retention_days' and (configurations -> 'ConfigurationProperties' ->> 'value')::INTEGER > 3; ``` + +### List servers for which Geo-Redundant backup feature is disabled + +```sql +select + name, + id, + location, + geo_redundant_backup +from + azure_postgresql_server +where + geo_redundant_backup = 'Disabled'; +``` From 4ea8f3f507aeb7eef575fe7da267ebdce79d2959 Mon Sep 17 00:00:00 2001 From: cbruno10 Date: Wed, 7 Apr 2021 12:45:58 -0400 Subject: [PATCH 7/7] Update examples --- docs/tables/azure_postgresql_server.md | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/tables/azure_postgresql_server.md b/docs/tables/azure_postgresql_server.md index 26f84c9e..309af479 100644 --- a/docs/tables/azure_postgresql_server.md +++ b/docs/tables/azure_postgresql_server.md @@ -4,7 +4,18 @@ Azure Database for PostgreSQL is a relational database service based on the open ## Examples -### List servers for which In-Transit Encryption is disabled +### Basic info + +```sql +select + name, + id, + location +from + azure_postgresql_server; +``` + +### List servers with encryption disabled ```sql select @@ -18,7 +29,7 @@ where ssl_enforcement = 'Disabled'; ``` -### List servers for which 'Allow access to Azure services' feature is enabled +### List servers that allow access to Azure services ```sql select @@ -37,7 +48,7 @@ where and rule -> 'FirewallRuleProperties' ->> 'endIpAddress' = '0.0.0.0'; ``` -## List servers for which Active Directory Admin is not configured +## List servers without an Active Directory admin ```sql select @@ -50,7 +61,7 @@ where server_administrators is null; ``` -### List servers for which 'log_checkpoints' is set to 'OFF' +### List servers with log checkpoints disabled ```sql select @@ -65,7 +76,7 @@ where and configurations -> 'ConfigurationProperties' ->> 'value' = 'OFF'; ``` -### List servers for which 'log_retention_days' is greater than 3 days +### List servers with a logging retention period greater than 3 days ```sql select @@ -80,7 +91,7 @@ where and (configurations -> 'ConfigurationProperties' ->> 'value')::INTEGER > 3; ``` -### List servers for which Geo-Redundant backup feature is disabled +### List servers with geo-redundant backup storage disabled ```sql select