diff --git a/provisioner/windows-restart/provisioner.go b/provisioner/windows-restart/provisioner.go index 126a0dd52..0593024c4 100644 --- a/provisioner/windows-restart/provisioner.go +++ b/provisioner/windows-restart/provisioner.go @@ -23,6 +23,12 @@ var retryableSleep = 5 * time.Second var TryCheckReboot = `shutdown /r /f /t 60 /c "packer restart test"` var AbortReboot = `shutdown /a` +var DefaultRegistryKeys = []string{ + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\PackagesPending", + "HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", +} + type Config struct { common.PackerConfig `mapstructure:",squash"` @@ -36,6 +42,12 @@ type Config struct { // The timeout for waiting for the machine to restart RestartTimeout time.Duration `mapstructure:"restart_timeout"` + // Whether to check the registry (see RegistryKeys) for pending reboots + CheckKey bool `mapstructure:"check_registry"` + + // custom keys to check for + RegistryKeys []string `mapstructure:"registry_keys"` + ctx interpolate.Context } @@ -73,6 +85,10 @@ func (p *Provisioner) Prepare(raws ...interface{}) error { p.config.RestartTimeout = 5 * time.Minute } + if len(p.config.RegistryKeys) == 0 { + p.config.RegistryKeys = DefaultRegistryKeys + } + return nil } @@ -212,7 +228,6 @@ var waitForCommunicator = func(p *Provisioner) error { log.Printf("Connected to machine") runCustomRestartCheck = false } - // This is the non-user-configurable check that powershell // modules have loaded. @@ -234,6 +249,37 @@ var waitForCommunicator = func(p *Provisioner) error { log.Printf("echo didn't succeed; retrying...") continue } + + if p.config.CheckKey { + log.Printf("Connected to machine") + shouldContinue := false + for _, RegKey := range p.config.RegistryKeys { + KeyTestCommand := winrm.Powershell(fmt.Sprintf(`Test-Path "%s"`, RegKey)) + cmdKeyCheck := &packer.RemoteCmd{Command: KeyTestCommand} + log.Printf("Checking registry for pending reboots") + var buf, buf2 bytes.Buffer + cmdKeyCheck.Stdout = &buf + cmdKeyCheck.Stdout = io.MultiWriter(cmdKeyCheck.Stdout, &buf2) + + err := p.comm.Start(cmdKeyCheck) + if err != nil { + log.Printf("Communication connection err: %s", err) + shouldContinue = true + } + cmdKeyCheck.Wait() + + stdoutToRead := buf2.String() + if strings.Contains(stdoutToRead, "True") { + log.Printf("RegistryKey %s exists; waiting...", KeyTestCommand) + shouldContinue = true + } else { + log.Printf("No Registry keys found; exiting wait loop") + } + } + if shouldContinue { + continue + } + } break } diff --git a/website/source/docs/provisioners/windows-restart.html.md b/website/source/docs/provisioners/windows-restart.html.md index ba0c8fd4e..b9ee04567 100644 --- a/website/source/docs/provisioners/windows-restart.html.md +++ b/website/source/docs/provisioners/windows-restart.html.md @@ -38,6 +38,28 @@ The reference of available configuration options is listed below. Optional parameters: +- `check_registry` (bool) - if `true`, checks for several registry keys that + indicate that the system is going to reboot. This is useful if an + installation kicks off a reboot and you want the provisioner to wait for + that reboot to complete before reconnecting. Please note that this option is + a beta feature, and we generally recommend that you finish installs that + auto-reboot (like windows updates) during your autounattend phase before our + winrm provisioner connects. + +- `registry_keys` (array of strings) - if `check-registry` is `true`, + windows-restart will not reconnect until after all of the listed keys are + no longer present in the registry. + + default: + + ``` + var DefaultRegistryKeys = []string{ + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootPending", + "HKLM:SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\PackagesPending", + "HKLM:Software\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing\\RebootInProgress", + } + ``` + - `restart_command` (string) - The command to execute to initiate the restart. By default this is `shutdown /r /f /t 0 /c "packer restart"`.