DscResources/MSFT_xMySqlServer/MSFT_xMySqlServer.psm1

<#
   DSC resource designed to create an instance of MySQL, using the supplied parameters as per resource schema.
 
   Copyright (c) Microsoft Corporation, 2014
#>


# NOTE: LocalizedData isn't used in this resource as there are no interactive/user visible strings

$mySqlVersion = "5.6"
$Debug = $true

################################################################################################################################
# Trace-Message (write verbose if Debug flag is on)
################################################################################################################################

Function Trace-Message
{
    param([string] $Message)

    if($Debug)
    {
        Write-Verbose $Message
    }
}


#region MySql Helpers
################################################################################################################################
# Get-MySqlInstallerFolder (helper to get localtion of MySql installer folder on local machine)
################################################################################################################################

function Get-MySqlInstallerFolder
{
    if($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
    {
        return "$(${env:ProgramFiles(x86)})\MySQL\MySQL Installer"
    }
    else
    {
        return "$(${env:ProgramFiles})\MySQL\MySQL Installer"
    }
}

$mySqlInstallerConsole = Join-path (Get-MySqlInstallerFolder) -ChildPath "MySQLInstallerConsole.exe"

################################################################################################################################
# Get-MySqlInstalledComponents (helper to get SQL installed components)
################################################################################################################################

function Get-MySqlInstalledComponents
{
    # we don't want to throw an exception here for the case where Ensure=Absent during Test/Get-DscConfiguration. Instead of failing
    # with an exception it should return false. Removing exception handling and throwing.
    if ((Test-Path -Path $mySqlInstallerConsole))
    {
        $statusResults = &$mySqlInstallerConsole --nowait --type=server --action=Status

        $statusResults | % {
        
            # split each line on the status delimiter
            $statusPair = $_ -split ":"

            # if the line contains a status, check the status
            if($statusPair.count -gt 1) {

                # if the status is installed return the product name
                if($statusPair[1].Trim() -ieq "installed.")
                {
                    Write-Output $statusPair[0].Trim()
                }
            }
        }
    }
    else
    {
        $null
    }
}

################################################################################################################################
# Get-MySqlArchitectureName (helper to get SQL architecture string in expected format)
################################################################################################################################

function Get-MySqlArchitectureName
{
    if($env:PROCESSOR_ARCHITECTURE -eq "AMD64")
    {
        return "winx64"
    }
    else
    {
        return "win32"
    }
}

################################################################################################################################
# Get-MySqlProductName (helper to get product name of MySQL)
################################################################################################################################

function Get-MySqlProductName
{
    return "mysql-server-$mySqlVersion-$(Get-MySqlArchitectureName)"
}

################################################################################################################################
# Get-MySqlCatalogName (helper to get the MySQL catalog name)
################################################################################################################################

function Get-MySqlCatalogName
{
    return "mysql-$mySqlVersion-$(Get-MySqlArchitectureName)"
}

#endregion

#region *-TargetResource implementation

################################################################################################################################
# Get-TargetResource (params: ServiceName, Ensure, RootPassword)
################################################################################################################################

function Get-TargetResource
{
    [OutputType([Hashtable])]
    param
    (
        [parameter(Mandatory = $true)]
        [string] $ServiceName,
        
        [ValidateSet("Present", "Absent")]
        [string] $Ensure = "Present",

        [parameter(Mandatory = $true)]
        [pscredential] $RootPassword
    )
    
    $Ensure = "Absent"
    $MySqlInstalled = $false 

    # get installed products
    $products = @(Get-MySqlInstalledComponents)

    if ($products -ne $null)
    {
        foreach($product in $products)
        {
            Trace-Message  "'$product' is currently installed"
        }
    
        Trace-Message "Checking to see if installed products contains '$((Get-MySqlProductName))'"

        if($products -contains (Get-MySqlProductName))
        {
            $MySqlInstalled  = $true 
        }

        if($MySqlInstalled )
        {
            Trace-Message "Present"
            $Ensure = "Present"
        }
    }

    return @{
        Ensure = $Ensure
        ServiceName = $ServiceName
    }
}

################################################################################################################################
# Test-TargetResource (params: ServiceName, Ensure, RootPassword)
################################################################################################################################

function Test-TargetResource 
{
    [OutputType([Boolean])]
    [CmdletBinding(SupportsShouldProcess=$true)]
    param
    (
        [parameter(Mandatory = $true)]
        [string] $ServiceName,
        
        [ValidateSet("Present", "Absent")]
        [string] $Ensure = "Present",
        
        [parameter(Mandatory = $true)]
        [pscredential] $RootPassword
    )
    
    Trace-Message "Ensure is $Ensure"

    $status = Get-TargetResource @psboundparameters

    if($status.Ensure -eq $Ensure)
    {
        return $true
    }
    else
    {
        return $false
    }
}

################################################################################################################################
# Set-TargetResource (params: ServiceName, Ensure, RootPassword)
################################################################################################################################

function Set-TargetResource 
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    param
    (
        [parameter(Mandatory = $true)]
        [string] $ServiceName,

        [ValidateSet("Present", "Absent")]
        [string] $Ensure = "Present",

        [parameter(Mandatory = $true)]
        [pscredential] $RootPassword
        
    )
    
    if((Test-TargetResource @psboundparameters))
    {
        return
    }
    
    $status = Get-TargetResource @psboundparameters 

   <# config parameters:
      servertype - Config type [developer|server|dedicated] Default developer.
      enabletcpip - Use TCP/IP. Possible values [true | false] Default true.
      port - Port MySQL Server will use for communication. Default 3306
      openfirewall - Create a rule in the Windows firewall for the specified port.
      passwd - The default root password. Required.
      existingpasswd - Existing instances require the current password.
      servicename - The Service Name. Default MySQL56
      sauser - A valid username.
      sapass - The password for sauser.
      autostart - Autmatically start the service at system startup? Default true.
      generallog - Enable General Log [true|false]. Default false
      generallogname - General Log path/filename. Default MGMT-9737-3172.log
      slowlog - Enable Slow Query Log [true|false] Default false
      slowlogname - Slow Query Log path/filename. Default MGMT-9737-3172-slow.log
      slowtime - Time threshold for slow query log in seconds. Default 10
      binlog - Enable Bin Log [true|false] Default false
      binlogname - Bin Log path/filename base. Default MGMT-9737-3172-bin
      errorlogname - Error Log path/filename. Default. MGMT-9737-3172.err
      removedatadir - [true|false]. Default false. Only valid for remove action.
      timeout - Configuration timeout in seconds. The default is 180.
      #>


    # NOTE: BUGBUG: the reason we need to do this is as follows:
    # The first time a node gets configured with the sql instance, there is no trace of it
    # on the machine, which changes the way in which we construct the config parameter. If it is
    # the first time, the 'existingpasswd' argument cannot be given or it will fail. Once installed
    # the installation creates a datadir that does not get remove during uninstall. After that, every
    # time we try to install with the config, we have to provide the existingpasswd parameter, or installation/removal
    # will fail. This is an issue with the usage of removedatadir parameter, and this is the work-around

    $InstanceFlagpath = "$($env:ProgramFiles)\WindowsPowerShell\Modules\xMySQL\MySQLInstanceFlag.txt"
    Trace-Message "SQL instance flag path is $InstanceFlagpath"

    if (!(Test-Path -Path $InstanceFlagpath))
    {
        # this is the first time this is installed the machine (if Ensure=Present) and we cannot specify the existing pwd or it will fail
        Trace-Message "Instance flag not found. Omitting existing password parameter"
        $config = "--config=$(Get-MySqlProductName):passwd=$($RootPassword.GetNetworkCredential().Password);servicename=$ServiceName;autostartservice=true;servertype=server"
    }
    else
    {
        # in this case, we found the instance flag marker so we know that we need to specify the existingpwd argument.
        Trace-Message "Instance flag was found. Using optional parameter existingpasswd"
        $config = "--config=$(Get-MySqlProductName):passwd=$($RootPassword.GetNetworkCredential().Password);servicename=$ServiceName;existingpasswd=$($RootPassword.GetNetworkCredential().Password);autostartservice=true;servertype=server"
    }
    
    Trace-Message "mySqlInstallerConsole is $mySqlInstallerConsole"

    if($Ensure -eq "Present")
    {
        if(-not $status.MySqlInstalled)
        {
            Trace-Message "Installing MySQL"
            &$mySqlInstallerConsole --nowait --action=Install "--catalog=$(Get-MySqlCatalogName)" "--product=$(Get-MySqlProductName)" $config

            # don't stamp the machine until after the installation has completed, successfully!
            Trace-Message "Creating instance flag"
            New-Item -ItemType File -Path $InstanceFlagpath -Force
            Trace-Message "Instance flag has been created"
        }
    }
    else
    {
        Trace-Message "Removing MySQL"
        &$mySqlInstallerConsole --nowait --type=server --action=Remove $config
    }

}
#endregion

Export-ModuleMember -function Get-TargetResource, Set-TargetResource, Test-TargetResource