#
# Cookbook:: irrigate-mdt
# Recipe:: default
#
# Copyright:: 2018, The Orchard, All Rights Reserved.

# Install MDT Build DSC module, and skip if already installed
powershell_script 'Install cMDTBuildLab DSC Module' do
  code 'Install-Module -Name cMDTBuildLab -SkipPublisherCheck -Force'
  not_if '(Get-InstalledModule -Name cMDTBuildLab).Installed'
end

# Install cMDT DSC module, and skip if already installed
powershell_script 'Install cMDT DSC Module' do
  code 'Install-Module -Name cMDT -SkipPublisherCheck -Force'
  not_if '(Get-InstalledModule -Name cMDT).Installed'
end

# Enable DataDeduplication feature
dsc_resource 'DataDeduplicationFeautre' do
  resource :WindowsFeature
  property :Name,   'FS-Data-Deduplication'
  property :Ensure, 'Present'
end

# Create Source folder
directory node['mdt']['source_path'] do
  action :create
end

# Install custom MDT module
directory "#{node['mdt']['psmdt_module_path']}\\#{node['mdt']['psmdt_module_version']}" do
  recursive true
  action :create
end

cookbook_file "#{node['mdt']['psmdt_module_path']}\\#{node['mdt']['psmdt_module_version']}\\irrigateMDT.psm1" do
  source 'irrigateMDT.psm1'
  action :create
end

cookbook_file "#{node['mdt']['psmdt_module_path']}\\#{node['mdt']['psmdt_module_version']}\\irrigateMDT.psd1" do
  source 'irrigateMDT.psd1'
  action :create
end

# Download prereqs
dsc_resource 'cMDTBuildPreReqs' do
  resource :cMDTBuildPreReqs
  property :DownloadPath, node['mdt']['source_path']
end

# Install ADK
dsc_resource 'InstallADK' do
  resource :Package
  property :Name,       'Windows Assessment and Deployment Kit - Windows 10'
  property :Ensure,     'Present'
  property :Path,       "#{node['mdt']['source_path']}\\Windows Assessment and Deployment Kit\\adksetup.exe"
  property :ProductId,  node['mdt']['adk_product_id']
  property :Arguments,  '/quiet /features OptionId.DeploymentTools OptionId.WindowsPreinstallationEnvironment'
  property :ReturnCode, [0]
  property :Ensure,     'Present'
  notifies :reboot_now, 'reboot[now]', :immediately
end

# Install MDT
dsc_resource 'InstallMDT' do
  resource :Package
  property :Name,       "Microsoft Deployment Toolkit (#{node['mdt']['mdt_package_version']})"
  property :Ensure,     'Present'
  property :Path,       "#{node['mdt']['source_path']}\\Microsoft Deployment Toolkit\\MicrosoftDeploymentToolkit_x64.msi"
  property :ProductId,  node['mdt']['mdt_product_id']
  property :ReturnCode, [0]
  property :Ensure,     'Present'
end

mdt_shares = {
  'Build' => {
    'psdrive_name' => node['mdt']['build_psdrive_name'],
    'psdrive_path' => "#{node['mdt']['build_drive_path']}#{node['mdt']['build_share_name']}",
    'drive_path'   => node['mdt']['build_drive_path'],
    'drive_name'   => node['mdt']['build_share_name'],
  },
  'Deploy' => {
    'psdrive_name' => node['mdt']['deploy_psdrive_name'],
    'psdrive_path' => "#{node['mdt']['deploy_drive_path']}#{node['mdt']['deploy_share_name']}",
    'drive_path'   => node['mdt']['deploy_drive_path'],
    'drive_name'   => node['mdt']['deploy_share_name'],
  },
}

# Combine build and deploy apps
all_applications = {
  'Build'  => node['mdt']['applications']['build'],
  'Deploy' => node['mdt']['applications']['deploy'],
}

# Combine build and deploy selection profiles
all_profiles = {
  'Build'  => node['mdt']['selection_profiles']['build'],
  'Deploy' => node['mdt']['selection_profiles']['deploy'],
}

# Combine build and deploy task sequences
all_task_sequences = {
  'Build'  => node['mdt']['task_sequences']['build'],
  'Deploy' => node['mdt']['task_sequences']['deploy'],
}

ldap_credentials = data_bag_item('secrets', node['mdt']['ldap_credentials_data_bag'])
ldap_user = ldap_credentials['ldap_user'].chomp
ldap_password = ldap_credentials['ldap_password'].chomp
ldap_domain = ldap_credentials['ldap_domain'].chomp

