PowerShell 5.0 class based DSC resources simplifies the
development of custom resources by making use of the concepts from object
oriented programming like class, properties, methods, inheritance, method
overriding etc. With the support for inheritance now it’s much easier to create
a composite resource or a resource that can make use of the functionality of an
existing resource to do something extra.
We'll see how we can use this functionality to build a SharePoint content database that makes use of the xDatabase resource to further extend the database properties based on the specifications. The resources are both saved in the same file and the base resource must at least include one key property and the Get, Set and Test method in the class defined resource or the base resource.
To create the folder structure, modules and manifest files,
we use the code snippet from the previous post.
$resourcePath = "C:\Program
Files\WindowsPowerShell\Modules\xSPContentDb"
#Create the folder in PSModulePath
New-Item -ItemType
Directory -Path
"$resourcePath"
#Create a module manifest for the DSC resource
New-ModuleManifest -Path "$resourcePath\xSPContentDb.psd1" `
-Author
"Prajeesh Prathap" `
-CompanyName
"Prowareness" `
-Guid
([System.Guid]::NewGuid()) `
-RootModule
"xSPContentDb.psm1" `
-Description
"DSC resource sample" `
-PowerShellVersion
5.0 `
-DscResourcesToExport
@('xSPContentDb',
'xDatabase') `
-ModuleVersion
1.0 `
-Copyright
'(c) 2015 Prajeesh Prathap. All rights reserved'
PSEDIT "$resourcePath\xSPContentDb.psd1"
#Create the module files
New-Item -ItemType
File -Path
"$resourcePath\xSPContentDb.psm1"
PSEDIT "$resourcePath\xSPContentDb.psm1"
The base resource (xDatabase) is defined as given below. As
you can see, we’ve implemented the Get, Set and Test methods in the base
resource. When we create the resource for the Content database, we’ll see how
we can override some of these methods/ properties and add some additional
properties to the resource.
[DscResource()]
class xDatabase
{
[DsCProperty(Key)]
[System.String] $SQLServerInstance
[DsCProperty(Key)]
[System.String] $DBName
[DsCProperty(Mandatory)]
[System.String] $DataFileLocation
[DsCProperty(Mandatory)]
[System.String] $LogFileLocation
[DsCProperty(Mandatory)]
[System.Double] $InitialDataSizeInMB
[DsCProperty(Mandatory)]
[System.Double] $InitialLogSizeInMB
[DsCProperty(Mandatory)]
[System.Double] $DataGrowFactor
[DsCProperty(Mandatory)]
[System.Double] $LogGrowFactor
[DsCProperty(Mandatory)]
[System.String] $Collation
[DsCProperty(Mandatory)]
[Ensure] $Ensure
[DsCProperty(NotConfigurable)]
[Boolean] $IsValid
[xDatabase] Get()
{
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$sqlServer
= new-object
Microsoft.SqlServer.Management.Smo.Server($this.SQLServerInstance)
$this.IsValid = $false
foreach
($_ in
$sqlServer.Databases)
{
if
($_.Name
-eq $this.DBName)
{
$this.IsValid = $true
}
}
return
$this
}
[void] Set()
{
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$sqlServer
= new-object
Microsoft.SqlServer.Management.Smo.Server($this.SQLServerInstance)
if($this.Ensure
-eq [Ensure]::Present)
{
Write-Verbose
"Creating database $($this.DBName)"
$db
= new-object
Microsoft.SqlServer.Management.Smo.Database
($sqlServer,
$this.DBName)
if($this.Collation
-ne $null)
{
$db.Collation = $this.Collation
}
##
Create a new primary filegroup
$dbPrimaryFileGroup
= new-object
('Microsoft.SqlServer.Management.Smo.FileGroup')
($db, 'PRIMARY')
$db.FileGroups.Add($dbPrimaryFileGroup)
##
Create the data file and add it to the primary filegroup
$dbDataFile
= new-object
('Microsoft.SqlServer.Management.Smo.DataFile')
($dbPrimaryFileGroup, $this.DBName)
$dbPrimaryFileGroup.Files.Add($dbDataFile)
$dbDataFile.FileName = $this.DataFileLocation
+ '\' + $this.DBName + '.mdf'
$dbDataFile.Size = $this.InitialDataSizeInMB
* 1024
$dbDataFile.GrowthType =
"KB"
$dbDataFile.Growth = $this.DataGrowFactor
* 1024
$dbDataFile.IsPrimaryFile =
"True"
##
Create the log file
$dbLogFilename
= $this.DBName + "_Log"
$dbLogFile
= new-object
('Microsoft.SqlServer.Management.Smo.LogFile')
($db, $dbLogFilename)
$db.LogFiles.Add($dbLogFile)
$dbLogFile.FileName = $this.LogFileLocation
+ '\' + $dbLogFilename
+ '.ldf'
$dbLogFile.Size = $this.InitialLogSizeInMB
* 1024
$dbLogFile.GrowthType ="KB"
$dbLogFile.Growth = $this.LogGrowFactor
* 1024
$db.Create()
}
else
{
$db
= new-object
Microsoft.SqlServer.Management.Smo.Database
$db
= $sqlServer.Databases[$this.DBName]
if($db -ne $null)
{
Write-Verbose
"Dropping database $($this.DBName)"
$db.Drop()
}
}
}
[bool] Test()
{
$result
= $this.Get()
if($this.Ensure
-eq [Ensure]::Present)
{
if($result.IsValid)
{
Write-Verbose
"Database $($this.DBName) already
exists on SQL Server: $($this.SQLServerInstance)"
return
$true
}
else
{
Write-Verbose
"Database $($this.DBName) does not
exist on SQL Server : $($this.SQLServerInstance),
the database will be created"
return
$false
}
}
else
{
if($this.IsValid)
{
Write-Verbose
"Database $($this.DBName) exists on
SQL Server : $($this.SQLServerInstance),
the database will be removed"
return
$false
}
else
{
Write-Verbose
"Database $($this.DBName) does not
exist on SQL Server: $($this.SQLServerInstance)"
return
$true
}
}
}
}
The xSPContentDb resource extends the xDatabase resource and
for this example, I’ve added some default values to the location to manage the
primary and log files etc. The additional property to mention the RecoveryModel
on the database is configured at the xSPContentDb resource object. Also I’ve
decided to override the Test and Set methods to add additional checks and
alterations on the database.
[DscResource()]
class xSPContentDb
: xDatabase
{
[DsCProperty(NotConfigurable)]
[System.String] $DataFileLocation
= "D:\SQLDataFiles"
[DsCProperty(NotConfigurable)]
[System.String] $LogFileLocation
= "D:\SQLLogFile"
[DsCProperty(NotConfigurable)]
[System.Double] $InitialDataSizeInMB
= 20
[DsCProperty(NotConfigurable)]
[System.Double] $InitialLogSizeInMB
= 5
[DsCProperty(NotConfigurable)]
[System.Double] $DataGrowFactor
= 5
[DsCProperty(NotConfigurable)]
[System.Double] $LogGrowFactor
= 2
[DsCProperty(Mandatory)]
[System.String] $RecoveryMode
[void] Set()
{
$isValid
= ([xDatabase]$this).Test()
if($isValid)
{
Write-Verbose
"Setting recovery mode on database $($this.DBName)"
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$sqlServer =
new-object Microsoft.SqlServer.Management.Smo.Server($this.SQLServerInstance)
$db
= new-object
Microsoft.SqlServer.Management.Smo.Database
$db
= $sqlServer.Databases[$this.DBName]
$db.RecoveryModel =
$this.RecoveryMode
$db.Alter()
}
else
{
([xDatabase]$this).Set()
}
}
[bool] Test()
{
$isValid
= ([xDatabase]$this).Test()
if($isValid)
{
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$sqlServer =
new-object Microsoft.SqlServer.Management.Smo.Server($this.SQLServerInstance)
$db
= new-object
Microsoft.SqlServer.Management.Smo.Database
$db
= $sqlServer.Databases[$this.DBName]
if($db.RecoveryModel
-eq "Simple")
{
if($this.RecoveryMode
-eq "Simple")
{
Write-Verbose
"$($this.DBName) already has
recovery mode set to Simple"
return
$true
}
else
{
Write-Verbose
"$($this.DBName) already does
not have recovery mode set to Simple. Will be set to simple"
return
$false
}
}
else
{
if($this.RecoveryMode
-eq "Simple")
{
Write-Verbose
"$($this.DBName) already does
not have recovery mode set to Simple. Will be set to Full"
return
$false
}
else
{
Write-Verbose
"$($this.DBName) already has
recovery mode set to Full"
return
$true
}
}
}
return
$false
}
}
Note
that some of the properties that are mandatory in the base resource are not
mandatory in the xSPContentDb class. Also the ([xDatabase]$this).Test() syntax is used to call the methods
on the base class.
Now you
can create a configuration for creating a SharePoint content database like.
Configuration SPContentDBConfig
{
Import-Dscresource
-ModuleName xSPContentDb
xSPContentDb
SPContentDB
{
SQLServerInstance = MYServer'
DBName =
'PS5DSCDemoDB'
Collation =
"Latin1_General_CI_AS_KS_WS"
RecoveryMode =
"Full"
Ensure =
"Present"
}
}
SPContentDBConfig
PS E:\PSDemo5> Start-DscConfiguration -Wait -Force -Path
E:\PSDemo5\SPContentDBConfig -Verbose -Debug
VERBOSE: Perform operation 'Invoke CimMethod' with following
parameters, ''methodName' = SendConfigurationApply,'className' =
MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windo
ws/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer PRA-DEV with user
sid S-1-5-21-1229272821-1801674531-839522115-133922.
VERBOSE: [PRA-DEV]: LCM: [
Start Set ]
VERBOSE: [PRA-DEV]: LCM: [
Start Resource ] [[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]: LCM: [
Start Test ]
[[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]:
[[xSPContentDb]SPContentDB] Database PS5DSCDemoDB does not exist on SQL
Server : PRA-DEV, the database will be created
VERBOSE: [PRA-DEV]: LCM: [
End Test ]
[[xSPContentDb]SPContentDB] in
0.7500 seconds.
VERBOSE: [PRA-DEV]: LCM: [
Start Set ]
[[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]:
[[xSPContentDb]SPContentDB] Database PS5DSCDemoDB does not exist on SQL
Server : PRA-DEV, the database will be created
VERBOSE: [PRA-DEV]:
[[xSPContentDb]SPContentDB] Creating database PS5DSCDemoDB
VERBOSE: [PRA-DEV]: LCM: [
End Set ]
[[xSPContentDb]SPContentDB] in
8.6090 seconds.
VERBOSE: [PRA-DEV]: LCM: [
End Resource ] [[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]: LCM: [
End Set ]
VERBOSE: [PRA-DEV]: LCM: [
End Set ]
in 10.5460 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 10.66
seconds
PS E:\PSDemo5> E:\PSDemo5\SPContentDBConfig.ps1
Directory: E:\PSDemo5\SPContentDBConfig
Mode
LastWriteTime Length
Name
----
------------- ------
----
-a----
3-6-2015 16:49 2082 localhost.mof
PS E:\PSDemo5> Start-DscConfiguration -Wait -Force -Path
E:\PSDemo5\SPContentDBConfig -Verbose -Debug
VERBOSE: Perform operation 'Invoke CimMethod' with following
parameters, ''methodName' = SendConfigurationApply,'className' =
MSFT_DSCLocalConfigurationManager,'namespaceName' = root/Microsoft/Windo
ws/DesiredStateConfiguration'.
VERBOSE: An LCM method call arrived from computer PRA-DEV with user
sid S-1-5-21-1229272821-1801674531-839522115-133922.
VERBOSE: [PRA-DEV]: LCM: [
Start Set ]
VERBOSE: [PRA-DEV]: LCM: [
Start Resource ] [[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]: LCM: [
Start Test ]
[[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]:
[[xSPContentDb]SPContentDB] Database PS5DSCDemoDB already exists on SQL
Server: PRA-DEV
VERBOSE: [PRA-DEV]:
[[xSPContentDb]SPContentDB] PS5DSCDemoDB already has recovery mode set
to Full
VERBOSE: [PRA-DEV]: LCM: [
End Test ]
[[xSPContentDb]SPContentDB] in
0.8910 seconds.
VERBOSE: [PRA-DEV]: LCM: [
Skip Set ]
[[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]: LCM: [
End Resource ] [[xSPContentDb]SPContentDB]
VERBOSE: [PRA-DEV]: LCM: [
End Set ]
VERBOSE: [PRA-DEV]: LCM: [
End Set ]
in 1.9210 seconds.
VERBOSE: Operation 'Invoke CimMethod' complete.
VERBOSE: Time taken for configuration job to complete is 2.003
seconds
PS E:\PSDemo5> Test-DscConfiguration -Path E:\PSDemo5\SPContentDBConfig
| fl
InDesiredState
: True
ResourcesInDesiredState
: {[xSPContentDb]SPContentDB}
ResourcesNotInDesiredState :
ReturnValue
: 0
PSComputerName
: localhost
No comments:
Post a Comment