Class: VagrantPlugins::WindowsDomain::Provisioner

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-windows-domain/provisioner.rb

Overview

Windows Domain Provisioner Plugin.

Connects and Removes a guest Machine from a Windows Domain.

Constant Summary collapse

WINDOWS_DOMAIN_GUEST_RUNNER_PATH =

Default path for storing the transient script runner

"c:/tmp/vagrant-windows-domain-runner.ps1"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(machine, config) ⇒ Provisioner

Constructs the Provisioner Plugin.

Parameters:

  • machine (Machine)

    The guest machine that is to be provisioned.

  • config (Config)

    The Configuration object used by the Provisioner.



36
37
38
39
40
41
# File 'lib/vagrant-windows-domain/provisioner.rb', line 36

def initialize(machine, config)
  super

  @logger = Log4r::Logger.new("vagrant::provisioners::vagrant_windows_domain")
  @restart_sleep_duration = 10
end

Instance Attribute Details

#old_computer_nameObject

The current Computer Name.

Used to determine whether or not we need to rename the computer on join. This parameter should not be manually set.



29
30
31
# File 'lib/vagrant-windows-domain/provisioner.rb', line 29

def old_computer_name
  @old_computer_name
end

#restart_sleep_durationObject

Returns the value of attribute restart_sleep_duration.



23
24
25
# File 'lib/vagrant-windows-domain/provisioner.rb', line 23

def restart_sleep_duration
  @restart_sleep_duration
end

Instance Method Details

#configure(root_config) ⇒ Object

Configures the Provisioner.

Parameters:

  • root_config (Config)

    The default configuration from the Vagrant hierarchy.

Raises:



46
47
48
# File 'lib/vagrant-windows-domain/provisioner.rb', line 46

def configure(root_config)
  raise WindowsDomainError, :unsupported_platform if !windows?
end

#destroyObject

Cleanup after a destroy action.

This is the method called when destroying a machine that allows for any state related to the machine created by the provisioner to be cleaned up.



105
106
107
108
109
110
111
112
113
# File 'lib/vagrant-windows-domain/provisioner.rb', line 105

def destroy
  if @config && @config.include?("domain")
    set_credentials
    leave_domain
  else
    @logger.debug("Not leaving domain on `destroy` action - no valid configuration detected")
    return
  end
end

#generate_command_arguments(add_to_domain = true) ⇒ Object

Generates the argument list



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/vagrant-windows-domain/provisioner.rb', line 162

def generate_command_arguments(add_to_domain=true)

  if add_to_domain
    params = {"-DomainName" => @config.domain }

    if @config.unsecure
      params["-Unsecure"] = nil
    else
      params["-Credential $credentials"] = nil
    end

    if @config.computer_name != nil && @config.computer_name != @old_computer_name
      params["-NewName"] = "'#{@config.computer_name}'"
    end

    if @config.ou_path
      params["-OUPath"] = "'#{@config.ou_path}'"
    end
  else
    params = {}
    if !@config.unsecure
      params["-UnjoinDomainCredential $credentials"] = nil
    end
  end

  # Remove with unsecure
  join_params = @config.join_options.map { |a| "#{a}" }.join(',')
  params.map { |k,v| "#{k}" + (!v.nil? ? " #{v}": '') }.join(' ') + join_params
  
end

#generate_command_runner_script(add_to_domain = true) ⇒ String

Generates a PowerShell runner script from an ERB template

Parameters:

  • add_to_domain (boolean) (defaults to: true)

    Whether or not to add or remove the computer to the domain (default: true).

Returns:

  • (String)

    The interpolated PowerShell script.



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/vagrant-windows-domain/provisioner.rb', line 147

def generate_command_runner_script(add_to_domain=true)
  path = File.expand_path("../templates/runner.ps1", __FILE__)

  Vagrant::Util::TemplateRenderer.render(path, options: {
      config: @config,
      username: @config.username,
      password: @config.password,
      domain: @config.domain,
      add_to_domain: add_to_domain,
      unsecure: @config.unsecure,
      parameters: generate_command_arguments(add_to_domain)
  })
end

#get_guest_computer_name(machine) ⇒ Object

Gets the Computer Name from the guest machine



246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/vagrant-windows-domain/provisioner.rb', line 246

def get_guest_computer_name(machine)
  computerName = ""
  machine.communicate.shell.powershell("$env:COMPUTERNAME") do |type, data|
    if !data.chomp.empty?
      if [:stderr, :stdout].include?(type)
        computerName = data.chomp
        @logger.info("Detected guest computer name: #{computerName}")
      end
    end
  end

  computerName
end

#join_domainObject

Join the guest machine to a Windows Domain.

Generates, writes and runs a script to join a domain.



72
73
74
# File 'lib/vagrant-windows-domain/provisioner.rb', line 72

def join_domain        
  run_remote_command_runner(write_command_runner_script(generate_command_runner_script(true)))
