Thursday, September 10, 2015

Silent installation of Visual Studio using PowerShell DSC

Given that you have the setup created for silent installation of Visual Studio from a network Share or local drive location, you can use DSC to ensure that the Product is available or not available in your machine. To setup the Visual studio media follow the documentation here.

Once you have the media created you can use the DSC resource given below to install Visual Studio on your machine.

enum Ensure
{
       Absent
       Present
}

[DscResource()]
class xVisualStudio2015
{
       [DscProperty(Key)]
       [string] $ProductName

       [DscProperty(Mandatory)]
       [string] $ExecutablePath

       [DscProperty(Mandatory)]
       [string] $AdminDeploymentFile

       [DscProperty()]
       [string] $ProductKey

       [DscProperty(Mandatory)]
       [Ensure] $Ensure

       [DscProperty(NotConfigurable)]
       [bool] $IsValid


       [xVisualStudio2015] Get()
       {
              $vsPackage = $this.GetInstalledSoftwares() |? {$_.DisplayName -eq $this.ProductName}
              if(($this.Ensure -eq [Ensure]::Present) -and $vsPackage)
              {
                     $this.IsValid = $true
              }
              else
              {
                     $this.IsValid = $false
              }
              return $this
       }

       [void] Set()
       {
              if(-not (Test-Path $this.ExecutablePath))
              {
                     throw "Invalid path : $($this.ExecutablePath)"
              }
              if($this.Ensure -eq [Ensure]::Present)
              {
                     if(-not (Test-Path $this.AdminDeploymentFile))
                     {
                           throw "Invalid path : $($this.AdminDeploymentFile)"
                     }

                     $args = "/Quiet /NoRestart /AdminFile $this.AdminDeploymentFile /Log $Env:Temp\VisualStudio2015_Install.log"
                     if($this.ProductKey)
                     {
                           $args = $args + " /ProductKey $this.ProductKey"
                     }
                     "Installation arguments : $args" | Write-Debug

                     "Starting installation" | Write-Verbose

                     Start-Process -FilePath $this.ExecutablePath -ArgumentList $args -Wait -NoNewWindow      

                     "Successfully completed the installation" | Write-Verbose
              }
              else
              {
                     $args = "/Quiet /Force /Uninstall /Log $Env:Temp\VisualStudio2015_Uninstall.log"

                     "Uninstallation arguments : $args" | Write-Debug
                     "Starting uninstallation" | Write-Verbose

                     Start-Process -FilePath $this.ExecutablePath -ArgumentList $args -Wait -NoNewWindow      

                     "Successfully completed the uninstallation" | Write-Verbose
              }
       }

       [bool] Test()
       {
              $vsPackage = $this.GetInstalledSoftwares() |? {$_.DisplayName -eq $this.ProductName}
              if($this.Ensure -eq [Ensure]::Present)
              {
                     if($vsPackage)
                     {
                           return $true
                     }
                     else
                     {
                           return $false
                     }
              }
              else
              {
                     if($vsPackage)
                     {
                           return $false
                     }
                     else
                     {
                           return $true
                     }
              }
       }

       [PSObject[]] RetrievePackages($path, $registry)
       {
              $packages = @()
              $key = $registry.OpenSubKey($path)
              $subKeys = $key.GetSubKeyNames() |% {
                     $subKeyPath = $path + "\\" + $_
                     $packageKey = $registry.OpenSubKey($subKeyPath)
                     $package = New-Object PSObject
                     $package | Add-Member -MemberType NoteProperty -Name "DisplayName" -Value $($packageKey.GetValue("DisplayName"))
                     $package | Add-Member -MemberType NoteProperty -Name "DisplayVersion" -Value $($packageKey.GetValue("DisplayVersion"))
                     $package | Add-Member -MemberType NoteProperty -Name "UninstallString" -Value $($packageKey.GetValue("UninstallString"))
                     $package | Add-Member -MemberType NoteProperty -Name "Publisher" -Value $($packageKey.GetValue("Publisher"))           
                     $packages += $package     
              }
              return $packages
       }

       [PSCustomObject] GetInstalledSoftwares()
       {
              $installedSoftwares = @{}
              $path = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"
              $registry32 = [microsoft.win32.registrykey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry32)
              $registry64 = [microsoft.win32.registrykey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
       
              $packages = $this.RetrievePackages($path, $registry32)
              $packages += $this.RetrievePackages($path, $registry64)

              $packages.Where({$_.DisplayName}) |% {
                     if(-not($installedSoftwares.ContainsKey($_.DisplayName)))
                     {
                           $installedSoftwares.Add($_.DisplayName, $_)
                     }
              }
              return $installedSoftwares.Values
       }
}

Creating a configuration for the resource needs an Executable path, Product name and AdminDeploymentFile.xml.

Configuration VisualStudio2015Config
{
    Import-Dscresource -ModuleName xVisualStudio2015   
  
    xVisualStudio2015 VisualStudio2015
    {
        ExecutablePath = "C:\Softwares\VS2015\vs_enterprise.exe"
        ProductName = "Microsoft Visual Studio Enterprise 2015"
        AdminDeploymentFile = " C:\Softwares\VS2015\AdminDeployment.xml"
        Ensure = "Present"       
    }      
}

VisualStudio2015Config 

PS C:\PowerShell\DSC> Start-DscConfiguration -Path .\VisualStudio2015Config -Wait -Verbose -Force
VERBOSE: Perform operation 'Invoke CimMethod' with following parameters, ''methodName' = SendConfigurationApply,'className' = MSFT_DSCLocalConfigurationManager,'name
spaceName' = root/Microsoft/Windows/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer VM2012-PRADEV with user sid S-1-5-21-1229272821-1801674531-839522115-133922.
VERBOSE: [VM2012-PRADEV]: LCM:  [ Start  Set      ]
VERBOSE: [VM2012-PRADEV]: LCM:  [ Start  Resource ]  [[xVisualStudio2015]VisualStudio2015]
VERBOSE: [VM2012-PRADEV]: LCM:  [ Start  Test     ]  [[xVisualStudio2015]VisualStudio2015]
VERBOSE: [VM2012-PRADEV]: LCM:  [ End    Test     ]  [[xVisualStudio2015]VisualStudio2015]  in 1.5860 seconds.
VERBOSE: [VM2012-PRADEV]: LCM:  [ Skip   Set      ]  [[xVisualStudio2015]VisualStudio2015]
VERBOSE: [VM2012-PRADEV]: LCM:  [ End    Resource ]  [[xVisualStudio2015]VisualStudio2015]
VERBOSE: [VM2012-PRADEV]: LCM:  [ End    Set      ]
VERBOSE: [VM2012-PRADEV]: LCM:  [ End    Set      ]    in  2.4780 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 2.626 seconds


No comments: