func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { return &compute.VirtualMachineCaptureParameters{ DestinationContainerName: &c.CaptureContainerName, VhdPrefix: &c.CaptureNamePrefix, OverwriteVhds: to.BoolPtr(false), } }
func newOSProfile(vmName string, instanceConfig *instancecfg.InstanceConfig) (*compute.OSProfile, os.OSType, error) { logger.Debugf("creating OS profile for %q", vmName) customData, err := providerinit.ComposeUserData(instanceConfig, nil, AzureRenderer{}) if err != nil { return nil, os.Unknown, errors.Annotate(err, "composing user data") } osProfile := &compute.OSProfile{ ComputerName: to.StringPtr(vmName), CustomData: to.StringPtr(string(customData)), } seriesOS, err := jujuseries.GetOSFromSeries(instanceConfig.Series) if err != nil { return nil, os.Unknown, errors.Trace(err) } switch seriesOS { case os.Ubuntu, os.CentOS, os.Arch: // SSH keys are handled by custom data, but must also be // specified in order to forego providing a password, and // disable password authentication. publicKeys := []compute.SSHPublicKey{{ Path: to.StringPtr("/home/ubuntu/.ssh/authorized_keys"), KeyData: to.StringPtr(instanceConfig.AuthorizedKeys), }} osProfile.AdminUsername = to.StringPtr("ubuntu") osProfile.LinuxConfiguration = &compute.LinuxConfiguration{ DisablePasswordAuthentication: to.BoolPtr(true), SSH: &compute.SSHConfiguration{PublicKeys: &publicKeys}, } case os.Windows: osProfile.AdminUsername = to.StringPtr("JujuAdministrator") // A password is required by Azure, but we will never use it. // We generate something sufficiently long and random that it // should be infeasible to guess. osProfile.AdminPassword = to.StringPtr(randomAdminPassword()) osProfile.WindowsConfiguration = &compute.WindowsConfiguration{ ProvisionVMAgent: to.BoolPtr(true), EnableAutomaticUpdates: to.BoolPtr(true), // TODO(?) add WinRM configuration here. } default: return nil, os.Unknown, errors.NotSupportedf("%s", seriesOS) } return osProfile, seriesOS, nil }
func createVirtualMachine( group resources.ResourceGroup, vmName, adminName, adminPassword string, availSet compute.AvailabilitySet, networkInterface network.Interface, arm arm.Client) error { vmc := arm.VirtualMachines() netRefs := make([]compute.NetworkInterfaceReference, 1, 1) netRefs[0] = compute.NetworkInterfaceReference{ID: networkInterface.ID} groupName := *group.Name accountName := groupName vmParams := compute.VirtualMachine{ Location: group.Location, Properties: &compute.VirtualMachineProperties{ AvailabilitySet: &compute.SubResource{ID: availSet.ID}, HardwareProfile: &compute.HardwareProfile{VMSize: compute.StandardA0}, NetworkProfile: &compute.NetworkProfile{NetworkInterfaces: &netRefs}, StorageProfile: &compute.StorageProfile{ ImageReference: &compute.ImageReference{ Publisher: to.StringPtr("MicrosoftWindowsServer"), Offer: to.StringPtr("WindowsServer"), Sku: to.StringPtr("2012-R2-Datacenter"), Version: to.StringPtr("latest"), }, OsDisk: &compute.OSDisk{ Name: to.StringPtr("mytestod1"), CreateOption: compute.FromImage, Vhd: &compute.VirtualHardDisk{ URI: to.StringPtr("http://" + accountName + ".blob.core.windows.net/vhds/mytestod1.vhd"), }, }, }, OsProfile: &compute.OSProfile{ AdminUsername: to.StringPtr(adminName), AdminPassword: to.StringPtr(adminPassword), ComputerName: to.StringPtr(vmName), WindowsConfiguration: &compute.WindowsConfiguration{ProvisionVMAgent: to.BoolPtr(true)}, }, }, } if _, err := vmc.CreateOrUpdate(groupName, vmName, vmParams); err != nil { return fmt.Errorf("Failed to create virtual machine '%s' in location '%s': '%s'\n", vmName, *group.Location, err.Error()) } return nil }
// createVMExtension creates a CustomScript VM extension for the given VM // which will execute the CustomData on the machine as a script. func createVMExtension( vmExtensionClient compute.VirtualMachineExtensionsClient, os jujuos.OSType, resourceGroup, vmName, location string, vmTags map[string]string, ) error { var commandToExecute, extensionPublisher, extensionType, extensionVersion string switch os { case jujuos.Windows: commandToExecute = windowsExecuteCustomScriptCommand extensionPublisher = windowsCustomScriptPublisher extensionType = windowsCustomScriptType extensionVersion = windowsCustomScriptVersion case jujuos.CentOS: commandToExecute = linuxExecuteCustomScriptCommand extensionPublisher = linuxCustomScriptPublisher extensionType = linuxCustomScriptType extensionVersion = linuxCustomScriptVersion default: // Ubuntu renders CustomData as cloud-config, and interprets // it with cloud-init. Windows and CentOS do not use cloud-init // on Azure. return errors.NotSupportedf("CustomScript extension for OS %q", os) } extensionSettings := map[string]*string{ "commandToExecute": to.StringPtr(commandToExecute), } extension := compute.VirtualMachineExtension{ Location: to.StringPtr(location), Tags: toTagsPtr(vmTags), Properties: &compute.VirtualMachineExtensionProperties{ Publisher: to.StringPtr(extensionPublisher), Type: to.StringPtr(extensionType), TypeHandlerVersion: to.StringPtr(extensionVersion), AutoUpgradeMinorVersion: to.BoolPtr(true), Settings: &extensionSettings, }, } _, err := vmExtensionClient.CreateOrUpdate( resourceGroup, vmName, extensionName, extension, ) return err }
func (s *environSuite) SetUpTest(c *gc.C) { s.BaseSuite.SetUpTest(c) s.storageClient = azuretesting.MockStorageClient{} s.sender = nil s.provider, _ = newProviders(c, azure.ProviderConfig{ Sender: &s.sender, RequestInspector: requestRecorder(&s.requests), NewStorageClient: s.storageClient.NewClient, }) emptyTags := make(map[string]*string) s.tags = map[string]*string{ "juju-machine-name": to.StringPtr("machine-0"), } vmSizes := []compute.VirtualMachineSize{{ Name: to.StringPtr("Standard_D1"), NumberOfCores: to.IntPtr(1), OsDiskSizeInMB: to.IntPtr(1047552), ResourceDiskSizeInMB: to.IntPtr(51200), MemoryInMB: to.IntPtr(3584), MaxDataDiskCount: to.IntPtr(2), }} s.vmSizes = &compute.VirtualMachineSizeListResult{Value: &vmSizes} s.storageNameAvailabilityResult = &storage.CheckNameAvailabilityResult{ NameAvailable: to.BoolPtr(true), } s.storageAccount = &storage.Account{ Name: to.StringPtr("my-storage-account"), Type: to.StringPtr("Standard_LRS"), Properties: &storage.AccountProperties{ PrimaryEndpoints: &storage.Endpoints{ Blob: to.StringPtr(fmt.Sprintf("https://%s.blob.storage.azurestack.local/", fakeStorageAccount)), }, }, } s.storageAccountKeys = &storage.AccountKeys{ Key1: to.StringPtr("key-1"), } addressPrefixes := make([]string, 256) for i := range addressPrefixes { addressPrefixes[i] = fmt.Sprintf("10.%d.0.0/16", i) } s.vnet = &network.VirtualNetwork{ ID: to.StringPtr("juju-internal"), Name: to.StringPtr("juju-internal"), Location: to.StringPtr("westus"), Tags: &emptyTags, Properties: &network.VirtualNetworkPropertiesFormat{ AddressSpace: &network.AddressSpace{&addressPrefixes}, }, } s.subnet = &network.Subnet{ ID: to.StringPtr("subnet-id"), Name: to.StringPtr("juju-testenv-model-deadbeef-0bad-400d-8000-4b1d0d06f00d"), Properties: &network.SubnetPropertiesFormat{ AddressPrefix: to.StringPtr("10.0.0.0/16"), }, } s.ubuntuServerSKUs = []compute.VirtualMachineImageResource{ {Name: to.StringPtr("12.04-LTS")}, {Name: to.StringPtr("12.10")}, {Name: to.StringPtr("14.04-LTS")}, {Name: to.StringPtr("15.04")}, {Name: to.StringPtr("15.10")}, } s.publicIPAddress = &network.PublicIPAddress{ ID: to.StringPtr("public-ip-id"), Name: to.StringPtr("machine-0-public-ip"), Location: to.StringPtr("westus"), Tags: &s.tags, Properties: &network.PublicIPAddressPropertiesFormat{ PublicIPAllocationMethod: network.Dynamic, IPAddress: to.StringPtr("1.2.3.4"), }, } // Existing IPs/NICs. These are the results of querying NICs so we // can tell which IP to allocate. oldIPConfigurations := []network.InterfaceIPConfiguration{{ ID: to.StringPtr("ip-configuration-0-id"), Name: to.StringPtr("ip-configuration-0"), Properties: &network.InterfaceIPConfigurationPropertiesFormat{ PrivateIPAddress: to.StringPtr("10.0.0.4"), PrivateIPAllocationMethod: network.Static, Subnet: &network.SubResource{ID: s.subnet.ID}, }, }} oldNetworkInterfaces := []network.Interface{{ ID: to.StringPtr("network-interface-0-id"), Name: to.StringPtr("network-interface-0"), Properties: &network.InterfacePropertiesFormat{ IPConfigurations: &oldIPConfigurations, Primary: to.BoolPtr(true), }, }} s.oldNetworkInterfaces = &network.InterfaceListResult{ Value: &oldNetworkInterfaces, } // nsgID is the name of the internal network security group. This NSG // is created when the environment is created. nsgID := path.Join( "/subscriptions", fakeSubscriptionId, "resourceGroups", "juju-testenv-model-"+testing.ModelTag.Id(), "providers/Microsoft.Network/networkSecurityGroups/juju-internal", ) // The newly created IP/NIC. newIPConfigurations := []network.InterfaceIPConfiguration{{ ID: to.StringPtr("ip-configuration-1-id"), Name: to.StringPtr("primary"), Properties: &network.InterfaceIPConfigurationPropertiesFormat{ PrivateIPAddress: to.StringPtr("10.0.0.5"), PrivateIPAllocationMethod: network.Static, Subnet: &network.SubResource{ID: s.subnet.ID}, PublicIPAddress: &network.SubResource{ID: s.publicIPAddress.ID}, }, }} s.newNetworkInterface = &network.Interface{ ID: to.StringPtr("network-interface-1-id"), Name: to.StringPtr("network-interface-1"), Location: to.StringPtr("westus"), Tags: &s.tags, Properties: &network.InterfacePropertiesFormat{ IPConfigurations: &newIPConfigurations, NetworkSecurityGroup: &network.SubResource{to.StringPtr(nsgID)}, }, } s.jujuAvailabilitySet = &compute.AvailabilitySet{ ID: to.StringPtr("juju-availability-set-id"), Name: to.StringPtr("juju"), Location: to.StringPtr("westus"), Tags: &emptyTags, } sshPublicKeys := []compute.SSHPublicKey{{ Path: to.StringPtr("/home/ubuntu/.ssh/authorized_keys"), KeyData: to.StringPtr(testing.FakeAuthKeys), }} networkInterfaceReferences := []compute.NetworkInterfaceReference{{ ID: s.newNetworkInterface.ID, Properties: &compute.NetworkInterfaceReferenceProperties{ Primary: to.BoolPtr(true), }, }} s.virtualMachine = &compute.VirtualMachine{ ID: to.StringPtr("machine-0-id"), Name: to.StringPtr("machine-0"), Location: to.StringPtr("westus"), Tags: &s.tags, Properties: &compute.VirtualMachineProperties{ HardwareProfile: &compute.HardwareProfile{ VMSize: "Standard_D1", }, StorageProfile: &compute.StorageProfile{ ImageReference: &compute.ImageReference{ Publisher: to.StringPtr("Canonical"), Offer: to.StringPtr("UbuntuServer"), Sku: to.StringPtr("12.10"), Version: to.StringPtr("latest"), }, OsDisk: &compute.OSDisk{ Name: to.StringPtr("machine-0"), CreateOption: compute.FromImage, Caching: compute.ReadWrite, Vhd: &compute.VirtualHardDisk{ URI: to.StringPtr(fmt.Sprintf( "https://%s.blob.storage.azurestack.local/osvhds/machine-0.vhd", fakeStorageAccount, )), }, }, }, OsProfile: &compute.OSProfile{ ComputerName: to.StringPtr("machine-0"), CustomData: to.StringPtr("<juju-goes-here>"), AdminUsername: to.StringPtr("ubuntu"), LinuxConfiguration: &compute.LinuxConfiguration{ DisablePasswordAuthentication: to.BoolPtr(true), SSH: &compute.SSHConfiguration{ PublicKeys: &sshPublicKeys, }, }, }, NetworkProfile: &compute.NetworkProfile{ NetworkInterfaces: &networkInterfaceReferences, }, AvailabilitySet: &compute.SubResource{ID: s.jujuAvailabilitySet.ID}, ProvisioningState: to.StringPtr("Successful"), }, } }
func newNetworkProfile( client network.ManagementClient, vmName string, apiPort *int, internalSubnet *network.Subnet, resourceGroup string, location string, tags map[string]string, ) (*compute.NetworkProfile, error) { logger.Debugf("creating network profile for %q", vmName) // Create a public IP for the NIC. Public IP addresses are dynamic. logger.Debugf("- allocating public IP address") pipClient := network.PublicIPAddressesClient{client} publicIPAddressParams := network.PublicIPAddress{ Location: to.StringPtr(location), Tags: toTagsPtr(tags), Properties: &network.PublicIPAddressPropertiesFormat{ PublicIPAllocationMethod: network.Dynamic, }, } publicIPAddressName := vmName + "-public-ip" publicIPAddress, err := pipClient.CreateOrUpdate(resourceGroup, publicIPAddressName, publicIPAddressParams) if err != nil { return nil, errors.Annotatef(err, "creating public IP address for %q", vmName) } // Determine the next available private IP address. nicClient := network.InterfacesClient{client} privateIPAddress, err := nextSubnetIPAddress(nicClient, resourceGroup, internalSubnet) if err != nil { return nil, errors.Annotatef(err, "querying private IP addresses") } // Create a primary NIC for the machine. This needs to be static, so // that we can create security rules that don't become invalid. logger.Debugf("- creating primary NIC") ipConfigurations := []network.InterfaceIPConfiguration{{ Name: to.StringPtr("primary"), Properties: &network.InterfaceIPConfigurationPropertiesFormat{ PrivateIPAddress: to.StringPtr(privateIPAddress), PrivateIPAllocationMethod: network.Static, Subnet: &network.SubResource{internalSubnet.ID}, PublicIPAddress: &network.SubResource{publicIPAddress.ID}, }, }} primaryNicName := vmName + "-primary" primaryNicParams := network.Interface{ Location: to.StringPtr(location), Tags: toTagsPtr(tags), Properties: &network.InterfacePropertiesFormat{ IPConfigurations: &ipConfigurations, }, } primaryNic, err := nicClient.CreateOrUpdate(resourceGroup, primaryNicName, primaryNicParams) if err != nil { return nil, errors.Annotatef(err, "creating network interface for %q", vmName) } // Create a network security rule for the machine if we need to open // the API server port. if apiPort != nil { logger.Debugf("- querying network security group") securityGroupClient := network.SecurityGroupsClient{client} securityGroupName := internalSecurityGroupName securityGroup, err := securityGroupClient.Get(resourceGroup, securityGroupName) if err != nil { return nil, errors.Annotate(err, "querying network security group") } // NOTE(axw) this looks like TOCTTOU race territory, but it's // safe because we only allocate/deallocate rules in this // range during machine (de)provisioning, which is managed by // a single goroutine. Non-internal ports are managed by the // firewaller exclusively. nextPriority, err := nextSecurityRulePriority( securityGroup, securityRuleInternalSSHInbound+1, securityRuleInternalMax, ) if err != nil { return nil, errors.Trace(err) } apiSecurityRuleName := fmt.Sprintf("%s-api", vmName) apiSecurityRule := network.SecurityRule{ Name: to.StringPtr(apiSecurityRuleName), Properties: &network.SecurityRulePropertiesFormat{ Description: to.StringPtr("Allow API access to server machines"), Protocol: network.SecurityRuleProtocolTCP, SourceAddressPrefix: to.StringPtr("*"), SourcePortRange: to.StringPtr("*"), DestinationAddressPrefix: to.StringPtr(privateIPAddress), DestinationPortRange: to.StringPtr(fmt.Sprint(*apiPort)), Access: network.Allow, Priority: to.IntPtr(nextPriority), Direction: network.Inbound, }, } logger.Debugf("- creating API network security rule") securityRuleClient := network.SecurityRulesClient{client} _, err = securityRuleClient.CreateOrUpdate( resourceGroup, securityGroupName, apiSecurityRuleName, apiSecurityRule, ) if err != nil { return nil, errors.Annotate(err, "creating API network security rule") } } // For now we only attach a single, flat network to each machine. networkInterfaces := []compute.NetworkInterfaceReference{{ ID: primaryNic.ID, Properties: &compute.NetworkInterfaceReferenceProperties{ Primary: to.BoolPtr(true), }, }} return &compute.NetworkProfile{&networkInterfaces}, nil }