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