diff --git a/azure/plugin.go b/azure/plugin.go index 533c7dbc..2e9e63cd 100644 --- a/azure/plugin.go +++ b/azure/plugin.go @@ -61,6 +61,7 @@ func Plugin(ctx context.Context) *plugin.Plugin { "azure_compute_virtual_machine_scale_set": tableAzureComputeVirtualMachineScaleSet(ctx), "azure_compute_virtual_machine_scale_set_network_interface": tableAzureComputeVirtualMachineScaleSetNetworkInterface(ctx), "azure_compute_virtual_machine_scale_set_vm": tableAzureComputeVirtualMachineScaleSetVm(ctx), + "azure_compute_ssh_key": tableAzureComputeSshKey(ctx), "azure_container_registry": tableAzureContainerRegistry(ctx), "azure_cosmosdb_account": tableAzureCosmosDBAccount(ctx), "azure_cosmosdb_mongo_database": tableAzureCosmosDBMongoDatabase(ctx), diff --git a/azure/table_azure_compute_ssh_key.go b/azure/table_azure_compute_ssh_key.go new file mode 100644 index 00000000..741ac386 --- /dev/null +++ b/azure/table_azure_compute_ssh_key.go @@ -0,0 +1,160 @@ +package azure + +import ( + "context" + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2020-06-01/compute" + "github.com/turbot/steampipe-plugin-sdk/v4/grpc/proto" + "github.com/turbot/steampipe-plugin-sdk/v4/plugin" + "github.com/turbot/steampipe-plugin-sdk/v4/plugin/transform" +) + +//// TABLE DEFINITION //// + +func tableAzureComputeSshKey(_ context.Context) *plugin.Table { + return &plugin.Table{ + Name: "azure_compute_ssh_key", + Description: "Azure Compute SSH Key", + Get: &plugin.GetConfig{ + KeyColumns: plugin.AllColumns([]string{"name", "resource_group"}), + Hydrate: getAzureComputeSshKey, + IgnoreConfig: &plugin.IgnoreConfig{ + ShouldIgnoreErrorFunc: isNotFoundError([]string{"ResourceGroupNotFound", "ResourceNotFound", "404"}), + }, + }, + List: &plugin.ListConfig{ + Hydrate: listAzureComputeSshKeys, + }, + Columns: azureColumns([]*plugin.Column{ + { + Name: "id", + Description: "The unique id identifying the resource in subscription", + Type: proto.ColumnType_STRING, + Transform: transform.FromGo(), + }, + { + Name: "name", + Description: "Name of the ssh key", + Type: proto.ColumnType_STRING, + }, + { + Name: "type", + Description: "The type of the resource in Azure", + Type: proto.ColumnType_STRING, + }, + { + Name: "public_key", + Description: "SSH public key", + Type: proto.ColumnType_STRING, + Transform: transform.FromField("SSHPublicKeyResourceProperties.PublicKey"), + }, + + // Azure standard columns + { + Name: "region", + Description: ColumnDescriptionRegion, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Location").Transform(toLower), + }, + { + Name: "tags", + Description: ColumnDescriptionTags, + Type: proto.ColumnType_JSON, + }, + { + Name: "resource_group", + Description: ColumnDescriptionResourceGroup, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("ID").Transform(extractResourceGroupFromID), + }, + + // Steampipe standard columns + { + Name: "title", + Description: ColumnDescriptionTitle, + Type: proto.ColumnType_STRING, + Transform: transform.FromField("Name"), + }, + { + Name: "akas", + Description: ColumnDescriptionAkas, + Type: proto.ColumnType_JSON, + Transform: transform.FromField("ID").Transform(idToAkas), + }, + }), + } +} + +//// LIST FUNCTION //// + +func listAzureComputeSshKeys(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("listAzureComputeSshKeys") + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + + subscriptionID := session.SubscriptionID + client := compute.NewSSHPublicKeysClientWithBaseURI(session.ResourceManagerEndpoint, subscriptionID) + client.Authorizer = session.Authorizer + result, err := client.ListBySubscription(ctx) + if err != nil { + return nil, err + } + + for _, key := range result.Values() { + d.StreamListItem(ctx, key) + // 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.QueryStatus.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + + for result.NotDone() { + err = result.NextWithContext(ctx) + if err != nil { + return nil, err + } + + for _, key := range result.Values() { + d.StreamListItem(ctx, key) + // 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.QueryStatus.RowsRemaining(ctx) == 0 { + return nil, nil + } + } + } + + return nil, nil +} + +//// HYDRATE FUNCTION //// + +func getAzureComputeSshKey(ctx context.Context, d *plugin.QueryData, h *plugin.HydrateData) (interface{}, error) { + plugin.Logger(ctx).Trace("getAzureComputeSshKey") + + name := d.KeyColumnQuals["name"].GetStringValue() + resourceGroup := d.KeyColumnQuals["resource_group"].GetStringValue() + + session, err := GetNewSession(ctx, d, "MANAGEMENT") + if err != nil { + return nil, err + } + subscriptionID := session.SubscriptionID + client := compute.NewSSHPublicKeysClientWithBaseURI(session.ResourceManagerEndpoint, 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 +} diff --git a/docs/tables/table_azure_compute_ssh_key.md b/docs/tables/table_azure_compute_ssh_key.md new file mode 100644 index 00000000..f6f68e7f --- /dev/null +++ b/docs/tables/table_azure_compute_ssh_key.md @@ -0,0 +1,18 @@ +# Table: table_azure_compute_ssh_key + +Azure SSH public key used by VM. + +## Examples + +### Retrieve SSH public key by name + +```sql +select + name, + publicKey +from + table_azure_compute_ssh_key +where + name = 'key-name.'; +``` +