func (clst *azureCluster) configureStorageAccount(location string) (storage.Account, error) { var storageAccount storage.Account // Storage name needs to be globally unique, with a limit of 24 characters. storageName := randomString(24) cna, err := clst.azureClient.storageCheckName( storage.AccountCheckNameAvailabilityParameters{ Name: &storageName, Type: stringPtr("Microsoft.Storage/storageAccounts")}) if err != nil { return storageAccount, err } if !*cna.NameAvailable { return storageAccount, errors.New("storage account is not available") } properties := storage.AccountPropertiesCreateParameters{ AccountType: storage.AccountType(storageType), } param := storage.AccountCreateParameters{ Location: &location, Properties: &properties, Tags: &map[string]*string{nsTag: &clst.namespace}, } cancel := make(chan struct{}) if _, err := clst.azureClient.storageCreate(resourceGroupName, storageName, param, cancel); err != nil { return storageAccount, err } return clst.azureClient.storageGet(resourceGroupName, storageName) }
// Create creates the virtual machine. func (d *Driver) Create() error { // NOTE(ahmetalpbalkan): We can probably parallelize the sh*t out of this. // However that would lead to a concurrency logic and while creation of a // resource fails, other ones would be kicked off, which could lead to a // resource leak. This is slower but safer. c, err := d.newAzureClient() if err != nil { return err } var customData string if d.CustomDataFile != "" { buf, err := ioutil.ReadFile(d.CustomDataFile) if err != nil { return err } customData = base64.StdEncoding.EncodeToString(buf) } if err := c.CreateResourceGroup(d.ResourceGroup, d.Location); err != nil { return err } if err := c.CreateAvailabilitySetIfNotExists(d.ctx, d.ResourceGroup, d.AvailabilitySet, d.Location); err != nil { return err } if err := c.CreateNetworkSecurityGroup(d.ctx, d.ResourceGroup, d.naming().NSG(), d.Location, d.ctx.FirewallRules); err != nil { return err } vnetResourceGroup, vNetName := parseVirtualNetwork(d.VirtualNetwork, d.ResourceGroup) if err := c.CreateVirtualNetworkIfNotExists(vnetResourceGroup, vNetName, d.Location); err != nil { return err } if err := c.CreateSubnet(d.ctx, vnetResourceGroup, vNetName, d.SubnetName, d.SubnetPrefix); err != nil { return err } if d.NoPublicIP { log.Info("Not creating a public IP address.") } else { if err := c.CreatePublicIPAddress(d.ctx, d.ResourceGroup, d.naming().IP(), d.Location, d.StaticPublicIP); err != nil { return err } } if err := c.CreateNetworkInterface(d.ctx, d.ResourceGroup, d.naming().NIC(), d.Location, d.ctx.PublicIPAddressID, d.ctx.SubnetID, d.ctx.NetworkSecurityGroupID, d.PrivateIPAddr); err != nil { return err } if err := c.CreateStorageAccount(d.ctx, d.ResourceGroup, d.Location, storage.AccountType(d.StorageType)); err != nil { return err } if err := d.generateSSHKey(d.ctx); err != nil { return err } if err := c.CreateVirtualMachine(d.ResourceGroup, d.naming().VM(), d.Location, d.Size, d.ctx.AvailabilitySetID, d.ctx.NetworkInterfaceID, d.BaseDriver.SSHUser, d.ctx.SSHPublicKey, d.Image, customData, d.ctx.StorageAccount); err != nil { return err } return nil }
// resourceArmStorageAccountUpdate is unusual in the ARM API where most resources have a combined // and idempotent operation for CreateOrUpdate. In particular updating all of the parameters // available requires a call to Update per parameter... func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).storageServiceClient id, err := parseAzureResourceID(d.Id()) if err != nil { return err } storageAccountName := id.Path["storageAccounts"] resourceGroupName := id.ResourceGroup d.Partial(true) if d.HasChange("account_type") { accountType := d.Get("account_type").(string) opts := storage.AccountUpdateParameters{ Properties: &storage.AccountPropertiesUpdateParameters{ AccountType: storage.AccountType(accountType), }, } accResp, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err) } _, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK) if err != nil { return fmt.Errorf("Error updating Azure Storage Account type %q: %s", storageAccountName, err) } d.SetPartial("account_type") } if d.HasChange("tags") { tags := d.Get("tags").(map[string]interface{}) opts := storage.AccountUpdateParameters{ Tags: expandTags(tags), } accResp, err := client.Update(resourceGroupName, storageAccountName, opts) if err != nil { return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err) } _, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK) if err != nil { return fmt.Errorf("Error updating Azure Storage Account tags %q: %s", storageAccountName, err) } d.SetPartial("tags") } d.Partial(false) return nil }
func (c *AzureClient) CreateInstance(instanceId string, parameters interface{}) (string, string, error) { var resourceGroupName, storageAccountName, location string var accountType storage.AccountType switch parameters.(type) { case map[string]interface{}: param := parameters.(map[string]interface{}) if param["resource_group_name"] != nil { resourceGroupName = param["resource_group_name"].(string) } else { resourceGroupName = RESOURCE_GROUP_NAME_PREFIX + instanceId } if param["location"] != nil { location = param["location"].(string) } else { location = LOCATION } if param["account_type"] != nil { accountType = storage.AccountType(param["account_type"].(string)) } else { accountType = storage.StandardLRS } default: resourceGroupName = RESOURCE_GROUP_NAME_PREFIX + instanceId location = LOCATION accountType = storage.StandardLRS } err := c.createResourceGroup(resourceGroupName, location) if err != nil { fmt.Printf("Creating resource group %s failed with error:\n%v\n", resourceGroupName, err) return "", "", err } storageAccountName = STORAGE_ACCOUNT_NAME_PREFIX + strings.Replace(instanceId, "-", "", -1)[0:22] err = c.createStorageAccount(resourceGroupName, storageAccountName, location, accountType) if err != nil { fmt.Printf("Creating storage account %s.%s failed with error:\n%v\n", resourceGroupName, storageAccountName, err) return "", "", err } return resourceGroupName, storageAccountName, nil }
func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).storageServiceClient resourceGroupName := d.Get("resource_group_name").(string) storageAccountName := d.Get("name").(string) accountType := d.Get("account_type").(string) location := d.Get("location").(string) tags := d.Get("tags").(map[string]interface{}) opts := storage.AccountCreateParameters{ Location: &location, Properties: &storage.AccountPropertiesCreateParameters{ AccountType: storage.AccountType(accountType), }, Tags: expandTags(tags), } accResp, err := client.Create(resourceGroupName, storageAccountName, opts) if err != nil { return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err) } _, err = pollIndefinitelyAsNeeded(client.Client, accResp.Response.Response, http.StatusOK) if err != nil { return fmt.Errorf("Error creating Azure Storage Account %q: %s", storageAccountName, err) } // The only way to get the ID back apparently is to read the resource again account, err := client.GetProperties(resourceGroupName, storageAccountName) if err != nil { return fmt.Errorf("Error retrieving Azure Storage Account %q: %s", storageAccountName, err) } d.SetId(*account.ID) return resourceArmStorageAccountRead(d, meta) }
func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).storageServiceClient resourceGroupName := d.Get("resource_group_name").(string) storageAccountName := d.Get("name").(string) accountType := d.Get("account_type").(string) location := d.Get("location").(string) tags := d.Get("tags").(map[string]interface{}) opts := storage.AccountCreateParameters{ Location: &location, Properties: &storage.AccountPropertiesCreateParameters{ AccountType: storage.AccountType(accountType), }, Tags: expandTags(tags), } _, err := client.Create(resourceGroupName, storageAccountName, opts, make(chan struct{})) if err != nil { return fmt.Errorf("Error creating Azure Storage Account '%s': %s", storageAccountName, err) } // The only way to get the ID back apparently is to read the resource again read, err := client.GetProperties(resourceGroupName, storageAccountName) if err != nil { return err } if read.ID == nil { return fmt.Errorf("Cannot read Storage Account %s (resource group %s) ID", storageAccountName, resourceGroupName) } d.SetId(*read.ID) return resourceArmStorageAccountRead(d, meta) }
func validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { err := config.Validate(newCfg, oldCfg) if err != nil { return nil, err } validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } // Ensure required configuration is provided. for _, key := range requiredConfigAttributes { if value, ok := validated[key].(string); !ok || value == "" { return nil, errors.Errorf("%q config not specified", key) } } if oldCfg != nil { // Ensure immutable configuration isn't changed. oldUnknownAttrs := oldCfg.UnknownAttrs() for _, key := range immutableConfigAttributes { oldValue, hadValue := oldUnknownAttrs[key].(string) if hadValue { newValue, haveValue := validated[key].(string) if !haveValue { return nil, errors.Errorf( "cannot remove immutable %q config", key, ) } if newValue != oldValue { return nil, errors.Errorf( "cannot change immutable %q config (%v -> %v)", key, oldValue, newValue, ) } } // It's valid to go from not having to having. } // TODO(axw) figure out how we intend to handle changing // secrets, such as application key } location := canonicalLocation(validated[configAttrLocation].(string)) appId := validated[configAttrAppId].(string) subscriptionId := validated[configAttrSubscriptionId].(string) tenantId := validated[configAttrTenantId].(string) appPassword := validated[configAttrAppPassword].(string) storageAccount, _ := validated[configAttrStorageAccount].(string) storageAccountKey, _ := validated[configAttrStorageAccountKey].(string) storageAccountType := validated[configAttrStorageAccountType].(string) controllerResourceGroup := validated[configAttrControllerResourceGroup].(string) if newCfg.FirewallMode() == config.FwGlobal { // We do not currently support the "global" firewall mode. return nil, errNoFwGlobal } if !isKnownStorageAccountType(storageAccountType) { return nil, errors.Errorf( "invalid storage account type %q, expected one of: %q", storageAccountType, knownStorageAccountTypes, ) } token, err := azure.NewServicePrincipalToken( appId, appPassword, tenantId, azure.AzureResourceManagerScope, ) if err != nil { return nil, errors.Annotate(err, "constructing service principal token") } azureConfig := &azureModelConfig{ newCfg, token, subscriptionId, location, storageAccount, storageAccountKey, storage.AccountType(storageAccountType), controllerResourceGroup, } return azureConfig, nil }
func validateConfig(newCfg, oldCfg *config.Config) (*azureModelConfig, error) { err := config.Validate(newCfg, oldCfg) if err != nil { return nil, err } validated, err := newCfg.ValidateUnknownAttrs(configFields, configDefaults) if err != nil { return nil, err } // Ensure required configuration is provided. for _, key := range requiredConfigAttributes { if value, ok := validated[key].(string); !ok || value == "" { return nil, errors.Errorf("%q config not specified", key) } } if oldCfg != nil { // Ensure immutable configuration isn't changed. oldUnknownAttrs := oldCfg.UnknownAttrs() for _, key := range immutableConfigAttributes { oldValue, hadValue := oldUnknownAttrs[key].(string) if hadValue { newValue, haveValue := validated[key].(string) if !haveValue { return nil, errors.Errorf( "cannot remove immutable %q config", key, ) } if newValue != oldValue { return nil, errors.Errorf( "cannot change immutable %q config (%v -> %v)", key, oldValue, newValue, ) } } // It's valid to go from not having to having. } // TODO(axw) figure out how we intend to handle changing // secrets, such as application key } // Resource group names must not exceed 80 characters. Resource group // names are based on the model UUID and model name, the latter of // which the model creator controls. modelTag := names.NewModelTag(newCfg.UUID()) resourceGroup := resourceGroupName(modelTag, newCfg.Name()) if n := len(resourceGroup); n > resourceNameLengthMax { smallestResourceGroup := resourceGroupName(modelTag, "") return nil, errors.Errorf(`resource group name %q is too long Please choose a model name of no more than %d characters.`, resourceGroup, resourceNameLengthMax-len(smallestResourceGroup), ) } location := canonicalLocation(validated[configAttrLocation].(string)) endpoint := validated[configAttrEndpoint].(string) storageEndpoint := validated[configAttrStorageEndpoint].(string) appId := validated[configAttrAppId].(string) subscriptionId := validated[configAttrSubscriptionId].(string) tenantId := validated[configAttrTenantId].(string) appPassword := validated[configAttrAppPassword].(string) storageAccount, _ := validated[configAttrStorageAccount].(string) storageAccountKey, _ := validated[configAttrStorageAccountKey].(string) storageAccountType := validated[configAttrStorageAccountType].(string) controllerResourceGroup := validated[configAttrControllerResourceGroup].(string) if newCfg.FirewallMode() == config.FwGlobal { // We do not currently support the "global" firewall mode. return nil, errNoFwGlobal } if !isKnownStorageAccountType(storageAccountType) { return nil, errors.Errorf( "invalid storage account type %q, expected one of: %q", storageAccountType, knownStorageAccountTypes, ) } // The Azure storage code wants the endpoint host only, not the URL. storageEndpointURL, err := url.Parse(storageEndpoint) if err != nil { return nil, errors.Annotate(err, "parsing storage endpoint URL") } token, err := azure.NewServicePrincipalToken( appId, appPassword, tenantId, azure.AzureResourceManagerScope, ) if err != nil { return nil, errors.Annotate(err, "constructing service principal token") } azureConfig := &azureModelConfig{ newCfg, token, subscriptionId, location, endpoint, storageEndpointURL.Host, storageAccount, storageAccountKey, storage.AccountType(storageAccountType), controllerResourceGroup, } return azureConfig, nil }