mdt_shares.each do |share_type, share_properties|
  psdrive_name = share_properties['psdrive_name']
  psdrive_path = share_properties['psdrive_path']
  drive_name   = share_properties['drive_name']
  drive_path   = share_properties['drive_path']

  # Create Build and Deployment directories
  dsc_resource 'MDTDeploymentDirectory' do
    resource :cMDTBuildDirectory
    property :Name, drive_name
    property :Path, drive_path
  end

  # Create MDT Persistent Drive
  dsc_resource 'MDTDeployPersistentDrive' do
    resource :cMDTBuildPersistentDrive
    property :Name,        psdrive_name
    property :Path,        psdrive_path
    property :Description, "MDT #{share_type} Share"
    property :NetworkPath, "\\#{node['hostname']}\\MDT#{share_type}$"
  end

  # TS Directory Structure
  dsc_resource 'CreateTsDirectory' do
    resource :cMDTBuildDirectory
    property :Name,        share_type
    property :Path,        "#{psdrive_name}:\\Task Sequences"
    property :PSDriveName, psdrive_name
    property :PSDrivePath, psdrive_path
  end

  # OS Directory Structure
  node['mdt']['os_directories'].each do |os, versions|
    ['Operating Systems', "Task Sequences\\#{share_type}"] .each do |path|
      dsc_resource "CreateOsDirectory_#{os}" do
        resource :cMDTBuildDirectory
        property :Name,        os
        property :Path,        "#{psdrive_name}:\\#{path}"
        property :PSDriveName, psdrive_name
        property :PSDrivePath, psdrive_path
      end
    end

    versions.each do |version, builds|
      dsc_resource "CreateOsVersionDirectory_#{os}_#{version}" do
        resource :cMDTBuildDirectory
        property :Name,        version
        property :Path,        "#{psdrive_name}:\\Operating Systems\\#{os}"
        property :PSDriveName, psdrive_name
        property :PSDrivePath, psdrive_path
      end

      builds.each do |build|
        dsc_resource "CreateOsBuildDirectory_#{os}_#{build}" do
          resource :cMDTBuildDirectory
          property :Name,        build
          property :Path,        "#{psdrive_name}:\\Operating Systems\\#{os}\\#{version}"
          property :PSDriveName, psdrive_name
          property :PSDrivePath, psdrive_path
        end
      end
    end
  end

  # Apps Directory Structure
  all_applications.each do |app_group, publishers|
    next unless app_group == share_type

    dsc_resource "ApplicationGroup_#{app_group}" do
      resource :cMDTBuildDirectory
      property :Name,        app_group
      property :Path,        "#{psdrive_name}:\\Applications"
      property :PSDriveName, psdrive_name
      property :PSDrivePath, psdrive_path
    end

    publishers.each do |publisher, applications|
      dsc_resource "ApplicationPublisher_#{publisher}" do
        resource :cMDTBuildDirectory
        property :Name,        publisher
        property :Path,        "#{psdrive_name}:\\Applications\\#{app_group}"
        property :PSDriveName, psdrive_name
        property :PSDrivePath, psdrive_path
      end

      next if applications.empty?
      applications.each do |name, app_versions|
        app_versions.each do |application|
          dsc_resource "Application_#{name}" do
            resource :cMDTBuildDirectory
            property :Name,        name
            property :Path,        "#{psdrive_name}:\\Applications\\#{app_group}\\#{publisher}"
            property :PSDriveName, psdrive_name
            property :PSDrivePath, psdrive_path
          end

          dsc_resource "Application_#{name}_Version_#{application['Version']}" do
            resource :cMDTBuildDirectory
            property :Name,        application['Version']
            property :Path,        "#{psdrive_name}:\\Applications\\#{app_group}\\#{publisher}\\#{name}"
            property :PSDriveName, psdrive_name
            property :PSDrivePath, psdrive_path
          end
        end
      end
    end
  end

  # CustomSettings.ini file for DeploymentShare
  template "#{psdrive_path}\\Control\\CustomSettings.ini" do
    source 'customsettings_ini.erb'
  end

  # Bootstrap.ini file for DeploymentShare
  template "#{psdrive_path}\\Control\\Bootstrap.ini" do
    source 'bootstrap_ini.erb'
    variables(
      'drive_name' => drive_name,
      'ldap_user' => ldap_user,
      'ldap_password' => ldap_password,
      'ldap_domain' => ldap_domain,
    )
  end

  # Boot image
  dsc_resource 'BootImage' do
    resource :cMDTBuildUpdateBootImage
    property :Version,                    node['mdt']['boot_image']['Version']
    property :PSDeploymentShare,          psdrive_name
    property :PSDrivePath,                psdrive_path
    property :ExtraDirectory,             node['mdt']['boot_image']['ExtraDirectory']
    property :BackgroundFile,             node['mdt']['boot_image']['BackgroundFile']
    property :LiteTouchWIMDescription,    node['mdt']['boot_image']['LiteTouchWIMDescription']
  end

  # Import Operating Systems for Build share
  # Importing only original versions, golden images will be imported by CI/CD process
  node['mdt']['os_directories'].each do |os, versions|
    next unless share_type == 'Build'
    versions.each do |version, builds|
      next unless version == 'Original'
      builds.each do |build|
        dsc_resource "ImportOperatingSystem_#{os}" do
          resource :cMDTBuildOperatingSystem
          property :Name,        os
          property :Path,        "#{psdrive_name}:\\Operating Systems\\#{os}\\#{version}\\#{build}"
          property :SourcePath,  "#{node['mdt']['source_path']}\\#{os}\\#{version}\\#{build}"
          property :PSDriveName, psdrive_name
          property :PSDrivePath, psdrive_path
        end
      end
    end
  end

  # Import Applications
  all_applications.each do |app_type, publishers|
    next unless app_type == share_type
    publishers.each do |publisher, applications|
      next if applications.empty?
      applications.each do |name, app_versions|
        app_versions.each do |application|
          dsc_resource "ImportApplication_#{name}" do
            resource :cMDTApplication
            property :Name,                  "#{application['Name']}_#{application['Version']}"
            property :ShortName,             "#{application['Name']}_#{application['Version']}"
            property :Publisher,             publisher
            property :Language,              'en-US'
            property :Path,                  "#{psdrive_name}:\\Applications\\#{app_type}\\#{publisher}\\#{name}\\#{application['Version']}"
            property :CommandLine,           application['CommandLine']
            property :WorkingDirectory,      ".\\Applications\\#{application['Name']}_#{application['Version']}"
            property :ApplicationSourcePath, "#{node['mdt']['source_path']}\\#{application['ApplicationSourcePath']}"
            property :DestinationFolder,     "#{application['Name']}_#{application['Version']}"
            property :Enabled,               'True'
            property :Ensure,                'Present'
            property :Version,               application['Version']
            property :TempLocation,          node['mdt']['temp_location']
            property :PSDriveName,           psdrive_name
            property :PSDrivePath,           psdrive_path
          end
        end
      end
    end
  end

  # Because default selection profile dsc_resource cannot take more than one path
  # we need to add some logic to convert multiple paths into correct xml format.
  all_profiles.each do |profile_group, selection_profiles|
    next unless profile_group == share_type
    selection_profiles.each do |profile, settings|
      dsc_resource "SelectionProfile_#{profile}" do
        resource :cMDTBuildSelectionProfile
        property :Name,     settings['Name']
        property :Comments, settings['Comments']
        if settings['IncludePath'].length > 1
          full_path = ''
          full_path += "#{settings['IncludePath'][0]}\" />"
          settings['IncludePath'][1..-2].each do |path|
            full_path += "<Include path=\"#{path}\" />"
          end
          full_path += "<Include path=\"#{settings['IncludePath'].last}"
          property :IncludePath, full_path
        else
          property :IncludePath, settings['IncludePath'][0]
        end
        property :PSDriveName, psdrive_name
        property :PSDrivePath, psdrive_path
      end
    end
  end

  # Out-of-Box Drivers for Deployment share
  node['mdt']['drivers'].each do |platform, vendors|
    next unless share_type == 'Deploy'
    dsc_resource "PlatformDrivers_#{platform}" do
      resource :cMDTBuildDirectory
      property :Name,        platform
      property :Path,        "#{psdrive_name}:\\Out-of-Box Drivers"
      property :PSDriveName, psdrive_name
      property :PSDrivePath, psdrive_path
    end

    vendors.each do |vendor, models|
      dsc_resource "VendorDrivers_#{vendor}" do
        resource :cMDTBuildDirectory
        property :Name,        vendor
        property :Path,        "#{psdrive_name}:\\Out-of-Box Drivers\\#{platform}"
        property :PSDriveName, psdrive_name
        property :PSDrivePath, psdrive_path
      end

      models.each do |model, versions|
        dsc_resource "VendorDrivers_#{model}" do
          resource :cMDTBuildDirectory
          property :Name,        model
          property :Path,        "#{psdrive_name}:\\Out-of-Box Drivers\\#{platform}\\#{vendor}"
          property :PSDriveName, psdrive_name
          property :PSDrivePath, psdrive_path
        end

        versions.each do |version|
          dsc_resource "ImportDriver_#{model}_#{version}" do
            resource :cMDTDriver
            property :Ensure,       'Present'
            property :Name,         version
            property :Version,      version
            property :Path,         "#{psdrive_name}:\\Out-of-Box Drivers\\#{platform}\\#{vendor}\\#{model}"
            property :SourcePath,   "#{node['mdt']['source_path']}\\Drivers\\#{platform}\\#{vendor}\\#{model}"
            property :Comment,      "#{platform}-#{vendor}-#{model}"
            property :Enabled,      'True'
            property :PSDriveName,  psdrive_name
            property :PSDrivePath,  psdrive_path
            property :TempLocation, node['mdt']['temp_location']
            only_if { ::File.file?("#{node['mdt']['source_path']}\\Drivers\\#{platform}\\#{vendor}\\#{model}_#{version}.zip") }
          end
        end
      end
    end
  end

  # Copy Task Sequence templates
  node['mdt']['ts_templates'].each do |template_type, template_os|
    next unless template_type == share_type
    next if template_os.empty?
    template_os.each do |_os, templates|
      templates.each do |template|
        cookbook_file "#{psdrive_path}\\Templates\\#{template}" do
          source template
          action :create
        end
      end
    end
  end

  # Create Task Sequences
  all_task_sequences.each do |ts_type, os_family|
    next unless ts_type == share_type
    next if os_family.empty?
    os_family.each do |_os, task_sequences|
      task_sequences.each do |name, properties|
        # Task Sequence version folder
        dsc_resource "TaskSequenceDir_#{name}" do
          resource :cMDTBuildDirectory
          property :Name,        properties['TSVersion']
          property :Path,        "#{psdrive_name}:\\Task Sequences\\#{properties['TSType']}\\#{properties['Path']}"
          property :PSDriveName, psdrive_name
          property :PSDrivePath, psdrive_path
        end

        # Task Sequence OS folder for captured Golden image
        if properties['TSType'] == 'Build'
          dsc_resource "CreateOsDirectory_#{name}_v#{properties['TSVersion']}" do
            resource :cMDTBuildDirectory
            property :Name,        properties['TSVersion']
            property :Path,        "#{psdrive_name}:\\Operating Systems\\#{properties['Path']}\\Orchard_Golden"
            property :PSDriveName, psdrive_name
            property :PSDrivePath, psdrive_path
          end
        end

        # Create Task Sequence
        dsc_resource "CreateTaskSequence_#{name}" do
          resource :cMDTBuildTaskSequence
          property :Name,        "#{properties['Name']}_#{properties['TSVersion']}"
          property :Path,        "#{psdrive_name}:\\Task Sequences\\#{properties['TSType']}\\#{properties['Path']}\\#{properties['TSVersion']}"
          property :OSName,      "#{psdrive_name}:\\Operating Systems\\#{properties['OSName']}"
          property :Template,    properties['Template']
          property :ID,          properties['ID']
          property :OrgName,     properties['OrgName']
          property :Ensure,      'Present'
          property :PSDriveName, psdrive_name
          property :PSDrivePath, psdrive_path
        end

        # Customize Task Sequence
        properties['Customize'].each do |customize|
          dsc_resource "CustomizeTS_#{name}_step_#{customize['Name']}" do
            resource :irrigateMDTTaskSequenceCustomize
            property :TSFile,            "#{psdrive_path}\\Control\\#{properties['ID']}\\ts.xml"
            property :Name,              customize['Name']
            property :NewName,           customize['NewName'] || ''
            property :Type,              customize['Type']
            property :GroupName,         customize['GroupName'] || ''
            property :SubGroup,          customize['SubGroup'] || ''
            property :Disable,           customize['Disable'] || ''
            property :AddAfter,          customize['AddAfter'] || ''
            property :Description,       customize['Description'] || ''
            property :TSVarName,         customize['TSVarName'] || ''
            property :TSVarValue,        customize['TSVarValue'] || ''
            property :OSName,            customize['OSName'] || ''
            property :OSFeatures,        customize['OSFeatures'] || ''
            property :Command,           customize['Command'] || ''
            property :StartIn,           customize['StartIn'] || ''
            property :PSCommand,         customize['PSCommand'] || ''
            property :PSParameters,      customize['PSParameters'] || ''
            property :SelectionProfile,  customize['SelectionProfile'] || ''
            property :OSGUID,            customize['OSGUID'] || ''
            property :Condition,         customize['Condition'] || {}
            property :PSDriveName,       psdrive_name
            property :PSDrivePath,       psdrive_path
          end
        end
      end
    end
  end
end

reboot 'now' do
  action :nothing
  reason 'Cannot continue Chef run without a reboot.'
  delay_mins 2
end
