github.com/yoctocloud/packer@v0.6.2-0.20160520224004-e11a0a18423f/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  )
    24  
    25  type Builder struct {
    26  	config   *Config
    27  	stateBag multistep.StateBag
    28  	runner   multistep.Runner
    29  }
    30  
    31  const (
    32  	DefaultPublicIPAddressName = "packerPublicIP"
    33  	DefaultSasBlobContainer    = "system/Microsoft.Compute"
    34  	DefaultSasBlobPermission   = "r"
    35  	DefaultSecretName          = "packerKeyVaultSecret"
    36  )
    37  
    38  func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
    39  	c, warnings, errs := newConfig(raws...)
    40  	if errs != nil {
    41  		return warnings, errs
    42  	}
    43  
    44  	b.config = c
    45  
    46  	b.stateBag = new(multistep.BasicStateBag)
    47  	b.configureStateBag(b.stateBag)
    48  	b.setTemplateParameters(b.stateBag)
    49  
    50  	return warnings, errs
    51  }
    52  
    53  func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) {
    54  	ui.Say("Preparing builder ...")
    55  
    56  	log.Print(":: Configuration")
    57  	packerAzureCommon.DumpConfig(b.config, func(s string) { log.Print(s) })
    58  
    59  	b.stateBag.Put("hook", hook)
    60  	b.stateBag.Put(constants.Ui, ui)
    61  
    62  	spnCloud, spnKeyVault, err := b.getServicePrincipalTokens(ui.Say)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	ui.Message("Creating Azure Resource Manager (ARM) client ...")
    68  	azureClient, err := NewAzureClient(
    69  		b.config.SubscriptionID,
    70  		b.config.ResourceGroupName,
    71  		b.config.StorageAccount,
    72  		spnCloud,
    73  		spnKeyVault)
    74  
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	b.config.storageAccountBlobEndpoint, err = b.getBlobEndpoint(azureClient, b.config.ResourceGroupName, b.config.StorageAccount)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	b.setTemplateParameters(b.stateBag)
    85  
    86  	var steps []multistep.Step
    87  
    88  	if b.config.OSType == constants.Target_Linux {
    89  		steps = []multistep.Step{
    90  			NewStepCreateResourceGroup(azureClient, ui),
    91  			NewStepValidateTemplate(azureClient, ui, Linux),
    92  			NewStepDeployTemplate(azureClient, ui, Linux),
    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 b.config.OSType == constants.Target_Windows {
   107  		steps = []multistep.Step{
   108  			NewStepCreateResourceGroup(azureClient, ui),
   109  			NewStepValidateTemplate(azureClient, ui, KeyVault),
   110  			NewStepDeployTemplate(azureClient, ui, KeyVault),
   111  			NewStepGetCertificate(azureClient, ui),
   112  			NewStepSetCertificate(b.config, ui),
   113  			NewStepValidateTemplate(azureClient, ui, Windows),
   114  			NewStepDeployTemplate(azureClient, ui, Windows),
   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.ArmTemplateParameters, b.config.toTemplateParameters())
   218  	stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters())
   219  }
   220  
   221  func (b *Builder) getServicePrincipalTokens(say func(string)) (*azure.ServicePrincipalToken, *azure.ServicePrincipalToken, error) {
   222  	var servicePrincipalToken *azure.ServicePrincipalToken
   223  	var servicePrincipalTokenVault *azure.ServicePrincipalToken
   224  
   225  	var err error
   226  
   227  	if b.config.useDeviceLogin {
   228  		servicePrincipalToken, err = packerAzureCommon.Authenticate(*b.config.cloudEnvironment, b.config.SubscriptionID, say)
   229  		if err != nil {
   230  			return nil, nil, err
   231  		}
   232  	} else {
   233  		auth := NewAuthenticate(*b.config.cloudEnvironment, b.config.ClientID, b.config.ClientSecret, b.config.TenantID)
   234  
   235  		servicePrincipalToken, err = auth.getServicePrincipalToken()
   236  		if err != nil {
   237  			return nil, nil, err
   238  		}
   239  
   240  		servicePrincipalTokenVault, err = auth.getServicePrincipalTokenWithResource(packerAzureCommon.AzureVaultScope)
   241  		if err != nil {
   242  			return nil, nil, err
   243  		}
   244  	}
   245  
   246  	return servicePrincipalToken, servicePrincipalTokenVault, nil
   247  }