1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
// Package azure provides node discovery for Microsoft Azure.
package azure
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2015-06-15/network"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure/auth"
)
type Provider struct {
userAgent string
}
func (p *Provider) SetUserAgent(s string) {
p.userAgent = s
}
func (p *Provider) Help() string {
return `Microsoft Azure:
provider: "azure"
tenant_id: The id of the tenant
client_id: The id of the client
subscription_id: The id of the subscription
secret_access_key: The authentication credential
**NOTE** The secret_access_key value often may have an equals sign in it's value,
especially if generated from the Azure Portal. So is important to wrap in single quotes
eg. secret_acccess_key='fpOfcHQJAQBczjAxiVpeyLmX1M0M0KPBST+GU2GvEN4='
Variables can also be provided by environmental variables:
export ARM_SUBSCRIPTION_ID for subscription
export ARM_TENANT_ID for tenant
export ARM_CLIENT_ID for client
export ARM_CLIENT_SECRET for secret access key
If none of those options are given, the Azure SDK is using the default environment based authentication outlined
here https://docs.microsoft.com/en-us/go/azure/azure-sdk-go-authorization#use-environment-based-authentication
This will fallback to MSI if nothing is explicitly specified.
Use these configuration parameters when using tags:
tag_name: The name of the tag to filter on
tag_value: The value of the tag to filter on
Use these configuration parameters when using Virtual Machine Scale Sets:
resource_group: The name of the resource group to filter on
vm_scale_set: The name of the virtual machine scale set to filter on
When using tags the only permission needed is Microsoft.Network/networkInterfaces/*
When using Virtual Machine Scale Sets the only role action needed is Microsoft.Compute/virtualMachineScaleSets/*/read.
It is recommended you make a dedicated key used only for auto-joining.
`
}
// argsOrEnv allows you to pick an environmental variable for a setting if the arg is not set
func argsOrEnv(args map[string]string, key, env string) string {
if value, ok := args[key]; ok {
return value
}
return os.Getenv(env)
}
func (p *Provider) Addrs(args map[string]string, l *log.Logger) ([]string, error) {
var authorizer autorest.Authorizer
if args["provider"] != "azure" {
return nil, fmt.Errorf("discover-azure: invalid provider " + args["provider"])
}
if l == nil {
l = log.New(ioutil.Discard, "", 0)
}
// check for environmental variables, and use if the argument hasn't been set in config
tenantID := argsOrEnv(args, "tenant_id", "ARM_TENANT_ID")
clientID := argsOrEnv(args, "client_id", "ARM_CLIENT_ID")
subscriptionID := argsOrEnv(args, "subscription_id", "ARM_SUBSCRIPTION_ID")
secretKey := argsOrEnv(args, "secret_access_key", "ARM_CLIENT_SECRET")
// Try to use the argument and environment provided arguments first, if this fails fall back to the Azure
// SDK provided methods
if tenantID != "" && clientID != "" && secretKey != "" {
var err error
authorizer, err = auth.NewClientCredentialsConfig(clientID, secretKey, tenantID).Authorizer()
if err != nil {
return nil, fmt.Errorf("discover-azure (ClientCredentials): %s", err)
}
} else {
var err error
authorizer, err = auth.NewAuthorizerFromEnvironment()
if err != nil {
return nil, fmt.Errorf("discover-azure (EnvironmentCredentials): %s", err)
}
}
// Use tags if using network interfaces
tagName := args["tag_name"]
tagValue := args["tag_value"]
// Use resourceGroup and vmScaleSet if using vm scale sets
resourceGroup := args["resource_group"]
vmScaleSet := args["vm_scale_set"]
// Setup the client using autorest; followed the structure from Terraform
vmnet := network.NewInterfacesClient(subscriptionID)
vmnet.Sender = autorest.CreateSender(autorest.WithLogging(l))
vmnet.Authorizer = authorizer
if p.userAgent != "" {
vmnet.Client.UserAgent = p.userAgent
}
if tagName != "" && tagValue != "" && resourceGroup == "" && vmScaleSet == "" {
l.Printf("[DEBUG] discover-azure: using tag method. tag_name: %s, tag_value: %s", tagName, tagValue)
return fetchAddrsWithTags(tagName, tagValue, vmnet, l)
} else if resourceGroup != "" && vmScaleSet != "" && tagName == "" && tagValue == "" {
l.Printf("[DEBUG] discover-azure: using vm scale set method. resource_group: %s, vm_scale_set: %s", resourceGroup, vmScaleSet)
return fetchAddrsWithVmScaleSet(resourceGroup, vmScaleSet, vmnet, l)
} else {
l.Printf("[ERROR] discover-azure: tag_name: %s, tag_value: %s", tagName, tagValue)
l.Printf("[ERROR] discover-azure: resource_group %s, vm_scale_set %s", resourceGroup, vmScaleSet)
return nil, fmt.Errorf("discover-azure: unclear configuration. use (tag name and value) or (resouce_group and vm_scale_set)")
}
}
func fetchAddrsWithTags(tagName string, tagValue string, vmnet network.InterfacesClient, l *log.Logger) ([]string, error) {
// Get all network interfaces across resource groups
// unless there is a compelling reason to restrict
ctx := context.Background()
netres, err := vmnet.ListAll(ctx)
if err != nil {
return nil, fmt.Errorf("discover-azure: %s", err)
}
if len(netres.Values()) == 0 {
return nil, fmt.Errorf("discover-azure: no interfaces")
}
// Choose any PrivateIPAddress with the matching tag
var addrs []string
for _, v := range netres.Values() {
var id string
if v.ID != nil {
id = *v.ID
} else {
id = "ip address id not found"
}
if v.Tags == nil {
l.Printf("[DEBUG] discover-azure: Interface %s has no tags", id)
continue
}
tv := v.Tags[tagName] // *string
if tv == nil {
l.Printf("[DEBUG] discover-azure: Interface %s did not have tag: %s", id, tagName)
continue
}
if *tv != tagValue {
l.Printf("[DEBUG] discover-azure: Interface %s tag value was: %s which did not match: %s", id, *tv, tagValue)
continue
}
if v.IPConfigurations == nil {
l.Printf("[DEBUG] discover-azure: Interface %s had no ip configuration", id)
continue
}
for _, x := range *v.IPConfigurations {
if x.PrivateIPAddress == nil {
l.Printf("[DEBUG] discover-azure: Interface %s had no private ip", id)
continue
}
iAddr := *x.PrivateIPAddress
l.Printf("[DEBUG] discover-azure: Interface %s has private ip: %s", id, iAddr)
addrs = append(addrs, iAddr)
}
}
l.Printf("[DEBUG] discover-azure: Found ip addresses: %v", addrs)
return addrs, nil
}
func fetchAddrsWithVmScaleSet(resourceGroup string, vmScaleSet string, vmnet network.InterfacesClient, l *log.Logger) ([]string, error) {
// Get all network interfaces for a specific virtual machine scale set
ctx := context.Background()
netres, err := vmnet.ListVirtualMachineScaleSetNetworkInterfaces(ctx, resourceGroup, vmScaleSet)
if err != nil {
return nil, fmt.Errorf("discover-azure: %s", err)
}
if len(netres.Values()) == 0 {
return nil, fmt.Errorf("discover-azure: no interfaces")
}
// Get all of PrivateIPAddresses we can.
var addrs []string
for _, v := range netres.Values() {
var id string
if v.ID != nil {
id = *v.ID
} else {
id = "ip address id not found"
}
if v.IPConfigurations == nil {
l.Printf("[DEBUG] discover-azure: Interface %s had no ip configuration", id)
continue
}
for _, x := range *v.IPConfigurations {
if x.PrivateIPAddress == nil {
l.Printf("[DEBUG] discover-azure: Interface %s had no private ip", id)
continue
}
iAddr := *x.PrivateIPAddress
l.Printf("[DEBUG] discover-azure: Interface %s has private ip: %s", id, iAddr)
addrs = append(addrs, iAddr)
}
}
l.Printf("[DEBUG] discover-azure: Found ip addresses: %v", addrs)
return addrs, nil
}
|