Class: VagrantPlugins::DSC::Provisioner

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

Overview

DSC Provisioner Plugin.

Runs the [Desired State Configuration](technet.microsoft.com/en-au/library/dn249912.aspx) system on a guest Virtual Machine, enabling you to quickly configure & bootstrap a Windows Virtual Machine in a repeatable, reliable fashion - the Vagrant way.

Constant Summary collapse

PowerShell_VERSION =
4
DSC_GUEST_RUNNER_PATH =

Default path for storing the transient script runner This should be removed in cleanup

"c:/tmp/vagrant-dsc-runner.ps1"

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.



32
33
34
35
36
# File 'lib/vagrant-dsc/provisioner.rb', line 32

def initialize(machine, config)
  super

  @logger = Log4r::Logger.new("vagrant::provisioners::dsc")
end

Instance Method Details

#cleanupObject

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.



109
110
111
# File 'lib/vagrant-dsc/provisioner.rb', line 109

def cleanup
  # Remove temp files? Or is this ONLY called in destroy (in which case those files will go anyway...)
end

#configure(root_config) ⇒ Object

Configures the Provisioner.

Parameters:

  • root_config (Config)

    The default configuration from the Vagrant hierarchy.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/vagrant-dsc/provisioner.rb', line 41

def configure(root_config)
  @logger.info("==> Configuring DSC")

  # Calculate the paths we're going to use based on the environment
  root_path = @machine.env.root_path
  @expanded_module_paths   = @config.expanded_module_paths(root_path)

  # Setup the module paths
  @module_paths = []
  @expanded_module_paths.each_with_index do |path, i|
    @module_paths << [path, File.join(config.temp_dir, "modules-#{i}")]
  end

  folder_opts = {}
  folder_opts[:type] = @config.synced_folder_type if @config.synced_folder_type
  folder_opts[:owner] = "root" if !@config.synced_folder_type

  # Share the manifests directory with the guest
  @logger.info("==> Sharing manifest #{File.expand_path(@config.manifests_path, root_path)} | #{manifests_guest_path} | #{folder_opts}")

  root_config.vm.synced_folder(
    File.expand_path(@config.manifests_path, root_path),
    manifests_guest_path, folder_opts)

  # Share the module paths
  @module_paths.each do |from, to|
    @logger.info("==> Sharing module folders #{from} | #{to}")
    root_config.vm.synced_folder(from, to, folder_opts)
  end
end

#generate_dsc_runner_scriptString

Generates a PowerShell DSC runner script from an ERB template

Returns:

  • (String)

    The interpolated PowerShell script.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/vagrant-dsc/provisioner.rb', line 155

def generate_dsc_runner_script
  path = File.expand_path("../templates/runner.ps1", __FILE__)

  script = Vagrant::Util::TemplateRenderer.render(path, options: {
      config: @config,
      module_paths: @module_paths.map { |k,v| v }.join(";"),
      mof_path: @config.mof_path,
      configuration_file: @config.configuration_file,
      configuration_data_file: @config.expanded_configuration_data_file,
      configuration_file_path: "#{@config.manifests_path}/#{File.basename @config.configuration_file}",
      configuration_name: @config.configuration_name,
      manifests_path: @config.manifests_path,
      temp_path: @config.temp_dir,
      parameters: @config.configuration_params.map { |k,v| "#{k}" + (!v.nil? ? " \"#{v}\"": '') }.join(" ")
  })
end

#install_dscObject

Install and Configure DSC where possible.

Operation is current unsupported, but is likely to be enabled as a flag when the plugin detects an unsupported OS.



142
143
144
145
146
147
148
149
150
# File 'lib/vagrant-dsc/provisioner.rb', line 142

def install_dsc
  # raise DSCError, I18n.t("vagrant_dsc.errors.manifest_missing", operation: "install_dsc")
  raise DSCUnsupportedOperation,  :operation => "install_dsc"
  # Install chocolatey

  # Ensure .NET 4.5 installed

  # Ensure WMF 4.0 is installed