end

#leave_domainObject Also known as: unjoin_domain

Removes the guest machine from a Windows Domain.

Generates, writes and runs a script to leave a domain.



79
80
81
# File 'lib/vagrant-windows-domain/provisioner.rb', line 79

def leave_domain
  run_remote_command_runner(write_command_runner_script(generate_command_runner_script(false)))
end

#provisionObject

Run the Provisioner!



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/vagrant-windows-domain/provisioner.rb', line 51

def provision
  verify_guest_capability

  @old_computer_name = get_guest_computer_name(machine)
  
  @machine.env.ui.say(:info, "Connecting guest machine to domain '#{config.domain}' with computer name '#{config.computer_name}'")

  set_credentials

  result = join_domain

  remove_command_runner_script

  if result
    restart_guest
  end
end

#remove_command_runner_scriptObject

Remove temporary run script as it may contain sensitive plain-text credentials.



214
215
216
# File 'lib/vagrant-windows-domain/provisioner.rb', line 214

def remove_command_runner_script
  @machine.communicate.sudo("del #{WINDOWS_DOMAIN_GUEST_RUNNER_PATH}")
end

#restart_guestObject

Restarts the Computer and waits



116
117
118
119
120
121
122
123
124
# File 'lib/vagrant-windows-domain/provisioner.rb', line 116

def restart_guest
  @machine.env.ui.say(:info, "Restarting computer for updates to take effect.")
  options = {}
  options[:provision_ignore_sentinel] = false
  @machine.action(:reload, options)
  begin
    sleep @restart_sleep_duration
  end until @machine.communicate.ready?
end

#run_remote_command_runner(script_path) ⇒ boolean

Runs the PowerShell script on the guest machine.

Streams the output of the command to the UI

Returns:

  • (boolean)

    The result of the remote command



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/vagrant-windows-domain/provisioner.rb', line 222

def run_remote_command_runner(script_path)
  @machine.ui.info(I18n.t(
    "vagrant_windows_domain.running"))

  # A bit of an ugly dance, but this is how we get neat, colourised output and exit codes from a Powershell run
  last_type = nil
  new_line = ""
  error = false
  machine.communicate.shell.powershell("powershell -ExecutionPolicy Bypass -OutputFormat Text -file #{script_path}") do |type, data|
    if !data.chomp.empty?
      error = true if type == :stderr
      if [:stderr, :stdout].include?(type)
        color = type == :stdout ? :green : :red
        new_line = "\r\n" if last_type != nil and last_type != type
        last_type = type
        @machine.ui.info( new_line + data.chomp, color: color, new_line: false, prefix: false)
      end
    end
  end

  error == false
end

#set_credentialsObject

Ensure credentials are provided.

Get username/password from user if not provided as part of the config.



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/vagrant-windows-domain/provisioner.rb', line 88

def set_credentials
  if (config.username == nil)
    @logger.info("==> Requesting username as none provided")
    config.username = @machine.env.ui.ask("Please enter your domain username: ")
  end

  if (config.password == nil)
    @logger.info("==> Requesting password as none provided")
    config.password = @machine.env.ui.ask("Please enter your domain password (output will be hidden): ", {:echo => false})
  end
end

#verify_binary(binary) ⇒ Object

Verify a binarycommand is executable on the guest machine.



134
135
136
137
138
139
140
141
# File 'lib/vagrant-windows-domain/provisioner.rb', line 134

def verify_binary(binary)
  @machine.communicate.sudo(
    "which #{binary}",
    error_class: WindowsDomainError,
    error_key: :binary_not_detected,
    domain: config.domain,
    binary: binary)
end

#verify_guest_capabilityObject

Verify that we can call the remote operations. Required to add the computer to a Domain.



128
129
130
131
# File 'lib/vagrant-windows-domain/provisioner.rb', line 128

def verify_guest_capability
  verify_binary("Add-Computer")
  verify_binary("Remove-Computer")
end

#windows?Boolean

Is the guest Windows?

Returns:

  • (Boolean)


261
262
263
264
# File 'lib/vagrant-windows-domain/provisioner.rb', line 261

def windows?
  # If using WinRM, we can assume we are on Windows
  @machine.config.vm.communicator == :winrm
end

#write_command_runner_script(script) ⇒ String

Writes the PowerShell runner script to a location on the guest.

Parameters:

  • script (String)

    The PowerShell runner script.

Returns:

  • (String)

    the Path to the uploaded location on the guest machine.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/vagrant-windows-domain/provisioner.rb', line 197

def write_command_runner_script(script)
  guest_script_path = WINDOWS_DOMAIN_GUEST_RUNNER_PATH
  file = Tempfile.new(["vagrant-windows-domain-runner", "ps1"])
  begin
    file.write(script)
    file.fsync
    file.close
    @machine.communicate.upload(file.path, guest_script_path)
  ensure
    file.close
    file.unlink
  end
  guest_script_path
end