Recently I challenged myself to see how far we can go with PowerShell DSC and non Microsoft Products. My goal in this challenge was to install and configure PHP and MySQL on a blank Windows Server 2012 R2 offline server.
Why would someone do this?
Because I wanted gain a better understanding on how far PowerShell DSC configurations are useful in challenging tasks with non Windows or Microsoft Products. Another outcome should be a fully functional PHP server to host PIWIK for a SharePoint Farm.
And why would someone try this on a offline server without any connection to the internet? This a very common scenario I’m facing with customers.
What do we start with?
As I’m writing this blog, PHP version 7.2 is the current one. I used to work with PHP before in several projects and used XAMPP to host my applications for development purposes. XAMPP bundles Apache, PHP and MySQL. To get started with XAMPP on a windows system is a breeze.
Installing all that stuff manually works fine and there are many options available supporting the installations: e.g. the IIS Web Platform Installer – But not in an offline scenario. The same fact is preventing me from using chocolatey in PowerShell DSC. I do not have an offline option.
I started with the following example provided through the xWebAdministration DSC Resource:PowerShellDsc/xWebAdministration – Examples: Registering PHP
Install the necessary Windows Features and disable not needed IIS Pools
I use the following lines of PowerShell to configure Windows Server and IIS with DSC:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$features = @("Web-Server", | |
"Web-Mgmt-Tools", | |
"web-Default-Doc", | |
"Web-Dir-Browsing", | |
"Web-Http-Errors", | |
"Web-Static-Content", | |
"Web-Http-Logging", | |
"web-Stat-Compression", | |
"web-Filtering", | |
"web-CGI", | |
"web-ISAPI-Ext", | |
"web-ISAPI-Filter" | |
) | |
$dependsOn = @() | |
$features | ForEach-Object –Process { | |
WindowsFeature $_ { | |
Name = $_ | |
Ensure = "Present" | |
} | |
$dependsOn += "[WindowsFeature]$($_)" | |
} | |
@( | |
".NET v2.0" , | |
".NET v2.0 Classic", | |
".NET v4.5", | |
".NET v4.5 Classic", | |
"Classic .NET AppPool", | |
"DefaultAppPool" | |
) | ForEach-Object –Process { | |
xWebAppPool "Remove-$($_.ToString().Replace(" ","").Replace(".",""))" | |
{ | |
Name = $_ | |
Ensure = "Absent" | |
DependsOn = $dependsOn | |
} | |
} | |
xWebSite RemoveDefaultWebSite | |
{ | |
Name = "Default Web Site" | |
PhysicalPath = "C:\inetpub\wwwroot" | |
Ensure = "Absent" | |
DependsOn = $dependsOn | |
} | |
Package vcRedist { | |
Path = "$($SourceFolder)\VC_redist.x64.exe" | |
ProductId = "{6c6356fe-cbfa-4944-9bed-a9e99f45cb7a}" | |
Name = "Microsoft Visual C++ 2017 Redistributable (x64) – 14.11.25325" | |
Arguments = "/install /passive" | |
} | |
Archive PHP { | |
Path = "$($SourceFolder)\php-7.1.12-nts-Win32-VC14-x64.zip" | |
Destination = $phpDirectory | |
} | |
File PhpWinCache { | |
SourcePath = "$($SourceFolder)\WinCache\php_wincache.dll" | |
DestinationPath = "$($phpDirectory)\ext" | |
Type = "File" | |
MatchSource = $true | |
DependsOn = @("[Archive]PHP") | |
} | |
File PhpIni { | |
SourcePath = "$($SourceFolder)\php.ini" | |
DestinationPath = "$($phpDirectory)\php.ini" | |
MatchSource = $true | |
Force = $true | |
Checksum = "SHA-512" | |
Ensure = "Present" | |
DependsOn = @("[Archive]PHP") | |
} | |
xIisModule PhpHandler { | |
Name = "phpFastCgi" | |
Path = "$($phpDirectory)\php-cgi.exe" | |
RequestPath = "*.php" | |
Verb = "*" | |
Ensure = "Present" | |
ModuleType = "FastCgiModule" | |
DependsOn = @("[Package]vcRedist", "[Archive]PHP") + $dependsOn | |
} | |
Script FixIisModule { | |
GetScript = { | |
return @{ | |
Result = "" | |
} | |
} | |
SetScript = { | |
Import-Module WebAdministration | |
Set-WebHandler phpFastCgi –Modules FastCgiModule | |
} | |
TestScript = { | |
Import-Module WebAdministration | |
$fastCgi = Get-WebHandler phpFastCgi | |
return $fastCgi.Modules -eq "FastCgiModule" | |
} | |
DependsOn = "[xIisModule]PhpHandler" | |
} | |
Environment PathPhp { | |
Name = "Path" | |
Value = ";$($phpDirectory)" | |
Ensure = "Present" | |
Path = $true | |
DependsOn = "[Archive]PHP" | |
} |
After completing the IIS configuration we can start with the installation of PHP and MySQL.
Configuration of PHP and the WinCache Extension
With PHP 7.1 there are several installation options available. I opted for the Non Thread Save version of PHP and downloaded this version on my client and made it available on the server.
Additionally you will need to download:
The installation of PHP is fairly simple. It consists of the following steps:
- Install Microsoft Visual C++ 2017
- Extract the PHP zip file to your “installation” folder
- Copy the WinCache DLL to the ext folder in your PHP “installation” folder
- Rename the php.ini-production to php.ini
- Enable the WinCache extension in the php.ini
- Add the Php Handler
- Configure the Path for PHP
Steps 1 to 4 are regualr DSC Resources:
- Package Resource
- Archive Resource
- File Resource
- File Resource
- See below
- xIisModule Resource – Currently with one Issue
- Environment Resource
For Step 5 we have to write some Code using the script resource:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Script PhpWinCache { | |
GetScript = { | |
return @{ | |
Result = "" | |
} | |
} | |
TestScript = { | |
$iniFile = "$($phpDirectory)\php.ini" | |
$currentStatus = Select-String –Path $iniFile –Pattern "^extension=php_wincache.dll" | |
Write-Debug "$($currentStatus)" | |
return ($null -ne $currentStatus) | |
} | |
SetScript = { | |
$iniFile = "$($phpDirectory)\php.ini" | |
Add-Content –Path $iniFile –Value "`r`nextension=php_wincache.dll" –Force | |
} | |
DependsOn = @("[File]PhpWinCache", "[File]PhpIni") | |
} |
Configuration of MySQL
The MySQL Part is fairly simple. Download the current MySQL Version:
- MySQL Installer Community (5.7.20.0)
- Visual C++ Redistributable Packages for Visual Studio 2013 – Version 12.0.40660 (Version is important!)
and open this example provided through the xMySql Resource to finish your DSC configuration:
I modified the package resource to match the current MySQL version:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Package MySqlInstaller { | |
Path = "$($SourceFolder)\mysql-installer-community-5.7.20.0.msi" | |
ProductId = "{7999380B-7D4E-4E76-B61D-3AFCA710F24D}" | |
Name = "MySQL Installer – Community" | |
Arguments = "" | |
} | |
Package vcRedist2013 { | |
Path = "$($SourceFolder)\vcredist_x64.exe" | |
ProductId = "{ef6b00ec-13e1-4c25-9064-b2f383cb8412}" | |
Name = "Microsoft Visual C++ 2013 Redistributable (x64) – 12.0.40660" | |
Arguments = "/install /passive" | |
} | |
xMySqlServer MySqlInstance { | |
MySqlVersion = "5.7.20" | |
RootPassword = $MySqlRootPassword | |
Port = 3306 | |
Ensure = "Present" | |
DependsOn = @("[Package]MySqlInstaller","[Package]vcRedist2013") | |
} | |
Script FixReadErrorinMySqlResource { | |
GetScript = { | |
return @{ | |
Result = "" | |
} | |
} | |
SetScript = { | |
$filePath = "C:\Program Files\WindowsPowerShell\Modules\xMySql\2.1.0.0\MSFT_xMySqlUtilities.psm1" | |
$newLine = @" | |
if(`$line -match [RegEx]::Escape("mysql: [Warning] Using a password on the command")) | |
{ | |
break | |
} | |
"@ | |
$content = Get-Content $filePath | |
$content[241] = "{0}`r`n{1}" -f $newLine, $content[241] | |
$content | Set-Content $filePath | |
Import-Module $filePath | |
} | |
TestScript = { | |
$content = Get-Content "C:\Program Files\WindowsPowerShell\Modules\xMySql\2.1.0.0\MSFT_xMySqlUtilities.psm1" | |
if( $content.length -eq 319 -and | |
$content[241] -eq " if(`$line -match [RegEx]::Escape(`"mysql: [Warning] Using a password on the command`"))") | |
{ | |
return $true | |
} | |
return $false | |
} | |
} | |
xMySqlServer MySqlInstance { | |
MySqlVersion = "5.7.20" | |
RootPassword = $MySqlRootPassword | |
Port = 3306 | |
Ensure = "Present" | |
DependsOn = @("[Package]MySqlInstaller", "[Script]FixReadErrorinMySqlResource) | |
} | |
xMySqlDatabase MySqlPiwikDb { | |
MySqlVersion = "5.7.20" | |
DatabaseName = "Piwik" | |
RootCredential = $MySqlRootPassword | |
Ensure = "Present" | |
DependsOn = @("[xMySqlServer]MySqlInstance", "[Script]FixReadErrorinMySqlResource) | |
} |
Summary
The installation process of PHP and MySQL on a Windows Server 2012 R2 is straight forward. Adding the WinCache Extension was tricky.
Biggest challenge: The examples provided in the github repsitories of xWebAdministration, xMySql were not up to date. The Guids and used versions differ from the current ones.
What’s next?
The next step would be to
- Create a new IIS Web Application
- Configure this IIS Web Application for HTTPS
- Provision and configure PIWIK
Hello Andi,
I am new to powershell dsc. Was wondering if you can share the whole dsc script for php on IIS. I am getting confused between your 2 scripts IISInstallationDSC and phowincacheextension.
Regards,
Sai
LikeLike
Thanks for the feedback. I added the part about the PHP setup.
LikeLike
Hello Andi,
I have one question, if I want use how should I write parameter in the request?
best regards
Gunter
LikeLike
Hello Gunter,
What kind of parameters do you want to use?
BR Andi
LikeLike