Skip to content

Commit

Permalink
Add table azure_bastion_host. Closes turbot#576 (turbot#580)
Browse files Browse the repository at this point in the history
Co-authored-by: misraved <[email protected]>
Co-authored-by: ParthaI <[email protected]>
Co-authored-by: Ved misra <[email protected]>
  • Loading branch information
4 people authored Mar 15, 2023
1 parent 0f42dc6 commit 85c767e
Show file tree
Hide file tree
Showing 14 changed files with 432 additions and 0 deletions.
Empty file.
11 changes: 11 additions & 0 deletions azure-test/tests/azure_bastion_host/test-get-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"dns_name": "{{ output.resource_dns_name.value }}",
"id": "{{ output.resource_id.value }}",
"name": "{{resourceName}}",
"resource_group": "{{resourceName}}",
"tags": {
"name": "{{resourceName}}"
}
}
]
11 changes: 11 additions & 0 deletions azure-test/tests/azure_bastion_host/test-get-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
select
lower(id) as id,
name,
dns_name,
resource_group,
tags
from
azure_bastion_host
where
name = '{{resourceName}}'
and resource_group = '{{resourceName}}';
11 changes: 11 additions & 0 deletions azure-test/tests/azure_bastion_host/test-list-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"dns_name": "{{ output.resource_dns_name.value }}",
"id": "{{ output.resource_id.value }}",
"name": "{{resourceName}}",
"resource_group": "{{resourceName}}",
"tags": {
"name": "{{resourceName}}"
}
}
]
10 changes: 10 additions & 0 deletions azure-test/tests/azure_bastion_host/test-list-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
select
lower(id) as id,
name,
dns_name,
resource_group,
tags
from
azure_bastion_host
where
name = '{{resourceName}}';
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
null
11 changes: 11 additions & 0 deletions azure-test/tests/azure_bastion_host/test-not-found-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
select
id,
name,
dns_name,
region,
resource_group
from
azure_bastion_host
where
name = 'dummy-{{resourceName}}'
and resource_group = '{{resourceName}}'
10 changes: 10 additions & 0 deletions azure-test/tests/azure_bastion_host/test-turbot-expected.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"akas": [
"{{ output.resource_aka_lower.value }}",
"{{ output.resource_aka_lower.value }}"
],
"name": "{{resourceName}}",
"title": "{{resourceName}}"
}
]
12 changes: 12 additions & 0 deletions azure-test/tests/azure_bastion_host/test-turbot-query.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
select
name,
to_jsonb(string_to_array(STRING_AGG(lower(x), ','), ',')) as akas,
title
from
azure.azure_bastion_host,
jsonb_array_elements_text(akas) x
where
name = '{{resourceName}}'
and resource_group = '{{resourceName}}'
group by
name, title;
1 change: 1 addition & 0 deletions azure-test/tests/azure_bastion_host/variables.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
100 changes: 100 additions & 0 deletions azure-test/tests/azure_bastion_host/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@

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 = "d46d7416-f95f-4771-bbb5-529d4c76659c"
description = "Azure subscription used for the test."
}

provider "azurerm" {
# Cannot be passed as a variable
environment = var.azure_environment
subscription_id = var.azure_subscription
features {}
}

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_virtual_network" "named_test_resource" {
name = var.resource_name
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.named_test_resource.location
resource_group_name = azurerm_resource_group.named_test_resource.name
}

resource "azurerm_subnet" "named_test_resource" {
name = "AzureBastionSubnet"
resource_group_name = azurerm_resource_group.named_test_resource.name
virtual_network_name = azurerm_virtual_network.named_test_resource.name
address_prefixes = ["10.0.1.0/24"]
}

resource "azurerm_public_ip" "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
allocation_method = "Static"
sku = "Standard"
}

resource "azurerm_bastion_host" "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

ip_configuration {
name = "configuration"
subnet_id = azurerm_subnet.named_test_resource.id
public_ip_address_id = azurerm_public_ip.named_test_resource.id
}
tags = {
name = var.resource_name
}
}


output "resource_aka" {
value = "azure://${azurerm_bastion_host.named_test_resource.id}"
}

output "resource_aka_lower" {
value = "azure://${lower(azurerm_bastion_host.named_test_resource.id)}"
}

output "resource_name" {
value = var.resource_name
}

output "resource_id" {
value = lower(azurerm_bastion_host.named_test_resource.id)
}

output "resource_dns_name" {
value = azurerm_bastion_host.named_test_resource.dns_name
}

output "subscription_id" {
value = var.azure_subscription
}
1 change: 1 addition & 0 deletions azure/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"azure_application_security_group": tableAzureApplicationSecurityGroup(ctx),
"azure_automation_account": tableAzureApAutomationAccount(ctx),
"azure_automation_variable": tableAzureApAutomationVariable(ctx),
"azure_bastion_host": tableAzureBastionHost(ctx),
"azure_batch_account": tableAzureBatchAccount(ctx),
"azure_cognitive_account": tableAzureCognitiveAccount(ctx),
"azure_compute_availability_set": tableAzureComputeAvailabilitySet(ctx),
Expand Down
186 changes: 186 additions & 0 deletions azure/table_azure_bastion_host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package azure

import (
"context"

"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2020-05-01/network"
"github.com/turbot/steampipe-plugin-sdk/v5/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/v5/plugin/transform"

"github.com/turbot/steampipe-plugin-sdk/v5/plugin"
)

//// TABLE DEFINITION ////

func tableAzureBastionHost(_ context.Context) *plugin.Table {
return &plugin.Table{
Name: "azure_bastion_host",
Description: "Azure Bastion Host",
Get: &plugin.GetConfig{
KeyColumns: plugin.AllColumns([]string{"name", "resource_group"}),
Hydrate: getBastionHost,
IgnoreConfig: &plugin.IgnoreConfig{
ShouldIgnoreErrorFunc: isNotFoundError([]string{"ResourceGroupNotFound", "ResourceNotFound", "404"}),
},
},
List: &plugin.ListConfig{
Hydrate: listBastionHosts,
},
Columns: azureColumns([]*plugin.Column{
{
Name: "name",
Type: proto.ColumnType_STRING,
Description: "The friendly name that identifies the bastion host.",
},
{
Name: "id",
Description: "Contains ID to identify a bastion host uniquely.",
Transform: transform.FromGo(),
Type: proto.ColumnType_STRING,
},
{
Name: "dns_name",
Description: "FQDN for the endpoint on which bastion host is accessible.",
Transform: transform.FromField("BastionHostPropertiesFormat.DNSName"),
Type: proto.ColumnType_STRING,
},
{
Name: "etag",
Description: "A unique read-only string that changes whenever the resource is updated.",
Type: proto.ColumnType_STRING,
},
{
Name: "provisioning_state",
Description: "The provisioning state of the bastion host resource.",
Transform: transform.FromField("BastionHostPropertiesFormat.ProvisioningState"),
Type: proto.ColumnType_STRING,
},
{
Name: "type",
Description: "The resource type of the bastion host.",
Type: proto.ColumnType_STRING,
},
{
Name: "ip_configurations",
Description: "IP configuration of the bastion host resource.",
Transform: transform.FromField("BastionHostPropertiesFormat.IPConfigurations"),
Type: proto.ColumnType_JSON,
},

// Steampipe standard columns
{
Name: "akas",
Description: ColumnDescriptionAkas,
Transform: transform.FromField("ID").Transform(idToAkas),
Type: proto.ColumnType_JSON,
},
{
Name: "tags",
Description: ColumnDescriptionTags,
Type: proto.ColumnType_JSON,
},
{
Name: "title",
Description: ColumnDescriptionTitle,
Transform: transform.FromField("Name"),
Type: proto.ColumnType_STRING,
},

// Azure standard columns
{
Name: "region",
Description: ColumnDescriptionRegion,
Transform: transform.FromField("Location").Transform(toLower),
Type: proto.ColumnType_STRING,
},
{
Name: "resource_group",
Description: ColumnDescriptionResourceGroup,
Transform: transform.FromField("ID").Transform(extractResourceGroupFromID),
Type: proto.ColumnType_STRING,
},
}),
}
}

//// FETCH FUNCTIONS ////

func listBastionHosts(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
session, err := GetNewSession(ctx, d, "MANAGEMENT")
if err != nil {
logger.Error("azure_bastion_host.listBastionHosts", "client_error", err)
return nil, err
}
subscriptionID := session.SubscriptionID
bastionClient := network.NewBastionHostsClientWithBaseURI(session.ResourceManagerEndpoint, subscriptionID)
bastionClient.Authorizer = session.Authorizer

result, err := bastionClient.List(ctx)
if err != nil {
logger.Error("azure_bastion_host.listBastionHosts", "api_error", err)
return nil, err
}

for _, host := range result.Values() {
d.StreamListItem(ctx, host)

// Check if context has been cancelled or if the limit has been hit (if specified)
// if there is a limit, it will return the number of rows required to reach this limit
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}

for result.NotDone() {
err = result.NextWithContext(ctx)
if err != nil {
logger.Error("azure_bastion_host.listBastionHosts", "api_error", err)
return nil, err
}

for _, host := range result.Values() {
d.StreamListItem(ctx, host)

// Check if context has been cancelled or if the limit has been hit (if specified)
// if there is a limit, it will return the number of rows required to reach this limit
if d.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
}

return nil, nil
}

func getBastionHost(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) {
logger := plugin.Logger(ctx)
name := d.EqualsQuals["name"].GetStringValue()
resourceGroup := d.EqualsQuals["resource_group"].GetStringValue()
if name == "" || resourceGroup == "" {
return nil, nil
}

session, err := GetNewSession(ctx, d, "MANAGEMENT")
if err != nil {
logger.Error("azure_bastion_host.getBastionHost", "client_error", err)
return nil, err
}
subscriptionID := session.SubscriptionID
bastionClient := network.NewBastionHostsClientWithBaseURI(session.ResourceManagerEndpoint, subscriptionID)
bastionClient.Authorizer = session.Authorizer

result, err := bastionClient.Get(ctx, resourceGroup, name)
if err != nil {
logger.Error("azure_bastion_host.getBastionHost", "api_error", err)
return nil, err
}

// In some cases resource does not give any notFound error
// instead of notFound error, it returns empty data
if result.ID != nil {
return result, nil
}

return nil, nil
}
Loading

0 comments on commit 85c767e

Please sign in to comment.