end

#manifests_guest_pathObject

Local path (guest path) to the manifests directory.



114
115
116
# File 'lib/vagrant-dsc/provisioner.rb', line 114

def manifests_guest_path
    File.join(config.temp_dir, config.manifests_path)
end

#provisionObject

Provision the guest machine with DSC.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/vagrant-dsc/provisioner.rb', line 73

def provision
  @logger.info("==> Provisioning DSC man! #{Vagrant.source_root}")

  # If the machine has a wait for reboot functionality, then
  # do that (primarily Windows)
  if @machine.guest.capability?(:wait_for_reboot)
    @machine.guest.capability(:wait_for_reboot)
  end

  # Check that the shared folders are properly shared
  check = []
  check << manifests_guest_path
  @module_paths.each do |host_path, guest_path|
    check << guest_path
  end

  # Make sure the temporary directory is properly set up
  @machine.communicate.tap do |comm|
    comm.sudo("mkdir -p #{config.temp_dir}")
    comm.sudo("chmod 0777 #{config.temp_dir}")
  end

  verify_shared_folders(check)

  verify_dsc

  write_dsc_runner_script(generate_dsc_runner_script)

  run_dsc_apply
end

#run_dsc_applyObject

Runs the DSC Configuration on the guest machine.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/vagrant-dsc/provisioner.rb', line 193

def run_dsc_apply

  @machine.ui.info(I18n.t(
    "vagrant_dsc.running_dsc",
    manifest: config.configuration_file))

  # 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 #{DSC_GUEST_RUNNER_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

#verify_binary(binary) ⇒ Object

Verify the DSC binary is executable on the guest machine.



130
131
132
133
134
135
136
# File 'lib/vagrant-dsc/provisioner.rb', line 130

def verify_binary(binary)
  @machine.communicate.sudo(
    "which #{binary}",
    error_class: DSCError,
    error_key: :dsc_not_detected,
    binary: binary)
end

#verify_dscObject

Verify that a current version of WMF/Powershell is enabled on the guest.



119
120
121
122
123
124
125
126
127
# File 'lib/vagrant-dsc/provisioner.rb', line 119

def verify_dsc
  verify_binary("Start-DscConfiguration")

  # Confirm WMF 4.0+ in $PSVersionTable
  @machine.communicate.test(
      "(($PSVersionTable | ConvertTo-json | ConvertFrom-Json).PSVersion.Major) -ge #{PowerShell_VERSION}",
      error_class: DSCError,
      error_key: :dsc_incorrect_PowerShell_version )
end

#verify_shared_folders(folders) ⇒ Object

Verify that the shared folders have been properly configured on the guest machine.



220
221
222
223
224
225
226
227
# File 'lib/vagrant-dsc/provisioner.rb', line 220

def verify_shared_folders(folders)
  folders.each do |folder|
    @logger.info("Checking for shared folder: #{folder}")
    if !@machine.communicate.test("test -d #{folder}", sudo: true)
      raise DSCError, :missing_shared_folders
    end
  end
end

#windows?Boolean

If on using WinRM, we can assume we are on Windows

Returns:

  • (Boolean)


230
231
232
# File 'lib/vagrant-dsc/provisioner.rb', line 230

def windows?
  @machine.config.vm.communicator == :winrm
end

#write_dsc_runner_script(script) ⇒ String

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

Parameters:

  • script (String)

    The PowerShell DSC runner script.

Returns:

  • (String)

    the Path to the uploaded location on the guest machine.



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/vagrant-dsc/provisioner.rb', line 176

def write_dsc_runner_script(script)
  guest_script_path = DSC_GUEST_RUNNER_PATH
  # TODO: Get a counter in here in case of multiple runs
  file = Tempfile.new(["vagrant-dsc-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