Sunday, August 16, 2015

Using DSC, OneGet and chef to install chocolatey packages

Continuing from the previous sets of posts, we'll see how to make use of the packages available in our chocolatey repository and install them using Chef.

Installation of packages from a Chocolatey source during a chef client run can be achieved in a number of ways. We can make use of the Chocolatey cookbook that is available as part of the chef community cookbooks. If you want to make use of DSC to download and install packages, then you can use a resource to install packages using OneGet on the machine.

In this part of the series, I’ll show how we can use DSC to install packages from the OneGet source.
The first step is to ensure that WMF 5.0 is installed on the server. We can make use of the chocolatey cookbook to install PowerShell 5.0 on the server as given below.

This will ensure that the required PowerShell version is installed on the server.

Once we have WMF 5.0 installed, then we can use DSC to install packages on the server.
For installing and uninstalling packages, I’ve created a DSC resource. The resource code is as given below.

enum Ensure
{
    Absent
    Present
}

[DscResource()]
class xPackageManagement
{
    [DsCProperty(Key)]
    [System.String] $Name

    [DsCProperty(Mandatory)]
    [Ensure] $Ensure

    [DsCProperty()]
    [System.String] $Version

    [DsCProperty()]
    [System.String] $Source

    [DsCProperty(NotConfigurable)]
    [Boolean] $IsValid

    [xPackageManagement] Get()
    {       
        $this.IsValid = $false
        if(-not ([string]::IsNullOrWhiteSpace($this.Version)))
        {
            $package = Get-Package |? {($_.Name -eq $this.Name) -and ($_.Version -eq $this.Version)}
        }
        else
        {
            $package = Get-Package |? {$_.Name -eq $this.Name}
        }

        if($this.Ensure -eq [Ensure]::Present)
        {
            if($package -ne $null)
            {
                $this.IsValid = $true
            }
        }
        else
        {
            if($package -eq $null)
            {
                $this.IsValid = $true
            }
        }
        return $this  
    }

    [void] Set()
    {                  
      
        if($this.Ensure -eq [Ensure]::Present)
        {      
            if(-not ([String]::IsNullOrWhiteSpace($this.Source)))
            {
                if(-not ([string]::IsNullOrWhiteSpace($this.Version)))
                {
                    Install-Package -Name $this.Name -RequiredVersion $this.Version -Source $this.Source
                }
                else
                {
                    Install-Package -Name $this.Name -Source $this.Source
                }
            }
            else
            {
                if(-not ([string]::IsNullOrWhiteSpace($this.Version)))
                {
                    Install-Package -Name $this.Name -RequiredVersion $this.Version
                }
                else
                {
                    Install-Package -Name $this.Name
                }
            }
        }
        else
        {
            if(-not ([string]::IsNullOrWhiteSpace($this.Version)))
            {
                Get-Package |? {($_.Name -eq $this.Name) -and ($_.Version -eq $this.Version)} | Uninstall-Package -Force
            }
            else
            {
                Get-Package |? {$_.Name -eq $this.Name} | Uninstall-Package -Force
            }           
        }
    }

    [bool] Test()
    {
        $result = $this.Get()

        if($this.Ensure -eq [Ensure]::Present)
        {      
            if($result.IsValid)
            {
                if(-not ([String]::IsNullOrWhiteSpace($this.Version)))
                {
                    Write-Verbose "The pacakge $($this.Name) with version $($this.Version) already exists"    
                    return $true
                }
                else
                {
                    Write-Verbose "The pacakge $($this.Name) already exists"    
                    return $true
                }
            }
            else
            {
                if(-not ([String]::IsNullOrWhiteSpace($this.Version)))
                {
                    Write-Verbose "The pacakge $($this.Name) with version $($this.Version) does not exists. This will be installed"    
                    return $false
                }
                else
                {
                    Write-Verbose "The pacakge $($this.Name) does not exists. This will be installed"    
                    return $false
                }                    
            }
        }
        else
        {
            if($result.IsValid)
            {
                Write-Verbose "The pacakge $($this.Name) does not exists"    
                return $true
            }
            else
            {
                if(-not ([String]::IsNullOrWhiteSpace($this.Version)))
                {
                    Write-Verbose "The pacakge $($this.Name) with version $($this.Version) exists. This will be uninstalled"    
                    return $false
                }
                else
                {
                    Write-Verbose "The pacakge $($this.Name) exists. This will be uninstalled"    
                    return $false
                }                 
            }
        }
    }
}

You can also download the code from the GitHub repository https://github.com/prajeeshprathap/DSCResources

Before calling this resource, we need to first register the new package source that contains our packages. We'll make use of DSC here also.

enum Ensure
{
    Absent
    Present
}

[DscResource()]
class xChocolateySource
{
    [DsCProperty(Key)]
    [System.String] $Name

    [DsCProperty(Mandatory)]
    [System.String] $Location

    [DsCProperty(Mandatory)]
    [Ensure] $Ensure

    [DsCProperty(NotConfigurable)]
    [Boolean] $IsValid

    [xChocolateySource] Get()
    {      
        $this.IsValid = $false
        $packagesource = Get-PackageSource -ProviderName chocolatey -Name $this.Name       

        if($this.Ensure -eq [Ensure]::Present)
        {
            if($packagesource -ne $null)
            {
                $this.IsValid = $true
            }
        }
        else
        {
            if($packagesource -eq $null)
            {
                $this.IsValid = $true
            }
        }
        return $this 
    }

    [void] Set()
    {                 
     
        if($this.Ensure -eq [Ensure]::Present)
        {    
            Register-PackageSource -ProviderName Chocolatey -Name $this.Name -Location $this.Location -Force -Confirm:$true           
        }
        else
        {
            Unregister-PackageSource -Source $this.Name -ProviderName Chocolatey -Force         
        }
    }

    [bool] Test()
    {
        $result = $this.Get()

        if($this.Ensure -eq [Ensure]::Present)
        {     
            if($result.IsValid)
            {
                Write-Verbose "The pacakge source $($this.Name) already exists"   
                return $true               
            }
            else
            {
                Write-Verbose "The pacakge source $($this.Name) does not exist. This will be created"   
                return $false                  
            }
        }
        else
        {
            if($result.IsValid)
            {
                Write-Verbose "The pacakge source $($this.Name) does not exists"   
                return $true
            }
            else
            {
                Write-Verbose "The pacakge source $($this.Name) exists. This will be be removed"   
                return $false                
            }
        }
    }
}

Next we’ll create a cookbook and use this resource in the cookbook to install the packages.

Knife cookbook create packagemanagement

Edit the default.rd file to invoke the created resources


After adding the resources, we can now upload the package to the server and add it to the run list

Knife cookbook upload packagemanagement




Next chef run will execute the resource and install the package from the source.


No comments: