github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/builder/azure/arm/builder.go (about)

     1  // Copyright (c) Microsoft Corporation. All rights reserved.
     2  // Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
     3  
     4  package arm
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"log"
    10  	"strings"
    11  	"time"
    12  
    13  	packerAzureCommon "github.com/mitchellh/packer/builder/azure/common"
    14  
    15  	"github.com/Azure/go-autorest/autorest/azure"
    16  
    17  	"github.com/mitchellh/packer/builder/azure/common/constants"
    18  	"github.com/mitchellh/packer/builder/azure/common/lin"
    19  
    20  	"github.com/mitchellh/multistep"
    21  	packerCommon "github.com/mitchellh/packer/common"
    22  	"github.com/mitchellh/packer/helper/communicator"
    23  	"github.com/mitchellh/packer/packer"
    24  )
    25  
    26  type Builder struct {
    27  	config   *Config
    28  	stateBag multistep.StateBag
    29  	runner   multistep.Runner
    30  }
    31  
    32  const (
    33  	DefaultNicName             = "packerNic"
    34  	DefaultPublicIPAddressName = "packerPublicIP"
    35  	DefaultSasBlobContainer    = "system/Microsoft.Compute"
    36  	DefaultSasBlobPermission   = "r"
    37  	DefaultSecretName          = "packerKeyVaultSecret"
    38  )
    39  
    40  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    41  	c, warnings, errs := newConfig(raws...)
    42  	if errs != nil {
    43  		return warnings, errs
    44  	}
    45  
    46  	b.config = c
    47  
    48  	b.stateBag = new(multistep.BasicStateBag)
    49  	b.configureStateBag(b.stateBag)
    50  	b.setTemplateParameters(b.stateBag)
    51  
    52  	return warnings, errs
    53  }
    54  
    55  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
    56  	ui.Say("Running builder ...")
    57  
    58  	if err := newConfigRetriever().FillParameters(b.config); err != nil {
    59  		return nil, err
    60  	}
    61  
    62  	log.Print(":: Configuration")
    63  	packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) })
    64  
    65  	b.stateBag.Put("hook", hook)
    66  	b.stateBag.Put(constants.Ui, ui)
    67  
    68  	spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	ui.Message("Creating Azure Resource Manager (ARM) client ...")
    74  	azureClient, err := NewAzureClient(
    75  		b.config.SubscriptionID,
    76  		b.config.ResourceGroupName,
    77  		b.config.StorageAccount,
    78  		b.config.cloudEnvironment,
    79  		spnCloud,
    80  		spnKeyVault)
    81  
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	resolver := newResourceResolver(azureClient)
    87  	if err := resolver.Resolve(b.config); err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	b.config.storageAccountBlobEndpoint, err = b.getBlobEndpoint(azureClient, b.config.ResourceGroupName, b.config.StorageAccount)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	endpointConnectType := PublicEndpoint
    97  	if b.isPrivateNetworkCommunication() {
    98  		endpointConnectType = PrivateEndpoint
    99  	}
   100  
   101  	b.setTemplateParameters(b.stateBag)
   102  	var steps []multistep.Step
   103  
   104  	if b.config.OSType == constants.Target_Linux {
   105  		steps = []multistep.Step{
   106  			NewStepCreateResourceGroup(azureClient, ui),
   107  			NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
   108  			NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
   109  			NewStepGetIPAddress(azureClient, ui, endpointConnectType),
   110  			&communicator.StepConnectSSH{
   111  				Config:    &b.config.Comm,
   112  				Host:      lin.SSHHost,
   113  				SSHConfig: lin.SSHConfig(b.config.UserName),
   114  			},
   115  			&packerCommon.StepProvision{},
   116  			NewStepGetOSDisk(azureClient, ui),
   117  			NewStepPowerOffCompute(azureClient, ui),
   118  			NewStepCaptureImage(azureClient, ui),
   119  			NewStepDeleteResourceGroup(azureClient, ui),
   120  			NewStepDeleteOSDisk(azureClient, ui),
   121  		}
   122  	} else if b.config.OSType == constants.Target_Windows {
   123  		steps = []multistep.Step{
   124  			NewStepCreateResourceGroup(azureClient, ui),
   125  			NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
   126  			NewStepDeployTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
   127  			NewStepGetCertificate(azureClient, ui),
   128  			NewStepSetCertificate(b.config, ui),
   129  			NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
   130  			NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
   131  			NewStepGetIPAddress(azureClient, ui, endpointConnectType),
   132  			&communicator.StepConnectWinRM{
   133  				Config: &b.config.Comm,
   134  				Host: func(stateBag multistep.StateBag) (string, error) {
   135  					return stateBag.Get(constants.SSHHost).(string), nil
   136  				},
   137  				WinRMConfig: func(multistep.StateBag) (*communicator.WinRMConfig, error) {
   138  					return &communicator.WinRMConfig{
   139  						Username: b.config.UserName,
   140  						Password: b.config.tmpAdminPassword,
   141  					}, nil
   142  				},
   143  			},
   144  			&packerCommon.StepProvision{},
   145  			NewStepGetOSDisk(azureClient, ui),
   146  			NewStepPowerOffCompute(azureClient, ui),
   147  			NewStepCaptureImage(azureClient, ui),
   148  			NewStepDeleteResourceGroup(azureClient, ui),
   149  			NewStepDeleteOSDisk(azureClient, ui),
   150  		}
   151  	} else {
   152  		return nil, fmt.Errorf("Builder does not support the os_type '%s'", b.config.OSType)
   153  	}
   154  
   155  	if b.config.PackerDebug {
   156  		ui.Message(fmt.Sprintf("temp admin user: '%s'", b.config.UserName))
   157  		ui.Message(fmt.Sprintf("temp admin password: '%s'", b.config.Password))
   158  	}
   159  
   160  	b.runner = packerCommon.NewRunner(steps, b.config.PackerConfig, ui)
   161  	b.runner.Run(b.stateBag)
   162  
   163  	// Report any errors.
   164  	if rawErr, ok := b.stateBag.GetOk(constants.Error); ok {
   165  		return nil, rawErr.(error)
   166  	}
   167  
   168  	// If we were interrupted or cancelled, then just exit.
   169  	if _, ok := b.stateBag.GetOk(multistep.StateCancelled); ok {
   170  		return nil, errors.New("Build was cancelled.")
   171  	}
   172  
   173  	if _, ok := b.stateBag.GetOk(multistep.StateHalted); ok {
   174  		return nil, errors.New("Build was halted.")
   175  	}
   176  
   177  	if template, ok := b.stateBag.GetOk(constants.ArmCaptureTemplate); ok {
   178  		return NewArtifact(
   179  			template.(*CaptureTemplate),
   180  			func(name string) string {
   181  				month := time.Now().AddDate(0, 1, 0).UTC()
   182  				sasUrl, _ := azureClient.BlobStorageClient.GetBlobSASURI(DefaultSasBlobContainer, name, month, DefaultSasBlobPermission)
   183  				return sasUrl
   184  			})
   185  	}
   186  
   187  	return &Artifact{}, nil
   188  }
   189  
   190  func (b *Builder) isPrivateNetworkCommunication() bool {
   191  	return b.config.VirtualNetworkName != ""
   192  }
   193  
   194  func (b *Builder) Cancel() {
   195  	if b.runner != nil {
   196  		log.Println("Cancelling the step runner...")
   197  		b.runner.Cancel()
   198  	}
   199  }
   200  
   201  func (b *Builder) getBlobEndpoint(client *AzureClient, resourceGroupName string, storageAccountName string) (string, error) {
   202  	account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName)
   203  	if err != nil {
   204  		return "", err
   205  	}
   206  
   207  	return *account.Properties.PrimaryEndpoints.Blob, nil
   208  }
   209  
   210  func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
   211  	stateBag.Put(constants.AuthorizedKey, b.config.sshAuthorizedKey)
   212  	stateBag.Put(constants.PrivateKey, b.config.sshPrivateKey)
   213  
   214  	stateBag.Put(constants.ArmTags, &b.config.AzureTags)
   215  	stateBag.Put(constants.ArmComputeName, b.config.tmpComputeName)
   216  	stateBag.Put(constants.ArmDeploymentName, b.config.tmpDeploymentName)
   217  	stateBag.Put(constants.ArmKeyVaultName, b.config.tmpKeyVaultName)
   218  	stateBag.Put(constants.ArmLocation, b.config.Location)
   219  	stateBag.Put(constants.ArmNicName, DefaultNicName)
   220  	stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName)
   221  	stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName)
   222  	stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount)
   223  }
   224  
   225  func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) {
   226  	stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters())
   227  }
   228  
   229  func (b *Builder) getServicePrincipalTokens(say func(string)) (*azure.ServicePrincipalToken, *azure.ServicePrincipalToken, error) {
   230  	var servicePrincipalToken *azure.ServicePrincipalToken
   231  	var servicePrincipalTokenVault *azure.ServicePrincipalToken
   232  
   233  	var err error
   234  
   235  	if b.config.useDeviceLogin {
   236  		servicePrincipalToken, err = packerAzureCommon.Authenticate(*b.config.cloudEnvironment, b.config.TenantID, say)
   237  		if err != nil {
   238  			return nil, nil, err
   239  		}
   240  	} else {
   241  		auth := NewAuthenticate(*b.config.cloudEnvironment, b.config.ClientID, b.config.ClientSecret, b.config.TenantID)
   242  
   243  		servicePrincipalToken, err = auth.getServicePrincipalToken()
   244  		if err != nil {
   245  			return nil, nil, err
   246  		}
   247  
   248  		servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(
   249  			strings.TrimRight(b.config.cloudEnvironment.KeyVaultEndpoint, "/"))
   250  
   251  		if err != nil {
   252  			return nil, nil, err
   253  		}
   254  	}
   255  
   256  	return servicePrincipalToken, servicePrincipalTokenVault, nil
   257  }